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