blob: df872bbf24cb1ea48d33a5d10479b02950c39044 [file] [log] [blame]
John Stiles44e96be2020-08-31 13:16:04 -04001/*
2 * Copyright 2020 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SKSL_INLINER
9#define SKSL_INLINER
10
11#include <memory>
12#include <unordered_map>
13
Ethan Nicholas6f4eee22021-01-11 12:37:42 -050014#include "src/sksl/SkSLMangler.h"
John Stiles44e96be2020-08-31 13:16:04 -040015#include "src/sksl/ir/SkSLProgram.h"
John Stilesa5f3c312020-09-22 12:05:16 -040016#include "src/sksl/ir/SkSLVariableReference.h"
John Stiles44e96be2020-08-31 13:16:04 -040017
18namespace SkSL {
19
Ethan Nicholas7bd60432020-09-25 14:31:59 -040020class Block;
John Stiles44e96be2020-08-31 13:16:04 -040021class Context;
Ethan Nicholas1e9f7f32020-10-08 05:28:32 -040022class Expression;
23class FunctionCall;
Ethan Nicholas6f87de72020-10-26 15:06:46 -040024class FunctionDefinition;
John Stiles2d7973a2020-10-02 15:01:03 -040025struct InlineCandidate;
26struct InlineCandidateList;
Ethan Nicholas041fd0a2020-10-07 16:42:04 -040027class ModifiersPool;
Ethan Nicholas1e9f7f32020-10-08 05:28:32 -040028class Statement;
John Stiles44e96be2020-08-31 13:16:04 -040029class SymbolTable;
Ethan Nicholas041fd0a2020-10-07 16:42:04 -040030class Variable;
John Stiles44e96be2020-08-31 13:16:04 -040031
32/**
33 * Converts a FunctionCall in the IR to a set of statements to be injected ahead of the function
34 * call, and a replacement expression. Can also detect cases where inlining isn't cleanly possible
35 * (e.g. return statements nested inside of a loop construct). The inliner isn't able to guarantee
36 * identical-to-GLSL execution order if the inlined function has visible side effects.
37 */
38class Inliner {
39public:
John Stiles7b920442020-12-17 10:43:41 -050040 Inliner(const Context* context) : fContext(context) {}
John Stiles44e96be2020-08-31 13:16:04 -040041
John Stiles10d39d92021-05-04 16:13:14 -040042 void reset();
John Stiles44e96be2020-08-31 13:16:04 -040043
John Stiles915a38c2020-09-14 09:38:13 -040044 /** Inlines any eligible functions that are found. Returns true if any changes are made. */
Brian Osman0006ad02020-11-18 15:38:39 -050045 bool analyze(const std::vector<std::unique_ptr<ProgramElement>>& elements,
John Stiles78047582020-12-16 16:17:41 -050046 std::shared_ptr<SymbolTable> symbols,
Brian Osman0006ad02020-11-18 15:38:39 -050047 ProgramUsage* usage);
John Stiles93442622020-09-11 12:11:27 -040048
John Stiles44e96be2020-08-31 13:16:04 -040049private:
John Stilese41b4ee2020-09-28 12:28:16 -040050 using VariableRewriteMap = std::unordered_map<const Variable*, std::unique_ptr<Expression>>;
John Stiles44e96be2020-08-31 13:16:04 -040051
John Stiles77702f12020-12-17 14:38:56 -050052 enum class ReturnComplexity {
John Stilesc5ff4862020-12-22 13:47:05 -050053 kSingleSafeReturn,
John Stiles77702f12020-12-17 14:38:56 -050054 kScopedReturns,
55 kEarlyReturns,
56 };
57
John Stilesd1204642021-02-17 16:30:02 -050058 const Program::Settings& settings() const { return fContext->fConfig->fSettings; }
59
Brian Osman0006ad02020-11-18 15:38:39 -050060 void buildCandidateList(const std::vector<std::unique_ptr<ProgramElement>>& elements,
John Stiles78047582020-12-16 16:17:41 -050061 std::shared_ptr<SymbolTable> symbols, ProgramUsage* usage,
Brian Osman0006ad02020-11-18 15:38:39 -050062 InlineCandidateList* candidateList);
John Stiles2d7973a2020-10-02 15:01:03 -040063
John Stiles44e96be2020-08-31 13:16:04 -040064 std::unique_ptr<Expression> inlineExpression(int offset,
65 VariableRewriteMap* varMap,
John Stilesd7cc0932020-11-30 12:24:27 -050066 SymbolTable* symbolTableForExpression,
John Stiles44e96be2020-08-31 13:16:04 -040067 const Expression& expression);
68 std::unique_ptr<Statement> inlineStatement(int offset,
69 VariableRewriteMap* varMap,
70 SymbolTable* symbolTableForStatement,
John Stiles77702f12020-12-17 14:38:56 -050071 std::unique_ptr<Expression>* resultExpr,
72 ReturnComplexity returnComplexity,
Brian Osman3887a012020-09-30 13:22:27 -040073 const Statement& statement,
74 bool isBuiltinCode);
John Stiles44e96be2020-08-31 13:16:04 -040075
John Stiles77702f12020-12-17 14:38:56 -050076 /** Determines if a given function has multiple and/or early returns. */
77 static ReturnComplexity GetReturnComplexity(const FunctionDefinition& funcDef);
78
John Stiles2d7973a2020-10-02 15:01:03 -040079 using InlinabilityCache = std::unordered_map<const FunctionDeclaration*, bool>;
80 bool candidateCanBeInlined(const InlineCandidate& candidate, InlinabilityCache* cache);
81
John Stiles9b9415e2020-11-23 14:48:06 -050082 using FunctionSizeCache = std::unordered_map<const FunctionDeclaration*, int>;
83 int getFunctionSize(const FunctionDeclaration& fnDecl, FunctionSizeCache* cache);
84
85 /**
86 * Processes the passed-in FunctionCall expression. The FunctionCall expression should be
87 * replaced with `fReplacementExpr`. If non-null, `fInlinedBody` should be inserted immediately
88 * above the statement containing the inlined expression.
89 */
90 struct InlinedCall {
91 std::unique_ptr<Block> fInlinedBody;
92 std::unique_ptr<Expression> fReplacementExpr;
93 };
John Stiles78047582020-12-16 16:17:41 -050094 InlinedCall inlineCall(FunctionCall*,
95 std::shared_ptr<SymbolTable>,
John Stiles30fce9c2021-03-18 09:24:06 -040096 const ProgramUsage&,
John Stiles78047582020-12-16 16:17:41 -050097 const FunctionDeclaration* caller);
John Stiles9b9415e2020-11-23 14:48:06 -050098
John Stiles7b920442020-12-17 10:43:41 -050099 /** Creates a scratch variable for the inliner to use. */
100 struct InlineVariable {
101 const Variable* fVarSymbol;
102 std::unique_ptr<Statement> fVarDecl;
103 };
John Stiles769146f2021-09-15 21:47:00 -0400104 InlineVariable makeInlineVariable(skstd::string_view baseName,
John Stiles7b920442020-12-17 10:43:41 -0500105 const Type* type,
106 SymbolTable* symbolTable,
107 Modifiers modifiers,
108 bool isBuiltinCode,
109 std::unique_ptr<Expression>* initialValue);
110
John Stiles9b9415e2020-11-23 14:48:06 -0500111 /** Adds a scope to inlined bodies returned by `inlineCall`, if one is required. */
112 void ensureScopedBlocks(Statement* inlinedBody, Statement* parentStmt);
113
114 /** Checks whether inlining is viable for a FunctionCall, modulo recursion and function size. */
115 bool isSafeToInline(const FunctionDefinition* functionDef);
John Stiles2d7973a2020-10-02 15:01:03 -0400116
John Stiles10d39d92021-05-04 16:13:14 -0400117 ModifiersPool& modifiersPool() const { return *fContext->fModifiersPool; }
John Stilesf2872e62021-05-04 11:38:43 -0400118
John Stiles44e96be2020-08-31 13:16:04 -0400119 const Context* fContext = nullptr;
John Stiles031a7672020-11-13 16:13:18 -0500120 int fInlinedStatementCounter = 0;
John Stiles44e96be2020-08-31 13:16:04 -0400121};
122
123} // namespace SkSL
124
125#endif // SKSL_INLINER