/* * Copyright 2020 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SKSL_INLINER #define SKSL_INLINER #include #include #include "src/sksl/SkSLMangler.h" #include "src/sksl/ir/SkSLProgram.h" #include "src/sksl/ir/SkSLVariableReference.h" namespace SkSL { class Block; class Context; class Expression; class FunctionCall; class FunctionDefinition; struct InlineCandidate; struct InlineCandidateList; class ModifiersPool; class Statement; class SymbolTable; class Variable; /** * Converts a FunctionCall in the IR to a set of statements to be injected ahead of the function * call, and a replacement expression. Can also detect cases where inlining isn't cleanly possible * (e.g. return statements nested inside of a loop construct). The inliner isn't able to guarantee * identical-to-GLSL execution order if the inlined function has visible side effects. */ class Inliner { public: Inliner(const Context* context) : fContext(context) {} void reset(); /** Inlines any eligible functions that are found. Returns true if any changes are made. */ bool analyze(const std::vector>& elements, std::shared_ptr symbols, ProgramUsage* usage); private: using VariableRewriteMap = std::unordered_map>; enum class ReturnComplexity { kSingleSafeReturn, kScopedReturns, kEarlyReturns, }; const Program::Settings& settings() const { return fContext->fConfig->fSettings; } void buildCandidateList(const std::vector>& elements, std::shared_ptr symbols, ProgramUsage* usage, InlineCandidateList* candidateList); std::unique_ptr inlineExpression(int line, VariableRewriteMap* varMap, SymbolTable* symbolTableForExpression, const Expression& expression); std::unique_ptr inlineStatement(int line, VariableRewriteMap* varMap, SymbolTable* symbolTableForStatement, std::unique_ptr* resultExpr, ReturnComplexity returnComplexity, const Statement& statement, bool isBuiltinCode); /** * Searches the rewrite map for an rewritten Variable* for the passed-in one. Asserts if the * rewrite map doesn't contain the variable, or contains a different type of expression. */ static const Variable* RemapVariable(const Variable* variable, const VariableRewriteMap* varMap); /** Determines if a given function has multiple and/or early returns. */ static ReturnComplexity GetReturnComplexity(const FunctionDefinition& funcDef); using InlinabilityCache = std::unordered_map; bool candidateCanBeInlined(const InlineCandidate& candidate, const ProgramUsage& usage, InlinabilityCache* cache); using FunctionSizeCache = std::unordered_map; int getFunctionSize(const FunctionDeclaration& fnDecl, FunctionSizeCache* cache); /** * Processes the passed-in FunctionCall expression. The FunctionCall expression should be * replaced with `fReplacementExpr`. If non-null, `fInlinedBody` should be inserted immediately * above the statement containing the inlined expression. */ struct InlinedCall { std::unique_ptr fInlinedBody; std::unique_ptr fReplacementExpr; }; InlinedCall inlineCall(FunctionCall*, std::shared_ptr, const ProgramUsage&, const FunctionDeclaration* caller); /** Adds a scope to inlined bodies returned by `inlineCall`, if one is required. */ void ensureScopedBlocks(Statement* inlinedBody, Statement* parentStmt); /** Checks whether inlining is viable for a FunctionCall, modulo recursion and function size. */ bool isSafeToInline(const FunctionDefinition* functionDef, const ProgramUsage& usage); const Context* fContext = nullptr; int fInlinedStatementCounter = 0; }; } // namespace SkSL #endif // SKSL_INLINER