/* * 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 #ifndef SK_ENABLE_OPTIMIZE_SIZE #include "src/core/SkTHash.h" #include "src/sksl/SkSLContext.h" #include "src/sksl/SkSLMangler.h" #include "src/sksl/SkSLProgramSettings.h" #include "src/sksl/ir/SkSLBlock.h" #include "src/sksl/ir/SkSLExpression.h" #include #include namespace SkSL { class FunctionCall; class FunctionDeclaration; class FunctionDefinition; class Position; class ProgramElement; class ProgramUsage; class Statement; class SymbolTable; class Variable; struct InlineCandidate; struct InlineCandidateList; namespace Analysis { enum class ReturnComplexity; } /** * 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) {} /** Inlines any eligible functions that are found. Returns true if any changes are made. */ bool analyze(const std::vector>& elements, SymbolTable* symbols, ProgramUsage* usage); private: using VariableRewriteMap = skia_private::THashMap>; const ProgramSettings& settings() const { return fContext->fConfig->fSettings; } void buildCandidateList(const std::vector>& elements, SymbolTable* symbols, ProgramUsage* usage, InlineCandidateList* candidateList); std::unique_ptr inlineExpression(Position pos, VariableRewriteMap* varMap, SymbolTable* symbolTableForExpression, const Expression& expression); std::unique_ptr inlineStatement(Position pos, VariableRewriteMap* varMap, SymbolTable* symbolTableForStatement, std::unique_ptr* resultExpr, Analysis::ReturnComplexity returnComplexity, const Statement& statement, const ProgramUsage& usage, 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); using InlinabilityCache = skia_private::THashMap; bool candidateCanBeInlined(const InlineCandidate& candidate, const ProgramUsage& usage, InlinabilityCache* cache); bool functionCanBeInlined(const FunctionDeclaration& funcDecl, const ProgramUsage& usage, InlinabilityCache* cache); using FunctionSizeCache = skia_private::THashMap; 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(const FunctionCall&, SymbolTable*, 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; Mangler fMangler; int fInlinedStatementCounter = 0; }; } // namespace SkSL #endif // SK_ENABLE_OPTIMIZE_SIZE #endif // SKSL_INLINER