1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2020 Google LLC 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/sksl/SkSLInliner.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include <limits.h> 11cb93a386Sopenharmony_ci#include <memory> 12cb93a386Sopenharmony_ci#include <unordered_set> 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ci#include "include/private/SkSLLayout.h" 15cb93a386Sopenharmony_ci#include "src/sksl/analysis/SkSLProgramVisitor.h" 16cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLBinaryExpression.h" 17cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLBreakStatement.h" 18cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLChildCall.h" 19cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructor.h" 20cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorArray.h" 21cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorArrayCast.h" 22cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorCompound.h" 23cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorCompoundCast.h" 24cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h" 25cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorMatrixResize.h" 26cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorScalarCast.h" 27cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorSplat.h" 28cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorStruct.h" 29cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLContinueStatement.h" 30cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLDiscardStatement.h" 31cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLDoStatement.h" 32cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLExpressionStatement.h" 33cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLExternalFunctionCall.h" 34cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLExternalFunctionReference.h" 35cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLField.h" 36cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLFieldAccess.h" 37cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLForStatement.h" 38cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLFunctionCall.h" 39cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLFunctionDeclaration.h" 40cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLFunctionDefinition.h" 41cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLFunctionReference.h" 42cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLIfStatement.h" 43cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLIndexExpression.h" 44cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLInlineMarker.h" 45cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLInterfaceBlock.h" 46cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLLiteral.h" 47cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLNop.h" 48cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLPostfixExpression.h" 49cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLPrefixExpression.h" 50cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLReturnStatement.h" 51cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLSetting.h" 52cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLSwitchCase.h" 53cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLSwitchStatement.h" 54cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLSwizzle.h" 55cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLTernaryExpression.h" 56cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLUnresolvedFunction.h" 57cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLVarDeclarations.h" 58cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLVariable.h" 59cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLVariableReference.h" 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_cinamespace SkSL { 62cb93a386Sopenharmony_cinamespace { 63cb93a386Sopenharmony_ci 64cb93a386Sopenharmony_cistatic constexpr int kInlinedStatementLimit = 2500; 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_cistatic int count_returns_at_end_of_control_flow(const FunctionDefinition& funcDef) { 67cb93a386Sopenharmony_ci class CountReturnsAtEndOfControlFlow : public ProgramVisitor { 68cb93a386Sopenharmony_ci public: 69cb93a386Sopenharmony_ci CountReturnsAtEndOfControlFlow(const FunctionDefinition& funcDef) { 70cb93a386Sopenharmony_ci this->visitProgramElement(funcDef); 71cb93a386Sopenharmony_ci } 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ci bool visitExpression(const Expression& expr) override { 74cb93a386Sopenharmony_ci // Do not recurse into expressions. 75cb93a386Sopenharmony_ci return false; 76cb93a386Sopenharmony_ci } 77cb93a386Sopenharmony_ci 78cb93a386Sopenharmony_ci bool visitStatement(const Statement& stmt) override { 79cb93a386Sopenharmony_ci switch (stmt.kind()) { 80cb93a386Sopenharmony_ci case Statement::Kind::kBlock: { 81cb93a386Sopenharmony_ci // Check only the last statement of a block. 82cb93a386Sopenharmony_ci const auto& block = stmt.as<Block>(); 83cb93a386Sopenharmony_ci return block.children().size() && 84cb93a386Sopenharmony_ci this->visitStatement(*block.children().back()); 85cb93a386Sopenharmony_ci } 86cb93a386Sopenharmony_ci case Statement::Kind::kSwitch: 87cb93a386Sopenharmony_ci case Statement::Kind::kDo: 88cb93a386Sopenharmony_ci case Statement::Kind::kFor: 89cb93a386Sopenharmony_ci // Don't introspect switches or loop structures at all. 90cb93a386Sopenharmony_ci return false; 91cb93a386Sopenharmony_ci 92cb93a386Sopenharmony_ci case Statement::Kind::kReturn: 93cb93a386Sopenharmony_ci ++fNumReturns; 94cb93a386Sopenharmony_ci [[fallthrough]]; 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_ci default: 97cb93a386Sopenharmony_ci return INHERITED::visitStatement(stmt); 98cb93a386Sopenharmony_ci } 99cb93a386Sopenharmony_ci } 100cb93a386Sopenharmony_ci 101cb93a386Sopenharmony_ci int fNumReturns = 0; 102cb93a386Sopenharmony_ci using INHERITED = ProgramVisitor; 103cb93a386Sopenharmony_ci }; 104cb93a386Sopenharmony_ci 105cb93a386Sopenharmony_ci return CountReturnsAtEndOfControlFlow{funcDef}.fNumReturns; 106cb93a386Sopenharmony_ci} 107cb93a386Sopenharmony_ci 108cb93a386Sopenharmony_cistatic bool contains_recursive_call(const FunctionDeclaration& funcDecl) { 109cb93a386Sopenharmony_ci class ContainsRecursiveCall : public ProgramVisitor { 110cb93a386Sopenharmony_ci public: 111cb93a386Sopenharmony_ci bool visit(const FunctionDeclaration& funcDecl) { 112cb93a386Sopenharmony_ci fFuncDecl = &funcDecl; 113cb93a386Sopenharmony_ci return funcDecl.definition() ? this->visitProgramElement(*funcDecl.definition()) 114cb93a386Sopenharmony_ci : false; 115cb93a386Sopenharmony_ci } 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci bool visitExpression(const Expression& expr) override { 118cb93a386Sopenharmony_ci if (expr.is<FunctionCall>() && expr.as<FunctionCall>().function().matches(*fFuncDecl)) { 119cb93a386Sopenharmony_ci return true; 120cb93a386Sopenharmony_ci } 121cb93a386Sopenharmony_ci return INHERITED::visitExpression(expr); 122cb93a386Sopenharmony_ci } 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci bool visitStatement(const Statement& stmt) override { 125cb93a386Sopenharmony_ci if (stmt.is<InlineMarker>() && 126cb93a386Sopenharmony_ci stmt.as<InlineMarker>().function().matches(*fFuncDecl)) { 127cb93a386Sopenharmony_ci return true; 128cb93a386Sopenharmony_ci } 129cb93a386Sopenharmony_ci return INHERITED::visitStatement(stmt); 130cb93a386Sopenharmony_ci } 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci const FunctionDeclaration* fFuncDecl; 133cb93a386Sopenharmony_ci using INHERITED = ProgramVisitor; 134cb93a386Sopenharmony_ci }; 135cb93a386Sopenharmony_ci 136cb93a386Sopenharmony_ci return ContainsRecursiveCall{}.visit(funcDecl); 137cb93a386Sopenharmony_ci} 138cb93a386Sopenharmony_ci 139cb93a386Sopenharmony_cistatic std::unique_ptr<Statement>* find_parent_statement( 140cb93a386Sopenharmony_ci const std::vector<std::unique_ptr<Statement>*>& stmtStack) { 141cb93a386Sopenharmony_ci SkASSERT(!stmtStack.empty()); 142cb93a386Sopenharmony_ci 143cb93a386Sopenharmony_ci // Walk the statement stack from back to front, ignoring the last element (which is the 144cb93a386Sopenharmony_ci // enclosing statement). 145cb93a386Sopenharmony_ci auto iter = stmtStack.rbegin(); 146cb93a386Sopenharmony_ci ++iter; 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci // Anything counts as a parent statement other than a scopeless Block. 149cb93a386Sopenharmony_ci for (; iter != stmtStack.rend(); ++iter) { 150cb93a386Sopenharmony_ci std::unique_ptr<Statement>* stmt = *iter; 151cb93a386Sopenharmony_ci if (!(*stmt)->is<Block>() || (*stmt)->as<Block>().isScope()) { 152cb93a386Sopenharmony_ci return stmt; 153cb93a386Sopenharmony_ci } 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci 156cb93a386Sopenharmony_ci // There wasn't any parent statement to be found. 157cb93a386Sopenharmony_ci return nullptr; 158cb93a386Sopenharmony_ci} 159cb93a386Sopenharmony_ci 160cb93a386Sopenharmony_cistd::unique_ptr<Expression> clone_with_ref_kind(const Expression& expr, 161cb93a386Sopenharmony_ci VariableReference::RefKind refKind) { 162cb93a386Sopenharmony_ci std::unique_ptr<Expression> clone = expr.clone(); 163cb93a386Sopenharmony_ci Analysis::UpdateVariableRefKind(clone.get(), refKind); 164cb93a386Sopenharmony_ci return clone; 165cb93a386Sopenharmony_ci} 166cb93a386Sopenharmony_ci 167cb93a386Sopenharmony_ciclass CountReturnsWithLimit : public ProgramVisitor { 168cb93a386Sopenharmony_cipublic: 169cb93a386Sopenharmony_ci CountReturnsWithLimit(const FunctionDefinition& funcDef, int limit) : fLimit(limit) { 170cb93a386Sopenharmony_ci this->visitProgramElement(funcDef); 171cb93a386Sopenharmony_ci } 172cb93a386Sopenharmony_ci 173cb93a386Sopenharmony_ci bool visitExpression(const Expression& expr) override { 174cb93a386Sopenharmony_ci // Do not recurse into expressions. 175cb93a386Sopenharmony_ci return false; 176cb93a386Sopenharmony_ci } 177cb93a386Sopenharmony_ci 178cb93a386Sopenharmony_ci bool visitStatement(const Statement& stmt) override { 179cb93a386Sopenharmony_ci switch (stmt.kind()) { 180cb93a386Sopenharmony_ci case Statement::Kind::kReturn: { 181cb93a386Sopenharmony_ci ++fNumReturns; 182cb93a386Sopenharmony_ci fDeepestReturn = std::max(fDeepestReturn, fScopedBlockDepth); 183cb93a386Sopenharmony_ci return (fNumReturns >= fLimit) || INHERITED::visitStatement(stmt); 184cb93a386Sopenharmony_ci } 185cb93a386Sopenharmony_ci case Statement::Kind::kVarDeclaration: { 186cb93a386Sopenharmony_ci if (fScopedBlockDepth > 1) { 187cb93a386Sopenharmony_ci fVariablesInBlocks = true; 188cb93a386Sopenharmony_ci } 189cb93a386Sopenharmony_ci return INHERITED::visitStatement(stmt); 190cb93a386Sopenharmony_ci } 191cb93a386Sopenharmony_ci case Statement::Kind::kBlock: { 192cb93a386Sopenharmony_ci int depthIncrement = stmt.as<Block>().isScope() ? 1 : 0; 193cb93a386Sopenharmony_ci fScopedBlockDepth += depthIncrement; 194cb93a386Sopenharmony_ci bool result = INHERITED::visitStatement(stmt); 195cb93a386Sopenharmony_ci fScopedBlockDepth -= depthIncrement; 196cb93a386Sopenharmony_ci if (fNumReturns == 0 && fScopedBlockDepth <= 1) { 197cb93a386Sopenharmony_ci // If closing this block puts us back at the top level, and we haven't 198cb93a386Sopenharmony_ci // encountered any return statements yet, any vardecls we may have encountered 199cb93a386Sopenharmony_ci // up until this point can be ignored. They are out of scope now, and they were 200cb93a386Sopenharmony_ci // never used in a return statement. 201cb93a386Sopenharmony_ci fVariablesInBlocks = false; 202cb93a386Sopenharmony_ci } 203cb93a386Sopenharmony_ci return result; 204cb93a386Sopenharmony_ci } 205cb93a386Sopenharmony_ci default: 206cb93a386Sopenharmony_ci return INHERITED::visitStatement(stmt); 207cb93a386Sopenharmony_ci } 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci 210cb93a386Sopenharmony_ci int fNumReturns = 0; 211cb93a386Sopenharmony_ci int fDeepestReturn = 0; 212cb93a386Sopenharmony_ci int fLimit = 0; 213cb93a386Sopenharmony_ci int fScopedBlockDepth = 0; 214cb93a386Sopenharmony_ci bool fVariablesInBlocks = false; 215cb93a386Sopenharmony_ci using INHERITED = ProgramVisitor; 216cb93a386Sopenharmony_ci}; 217cb93a386Sopenharmony_ci 218cb93a386Sopenharmony_ci} // namespace 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_ciconst Variable* Inliner::RemapVariable(const Variable* variable, 221cb93a386Sopenharmony_ci const VariableRewriteMap* varMap) { 222cb93a386Sopenharmony_ci auto iter = varMap->find(variable); 223cb93a386Sopenharmony_ci if (iter == varMap->end()) { 224cb93a386Sopenharmony_ci SkDEBUGFAILF("rewrite map does not contain variable '%.*s'", 225cb93a386Sopenharmony_ci (int)variable->name().size(), variable->name().data()); 226cb93a386Sopenharmony_ci return variable; 227cb93a386Sopenharmony_ci } 228cb93a386Sopenharmony_ci Expression* expr = iter->second.get(); 229cb93a386Sopenharmony_ci SkASSERT(expr); 230cb93a386Sopenharmony_ci if (!expr->is<VariableReference>()) { 231cb93a386Sopenharmony_ci SkDEBUGFAILF("rewrite map contains non-variable replacement for '%.*s'", 232cb93a386Sopenharmony_ci (int)variable->name().size(), variable->name().data()); 233cb93a386Sopenharmony_ci return variable; 234cb93a386Sopenharmony_ci } 235cb93a386Sopenharmony_ci return expr->as<VariableReference>().variable(); 236cb93a386Sopenharmony_ci} 237cb93a386Sopenharmony_ci 238cb93a386Sopenharmony_ciInliner::ReturnComplexity Inliner::GetReturnComplexity(const FunctionDefinition& funcDef) { 239cb93a386Sopenharmony_ci int returnsAtEndOfControlFlow = count_returns_at_end_of_control_flow(funcDef); 240cb93a386Sopenharmony_ci CountReturnsWithLimit counter{funcDef, returnsAtEndOfControlFlow + 1}; 241cb93a386Sopenharmony_ci if (counter.fNumReturns > returnsAtEndOfControlFlow) { 242cb93a386Sopenharmony_ci return ReturnComplexity::kEarlyReturns; 243cb93a386Sopenharmony_ci } 244cb93a386Sopenharmony_ci if (counter.fNumReturns > 1) { 245cb93a386Sopenharmony_ci return ReturnComplexity::kScopedReturns; 246cb93a386Sopenharmony_ci } 247cb93a386Sopenharmony_ci if (counter.fVariablesInBlocks && counter.fDeepestReturn > 1) { 248cb93a386Sopenharmony_ci return ReturnComplexity::kScopedReturns; 249cb93a386Sopenharmony_ci } 250cb93a386Sopenharmony_ci return ReturnComplexity::kSingleSafeReturn; 251cb93a386Sopenharmony_ci} 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_civoid Inliner::ensureScopedBlocks(Statement* inlinedBody, Statement* parentStmt) { 254cb93a386Sopenharmony_ci // No changes necessary if this statement isn't actually a block. 255cb93a386Sopenharmony_ci if (!inlinedBody || !inlinedBody->is<Block>()) { 256cb93a386Sopenharmony_ci return; 257cb93a386Sopenharmony_ci } 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ci // No changes necessary if the parent statement doesn't require a scope. 260cb93a386Sopenharmony_ci if (!parentStmt || !(parentStmt->is<IfStatement>() || parentStmt->is<ForStatement>() || 261cb93a386Sopenharmony_ci parentStmt->is<DoStatement>())) { 262cb93a386Sopenharmony_ci return; 263cb93a386Sopenharmony_ci } 264cb93a386Sopenharmony_ci 265cb93a386Sopenharmony_ci Block& block = inlinedBody->as<Block>(); 266cb93a386Sopenharmony_ci 267cb93a386Sopenharmony_ci // The inliner will create inlined function bodies as a Block containing multiple statements, 268cb93a386Sopenharmony_ci // but no scope. Normally, this is fine, but if this block is used as the statement for a 269cb93a386Sopenharmony_ci // do/for/if/while, this isn't actually possible to represent textually; a scope must be added 270cb93a386Sopenharmony_ci // for the generated code to match the intent. In the case of Blocks nested inside other Blocks, 271cb93a386Sopenharmony_ci // we add the scope to the outermost block if needed. Zero-statement blocks have similar 272cb93a386Sopenharmony_ci // issues--if we don't represent the Block textually somehow, we run the risk of accidentally 273cb93a386Sopenharmony_ci // absorbing the following statement into our loop--so we also add a scope to these. 274cb93a386Sopenharmony_ci for (Block* nestedBlock = █; ) { 275cb93a386Sopenharmony_ci if (nestedBlock->isScope()) { 276cb93a386Sopenharmony_ci // We found an explicit scope; all is well. 277cb93a386Sopenharmony_ci return; 278cb93a386Sopenharmony_ci } 279cb93a386Sopenharmony_ci if (nestedBlock->children().size() != 1) { 280cb93a386Sopenharmony_ci // We found a block with multiple (or zero) statements, but no scope? Let's add a scope 281cb93a386Sopenharmony_ci // to the outermost block. 282cb93a386Sopenharmony_ci block.setIsScope(true); 283cb93a386Sopenharmony_ci return; 284cb93a386Sopenharmony_ci } 285cb93a386Sopenharmony_ci if (!nestedBlock->children()[0]->is<Block>()) { 286cb93a386Sopenharmony_ci // This block has exactly one thing inside, and it's not another block. No need to scope 287cb93a386Sopenharmony_ci // it. 288cb93a386Sopenharmony_ci return; 289cb93a386Sopenharmony_ci } 290cb93a386Sopenharmony_ci // We have to go deeper. 291cb93a386Sopenharmony_ci nestedBlock = &nestedBlock->children()[0]->as<Block>(); 292cb93a386Sopenharmony_ci } 293cb93a386Sopenharmony_ci} 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_civoid Inliner::reset() { 296cb93a386Sopenharmony_ci fContext->fMangler->reset(); 297cb93a386Sopenharmony_ci fInlinedStatementCounter = 0; 298cb93a386Sopenharmony_ci} 299cb93a386Sopenharmony_ci 300cb93a386Sopenharmony_cistd::unique_ptr<Expression> Inliner::inlineExpression(int line, 301cb93a386Sopenharmony_ci VariableRewriteMap* varMap, 302cb93a386Sopenharmony_ci SymbolTable* symbolTableForExpression, 303cb93a386Sopenharmony_ci const Expression& expression) { 304cb93a386Sopenharmony_ci auto expr = [&](const std::unique_ptr<Expression>& e) -> std::unique_ptr<Expression> { 305cb93a386Sopenharmony_ci if (e) { 306cb93a386Sopenharmony_ci return this->inlineExpression(line, varMap, symbolTableForExpression, *e); 307cb93a386Sopenharmony_ci } 308cb93a386Sopenharmony_ci return nullptr; 309cb93a386Sopenharmony_ci }; 310cb93a386Sopenharmony_ci auto argList = [&](const ExpressionArray& originalArgs) -> ExpressionArray { 311cb93a386Sopenharmony_ci ExpressionArray args; 312cb93a386Sopenharmony_ci args.reserve_back(originalArgs.size()); 313cb93a386Sopenharmony_ci for (const std::unique_ptr<Expression>& arg : originalArgs) { 314cb93a386Sopenharmony_ci args.push_back(expr(arg)); 315cb93a386Sopenharmony_ci } 316cb93a386Sopenharmony_ci return args; 317cb93a386Sopenharmony_ci }; 318cb93a386Sopenharmony_ci 319cb93a386Sopenharmony_ci switch (expression.kind()) { 320cb93a386Sopenharmony_ci case Expression::Kind::kBinary: { 321cb93a386Sopenharmony_ci const BinaryExpression& binaryExpr = expression.as<BinaryExpression>(); 322cb93a386Sopenharmony_ci return BinaryExpression::Make(*fContext, 323cb93a386Sopenharmony_ci expr(binaryExpr.left()), 324cb93a386Sopenharmony_ci binaryExpr.getOperator(), 325cb93a386Sopenharmony_ci expr(binaryExpr.right())); 326cb93a386Sopenharmony_ci } 327cb93a386Sopenharmony_ci case Expression::Kind::kLiteral: 328cb93a386Sopenharmony_ci return expression.clone(); 329cb93a386Sopenharmony_ci case Expression::Kind::kChildCall: { 330cb93a386Sopenharmony_ci const ChildCall& childCall = expression.as<ChildCall>(); 331cb93a386Sopenharmony_ci return ChildCall::Make(*fContext, 332cb93a386Sopenharmony_ci line, 333cb93a386Sopenharmony_ci childCall.type().clone(symbolTableForExpression), 334cb93a386Sopenharmony_ci childCall.child(), 335cb93a386Sopenharmony_ci argList(childCall.arguments())); 336cb93a386Sopenharmony_ci } 337cb93a386Sopenharmony_ci case Expression::Kind::kConstructorArray: { 338cb93a386Sopenharmony_ci const ConstructorArray& ctor = expression.as<ConstructorArray>(); 339cb93a386Sopenharmony_ci return ConstructorArray::Make(*fContext, line, 340cb93a386Sopenharmony_ci *ctor.type().clone(symbolTableForExpression), 341cb93a386Sopenharmony_ci argList(ctor.arguments())); 342cb93a386Sopenharmony_ci } 343cb93a386Sopenharmony_ci case Expression::Kind::kConstructorArrayCast: { 344cb93a386Sopenharmony_ci const ConstructorArrayCast& ctor = expression.as<ConstructorArrayCast>(); 345cb93a386Sopenharmony_ci return ConstructorArrayCast::Make(*fContext, line, 346cb93a386Sopenharmony_ci *ctor.type().clone(symbolTableForExpression), 347cb93a386Sopenharmony_ci expr(ctor.argument())); 348cb93a386Sopenharmony_ci } 349cb93a386Sopenharmony_ci case Expression::Kind::kConstructorCompound: { 350cb93a386Sopenharmony_ci const ConstructorCompound& ctor = expression.as<ConstructorCompound>(); 351cb93a386Sopenharmony_ci return ConstructorCompound::Make(*fContext, line, 352cb93a386Sopenharmony_ci *ctor.type().clone(symbolTableForExpression), 353cb93a386Sopenharmony_ci argList(ctor.arguments())); 354cb93a386Sopenharmony_ci } 355cb93a386Sopenharmony_ci case Expression::Kind::kConstructorCompoundCast: { 356cb93a386Sopenharmony_ci const ConstructorCompoundCast& ctor = expression.as<ConstructorCompoundCast>(); 357cb93a386Sopenharmony_ci return ConstructorCompoundCast::Make(*fContext, line, 358cb93a386Sopenharmony_ci *ctor.type().clone(symbolTableForExpression), 359cb93a386Sopenharmony_ci expr(ctor.argument())); 360cb93a386Sopenharmony_ci } 361cb93a386Sopenharmony_ci case Expression::Kind::kConstructorDiagonalMatrix: { 362cb93a386Sopenharmony_ci const ConstructorDiagonalMatrix& ctor = expression.as<ConstructorDiagonalMatrix>(); 363cb93a386Sopenharmony_ci return ConstructorDiagonalMatrix::Make(*fContext, line, 364cb93a386Sopenharmony_ci *ctor.type().clone(symbolTableForExpression), 365cb93a386Sopenharmony_ci expr(ctor.argument())); 366cb93a386Sopenharmony_ci } 367cb93a386Sopenharmony_ci case Expression::Kind::kConstructorMatrixResize: { 368cb93a386Sopenharmony_ci const ConstructorMatrixResize& ctor = expression.as<ConstructorMatrixResize>(); 369cb93a386Sopenharmony_ci return ConstructorMatrixResize::Make(*fContext, line, 370cb93a386Sopenharmony_ci *ctor.type().clone(symbolTableForExpression), 371cb93a386Sopenharmony_ci expr(ctor.argument())); 372cb93a386Sopenharmony_ci } 373cb93a386Sopenharmony_ci case Expression::Kind::kConstructorScalarCast: { 374cb93a386Sopenharmony_ci const ConstructorScalarCast& ctor = expression.as<ConstructorScalarCast>(); 375cb93a386Sopenharmony_ci return ConstructorScalarCast::Make(*fContext, line, 376cb93a386Sopenharmony_ci *ctor.type().clone(symbolTableForExpression), 377cb93a386Sopenharmony_ci expr(ctor.argument())); 378cb93a386Sopenharmony_ci } 379cb93a386Sopenharmony_ci case Expression::Kind::kConstructorSplat: { 380cb93a386Sopenharmony_ci const ConstructorSplat& ctor = expression.as<ConstructorSplat>(); 381cb93a386Sopenharmony_ci return ConstructorSplat::Make(*fContext, line, 382cb93a386Sopenharmony_ci *ctor.type().clone(symbolTableForExpression), 383cb93a386Sopenharmony_ci expr(ctor.argument())); 384cb93a386Sopenharmony_ci } 385cb93a386Sopenharmony_ci case Expression::Kind::kConstructorStruct: { 386cb93a386Sopenharmony_ci const ConstructorStruct& ctor = expression.as<ConstructorStruct>(); 387cb93a386Sopenharmony_ci return ConstructorStruct::Make(*fContext, line, 388cb93a386Sopenharmony_ci *ctor.type().clone(symbolTableForExpression), 389cb93a386Sopenharmony_ci argList(ctor.arguments())); 390cb93a386Sopenharmony_ci } 391cb93a386Sopenharmony_ci case Expression::Kind::kExternalFunctionCall: { 392cb93a386Sopenharmony_ci const ExternalFunctionCall& externalCall = expression.as<ExternalFunctionCall>(); 393cb93a386Sopenharmony_ci return std::make_unique<ExternalFunctionCall>(line, &externalCall.function(), 394cb93a386Sopenharmony_ci argList(externalCall.arguments())); 395cb93a386Sopenharmony_ci } 396cb93a386Sopenharmony_ci case Expression::Kind::kExternalFunctionReference: 397cb93a386Sopenharmony_ci return expression.clone(); 398cb93a386Sopenharmony_ci case Expression::Kind::kFieldAccess: { 399cb93a386Sopenharmony_ci const FieldAccess& f = expression.as<FieldAccess>(); 400cb93a386Sopenharmony_ci return FieldAccess::Make(*fContext, expr(f.base()), f.fieldIndex(), f.ownerKind()); 401cb93a386Sopenharmony_ci } 402cb93a386Sopenharmony_ci case Expression::Kind::kFunctionCall: { 403cb93a386Sopenharmony_ci const FunctionCall& funcCall = expression.as<FunctionCall>(); 404cb93a386Sopenharmony_ci return FunctionCall::Make(*fContext, 405cb93a386Sopenharmony_ci line, 406cb93a386Sopenharmony_ci funcCall.type().clone(symbolTableForExpression), 407cb93a386Sopenharmony_ci funcCall.function(), 408cb93a386Sopenharmony_ci argList(funcCall.arguments())); 409cb93a386Sopenharmony_ci } 410cb93a386Sopenharmony_ci case Expression::Kind::kFunctionReference: 411cb93a386Sopenharmony_ci return expression.clone(); 412cb93a386Sopenharmony_ci case Expression::Kind::kIndex: { 413cb93a386Sopenharmony_ci const IndexExpression& idx = expression.as<IndexExpression>(); 414cb93a386Sopenharmony_ci return IndexExpression::Make(*fContext, expr(idx.base()), expr(idx.index())); 415cb93a386Sopenharmony_ci } 416cb93a386Sopenharmony_ci case Expression::Kind::kMethodReference: 417cb93a386Sopenharmony_ci return expression.clone(); 418cb93a386Sopenharmony_ci case Expression::Kind::kPrefix: { 419cb93a386Sopenharmony_ci const PrefixExpression& p = expression.as<PrefixExpression>(); 420cb93a386Sopenharmony_ci return PrefixExpression::Make(*fContext, p.getOperator(), expr(p.operand())); 421cb93a386Sopenharmony_ci } 422cb93a386Sopenharmony_ci case Expression::Kind::kPostfix: { 423cb93a386Sopenharmony_ci const PostfixExpression& p = expression.as<PostfixExpression>(); 424cb93a386Sopenharmony_ci return PostfixExpression::Make(*fContext, expr(p.operand()), p.getOperator()); 425cb93a386Sopenharmony_ci } 426cb93a386Sopenharmony_ci case Expression::Kind::kSetting: 427cb93a386Sopenharmony_ci return expression.clone(); 428cb93a386Sopenharmony_ci case Expression::Kind::kSwizzle: { 429cb93a386Sopenharmony_ci const Swizzle& s = expression.as<Swizzle>(); 430cb93a386Sopenharmony_ci return Swizzle::Make(*fContext, expr(s.base()), s.components()); 431cb93a386Sopenharmony_ci } 432cb93a386Sopenharmony_ci case Expression::Kind::kTernary: { 433cb93a386Sopenharmony_ci const TernaryExpression& t = expression.as<TernaryExpression>(); 434cb93a386Sopenharmony_ci return TernaryExpression::Make(*fContext, expr(t.test()), 435cb93a386Sopenharmony_ci expr(t.ifTrue()), expr(t.ifFalse())); 436cb93a386Sopenharmony_ci } 437cb93a386Sopenharmony_ci case Expression::Kind::kTypeReference: 438cb93a386Sopenharmony_ci return expression.clone(); 439cb93a386Sopenharmony_ci case Expression::Kind::kVariableReference: { 440cb93a386Sopenharmony_ci const VariableReference& v = expression.as<VariableReference>(); 441cb93a386Sopenharmony_ci auto varMapIter = varMap->find(v.variable()); 442cb93a386Sopenharmony_ci if (varMapIter != varMap->end()) { 443cb93a386Sopenharmony_ci return clone_with_ref_kind(*varMapIter->second, v.refKind()); 444cb93a386Sopenharmony_ci } 445cb93a386Sopenharmony_ci return v.clone(); 446cb93a386Sopenharmony_ci } 447cb93a386Sopenharmony_ci default: 448cb93a386Sopenharmony_ci SkASSERT(false); 449cb93a386Sopenharmony_ci return nullptr; 450cb93a386Sopenharmony_ci } 451cb93a386Sopenharmony_ci} 452cb93a386Sopenharmony_ci 453cb93a386Sopenharmony_cistd::unique_ptr<Statement> Inliner::inlineStatement(int line, 454cb93a386Sopenharmony_ci VariableRewriteMap* varMap, 455cb93a386Sopenharmony_ci SymbolTable* symbolTableForStatement, 456cb93a386Sopenharmony_ci std::unique_ptr<Expression>* resultExpr, 457cb93a386Sopenharmony_ci ReturnComplexity returnComplexity, 458cb93a386Sopenharmony_ci const Statement& statement, 459cb93a386Sopenharmony_ci bool isBuiltinCode) { 460cb93a386Sopenharmony_ci auto stmt = [&](const std::unique_ptr<Statement>& s) -> std::unique_ptr<Statement> { 461cb93a386Sopenharmony_ci if (s) { 462cb93a386Sopenharmony_ci return this->inlineStatement(line, varMap, symbolTableForStatement, resultExpr, 463cb93a386Sopenharmony_ci returnComplexity, *s, isBuiltinCode); 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci return nullptr; 466cb93a386Sopenharmony_ci }; 467cb93a386Sopenharmony_ci auto blockStmts = [&](const Block& block) { 468cb93a386Sopenharmony_ci StatementArray result; 469cb93a386Sopenharmony_ci result.reserve_back(block.children().size()); 470cb93a386Sopenharmony_ci for (const std::unique_ptr<Statement>& child : block.children()) { 471cb93a386Sopenharmony_ci result.push_back(stmt(child)); 472cb93a386Sopenharmony_ci } 473cb93a386Sopenharmony_ci return result; 474cb93a386Sopenharmony_ci }; 475cb93a386Sopenharmony_ci auto expr = [&](const std::unique_ptr<Expression>& e) -> std::unique_ptr<Expression> { 476cb93a386Sopenharmony_ci if (e) { 477cb93a386Sopenharmony_ci return this->inlineExpression(line, varMap, symbolTableForStatement, *e); 478cb93a386Sopenharmony_ci } 479cb93a386Sopenharmony_ci return nullptr; 480cb93a386Sopenharmony_ci }; 481cb93a386Sopenharmony_ci 482cb93a386Sopenharmony_ci ++fInlinedStatementCounter; 483cb93a386Sopenharmony_ci 484cb93a386Sopenharmony_ci switch (statement.kind()) { 485cb93a386Sopenharmony_ci case Statement::Kind::kBlock: { 486cb93a386Sopenharmony_ci const Block& b = statement.as<Block>(); 487cb93a386Sopenharmony_ci return Block::Make(line, blockStmts(b), 488cb93a386Sopenharmony_ci SymbolTable::WrapIfBuiltin(b.symbolTable()), 489cb93a386Sopenharmony_ci b.isScope()); 490cb93a386Sopenharmony_ci } 491cb93a386Sopenharmony_ci 492cb93a386Sopenharmony_ci case Statement::Kind::kBreak: 493cb93a386Sopenharmony_ci case Statement::Kind::kContinue: 494cb93a386Sopenharmony_ci case Statement::Kind::kDiscard: 495cb93a386Sopenharmony_ci return statement.clone(); 496cb93a386Sopenharmony_ci 497cb93a386Sopenharmony_ci case Statement::Kind::kDo: { 498cb93a386Sopenharmony_ci const DoStatement& d = statement.as<DoStatement>(); 499cb93a386Sopenharmony_ci return DoStatement::Make(*fContext, stmt(d.statement()), expr(d.test())); 500cb93a386Sopenharmony_ci } 501cb93a386Sopenharmony_ci case Statement::Kind::kExpression: { 502cb93a386Sopenharmony_ci const ExpressionStatement& e = statement.as<ExpressionStatement>(); 503cb93a386Sopenharmony_ci return ExpressionStatement::Make(*fContext, expr(e.expression())); 504cb93a386Sopenharmony_ci } 505cb93a386Sopenharmony_ci case Statement::Kind::kFor: { 506cb93a386Sopenharmony_ci const ForStatement& f = statement.as<ForStatement>(); 507cb93a386Sopenharmony_ci // need to ensure initializer is evaluated first so that we've already remapped its 508cb93a386Sopenharmony_ci // declarations by the time we evaluate test & next 509cb93a386Sopenharmony_ci std::unique_ptr<Statement> initializer = stmt(f.initializer()); 510cb93a386Sopenharmony_ci 511cb93a386Sopenharmony_ci std::unique_ptr<LoopUnrollInfo> unrollInfo; 512cb93a386Sopenharmony_ci if (f.unrollInfo()) { 513cb93a386Sopenharmony_ci // The for loop's unroll-info points to the Variable in the initializer as the 514cb93a386Sopenharmony_ci // index. This variable has been rewritten into a clone by the inliner, so we need 515cb93a386Sopenharmony_ci // to update the loop-unroll info to point to the clone. 516cb93a386Sopenharmony_ci unrollInfo = std::make_unique<LoopUnrollInfo>(*f.unrollInfo()); 517cb93a386Sopenharmony_ci unrollInfo->fIndex = RemapVariable(unrollInfo->fIndex, varMap); 518cb93a386Sopenharmony_ci } 519cb93a386Sopenharmony_ci return ForStatement::Make(*fContext, line, std::move(initializer), expr(f.test()), 520cb93a386Sopenharmony_ci expr(f.next()), stmt(f.statement()), std::move(unrollInfo), 521cb93a386Sopenharmony_ci SymbolTable::WrapIfBuiltin(f.symbols())); 522cb93a386Sopenharmony_ci } 523cb93a386Sopenharmony_ci case Statement::Kind::kIf: { 524cb93a386Sopenharmony_ci const IfStatement& i = statement.as<IfStatement>(); 525cb93a386Sopenharmony_ci return IfStatement::Make(*fContext, line, i.isStatic(), expr(i.test()), 526cb93a386Sopenharmony_ci stmt(i.ifTrue()), stmt(i.ifFalse())); 527cb93a386Sopenharmony_ci } 528cb93a386Sopenharmony_ci case Statement::Kind::kInlineMarker: 529cb93a386Sopenharmony_ci case Statement::Kind::kNop: 530cb93a386Sopenharmony_ci return statement.clone(); 531cb93a386Sopenharmony_ci 532cb93a386Sopenharmony_ci case Statement::Kind::kReturn: { 533cb93a386Sopenharmony_ci const ReturnStatement& r = statement.as<ReturnStatement>(); 534cb93a386Sopenharmony_ci if (!r.expression()) { 535cb93a386Sopenharmony_ci // This function doesn't return a value. We won't inline functions with early 536cb93a386Sopenharmony_ci // returns, so a return statement is a no-op and can be treated as such. 537cb93a386Sopenharmony_ci return Nop::Make(); 538cb93a386Sopenharmony_ci } 539cb93a386Sopenharmony_ci 540cb93a386Sopenharmony_ci // If a function only contains a single return, and it doesn't reference variables from 541cb93a386Sopenharmony_ci // inside an Block's scope, we don't need to store the result in a variable at all. Just 542cb93a386Sopenharmony_ci // replace the function-call expression with the function's return expression. 543cb93a386Sopenharmony_ci SkASSERT(resultExpr); 544cb93a386Sopenharmony_ci if (returnComplexity <= ReturnComplexity::kSingleSafeReturn) { 545cb93a386Sopenharmony_ci *resultExpr = expr(r.expression()); 546cb93a386Sopenharmony_ci return Nop::Make(); 547cb93a386Sopenharmony_ci } 548cb93a386Sopenharmony_ci 549cb93a386Sopenharmony_ci // For more complex functions, assign their result into a variable. 550cb93a386Sopenharmony_ci SkASSERT(*resultExpr); 551cb93a386Sopenharmony_ci auto assignment = ExpressionStatement::Make( 552cb93a386Sopenharmony_ci *fContext, 553cb93a386Sopenharmony_ci BinaryExpression::Make( 554cb93a386Sopenharmony_ci *fContext, 555cb93a386Sopenharmony_ci clone_with_ref_kind(**resultExpr, VariableRefKind::kWrite), 556cb93a386Sopenharmony_ci Token::Kind::TK_EQ, 557cb93a386Sopenharmony_ci expr(r.expression()))); 558cb93a386Sopenharmony_ci 559cb93a386Sopenharmony_ci // Functions without early returns aren't wrapped in a for loop and don't need to worry 560cb93a386Sopenharmony_ci // about breaking out of the control flow. 561cb93a386Sopenharmony_ci return assignment; 562cb93a386Sopenharmony_ci } 563cb93a386Sopenharmony_ci case Statement::Kind::kSwitch: { 564cb93a386Sopenharmony_ci const SwitchStatement& ss = statement.as<SwitchStatement>(); 565cb93a386Sopenharmony_ci StatementArray cases; 566cb93a386Sopenharmony_ci cases.reserve_back(ss.cases().size()); 567cb93a386Sopenharmony_ci for (const std::unique_ptr<Statement>& switchCaseStmt : ss.cases()) { 568cb93a386Sopenharmony_ci const SwitchCase& sc = switchCaseStmt->as<SwitchCase>(); 569cb93a386Sopenharmony_ci cases.push_back(std::make_unique<SwitchCase>(line, expr(sc.value()), 570cb93a386Sopenharmony_ci stmt(sc.statement()))); 571cb93a386Sopenharmony_ci } 572cb93a386Sopenharmony_ci return SwitchStatement::Make(*fContext, line, ss.isStatic(), expr(ss.value()), 573cb93a386Sopenharmony_ci std::move(cases), SymbolTable::WrapIfBuiltin(ss.symbols())); 574cb93a386Sopenharmony_ci } 575cb93a386Sopenharmony_ci case Statement::Kind::kVarDeclaration: { 576cb93a386Sopenharmony_ci const VarDeclaration& decl = statement.as<VarDeclaration>(); 577cb93a386Sopenharmony_ci std::unique_ptr<Expression> initialValue = expr(decl.value()); 578cb93a386Sopenharmony_ci const Variable& variable = decl.var(); 579cb93a386Sopenharmony_ci 580cb93a386Sopenharmony_ci // We assign unique names to inlined variables--scopes hide most of the problems in this 581cb93a386Sopenharmony_ci // regard, but see `InlinerAvoidsVariableNameOverlap` for a counterexample where unique 582cb93a386Sopenharmony_ci // names are important. 583cb93a386Sopenharmony_ci const String* name = symbolTableForStatement->takeOwnershipOfString( 584cb93a386Sopenharmony_ci fContext->fMangler->uniqueName(variable.name(), symbolTableForStatement)); 585cb93a386Sopenharmony_ci auto clonedVar = std::make_unique<Variable>( 586cb93a386Sopenharmony_ci line, 587cb93a386Sopenharmony_ci &variable.modifiers(), 588cb93a386Sopenharmony_ci name->c_str(), 589cb93a386Sopenharmony_ci variable.type().clone(symbolTableForStatement), 590cb93a386Sopenharmony_ci isBuiltinCode, 591cb93a386Sopenharmony_ci variable.storage()); 592cb93a386Sopenharmony_ci (*varMap)[&variable] = VariableReference::Make(line, clonedVar.get()); 593cb93a386Sopenharmony_ci auto result = VarDeclaration::Make(*fContext, 594cb93a386Sopenharmony_ci clonedVar.get(), 595cb93a386Sopenharmony_ci decl.baseType().clone(symbolTableForStatement), 596cb93a386Sopenharmony_ci decl.arraySize(), 597cb93a386Sopenharmony_ci std::move(initialValue)); 598cb93a386Sopenharmony_ci symbolTableForStatement->takeOwnershipOfSymbol(std::move(clonedVar)); 599cb93a386Sopenharmony_ci return result; 600cb93a386Sopenharmony_ci } 601cb93a386Sopenharmony_ci default: 602cb93a386Sopenharmony_ci SkASSERT(false); 603cb93a386Sopenharmony_ci return nullptr; 604cb93a386Sopenharmony_ci } 605cb93a386Sopenharmony_ci} 606cb93a386Sopenharmony_ci 607cb93a386Sopenharmony_ciInliner::InlinedCall Inliner::inlineCall(FunctionCall* call, 608cb93a386Sopenharmony_ci std::shared_ptr<SymbolTable> symbolTable, 609cb93a386Sopenharmony_ci const ProgramUsage& usage, 610cb93a386Sopenharmony_ci const FunctionDeclaration* caller) { 611cb93a386Sopenharmony_ci using ScratchVariable = Variable::ScratchVariable; 612cb93a386Sopenharmony_ci 613cb93a386Sopenharmony_ci // Inlining is more complicated here than in a typical compiler, because we have to have a 614cb93a386Sopenharmony_ci // high-level IR and can't just drop statements into the middle of an expression or even use 615cb93a386Sopenharmony_ci // gotos. 616cb93a386Sopenharmony_ci // 617cb93a386Sopenharmony_ci // Since we can't insert statements into an expression, we run the inline function as extra 618cb93a386Sopenharmony_ci // statements before the statement we're currently processing, relying on a lack of execution 619cb93a386Sopenharmony_ci // order guarantees. Since we can't use gotos (which are normally used to replace return 620cb93a386Sopenharmony_ci // statements), we wrap the whole function in a loop and use break statements to jump to the 621cb93a386Sopenharmony_ci // end. 622cb93a386Sopenharmony_ci SkASSERT(fContext); 623cb93a386Sopenharmony_ci SkASSERT(call); 624cb93a386Sopenharmony_ci SkASSERT(this->isSafeToInline(call->function().definition(), usage)); 625cb93a386Sopenharmony_ci 626cb93a386Sopenharmony_ci ExpressionArray& arguments = call->arguments(); 627cb93a386Sopenharmony_ci const int line = call->fLine; 628cb93a386Sopenharmony_ci const FunctionDefinition& function = *call->function().definition(); 629cb93a386Sopenharmony_ci const Block& body = function.body()->as<Block>(); 630cb93a386Sopenharmony_ci const ReturnComplexity returnComplexity = GetReturnComplexity(function); 631cb93a386Sopenharmony_ci 632cb93a386Sopenharmony_ci StatementArray inlineStatements; 633cb93a386Sopenharmony_ci int expectedStmtCount = 1 + // Inline marker 634cb93a386Sopenharmony_ci 1 + // Result variable 635cb93a386Sopenharmony_ci arguments.size() + // Function argument temp-vars 636cb93a386Sopenharmony_ci body.children().size(); // Inlined code 637cb93a386Sopenharmony_ci 638cb93a386Sopenharmony_ci inlineStatements.reserve_back(expectedStmtCount); 639cb93a386Sopenharmony_ci inlineStatements.push_back(InlineMarker::Make(&call->function())); 640cb93a386Sopenharmony_ci 641cb93a386Sopenharmony_ci std::unique_ptr<Expression> resultExpr; 642cb93a386Sopenharmony_ci if (returnComplexity > ReturnComplexity::kSingleSafeReturn && 643cb93a386Sopenharmony_ci !function.declaration().returnType().isVoid()) { 644cb93a386Sopenharmony_ci // Create a variable to hold the result in the extra statements. We don't need to do this 645cb93a386Sopenharmony_ci // for void-return functions, or in cases that are simple enough that we can just replace 646cb93a386Sopenharmony_ci // the function-call node with the result expression. 647cb93a386Sopenharmony_ci ScratchVariable var = Variable::MakeScratchVariable(*fContext, 648cb93a386Sopenharmony_ci function.declaration().name(), 649cb93a386Sopenharmony_ci &function.declaration().returnType(), 650cb93a386Sopenharmony_ci Modifiers{}, 651cb93a386Sopenharmony_ci symbolTable.get(), 652cb93a386Sopenharmony_ci /*initialValue=*/nullptr); 653cb93a386Sopenharmony_ci inlineStatements.push_back(std::move(var.fVarDecl)); 654cb93a386Sopenharmony_ci resultExpr = VariableReference::Make(/*line=*/-1, var.fVarSymbol); 655cb93a386Sopenharmony_ci } 656cb93a386Sopenharmony_ci 657cb93a386Sopenharmony_ci // Create variables in the extra statements to hold the arguments, and assign the arguments to 658cb93a386Sopenharmony_ci // them. 659cb93a386Sopenharmony_ci VariableRewriteMap varMap; 660cb93a386Sopenharmony_ci for (int i = 0; i < arguments.count(); ++i) { 661cb93a386Sopenharmony_ci // If the parameter isn't written to within the inline function ... 662cb93a386Sopenharmony_ci Expression* arg = arguments[i].get(); 663cb93a386Sopenharmony_ci const Variable* param = function.declaration().parameters()[i]; 664cb93a386Sopenharmony_ci const ProgramUsage::VariableCounts& paramUsage = usage.get(*param); 665cb93a386Sopenharmony_ci if (!paramUsage.fWrite) { 666cb93a386Sopenharmony_ci // ... and can be inlined trivially (e.g. a swizzle, or a constant array index), 667cb93a386Sopenharmony_ci // or any expression without side effects that is only accessed at most once... 668cb93a386Sopenharmony_ci if ((paramUsage.fRead > 1) ? Analysis::IsTrivialExpression(*arg) 669cb93a386Sopenharmony_ci : !arg->hasSideEffects()) { 670cb93a386Sopenharmony_ci // ... we don't need to copy it at all! We can just use the existing expression. 671cb93a386Sopenharmony_ci varMap[param] = arg->clone(); 672cb93a386Sopenharmony_ci continue; 673cb93a386Sopenharmony_ci } 674cb93a386Sopenharmony_ci } 675cb93a386Sopenharmony_ci ScratchVariable var = Variable::MakeScratchVariable(*fContext, 676cb93a386Sopenharmony_ci param->name(), 677cb93a386Sopenharmony_ci &arg->type(), 678cb93a386Sopenharmony_ci param->modifiers(), 679cb93a386Sopenharmony_ci symbolTable.get(), 680cb93a386Sopenharmony_ci std::move(arguments[i])); 681cb93a386Sopenharmony_ci inlineStatements.push_back(std::move(var.fVarDecl)); 682cb93a386Sopenharmony_ci varMap[param] = VariableReference::Make(/*line=*/-1, var.fVarSymbol); 683cb93a386Sopenharmony_ci } 684cb93a386Sopenharmony_ci 685cb93a386Sopenharmony_ci for (const std::unique_ptr<Statement>& stmt : body.children()) { 686cb93a386Sopenharmony_ci inlineStatements.push_back(this->inlineStatement(line, &varMap, symbolTable.get(), 687cb93a386Sopenharmony_ci &resultExpr, returnComplexity, *stmt, 688cb93a386Sopenharmony_ci caller->isBuiltin())); 689cb93a386Sopenharmony_ci } 690cb93a386Sopenharmony_ci 691cb93a386Sopenharmony_ci SkASSERT(inlineStatements.count() <= expectedStmtCount); 692cb93a386Sopenharmony_ci 693cb93a386Sopenharmony_ci // Wrap all of the generated statements in a block. We need a real Block here, so we can't use 694cb93a386Sopenharmony_ci // MakeUnscoped. This is because we need to add another child statement to the Block later. 695cb93a386Sopenharmony_ci InlinedCall inlinedCall; 696cb93a386Sopenharmony_ci inlinedCall.fInlinedBody = Block::Make(line, std::move(inlineStatements), 697cb93a386Sopenharmony_ci /*symbols=*/nullptr, /*isScope=*/false); 698cb93a386Sopenharmony_ci 699cb93a386Sopenharmony_ci if (resultExpr) { 700cb93a386Sopenharmony_ci // Return our result expression as-is. 701cb93a386Sopenharmony_ci inlinedCall.fReplacementExpr = std::move(resultExpr); 702cb93a386Sopenharmony_ci } else if (function.declaration().returnType().isVoid()) { 703cb93a386Sopenharmony_ci // It's a void function, so it doesn't actually result in anything, but we have to return 704cb93a386Sopenharmony_ci // something non-null as a standin. 705cb93a386Sopenharmony_ci inlinedCall.fReplacementExpr = Literal::MakeBool(*fContext, line, /*value=*/false); 706cb93a386Sopenharmony_ci } else { 707cb93a386Sopenharmony_ci // It's a non-void function, but it never created a result expression--that is, it never 708cb93a386Sopenharmony_ci // returned anything on any path! This should have been detected in the function finalizer. 709cb93a386Sopenharmony_ci // Still, discard our output and generate an error. 710cb93a386Sopenharmony_ci SkDEBUGFAIL("inliner found non-void function that fails to return a value on any path"); 711cb93a386Sopenharmony_ci fContext->fErrors->error(function.fLine, "inliner found non-void function '" + 712cb93a386Sopenharmony_ci function.declaration().name() + 713cb93a386Sopenharmony_ci "' that fails to return a value on any path"); 714cb93a386Sopenharmony_ci inlinedCall = {}; 715cb93a386Sopenharmony_ci } 716cb93a386Sopenharmony_ci 717cb93a386Sopenharmony_ci return inlinedCall; 718cb93a386Sopenharmony_ci} 719cb93a386Sopenharmony_ci 720cb93a386Sopenharmony_cibool Inliner::isSafeToInline(const FunctionDefinition* functionDef, const ProgramUsage& usage) { 721cb93a386Sopenharmony_ci // A threshold of zero indicates that the inliner is completely disabled, so we can just return. 722cb93a386Sopenharmony_ci if (this->settings().fInlineThreshold <= 0) { 723cb93a386Sopenharmony_ci return false; 724cb93a386Sopenharmony_ci } 725cb93a386Sopenharmony_ci 726cb93a386Sopenharmony_ci // Enforce a limit on inlining to avoid pathological cases. (inliner/ExponentialGrowth.sksl) 727cb93a386Sopenharmony_ci if (fInlinedStatementCounter >= kInlinedStatementLimit) { 728cb93a386Sopenharmony_ci return false; 729cb93a386Sopenharmony_ci } 730cb93a386Sopenharmony_ci 731cb93a386Sopenharmony_ci if (functionDef == nullptr) { 732cb93a386Sopenharmony_ci // Can't inline something if we don't actually have its definition. 733cb93a386Sopenharmony_ci return false; 734cb93a386Sopenharmony_ci } 735cb93a386Sopenharmony_ci 736cb93a386Sopenharmony_ci if (functionDef->declaration().modifiers().fFlags & Modifiers::kNoInline_Flag) { 737cb93a386Sopenharmony_ci // Refuse to inline functions decorated with `noinline`. 738cb93a386Sopenharmony_ci return false; 739cb93a386Sopenharmony_ci } 740cb93a386Sopenharmony_ci 741cb93a386Sopenharmony_ci // We don't allow inlining a function with out parameters that are written to. 742cb93a386Sopenharmony_ci // (See skia:11326 for rationale.) 743cb93a386Sopenharmony_ci for (const Variable* param : functionDef->declaration().parameters()) { 744cb93a386Sopenharmony_ci if (param->modifiers().fFlags & Modifiers::Flag::kOut_Flag) { 745cb93a386Sopenharmony_ci ProgramUsage::VariableCounts counts = usage.get(*param); 746cb93a386Sopenharmony_ci if (counts.fWrite > 0) { 747cb93a386Sopenharmony_ci return false; 748cb93a386Sopenharmony_ci } 749cb93a386Sopenharmony_ci } 750cb93a386Sopenharmony_ci } 751cb93a386Sopenharmony_ci 752cb93a386Sopenharmony_ci // We don't have a mechanism to simulate early returns, so we can't inline if there is one. 753cb93a386Sopenharmony_ci return GetReturnComplexity(*functionDef) < ReturnComplexity::kEarlyReturns; 754cb93a386Sopenharmony_ci} 755cb93a386Sopenharmony_ci 756cb93a386Sopenharmony_ci// A candidate function for inlining, containing everything that `inlineCall` needs. 757cb93a386Sopenharmony_cistruct InlineCandidate { 758cb93a386Sopenharmony_ci std::shared_ptr<SymbolTable> fSymbols; // the SymbolTable of the candidate 759cb93a386Sopenharmony_ci std::unique_ptr<Statement>* fParentStmt; // the parent Statement of the enclosing stmt 760cb93a386Sopenharmony_ci std::unique_ptr<Statement>* fEnclosingStmt; // the Statement containing the candidate 761cb93a386Sopenharmony_ci std::unique_ptr<Expression>* fCandidateExpr; // the candidate FunctionCall to be inlined 762cb93a386Sopenharmony_ci FunctionDefinition* fEnclosingFunction; // the Function containing the candidate 763cb93a386Sopenharmony_ci}; 764cb93a386Sopenharmony_ci 765cb93a386Sopenharmony_cistruct InlineCandidateList { 766cb93a386Sopenharmony_ci std::vector<InlineCandidate> fCandidates; 767cb93a386Sopenharmony_ci}; 768cb93a386Sopenharmony_ci 769cb93a386Sopenharmony_ciclass InlineCandidateAnalyzer { 770cb93a386Sopenharmony_cipublic: 771cb93a386Sopenharmony_ci // A list of all the inlining candidates we found during analysis. 772cb93a386Sopenharmony_ci InlineCandidateList* fCandidateList; 773cb93a386Sopenharmony_ci 774cb93a386Sopenharmony_ci // A stack of the symbol tables; since most nodes don't have one, expected to be shallower than 775cb93a386Sopenharmony_ci // the enclosing-statement stack. 776cb93a386Sopenharmony_ci std::vector<std::shared_ptr<SymbolTable>> fSymbolTableStack; 777cb93a386Sopenharmony_ci // A stack of "enclosing" statements--these would be suitable for the inliner to use for adding 778cb93a386Sopenharmony_ci // new instructions. Not all statements are suitable (e.g. a for-loop's initializer). The 779cb93a386Sopenharmony_ci // inliner might replace a statement with a block containing the statement. 780cb93a386Sopenharmony_ci std::vector<std::unique_ptr<Statement>*> fEnclosingStmtStack; 781cb93a386Sopenharmony_ci // The function that we're currently processing (i.e. inlining into). 782cb93a386Sopenharmony_ci FunctionDefinition* fEnclosingFunction = nullptr; 783cb93a386Sopenharmony_ci 784cb93a386Sopenharmony_ci void visit(const std::vector<std::unique_ptr<ProgramElement>>& elements, 785cb93a386Sopenharmony_ci std::shared_ptr<SymbolTable> symbols, 786cb93a386Sopenharmony_ci InlineCandidateList* candidateList) { 787cb93a386Sopenharmony_ci fCandidateList = candidateList; 788cb93a386Sopenharmony_ci fSymbolTableStack.push_back(symbols); 789cb93a386Sopenharmony_ci 790cb93a386Sopenharmony_ci for (const std::unique_ptr<ProgramElement>& pe : elements) { 791cb93a386Sopenharmony_ci this->visitProgramElement(pe.get()); 792cb93a386Sopenharmony_ci } 793cb93a386Sopenharmony_ci 794cb93a386Sopenharmony_ci fSymbolTableStack.pop_back(); 795cb93a386Sopenharmony_ci fCandidateList = nullptr; 796cb93a386Sopenharmony_ci } 797cb93a386Sopenharmony_ci 798cb93a386Sopenharmony_ci void visitProgramElement(ProgramElement* pe) { 799cb93a386Sopenharmony_ci switch (pe->kind()) { 800cb93a386Sopenharmony_ci case ProgramElement::Kind::kFunction: { 801cb93a386Sopenharmony_ci FunctionDefinition& funcDef = pe->as<FunctionDefinition>(); 802cb93a386Sopenharmony_ci fEnclosingFunction = &funcDef; 803cb93a386Sopenharmony_ci this->visitStatement(&funcDef.body()); 804cb93a386Sopenharmony_ci break; 805cb93a386Sopenharmony_ci } 806cb93a386Sopenharmony_ci default: 807cb93a386Sopenharmony_ci // The inliner can't operate outside of a function's scope. 808cb93a386Sopenharmony_ci break; 809cb93a386Sopenharmony_ci } 810cb93a386Sopenharmony_ci } 811cb93a386Sopenharmony_ci 812cb93a386Sopenharmony_ci void visitStatement(std::unique_ptr<Statement>* stmt, 813cb93a386Sopenharmony_ci bool isViableAsEnclosingStatement = true) { 814cb93a386Sopenharmony_ci if (!*stmt) { 815cb93a386Sopenharmony_ci return; 816cb93a386Sopenharmony_ci } 817cb93a386Sopenharmony_ci 818cb93a386Sopenharmony_ci size_t oldEnclosingStmtStackSize = fEnclosingStmtStack.size(); 819cb93a386Sopenharmony_ci size_t oldSymbolStackSize = fSymbolTableStack.size(); 820cb93a386Sopenharmony_ci 821cb93a386Sopenharmony_ci if (isViableAsEnclosingStatement) { 822cb93a386Sopenharmony_ci fEnclosingStmtStack.push_back(stmt); 823cb93a386Sopenharmony_ci } 824cb93a386Sopenharmony_ci 825cb93a386Sopenharmony_ci switch ((*stmt)->kind()) { 826cb93a386Sopenharmony_ci case Statement::Kind::kBreak: 827cb93a386Sopenharmony_ci case Statement::Kind::kContinue: 828cb93a386Sopenharmony_ci case Statement::Kind::kDiscard: 829cb93a386Sopenharmony_ci case Statement::Kind::kInlineMarker: 830cb93a386Sopenharmony_ci case Statement::Kind::kNop: 831cb93a386Sopenharmony_ci break; 832cb93a386Sopenharmony_ci 833cb93a386Sopenharmony_ci case Statement::Kind::kBlock: { 834cb93a386Sopenharmony_ci Block& block = (*stmt)->as<Block>(); 835cb93a386Sopenharmony_ci if (block.symbolTable()) { 836cb93a386Sopenharmony_ci fSymbolTableStack.push_back(block.symbolTable()); 837cb93a386Sopenharmony_ci } 838cb93a386Sopenharmony_ci 839cb93a386Sopenharmony_ci for (std::unique_ptr<Statement>& blockStmt : block.children()) { 840cb93a386Sopenharmony_ci this->visitStatement(&blockStmt); 841cb93a386Sopenharmony_ci } 842cb93a386Sopenharmony_ci break; 843cb93a386Sopenharmony_ci } 844cb93a386Sopenharmony_ci case Statement::Kind::kDo: { 845cb93a386Sopenharmony_ci DoStatement& doStmt = (*stmt)->as<DoStatement>(); 846cb93a386Sopenharmony_ci // The loop body is a candidate for inlining. 847cb93a386Sopenharmony_ci this->visitStatement(&doStmt.statement()); 848cb93a386Sopenharmony_ci // The inliner isn't smart enough to inline the test-expression for a do-while 849cb93a386Sopenharmony_ci // loop at this time. There are two limitations: 850cb93a386Sopenharmony_ci // - We would need to insert the inlined-body block at the very end of the do- 851cb93a386Sopenharmony_ci // statement's inner fStatement. We don't support that today, but it's doable. 852cb93a386Sopenharmony_ci // - We cannot inline the test expression if the loop uses `continue` anywhere; that 853cb93a386Sopenharmony_ci // would skip over the inlined block that evaluates the test expression. There 854cb93a386Sopenharmony_ci // isn't a good fix for this--any workaround would be more complex than the cost 855cb93a386Sopenharmony_ci // of a function call. However, loops that don't use `continue` would still be 856cb93a386Sopenharmony_ci // viable candidates for inlining. 857cb93a386Sopenharmony_ci break; 858cb93a386Sopenharmony_ci } 859cb93a386Sopenharmony_ci case Statement::Kind::kExpression: { 860cb93a386Sopenharmony_ci ExpressionStatement& expr = (*stmt)->as<ExpressionStatement>(); 861cb93a386Sopenharmony_ci this->visitExpression(&expr.expression()); 862cb93a386Sopenharmony_ci break; 863cb93a386Sopenharmony_ci } 864cb93a386Sopenharmony_ci case Statement::Kind::kFor: { 865cb93a386Sopenharmony_ci ForStatement& forStmt = (*stmt)->as<ForStatement>(); 866cb93a386Sopenharmony_ci if (forStmt.symbols()) { 867cb93a386Sopenharmony_ci fSymbolTableStack.push_back(forStmt.symbols()); 868cb93a386Sopenharmony_ci } 869cb93a386Sopenharmony_ci 870cb93a386Sopenharmony_ci // The initializer and loop body are candidates for inlining. 871cb93a386Sopenharmony_ci this->visitStatement(&forStmt.initializer(), 872cb93a386Sopenharmony_ci /*isViableAsEnclosingStatement=*/false); 873cb93a386Sopenharmony_ci this->visitStatement(&forStmt.statement()); 874cb93a386Sopenharmony_ci 875cb93a386Sopenharmony_ci // The inliner isn't smart enough to inline the test- or increment-expressions 876cb93a386Sopenharmony_ci // of a for loop loop at this time. There are a handful of limitations: 877cb93a386Sopenharmony_ci // - We would need to insert the test-expression block at the very beginning of the 878cb93a386Sopenharmony_ci // for-loop's inner fStatement, and the increment-expression block at the very 879cb93a386Sopenharmony_ci // end. We don't support that today, but it's doable. 880cb93a386Sopenharmony_ci // - The for-loop's built-in test-expression would need to be dropped entirely, 881cb93a386Sopenharmony_ci // and the loop would be halted via a break statement at the end of the inlined 882cb93a386Sopenharmony_ci // test-expression. This is again something we don't support today, but it could 883cb93a386Sopenharmony_ci // be implemented. 884cb93a386Sopenharmony_ci // - We cannot inline the increment-expression if the loop uses `continue` anywhere; 885cb93a386Sopenharmony_ci // that would skip over the inlined block that evaluates the increment expression. 886cb93a386Sopenharmony_ci // There isn't a good fix for this--any workaround would be more complex than the 887cb93a386Sopenharmony_ci // cost of a function call. However, loops that don't use `continue` would still 888cb93a386Sopenharmony_ci // be viable candidates for increment-expression inlining. 889cb93a386Sopenharmony_ci break; 890cb93a386Sopenharmony_ci } 891cb93a386Sopenharmony_ci case Statement::Kind::kIf: { 892cb93a386Sopenharmony_ci IfStatement& ifStmt = (*stmt)->as<IfStatement>(); 893cb93a386Sopenharmony_ci this->visitExpression(&ifStmt.test()); 894cb93a386Sopenharmony_ci this->visitStatement(&ifStmt.ifTrue()); 895cb93a386Sopenharmony_ci this->visitStatement(&ifStmt.ifFalse()); 896cb93a386Sopenharmony_ci break; 897cb93a386Sopenharmony_ci } 898cb93a386Sopenharmony_ci case Statement::Kind::kReturn: { 899cb93a386Sopenharmony_ci ReturnStatement& returnStmt = (*stmt)->as<ReturnStatement>(); 900cb93a386Sopenharmony_ci this->visitExpression(&returnStmt.expression()); 901cb93a386Sopenharmony_ci break; 902cb93a386Sopenharmony_ci } 903cb93a386Sopenharmony_ci case Statement::Kind::kSwitch: { 904cb93a386Sopenharmony_ci SwitchStatement& switchStmt = (*stmt)->as<SwitchStatement>(); 905cb93a386Sopenharmony_ci if (switchStmt.symbols()) { 906cb93a386Sopenharmony_ci fSymbolTableStack.push_back(switchStmt.symbols()); 907cb93a386Sopenharmony_ci } 908cb93a386Sopenharmony_ci 909cb93a386Sopenharmony_ci this->visitExpression(&switchStmt.value()); 910cb93a386Sopenharmony_ci for (const std::unique_ptr<Statement>& switchCase : switchStmt.cases()) { 911cb93a386Sopenharmony_ci // The switch-case's fValue cannot be a FunctionCall; skip it. 912cb93a386Sopenharmony_ci this->visitStatement(&switchCase->as<SwitchCase>().statement()); 913cb93a386Sopenharmony_ci } 914cb93a386Sopenharmony_ci break; 915cb93a386Sopenharmony_ci } 916cb93a386Sopenharmony_ci case Statement::Kind::kVarDeclaration: { 917cb93a386Sopenharmony_ci VarDeclaration& varDeclStmt = (*stmt)->as<VarDeclaration>(); 918cb93a386Sopenharmony_ci // Don't need to scan the declaration's sizes; those are always IntLiterals. 919cb93a386Sopenharmony_ci this->visitExpression(&varDeclStmt.value()); 920cb93a386Sopenharmony_ci break; 921cb93a386Sopenharmony_ci } 922cb93a386Sopenharmony_ci default: 923cb93a386Sopenharmony_ci SkUNREACHABLE; 924cb93a386Sopenharmony_ci } 925cb93a386Sopenharmony_ci 926cb93a386Sopenharmony_ci // Pop our symbol and enclosing-statement stacks. 927cb93a386Sopenharmony_ci fSymbolTableStack.resize(oldSymbolStackSize); 928cb93a386Sopenharmony_ci fEnclosingStmtStack.resize(oldEnclosingStmtStackSize); 929cb93a386Sopenharmony_ci } 930cb93a386Sopenharmony_ci 931cb93a386Sopenharmony_ci void visitExpression(std::unique_ptr<Expression>* expr) { 932cb93a386Sopenharmony_ci if (!*expr) { 933cb93a386Sopenharmony_ci return; 934cb93a386Sopenharmony_ci } 935cb93a386Sopenharmony_ci 936cb93a386Sopenharmony_ci switch ((*expr)->kind()) { 937cb93a386Sopenharmony_ci case Expression::Kind::kExternalFunctionReference: 938cb93a386Sopenharmony_ci case Expression::Kind::kFieldAccess: 939cb93a386Sopenharmony_ci case Expression::Kind::kFunctionReference: 940cb93a386Sopenharmony_ci case Expression::Kind::kLiteral: 941cb93a386Sopenharmony_ci case Expression::Kind::kMethodReference: 942cb93a386Sopenharmony_ci case Expression::Kind::kSetting: 943cb93a386Sopenharmony_ci case Expression::Kind::kTypeReference: 944cb93a386Sopenharmony_ci case Expression::Kind::kVariableReference: 945cb93a386Sopenharmony_ci // Nothing to scan here. 946cb93a386Sopenharmony_ci break; 947cb93a386Sopenharmony_ci 948cb93a386Sopenharmony_ci case Expression::Kind::kBinary: { 949cb93a386Sopenharmony_ci BinaryExpression& binaryExpr = (*expr)->as<BinaryExpression>(); 950cb93a386Sopenharmony_ci this->visitExpression(&binaryExpr.left()); 951cb93a386Sopenharmony_ci 952cb93a386Sopenharmony_ci // Logical-and and logical-or binary expressions do not inline the right side, 953cb93a386Sopenharmony_ci // because that would invalidate short-circuiting. That is, when evaluating 954cb93a386Sopenharmony_ci // expressions like these: 955cb93a386Sopenharmony_ci // (false && x()) // always false 956cb93a386Sopenharmony_ci // (true || y()) // always true 957cb93a386Sopenharmony_ci // It is illegal for side-effects from x() or y() to occur. The simplest way to 958cb93a386Sopenharmony_ci // enforce that rule is to avoid inlining the right side entirely. However, it is 959cb93a386Sopenharmony_ci // safe for other types of binary expression to inline both sides. 960cb93a386Sopenharmony_ci Operator op = binaryExpr.getOperator(); 961cb93a386Sopenharmony_ci bool shortCircuitable = (op.kind() == Token::Kind::TK_LOGICALAND || 962cb93a386Sopenharmony_ci op.kind() == Token::Kind::TK_LOGICALOR); 963cb93a386Sopenharmony_ci if (!shortCircuitable) { 964cb93a386Sopenharmony_ci this->visitExpression(&binaryExpr.right()); 965cb93a386Sopenharmony_ci } 966cb93a386Sopenharmony_ci break; 967cb93a386Sopenharmony_ci } 968cb93a386Sopenharmony_ci case Expression::Kind::kChildCall: { 969cb93a386Sopenharmony_ci ChildCall& childCallExpr = (*expr)->as<ChildCall>(); 970cb93a386Sopenharmony_ci for (std::unique_ptr<Expression>& arg : childCallExpr.arguments()) { 971cb93a386Sopenharmony_ci this->visitExpression(&arg); 972cb93a386Sopenharmony_ci } 973cb93a386Sopenharmony_ci break; 974cb93a386Sopenharmony_ci } 975cb93a386Sopenharmony_ci case Expression::Kind::kConstructorArray: 976cb93a386Sopenharmony_ci case Expression::Kind::kConstructorArrayCast: 977cb93a386Sopenharmony_ci case Expression::Kind::kConstructorCompound: 978cb93a386Sopenharmony_ci case Expression::Kind::kConstructorCompoundCast: 979cb93a386Sopenharmony_ci case Expression::Kind::kConstructorDiagonalMatrix: 980cb93a386Sopenharmony_ci case Expression::Kind::kConstructorMatrixResize: 981cb93a386Sopenharmony_ci case Expression::Kind::kConstructorScalarCast: 982cb93a386Sopenharmony_ci case Expression::Kind::kConstructorSplat: 983cb93a386Sopenharmony_ci case Expression::Kind::kConstructorStruct: { 984cb93a386Sopenharmony_ci AnyConstructor& constructorExpr = (*expr)->asAnyConstructor(); 985cb93a386Sopenharmony_ci for (std::unique_ptr<Expression>& arg : constructorExpr.argumentSpan()) { 986cb93a386Sopenharmony_ci this->visitExpression(&arg); 987cb93a386Sopenharmony_ci } 988cb93a386Sopenharmony_ci break; 989cb93a386Sopenharmony_ci } 990cb93a386Sopenharmony_ci case Expression::Kind::kExternalFunctionCall: { 991cb93a386Sopenharmony_ci ExternalFunctionCall& funcCallExpr = (*expr)->as<ExternalFunctionCall>(); 992cb93a386Sopenharmony_ci for (std::unique_ptr<Expression>& arg : funcCallExpr.arguments()) { 993cb93a386Sopenharmony_ci this->visitExpression(&arg); 994cb93a386Sopenharmony_ci } 995cb93a386Sopenharmony_ci break; 996cb93a386Sopenharmony_ci } 997cb93a386Sopenharmony_ci case Expression::Kind::kFunctionCall: { 998cb93a386Sopenharmony_ci FunctionCall& funcCallExpr = (*expr)->as<FunctionCall>(); 999cb93a386Sopenharmony_ci for (std::unique_ptr<Expression>& arg : funcCallExpr.arguments()) { 1000cb93a386Sopenharmony_ci this->visitExpression(&arg); 1001cb93a386Sopenharmony_ci } 1002cb93a386Sopenharmony_ci this->addInlineCandidate(expr); 1003cb93a386Sopenharmony_ci break; 1004cb93a386Sopenharmony_ci } 1005cb93a386Sopenharmony_ci case Expression::Kind::kIndex: { 1006cb93a386Sopenharmony_ci IndexExpression& indexExpr = (*expr)->as<IndexExpression>(); 1007cb93a386Sopenharmony_ci this->visitExpression(&indexExpr.base()); 1008cb93a386Sopenharmony_ci this->visitExpression(&indexExpr.index()); 1009cb93a386Sopenharmony_ci break; 1010cb93a386Sopenharmony_ci } 1011cb93a386Sopenharmony_ci case Expression::Kind::kPostfix: { 1012cb93a386Sopenharmony_ci PostfixExpression& postfixExpr = (*expr)->as<PostfixExpression>(); 1013cb93a386Sopenharmony_ci this->visitExpression(&postfixExpr.operand()); 1014cb93a386Sopenharmony_ci break; 1015cb93a386Sopenharmony_ci } 1016cb93a386Sopenharmony_ci case Expression::Kind::kPrefix: { 1017cb93a386Sopenharmony_ci PrefixExpression& prefixExpr = (*expr)->as<PrefixExpression>(); 1018cb93a386Sopenharmony_ci this->visitExpression(&prefixExpr.operand()); 1019cb93a386Sopenharmony_ci break; 1020cb93a386Sopenharmony_ci } 1021cb93a386Sopenharmony_ci case Expression::Kind::kSwizzle: { 1022cb93a386Sopenharmony_ci Swizzle& swizzleExpr = (*expr)->as<Swizzle>(); 1023cb93a386Sopenharmony_ci this->visitExpression(&swizzleExpr.base()); 1024cb93a386Sopenharmony_ci break; 1025cb93a386Sopenharmony_ci } 1026cb93a386Sopenharmony_ci case Expression::Kind::kTernary: { 1027cb93a386Sopenharmony_ci TernaryExpression& ternaryExpr = (*expr)->as<TernaryExpression>(); 1028cb93a386Sopenharmony_ci // The test expression is a candidate for inlining. 1029cb93a386Sopenharmony_ci this->visitExpression(&ternaryExpr.test()); 1030cb93a386Sopenharmony_ci // The true- and false-expressions cannot be inlined, because we are only allowed to 1031cb93a386Sopenharmony_ci // evaluate one side. 1032cb93a386Sopenharmony_ci break; 1033cb93a386Sopenharmony_ci } 1034cb93a386Sopenharmony_ci default: 1035cb93a386Sopenharmony_ci SkUNREACHABLE; 1036cb93a386Sopenharmony_ci } 1037cb93a386Sopenharmony_ci } 1038cb93a386Sopenharmony_ci 1039cb93a386Sopenharmony_ci void addInlineCandidate(std::unique_ptr<Expression>* candidate) { 1040cb93a386Sopenharmony_ci fCandidateList->fCandidates.push_back( 1041cb93a386Sopenharmony_ci InlineCandidate{fSymbolTableStack.back(), 1042cb93a386Sopenharmony_ci find_parent_statement(fEnclosingStmtStack), 1043cb93a386Sopenharmony_ci fEnclosingStmtStack.back(), 1044cb93a386Sopenharmony_ci candidate, 1045cb93a386Sopenharmony_ci fEnclosingFunction}); 1046cb93a386Sopenharmony_ci } 1047cb93a386Sopenharmony_ci}; 1048cb93a386Sopenharmony_ci 1049cb93a386Sopenharmony_cistatic const FunctionDeclaration& candidate_func(const InlineCandidate& candidate) { 1050cb93a386Sopenharmony_ci return (*candidate.fCandidateExpr)->as<FunctionCall>().function(); 1051cb93a386Sopenharmony_ci} 1052cb93a386Sopenharmony_ci 1053cb93a386Sopenharmony_cibool Inliner::candidateCanBeInlined(const InlineCandidate& candidate, 1054cb93a386Sopenharmony_ci const ProgramUsage& usage, 1055cb93a386Sopenharmony_ci InlinabilityCache* cache) { 1056cb93a386Sopenharmony_ci const FunctionDeclaration& funcDecl = candidate_func(candidate); 1057cb93a386Sopenharmony_ci auto [iter, wasInserted] = cache->insert({&funcDecl, false}); 1058cb93a386Sopenharmony_ci if (wasInserted) { 1059cb93a386Sopenharmony_ci // Recursion is forbidden here to avoid an infinite death spiral of inlining. 1060cb93a386Sopenharmony_ci iter->second = this->isSafeToInline(funcDecl.definition(), usage) && 1061cb93a386Sopenharmony_ci !contains_recursive_call(funcDecl); 1062cb93a386Sopenharmony_ci } 1063cb93a386Sopenharmony_ci 1064cb93a386Sopenharmony_ci return iter->second; 1065cb93a386Sopenharmony_ci} 1066cb93a386Sopenharmony_ci 1067cb93a386Sopenharmony_ciint Inliner::getFunctionSize(const FunctionDeclaration& funcDecl, FunctionSizeCache* cache) { 1068cb93a386Sopenharmony_ci auto [iter, wasInserted] = cache->insert({&funcDecl, 0}); 1069cb93a386Sopenharmony_ci if (wasInserted) { 1070cb93a386Sopenharmony_ci iter->second = Analysis::NodeCountUpToLimit(*funcDecl.definition(), 1071cb93a386Sopenharmony_ci this->settings().fInlineThreshold); 1072cb93a386Sopenharmony_ci } 1073cb93a386Sopenharmony_ci return iter->second; 1074cb93a386Sopenharmony_ci} 1075cb93a386Sopenharmony_ci 1076cb93a386Sopenharmony_civoid Inliner::buildCandidateList(const std::vector<std::unique_ptr<ProgramElement>>& elements, 1077cb93a386Sopenharmony_ci std::shared_ptr<SymbolTable> symbols, ProgramUsage* usage, 1078cb93a386Sopenharmony_ci InlineCandidateList* candidateList) { 1079cb93a386Sopenharmony_ci // This is structured much like a ProgramVisitor, but does not actually use ProgramVisitor. 1080cb93a386Sopenharmony_ci // The analyzer needs to keep track of the `unique_ptr<T>*` of statements and expressions so 1081cb93a386Sopenharmony_ci // that they can later be replaced, and ProgramVisitor does not provide this; it only provides a 1082cb93a386Sopenharmony_ci // `const T&`. 1083cb93a386Sopenharmony_ci InlineCandidateAnalyzer analyzer; 1084cb93a386Sopenharmony_ci analyzer.visit(elements, symbols, candidateList); 1085cb93a386Sopenharmony_ci 1086cb93a386Sopenharmony_ci // Early out if there are no inlining candidates. 1087cb93a386Sopenharmony_ci std::vector<InlineCandidate>& candidates = candidateList->fCandidates; 1088cb93a386Sopenharmony_ci if (candidates.empty()) { 1089cb93a386Sopenharmony_ci return; 1090cb93a386Sopenharmony_ci } 1091cb93a386Sopenharmony_ci 1092cb93a386Sopenharmony_ci // Remove candidates that are not safe to inline. 1093cb93a386Sopenharmony_ci InlinabilityCache cache; 1094cb93a386Sopenharmony_ci candidates.erase(std::remove_if(candidates.begin(), 1095cb93a386Sopenharmony_ci candidates.end(), 1096cb93a386Sopenharmony_ci [&](const InlineCandidate& candidate) { 1097cb93a386Sopenharmony_ci return !this->candidateCanBeInlined( 1098cb93a386Sopenharmony_ci candidate, *usage, &cache); 1099cb93a386Sopenharmony_ci }), 1100cb93a386Sopenharmony_ci candidates.end()); 1101cb93a386Sopenharmony_ci 1102cb93a386Sopenharmony_ci // If the inline threshold is unlimited, or if we have no candidates left, our candidate list is 1103cb93a386Sopenharmony_ci // complete. 1104cb93a386Sopenharmony_ci if (this->settings().fInlineThreshold == INT_MAX || candidates.empty()) { 1105cb93a386Sopenharmony_ci return; 1106cb93a386Sopenharmony_ci } 1107cb93a386Sopenharmony_ci 1108cb93a386Sopenharmony_ci // Remove candidates on a per-function basis if the effect of inlining would be to make more 1109cb93a386Sopenharmony_ci // than `inlineThreshold` nodes. (i.e. if Func() would be inlined six times and its size is 1110cb93a386Sopenharmony_ci // 10 nodes, it should be inlined if the inlineThreshold is 60 or higher.) 1111cb93a386Sopenharmony_ci FunctionSizeCache functionSizeCache; 1112cb93a386Sopenharmony_ci FunctionSizeCache candidateTotalCost; 1113cb93a386Sopenharmony_ci for (InlineCandidate& candidate : candidates) { 1114cb93a386Sopenharmony_ci const FunctionDeclaration& fnDecl = candidate_func(candidate); 1115cb93a386Sopenharmony_ci candidateTotalCost[&fnDecl] += this->getFunctionSize(fnDecl, &functionSizeCache); 1116cb93a386Sopenharmony_ci } 1117cb93a386Sopenharmony_ci 1118cb93a386Sopenharmony_ci candidates.erase(std::remove_if(candidates.begin(), candidates.end(), 1119cb93a386Sopenharmony_ci [&](const InlineCandidate& candidate) { 1120cb93a386Sopenharmony_ci const FunctionDeclaration& fnDecl = candidate_func(candidate); 1121cb93a386Sopenharmony_ci if (fnDecl.modifiers().fFlags & Modifiers::kInline_Flag) { 1122cb93a386Sopenharmony_ci // Functions marked `inline` ignore size limitations. 1123cb93a386Sopenharmony_ci return false; 1124cb93a386Sopenharmony_ci } 1125cb93a386Sopenharmony_ci if (usage->get(fnDecl) == 1) { 1126cb93a386Sopenharmony_ci // If a function is only used once, it's cost-free to inline. 1127cb93a386Sopenharmony_ci return false; 1128cb93a386Sopenharmony_ci } 1129cb93a386Sopenharmony_ci if (candidateTotalCost[&fnDecl] <= this->settings().fInlineThreshold) { 1130cb93a386Sopenharmony_ci // We won't exceed the inline threshold by inlining this. 1131cb93a386Sopenharmony_ci return false; 1132cb93a386Sopenharmony_ci } 1133cb93a386Sopenharmony_ci // Inlining this function will add too many IRNodes. 1134cb93a386Sopenharmony_ci return true; 1135cb93a386Sopenharmony_ci }), 1136cb93a386Sopenharmony_ci candidates.end()); 1137cb93a386Sopenharmony_ci} 1138cb93a386Sopenharmony_ci 1139cb93a386Sopenharmony_cibool Inliner::analyze(const std::vector<std::unique_ptr<ProgramElement>>& elements, 1140cb93a386Sopenharmony_ci std::shared_ptr<SymbolTable> symbols, 1141cb93a386Sopenharmony_ci ProgramUsage* usage) { 1142cb93a386Sopenharmony_ci // A threshold of zero indicates that the inliner is completely disabled, so we can just return. 1143cb93a386Sopenharmony_ci if (this->settings().fInlineThreshold <= 0) { 1144cb93a386Sopenharmony_ci return false; 1145cb93a386Sopenharmony_ci } 1146cb93a386Sopenharmony_ci 1147cb93a386Sopenharmony_ci // Enforce a limit on inlining to avoid pathological cases. (inliner/ExponentialGrowth.sksl) 1148cb93a386Sopenharmony_ci if (fInlinedStatementCounter >= kInlinedStatementLimit) { 1149cb93a386Sopenharmony_ci return false; 1150cb93a386Sopenharmony_ci } 1151cb93a386Sopenharmony_ci 1152cb93a386Sopenharmony_ci InlineCandidateList candidateList; 1153cb93a386Sopenharmony_ci this->buildCandidateList(elements, symbols, usage, &candidateList); 1154cb93a386Sopenharmony_ci 1155cb93a386Sopenharmony_ci // Inline the candidates where we've determined that it's safe to do so. 1156cb93a386Sopenharmony_ci using StatementRemappingTable = std::unordered_map<std::unique_ptr<Statement>*, 1157cb93a386Sopenharmony_ci std::unique_ptr<Statement>*>; 1158cb93a386Sopenharmony_ci StatementRemappingTable statementRemappingTable; 1159cb93a386Sopenharmony_ci 1160cb93a386Sopenharmony_ci bool madeChanges = false; 1161cb93a386Sopenharmony_ci for (const InlineCandidate& candidate : candidateList.fCandidates) { 1162cb93a386Sopenharmony_ci FunctionCall& funcCall = (*candidate.fCandidateExpr)->as<FunctionCall>(); 1163cb93a386Sopenharmony_ci 1164cb93a386Sopenharmony_ci // Convert the function call to its inlined equivalent. 1165cb93a386Sopenharmony_ci InlinedCall inlinedCall = this->inlineCall(&funcCall, candidate.fSymbols, *usage, 1166cb93a386Sopenharmony_ci &candidate.fEnclosingFunction->declaration()); 1167cb93a386Sopenharmony_ci 1168cb93a386Sopenharmony_ci // Stop if an error was detected during the inlining process. 1169cb93a386Sopenharmony_ci if (!inlinedCall.fInlinedBody && !inlinedCall.fReplacementExpr) { 1170cb93a386Sopenharmony_ci break; 1171cb93a386Sopenharmony_ci } 1172cb93a386Sopenharmony_ci 1173cb93a386Sopenharmony_ci // Ensure that the inlined body has a scope if it needs one. 1174cb93a386Sopenharmony_ci this->ensureScopedBlocks(inlinedCall.fInlinedBody.get(), candidate.fParentStmt->get()); 1175cb93a386Sopenharmony_ci 1176cb93a386Sopenharmony_ci // Add references within the inlined body 1177cb93a386Sopenharmony_ci usage->add(inlinedCall.fInlinedBody.get()); 1178cb93a386Sopenharmony_ci 1179cb93a386Sopenharmony_ci // Look up the enclosing statement; remap it if necessary. 1180cb93a386Sopenharmony_ci std::unique_ptr<Statement>* enclosingStmt = candidate.fEnclosingStmt; 1181cb93a386Sopenharmony_ci for (;;) { 1182cb93a386Sopenharmony_ci auto iter = statementRemappingTable.find(enclosingStmt); 1183cb93a386Sopenharmony_ci if (iter == statementRemappingTable.end()) { 1184cb93a386Sopenharmony_ci break; 1185cb93a386Sopenharmony_ci } 1186cb93a386Sopenharmony_ci enclosingStmt = iter->second; 1187cb93a386Sopenharmony_ci } 1188cb93a386Sopenharmony_ci 1189cb93a386Sopenharmony_ci // Move the enclosing statement to the end of the unscoped Block containing the inlined 1190cb93a386Sopenharmony_ci // function, then replace the enclosing statement with that Block. 1191cb93a386Sopenharmony_ci // Before: 1192cb93a386Sopenharmony_ci // fInlinedBody = Block{ stmt1, stmt2, stmt3 } 1193cb93a386Sopenharmony_ci // fEnclosingStmt = stmt4 1194cb93a386Sopenharmony_ci // After: 1195cb93a386Sopenharmony_ci // fInlinedBody = null 1196cb93a386Sopenharmony_ci // fEnclosingStmt = Block{ stmt1, stmt2, stmt3, stmt4 } 1197cb93a386Sopenharmony_ci inlinedCall.fInlinedBody->children().push_back(std::move(*enclosingStmt)); 1198cb93a386Sopenharmony_ci *enclosingStmt = std::move(inlinedCall.fInlinedBody); 1199cb93a386Sopenharmony_ci 1200cb93a386Sopenharmony_ci // Replace the candidate function call with our replacement expression. 1201cb93a386Sopenharmony_ci usage->remove(candidate.fCandidateExpr->get()); 1202cb93a386Sopenharmony_ci usage->add(inlinedCall.fReplacementExpr.get()); 1203cb93a386Sopenharmony_ci *candidate.fCandidateExpr = std::move(inlinedCall.fReplacementExpr); 1204cb93a386Sopenharmony_ci madeChanges = true; 1205cb93a386Sopenharmony_ci 1206cb93a386Sopenharmony_ci // If anything else pointed at our enclosing statement, it's now pointing at a Block 1207cb93a386Sopenharmony_ci // containing many other statements as well. Maintain a fix-up table to account for this. 1208cb93a386Sopenharmony_ci statementRemappingTable[enclosingStmt] = &(*enclosingStmt)->as<Block>().children().back(); 1209cb93a386Sopenharmony_ci 1210cb93a386Sopenharmony_ci // Stop inlining if we've reached our hard cap on new statements. 1211cb93a386Sopenharmony_ci if (fInlinedStatementCounter >= kInlinedStatementLimit) { 1212cb93a386Sopenharmony_ci break; 1213cb93a386Sopenharmony_ci } 1214cb93a386Sopenharmony_ci 1215cb93a386Sopenharmony_ci // Note that nothing was destroyed except for the FunctionCall. All other nodes should 1216cb93a386Sopenharmony_ci // remain valid. 1217cb93a386Sopenharmony_ci } 1218cb93a386Sopenharmony_ci 1219cb93a386Sopenharmony_ci return madeChanges; 1220cb93a386Sopenharmony_ci} 1221cb93a386Sopenharmony_ci 1222cb93a386Sopenharmony_ci} // namespace SkSL 1223