Flutter Engine
The Flutter Engine
SkSLInliner.cpp
Go to the documentation of this file.
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
9
10#ifndef SK_ENABLE_OPTIMIZE_SIZE
11
12#include "include/core/SkSpan.h"
49#include "src/sksl/ir/SkSLNop.h"
66
67#include <algorithm>
68#include <climits>
69#include <cstddef>
70#include <memory>
71#include <string>
72#include <string_view>
73#include <utility>
74
75using namespace skia_private;
76
77namespace SkSL {
78namespace {
79
80static constexpr int kInlinedStatementLimit = 2500;
81
82static bool is_scopeless_block(Statement* stmt) {
83 return stmt->is<Block>() && !stmt->as<Block>().isScope();
84}
85
86static std::unique_ptr<Statement>* find_parent_statement(
87 const std::vector<std::unique_ptr<Statement>*>& stmtStack) {
88 SkASSERT(!stmtStack.empty());
89
90 // Walk the statement stack from back to front, ignoring the last element (which is the
91 // enclosing statement).
92 auto iter = stmtStack.rbegin();
93 ++iter;
94
95 // Anything counts as a parent statement other than a scopeless Block.
96 for (; iter != stmtStack.rend(); ++iter) {
97 std::unique_ptr<Statement>* stmt = *iter;
98 if (!is_scopeless_block(stmt->get())) {
99 return stmt;
100 }
101 }
102
103 // There wasn't any parent statement to be found.
104 return nullptr;
105}
106
107std::unique_ptr<Expression> clone_with_ref_kind(const Expression& expr,
109 Position pos) {
110 std::unique_ptr<Expression> clone = expr.clone(pos);
111 Analysis::UpdateVariableRefKind(clone.get(), refKind);
112 return clone;
113}
114
115} // namespace
116
117const Variable* Inliner::RemapVariable(const Variable* variable,
118 const VariableRewriteMap* varMap) {
119 std::unique_ptr<Expression>* remap = varMap->find(variable);
120 if (!remap) {
121 SkDEBUGFAILF("rewrite map does not contain variable '%.*s'",
122 (int)variable->name().size(), variable->name().data());
123 return variable;
124 }
125 Expression* expr = remap->get();
126 SkASSERT(expr);
127 if (!expr->is<VariableReference>()) {
128 SkDEBUGFAILF("rewrite map contains non-variable replacement for '%.*s'",
129 (int)variable->name().size(), variable->name().data());
130 return variable;
131 }
132 return expr->as<VariableReference>().variable();
133}
134
135void Inliner::ensureScopedBlocks(Statement* inlinedBody, Statement* parentStmt) {
136 // No changes necessary if this statement isn't actually a block.
137 if (!inlinedBody || !inlinedBody->is<Block>()) {
138 return;
139 }
140
141 // No changes necessary if the parent statement doesn't require a scope.
142 if (!parentStmt || !(parentStmt->is<IfStatement>() || parentStmt->is<ForStatement>() ||
143 parentStmt->is<DoStatement>() || is_scopeless_block(parentStmt))) {
144 return;
145 }
146
147 Block& block = inlinedBody->as<Block>();
148
149 // The inliner will create inlined function bodies as a Block containing multiple statements,
150 // but no scope. Normally, this is fine, but if this block is used as the statement for a
151 // do/for/if/while, the block needs to be scoped for the generated code to match the intent.
152 // In the case of Blocks nested inside other Blocks, we add the scope to the outermost block if
153 // needed.
154 for (Block* nestedBlock = &block;; ) {
155 if (nestedBlock->isScope()) {
156 // We found an explicit scope; all is well.
157 return;
158 }
159 if (nestedBlock->children().size() == 1 && nestedBlock->children()[0]->is<Block>()) {
160 // This block wraps another unscoped block; we need to go deeper.
161 nestedBlock = &nestedBlock->children()[0]->as<Block>();
162 continue;
163 }
164 // We found a block containing real statements (not just more blocks), but no scope.
165 // Let's add a scope to the outermost block.
166 block.setBlockKind(Block::Kind::kBracedScope);
167 return;
168 }
169}
170
171std::unique_ptr<Expression> Inliner::inlineExpression(Position pos,
172 VariableRewriteMap* varMap,
173 SymbolTable* symbolTableForExpression,
174 const Expression& expression) {
175 auto expr = [&](const std::unique_ptr<Expression>& e) -> std::unique_ptr<Expression> {
176 if (e) {
177 return this->inlineExpression(pos, varMap, symbolTableForExpression, *e);
178 }
179 return nullptr;
180 };
181 auto argList = [&](const ExpressionArray& originalArgs) -> ExpressionArray {
182 ExpressionArray args;
183 args.reserve_exact(originalArgs.size());
184 for (const std::unique_ptr<Expression>& arg : originalArgs) {
185 args.push_back(expr(arg));
186 }
187 return args;
188 };
189
190 switch (expression.kind()) {
191 case Expression::Kind::kBinary: {
192 const BinaryExpression& binaryExpr = expression.as<BinaryExpression>();
193 return BinaryExpression::Make(*fContext,
194 pos,
195 expr(binaryExpr.left()),
196 binaryExpr.getOperator(),
197 expr(binaryExpr.right()));
198 }
200 return expression.clone(pos);
202 return expression.clone(pos);
203 case Expression::Kind::kChildCall: {
204 const ChildCall& childCall = expression.as<ChildCall>();
205 return ChildCall::Make(*fContext,
206 pos,
207 childCall.type().clone(*fContext, symbolTableForExpression),
208 childCall.child(),
209 argList(childCall.arguments()));
210 }
211 case Expression::Kind::kConstructorArray: {
212 const ConstructorArray& ctor = expression.as<ConstructorArray>();
213 return ConstructorArray::Make(*fContext,
214 pos,
215 *ctor.type().clone(*fContext, symbolTableForExpression),
216 argList(ctor.arguments()));
217 }
218 case Expression::Kind::kConstructorArrayCast: {
219 const ConstructorArrayCast& ctor = expression.as<ConstructorArrayCast>();
221 *fContext,
222 pos,
223 *ctor.type().clone(*fContext, symbolTableForExpression),
224 expr(ctor.argument()));
225 }
226 case Expression::Kind::kConstructorCompound: {
227 const ConstructorCompound& ctor = expression.as<ConstructorCompound>();
229 *fContext,
230 pos,
231 *ctor.type().clone(*fContext, symbolTableForExpression),
232 argList(ctor.arguments()));
233 }
234 case Expression::Kind::kConstructorCompoundCast: {
235 const ConstructorCompoundCast& ctor = expression.as<ConstructorCompoundCast>();
237 *fContext,
238 pos,
239 *ctor.type().clone(*fContext, symbolTableForExpression),
240 expr(ctor.argument()));
241 }
242 case Expression::Kind::kConstructorDiagonalMatrix: {
243 const ConstructorDiagonalMatrix& ctor = expression.as<ConstructorDiagonalMatrix>();
245 *fContext,
246 pos,
247 *ctor.type().clone(*fContext, symbolTableForExpression),
248 expr(ctor.argument()));
249 }
250 case Expression::Kind::kConstructorMatrixResize: {
251 const ConstructorMatrixResize& ctor = expression.as<ConstructorMatrixResize>();
253 *fContext,
254 pos,
255 *ctor.type().clone(*fContext, symbolTableForExpression),
256 expr(ctor.argument()));
257 }
258 case Expression::Kind::kConstructorScalarCast: {
259 const ConstructorScalarCast& ctor = expression.as<ConstructorScalarCast>();
261 *fContext,
262 pos,
263 *ctor.type().clone(*fContext, symbolTableForExpression),
264 expr(ctor.argument()));
265 }
266 case Expression::Kind::kConstructorSplat: {
267 const ConstructorSplat& ctor = expression.as<ConstructorSplat>();
268 return ConstructorSplat::Make(*fContext,
269 pos,
270 *ctor.type().clone(*fContext, symbolTableForExpression),
271 expr(ctor.argument()));
272 }
273 case Expression::Kind::kConstructorStruct: {
274 const ConstructorStruct& ctor = expression.as<ConstructorStruct>();
275 return ConstructorStruct::Make(*fContext,
276 pos,
277 *ctor.type().clone(*fContext, symbolTableForExpression),
278 argList(ctor.arguments()));
279 }
280 case Expression::Kind::kFieldAccess: {
281 const FieldAccess& f = expression.as<FieldAccess>();
282 return FieldAccess::Make(*fContext, pos, expr(f.base()), f.fieldIndex(), f.ownerKind());
283 }
284 case Expression::Kind::kFunctionCall: {
285 const FunctionCall& funcCall = expression.as<FunctionCall>();
286 return FunctionCall::Make(*fContext,
287 pos,
288 funcCall.type().clone(*fContext, symbolTableForExpression),
289 funcCall.function(),
290 argList(funcCall.arguments()));
291 }
292 case Expression::Kind::kFunctionReference:
293 return expression.clone(pos);
295 const IndexExpression& idx = expression.as<IndexExpression>();
296 return IndexExpression::Make(*fContext, pos, expr(idx.base()), expr(idx.index()));
297 }
298 case Expression::Kind::kMethodReference:
299 return expression.clone(pos);
300 case Expression::Kind::kPrefix: {
301 const PrefixExpression& p = expression.as<PrefixExpression>();
302 return PrefixExpression::Make(*fContext, pos, p.getOperator(), expr(p.operand()));
303 }
304 case Expression::Kind::kPostfix: {
305 const PostfixExpression& p = expression.as<PostfixExpression>();
306 return PostfixExpression::Make(*fContext, pos, expr(p.operand()), p.getOperator());
307 }
308 case Expression::Kind::kSetting: {
309 const Setting& s = expression.as<Setting>();
310 return Setting::Make(*fContext, pos, s.capsPtr());
311 }
312 case Expression::Kind::kSwizzle: {
313 const Swizzle& s = expression.as<Swizzle>();
314 return Swizzle::Make(*fContext, pos, expr(s.base()), s.components());
315 }
316 case Expression::Kind::kTernary: {
317 const TernaryExpression& t = expression.as<TernaryExpression>();
318 return TernaryExpression::Make(*fContext, pos, expr(t.test()),
319 expr(t.ifTrue()), expr(t.ifFalse()));
320 }
321 case Expression::Kind::kTypeReference:
322 return expression.clone(pos);
323 case Expression::Kind::kVariableReference: {
324 const VariableReference& v = expression.as<VariableReference>();
325 std::unique_ptr<Expression>* remap = varMap->find(v.variable());
326 if (remap) {
327 return clone_with_ref_kind(**remap, v.refKind(), pos);
328 }
329 return expression.clone(pos);
330 }
331 default:
332 SkDEBUGFAILF("unsupported expression: %s", expression.description().c_str());
333 return nullptr;
334 }
335}
336
337std::unique_ptr<Statement> Inliner::inlineStatement(Position pos,
338 VariableRewriteMap* varMap,
339 SymbolTable* symbolTableForStatement,
340 std::unique_ptr<Expression>* resultExpr,
341 Analysis::ReturnComplexity returnComplexity,
342 const Statement& statement,
343 const ProgramUsage& usage,
344 bool isBuiltinCode) {
345 auto stmt = [&](const std::unique_ptr<Statement>& s) -> std::unique_ptr<Statement> {
346 if (s) {
347 return this->inlineStatement(pos, varMap, symbolTableForStatement, resultExpr,
348 returnComplexity, *s, usage, isBuiltinCode);
349 }
350 return nullptr;
351 };
352 auto expr = [&](const std::unique_ptr<Expression>& e) -> std::unique_ptr<Expression> {
353 if (e) {
354 return this->inlineExpression(pos, varMap, symbolTableForStatement, *e);
355 }
356 return nullptr;
357 };
358 auto variableModifiers = [&](const Variable& variable,
359 const Expression* initialValue) -> ModifierFlags {
360 return Transform::AddConstToVarModifiers(variable, initialValue, &usage);
361 };
362 auto makeWithChildSymbolTable = [&](auto callback) -> std::unique_ptr<Statement> {
363 SymbolTable* origSymbolTable = symbolTableForStatement;
364 auto childSymbols = std::make_unique<SymbolTable>(origSymbolTable, isBuiltinCode);
365 symbolTableForStatement = childSymbols.get();
366
367 std::unique_ptr<Statement> stmt = callback(std::move(childSymbols));
368
369 symbolTableForStatement = origSymbolTable;
370 return stmt;
371 };
372
373 ++fInlinedStatementCounter;
374
375 switch (statement.kind()) {
376 case Statement::Kind::kBlock:
377 return makeWithChildSymbolTable([&](std::unique_ptr<SymbolTable> symbolTable) {
378 const Block& block = statement.as<Block>();
379 StatementArray statements;
380 statements.reserve_exact(block.children().size());
381 for (const std::unique_ptr<Statement>& child : block.children()) {
382 statements.push_back(stmt(child));
383 }
384 return Block::Make(pos,
385 std::move(statements),
386 block.blockKind(),
387 std::move(symbolTable));
388 });
389
390 case Statement::Kind::kBreak:
392
395
397 return DiscardStatement::Make(*fContext, pos);
398
399 case Statement::Kind::kDo: {
400 const DoStatement& d = statement.as<DoStatement>();
401 return DoStatement::Make(*fContext, pos, stmt(d.statement()), expr(d.test()));
402 }
403 case Statement::Kind::kExpression: {
404 const ExpressionStatement& e = statement.as<ExpressionStatement>();
405 return ExpressionStatement::Make(*fContext, expr(e.expression()));
406 }
407 case Statement::Kind::kFor:
408 return makeWithChildSymbolTable([&](std::unique_ptr<SymbolTable> symbolTable) {
409 const ForStatement& f = statement.as<ForStatement>();
410 // We need to ensure `initializer` is evaluated first, so that we've already
411 // remapped its declaration by the time we evaluate `test` and `next`.
412 std::unique_ptr<Statement> initializerStmt = stmt(f.initializer());
413 std::unique_ptr<Expression> testExpr = expr(f.test());
414 std::unique_ptr<Expression> nextExpr = expr(f.next());
415 std::unique_ptr<Statement> bodyStmt = stmt(f.statement());
416
417 std::unique_ptr<LoopUnrollInfo> unrollInfo;
418 if (f.unrollInfo()) {
419 // The for loop's unroll-info points to the Variable in the initializer as the
420 // index. This variable has been rewritten into a clone by the inliner, so we
421 // need to update the loop-unroll info to point to the clone.
422 unrollInfo = std::make_unique<LoopUnrollInfo>(*f.unrollInfo());
423 unrollInfo->fIndex = RemapVariable(unrollInfo->fIndex, varMap);
424 }
425
426 return ForStatement::Make(*fContext, pos, ForLoopPositions{},
427 std::move(initializerStmt),
428 std::move(testExpr),
429 std::move(nextExpr),
430 std::move(bodyStmt),
431 std::move(unrollInfo),
432 std::move(symbolTable));
433 });
434
435 case Statement::Kind::kIf: {
436 const IfStatement& i = statement.as<IfStatement>();
437 return IfStatement::Make(*fContext, pos, expr(i.test()),
438 stmt(i.ifTrue()), stmt(i.ifFalse()));
439 }
441 return Nop::Make();
442
443 case Statement::Kind::kReturn: {
444 const ReturnStatement& r = statement.as<ReturnStatement>();
445 if (!r.expression()) {
446 // This function doesn't return a value. We won't inline functions with early
447 // returns, so a return statement is a no-op and can be treated as such.
448 return Nop::Make();
449 }
450
451 // If a function only contains a single return, and it doesn't reference variables from
452 // inside an Block's scope, we don't need to store the result in a variable at all. Just
453 // replace the function-call expression with the function's return expression.
454 SkASSERT(resultExpr);
455 if (returnComplexity <= Analysis::ReturnComplexity::kSingleSafeReturn) {
456 *resultExpr = expr(r.expression());
457 return Nop::Make();
458 }
459
460 // For more complex functions, we assign their result into a variable. We refuse to
461 // inline anything with early returns, so this should be safe to do; that is, on this
462 // control path, this is the last statement that will occur.
463 SkASSERT(*resultExpr);
465 *fContext,
467 *fContext,
468 pos,
469 clone_with_ref_kind(**resultExpr, VariableRefKind::kWrite, pos),
471 expr(r.expression())));
472 }
473 case Statement::Kind::kSwitch: {
474 const SwitchStatement& ss = statement.as<SwitchStatement>();
475 return SwitchStatement::Make(*fContext, pos, expr(ss.value()), stmt(ss.caseBlock()));
476 }
477 case Statement::Kind::kSwitchCase: {
478 const SwitchCase& sc = statement.as<SwitchCase>();
479 return sc.isDefault() ? SwitchCase::MakeDefault(pos, stmt(sc.statement()))
480 : SwitchCase::Make(pos, sc.value(), stmt(sc.statement()));
481 }
482 case Statement::Kind::kVarDeclaration: {
483 const VarDeclaration& decl = statement.as<VarDeclaration>();
484 std::unique_ptr<Expression> initialValue = expr(decl.value());
485 const Variable* variable = decl.var();
486
487 // We assign unique names to inlined variables--scopes hide most of the problems in this
488 // regard, but see `InlinerAvoidsVariableNameOverlap` for a counterexample where unique
489 // names are important.
490 const std::string* name = symbolTableForStatement->takeOwnershipOfString(
491 fMangler.uniqueName(variable->name(), symbolTableForStatement));
492 auto clonedVar =
494 variable->modifiersPosition(),
495 variable->layout(),
496 variableModifiers(*variable, initialValue.get()),
497 variable->type().clone(*fContext, symbolTableForStatement),
498 name->c_str(),
499 /*mangledName=*/"",
500 isBuiltinCode,
501 variable->storage());
502 varMap->set(variable, VariableReference::Make(pos, clonedVar.get()));
503 std::unique_ptr<Statement> result =
504 VarDeclaration::Make(*fContext,
505 clonedVar.get(),
506 decl.baseType().clone(*fContext, symbolTableForStatement),
507 decl.arraySize(),
508 std::move(initialValue));
509 symbolTableForStatement->add(*fContext, std::move(clonedVar));
510 return result;
511 }
512 default:
513 SkASSERT(false);
514 return nullptr;
515 }
516}
517
519 const Variable* param,
520 const ProgramUsage& usage) {
521 // If the parameter isn't written to within the inline function ...
522 const ProgramUsage::VariableCounts& paramUsage = usage.get(*param);
523 if (!paramUsage.fWrite) {
524 // ... and can be inlined trivially (e.g. a swizzle, or a constant array index),
525 // or any expression without side effects that is only accessed at most once...
526 if ((paramUsage.fRead > 1) ? Analysis::IsTrivialExpression(*arg)
527 : !Analysis::HasSideEffects(*arg)) {
528 // ... we don't need to copy it at all! We can just use the existing expression.
529 return false;
530 }
531 }
532 // We need a scratch variable.
533 return true;
534}
535
536Inliner::InlinedCall Inliner::inlineCall(const FunctionCall& call,
537 SymbolTable* symbolTable,
538 const ProgramUsage& usage,
539 const FunctionDeclaration* caller) {
540 using ScratchVariable = Variable::ScratchVariable;
541
542 // Inlining is more complicated here than in a typical compiler, because we have to have a
543 // high-level IR and can't just drop statements into the middle of an expression or even use
544 // gotos.
545 //
546 // Since we can't insert statements into an expression, we run the inline function as extra
547 // statements before the statement we're currently processing, relying on a lack of execution
548 // order guarantees.
549 SkASSERT(fContext);
550 SkASSERT(this->isSafeToInline(call.function().definition(), usage));
551
552 const ExpressionArray& arguments = call.arguments();
553 const Position pos = call.fPosition;
554 const FunctionDefinition& function = *call.function().definition();
555 const Block& body = function.body()->as<Block>();
557
558 StatementArray inlineStatements;
559 int expectedStmtCount = 1 + // Result variable
560 arguments.size() + // Function argument temp-vars
561 body.children().size(); // Inlined code
562
563 inlineStatements.reserve_exact(expectedStmtCount);
564
565 std::unique_ptr<Expression> resultExpr;
566 if (returnComplexity > Analysis::ReturnComplexity::kSingleSafeReturn &&
567 !function.declaration().returnType().isVoid()) {
568 // Create a variable to hold the result in the extra statements. We don't need to do this
569 // for void-return functions, or in cases that are simple enough that we can just replace
570 // the function-call node with the result expression.
571 ScratchVariable var = Variable::MakeScratchVariable(*fContext,
572 fMangler,
573 function.declaration().name(),
574 &function.declaration().returnType(),
575 symbolTable,
576 /*initialValue=*/nullptr);
577 inlineStatements.push_back(std::move(var.fVarDecl));
578 resultExpr = VariableReference::Make(Position(), var.fVarSymbol);
579 }
580
581 // Create variables in the extra statements to hold the arguments, and assign the arguments to
582 // them.
583 VariableRewriteMap varMap;
584 for (int i = 0; i < arguments.size(); ++i) {
585 const Expression* arg = arguments[i].get();
586 const Variable* param = function.declaration().parameters()[i];
587 if (!argument_needs_scratch_variable(arg, param, usage)) {
588 varMap.set(param, arg->clone());
589 continue;
590 }
591 ScratchVariable var = Variable::MakeScratchVariable(*fContext,
592 fMangler,
593 param->name(),
594 &arg->type(),
595 symbolTable,
596 arg->clone());
597 inlineStatements.push_back(std::move(var.fVarDecl));
598 varMap.set(param, VariableReference::Make(Position(), var.fVarSymbol));
599 }
600
601 for (const std::unique_ptr<Statement>& stmt : body.children()) {
602 inlineStatements.push_back(this->inlineStatement(pos, &varMap, symbolTable,
603 &resultExpr, returnComplexity, *stmt,
604 usage, caller->isBuiltin()));
605 }
606
607 SkASSERT(inlineStatements.size() <= expectedStmtCount);
608
609 // Wrap all of the generated statements in a block. We need a real Block here, because we need
610 // to add another child statement to the Block later.
611 InlinedCall inlinedCall;
612 inlinedCall.fInlinedBody = Block::MakeBlock(pos, std::move(inlineStatements),
613 Block::Kind::kUnbracedBlock);
614 if (resultExpr) {
615 // Return our result expression as-is.
616 inlinedCall.fReplacementExpr = std::move(resultExpr);
617 } else if (function.declaration().returnType().isVoid()) {
618 // It's a void function, so its result is the empty expression.
619 inlinedCall.fReplacementExpr = EmptyExpression::Make(pos, *fContext);
620 } else {
621 // It's a non-void function, but it never created a result expression--that is, it never
622 // returned anything on any path! This should have been detected in the function finalizer.
623 // Still, discard our output and generate an error.
624 SkDEBUGFAIL("inliner found non-void function that fails to return a value on any path");
625 fContext->fErrors->error(function.fPosition, "inliner found non-void function '" +
626 std::string(function.declaration().name()) +
627 "' that fails to return a value on any path");
628 inlinedCall = {};
629 }
630
631 return inlinedCall;
632}
633
634bool Inliner::isSafeToInline(const FunctionDefinition* functionDef, const ProgramUsage& usage) {
635 // A threshold of zero indicates that the inliner is completely disabled, so we can just return.
636 if (this->settings().fInlineThreshold <= 0) {
637 return false;
638 }
639
640 // Enforce a limit on inlining to avoid pathological cases. (inliner/ExponentialGrowth.sksl)
641 if (fInlinedStatementCounter >= kInlinedStatementLimit) {
642 return false;
643 }
644
645 if (functionDef == nullptr) {
646 // Can't inline something if we don't actually have its definition.
647 return false;
648 }
649
650 if (functionDef->declaration().modifierFlags().isNoInline()) {
651 // Refuse to inline functions decorated with `noinline`.
652 return false;
653 }
654
655 for (const Variable* param : functionDef->declaration().parameters()) {
656 // We don't allow inlining functions with parameters that are written-to, if they...
657 // - are `out` parameters (see skia:11326 for rationale.)
658 // - are arrays or structures (introducing temporary copies is non-trivial)
659 if ((param->modifierFlags() & ModifierFlag::kOut) ||
660 param->type().isArray() ||
661 param->type().isStruct()) {
662 ProgramUsage::VariableCounts counts = usage.get(*param);
663 if (counts.fWrite > 0) {
664 return false;
665 }
666 }
667 }
668
669 // We don't have a mechanism to simulate early returns, so we can't inline if there is one.
671}
672
673// A candidate function for inlining, containing everything that `inlineCall` needs.
675 SymbolTable* fSymbols; // the SymbolTable of the candidate
676 std::unique_ptr<Statement>* fParentStmt; // the parent Statement of the enclosing stmt
677 std::unique_ptr<Statement>* fEnclosingStmt; // the Statement containing the candidate
678 std::unique_ptr<Expression>* fCandidateExpr; // the candidate FunctionCall to be inlined
679 FunctionDefinition* fEnclosingFunction; // the Function containing the candidate
680};
681
683 std::vector<InlineCandidate> fCandidates;
684};
685
687public:
688 // A list of all the inlining candidates we found during analysis.
690
691 // A stack of the symbol tables; since most nodes don't have one, expected to be shallower than
692 // the enclosing-statement stack.
693 std::vector<SymbolTable*> fSymbolTableStack;
694 // A stack of "enclosing" statements--these would be suitable for the inliner to use for adding
695 // new instructions. Not all statements are suitable (e.g. a for-loop's initializer). The
696 // inliner might replace a statement with a block containing the statement.
697 std::vector<std::unique_ptr<Statement>*> fEnclosingStmtStack;
698 // The function that we're currently processing (i.e. inlining into).
700
701 void visit(const std::vector<std::unique_ptr<ProgramElement>>& elements,
703 InlineCandidateList* candidateList) {
704 fCandidateList = candidateList;
705 fSymbolTableStack.push_back(symbols);
706
707 for (const std::unique_ptr<ProgramElement>& pe : elements) {
708 this->visitProgramElement(pe.get());
709 }
710
711 fSymbolTableStack.pop_back();
712 fCandidateList = nullptr;
713 }
714
716 switch (pe->kind()) {
718 FunctionDefinition& funcDef = pe->as<FunctionDefinition>();
719
720 // If this function has parameter names that would shadow globally-scoped names, we
721 // don't scan it for inline candidates, because it's too late to mangle the names.
722 bool foundShadowingParameterName = false;
723 for (const Variable* param : funcDef.declaration().parameters()) {
724 if (fSymbolTableStack.front()->find(param->name())) {
725 foundShadowingParameterName = true;
726 break;
727 }
728 }
729
730 if (!foundShadowingParameterName) {
731 fEnclosingFunction = &funcDef;
732 this->visitStatement(&funcDef.body());
733 }
734 break;
735 }
736 default:
737 // The inliner can't operate outside of a function's scope.
738 break;
739 }
740 }
741
742 void visitStatement(std::unique_ptr<Statement>* stmt,
743 bool isViableAsEnclosingStatement = true) {
744 if (!*stmt) {
745 return;
746 }
747
748 Analysis::SymbolTableStackBuilder scopedStackBuilder(stmt->get(), &fSymbolTableStack);
749 // If this statement contains symbols that would shadow globally-scoped names, we don't look
750 // for any inline candidates, because it's too late to mangle the names.
751 if (scopedStackBuilder.foundSymbolTable() &&
752 fSymbolTableStack.back()->wouldShadowSymbolsFrom(fSymbolTableStack.front())) {
753 return;
754 }
755
756 size_t oldEnclosingStmtStackSize = fEnclosingStmtStack.size();
757
758 if (isViableAsEnclosingStatement) {
759 fEnclosingStmtStack.push_back(stmt);
760 }
761
762 switch ((*stmt)->kind()) {
763 case Statement::Kind::kBreak:
767 break;
768
769 case Statement::Kind::kBlock: {
770 Block& block = (*stmt)->as<Block>();
771 for (std::unique_ptr<Statement>& blockStmt : block.children()) {
772 this->visitStatement(&blockStmt);
773 }
774 break;
775 }
776 case Statement::Kind::kDo: {
777 DoStatement& doStmt = (*stmt)->as<DoStatement>();
778 // The loop body is a candidate for inlining.
779 this->visitStatement(&doStmt.statement());
780 // The inliner isn't smart enough to inline the test-expression for a do-while
781 // loop at this time. There are two limitations:
782 // - We would need to insert the inlined-body block at the very end of the do-
783 // statement's inner fStatement. We don't support that today, but it's doable.
784 // - We cannot inline the test expression if the loop uses `continue` anywhere; that
785 // would skip over the inlined block that evaluates the test expression. There
786 // isn't a good fix for this--any workaround would be more complex than the cost
787 // of a function call. However, loops that don't use `continue` would still be
788 // viable candidates for inlining.
789 break;
790 }
791 case Statement::Kind::kExpression: {
792 ExpressionStatement& expr = (*stmt)->as<ExpressionStatement>();
793 this->visitExpression(&expr.expression());
794 break;
795 }
796 case Statement::Kind::kFor: {
797 ForStatement& forStmt = (*stmt)->as<ForStatement>();
798 // The initializer and loop body are candidates for inlining.
799 this->visitStatement(&forStmt.initializer(),
800 /*isViableAsEnclosingStatement=*/false);
801 this->visitStatement(&forStmt.statement());
802
803 // The inliner isn't smart enough to inline the test- or increment-expressions
804 // of a for loop loop at this time. There are a handful of limitations:
805 // - We would need to insert the test-expression block at the very beginning of the
806 // for-loop's inner fStatement, and the increment-expression block at the very
807 // end. We don't support that today, but it's doable.
808 // - The for-loop's built-in test-expression would need to be dropped entirely,
809 // and the loop would be halted via a break statement at the end of the inlined
810 // test-expression. This is again something we don't support today, but it could
811 // be implemented.
812 // - We cannot inline the increment-expression if the loop uses `continue` anywhere;
813 // that would skip over the inlined block that evaluates the increment expression.
814 // There isn't a good fix for this--any workaround would be more complex than the
815 // cost of a function call. However, loops that don't use `continue` would still
816 // be viable candidates for increment-expression inlining.
817 break;
818 }
819 case Statement::Kind::kIf: {
820 IfStatement& ifStmt = (*stmt)->as<IfStatement>();
821 this->visitExpression(&ifStmt.test());
822 this->visitStatement(&ifStmt.ifTrue());
823 this->visitStatement(&ifStmt.ifFalse());
824 break;
825 }
826 case Statement::Kind::kReturn: {
827 ReturnStatement& returnStmt = (*stmt)->as<ReturnStatement>();
828 this->visitExpression(&returnStmt.expression());
829 break;
830 }
831 case Statement::Kind::kSwitch: {
832 SwitchStatement& switchStmt = (*stmt)->as<SwitchStatement>();
833 this->visitExpression(&switchStmt.value());
834 for (const std::unique_ptr<Statement>& switchCase : switchStmt.cases()) {
835 // The switch-case's fValue cannot be a FunctionCall; skip it.
836 this->visitStatement(&switchCase->as<SwitchCase>().statement());
837 }
838 break;
839 }
840 case Statement::Kind::kVarDeclaration: {
841 VarDeclaration& varDeclStmt = (*stmt)->as<VarDeclaration>();
842 // Don't need to scan the declaration's sizes; those are always literals.
843 this->visitExpression(&varDeclStmt.value());
844 break;
845 }
846 default:
848 }
849
850 // Pop our symbol and enclosing-statement stacks.
851 fEnclosingStmtStack.resize(oldEnclosingStmtStackSize);
852 }
853
854 void visitExpression(std::unique_ptr<Expression>* expr) {
855 if (!*expr) {
856 return;
857 }
858
859 switch ((*expr)->kind()) {
860 case Expression::Kind::kFieldAccess:
861 case Expression::Kind::kFunctionReference:
863 case Expression::Kind::kMethodReference:
864 case Expression::Kind::kSetting:
865 case Expression::Kind::kTypeReference:
866 case Expression::Kind::kVariableReference:
867 // Nothing to scan here.
868 break;
869
870 case Expression::Kind::kBinary: {
871 BinaryExpression& binaryExpr = (*expr)->as<BinaryExpression>();
872 this->visitExpression(&binaryExpr.left());
873
874 // Logical-and and logical-or binary expressions do not inline the right side,
875 // because that would invalidate short-circuiting. That is, when evaluating
876 // expressions like these:
877 // (false && x()) // always false
878 // (true || y()) // always true
879 // It is illegal for side-effects from x() or y() to occur. The simplest way to
880 // enforce that rule is to avoid inlining the right side entirely. However, it is
881 // safe for other types of binary expression to inline both sides.
882 Operator op = binaryExpr.getOperator();
883 bool shortCircuitable = (op.kind() == Operator::Kind::LOGICALAND ||
884 op.kind() == Operator::Kind::LOGICALOR);
885 if (!shortCircuitable) {
886 this->visitExpression(&binaryExpr.right());
887 }
888 break;
889 }
890 case Expression::Kind::kChildCall: {
891 ChildCall& childCallExpr = (*expr)->as<ChildCall>();
892 for (std::unique_ptr<Expression>& arg : childCallExpr.arguments()) {
893 this->visitExpression(&arg);
894 }
895 break;
896 }
897 case Expression::Kind::kConstructorArray:
898 case Expression::Kind::kConstructorArrayCast:
899 case Expression::Kind::kConstructorCompound:
900 case Expression::Kind::kConstructorCompoundCast:
901 case Expression::Kind::kConstructorDiagonalMatrix:
902 case Expression::Kind::kConstructorMatrixResize:
903 case Expression::Kind::kConstructorScalarCast:
904 case Expression::Kind::kConstructorSplat:
905 case Expression::Kind::kConstructorStruct: {
906 AnyConstructor& constructorExpr = (*expr)->asAnyConstructor();
907 for (std::unique_ptr<Expression>& arg : constructorExpr.argumentSpan()) {
908 this->visitExpression(&arg);
909 }
910 break;
911 }
912 case Expression::Kind::kFunctionCall: {
913 FunctionCall& funcCallExpr = (*expr)->as<FunctionCall>();
914 for (std::unique_ptr<Expression>& arg : funcCallExpr.arguments()) {
915 this->visitExpression(&arg);
916 }
917 this->addInlineCandidate(expr);
918 break;
919 }
921 IndexExpression& indexExpr = (*expr)->as<IndexExpression>();
922 this->visitExpression(&indexExpr.base());
923 this->visitExpression(&indexExpr.index());
924 break;
925 }
926 case Expression::Kind::kPostfix: {
927 PostfixExpression& postfixExpr = (*expr)->as<PostfixExpression>();
928 this->visitExpression(&postfixExpr.operand());
929 break;
930 }
931 case Expression::Kind::kPrefix: {
932 PrefixExpression& prefixExpr = (*expr)->as<PrefixExpression>();
933 this->visitExpression(&prefixExpr.operand());
934 break;
935 }
936 case Expression::Kind::kSwizzle: {
937 Swizzle& swizzleExpr = (*expr)->as<Swizzle>();
938 this->visitExpression(&swizzleExpr.base());
939 break;
940 }
941 case Expression::Kind::kTernary: {
942 TernaryExpression& ternaryExpr = (*expr)->as<TernaryExpression>();
943 // The test expression is a candidate for inlining.
944 this->visitExpression(&ternaryExpr.test());
945 // The true- and false-expressions cannot be inlined, because we are only allowed to
946 // evaluate one side.
947 break;
948 }
949 default:
951 }
952 }
953
954 void addInlineCandidate(std::unique_ptr<Expression>* candidate) {
955 fCandidateList->fCandidates.push_back(
957 find_parent_statement(fEnclosingStmtStack),
958 fEnclosingStmtStack.back(),
959 candidate,
961 }
962};
963
964static const FunctionDeclaration& candidate_func(const InlineCandidate& candidate) {
965 return (*candidate.fCandidateExpr)->as<FunctionCall>().function();
966}
967
968bool Inliner::functionCanBeInlined(const FunctionDeclaration& funcDecl,
969 const ProgramUsage& usage,
970 InlinabilityCache* cache) {
971 if (const bool* cachedInlinability = cache->find(&funcDecl)) {
972 return *cachedInlinability;
973 }
974 bool inlinability = this->isSafeToInline(funcDecl.definition(), usage);
975 cache->set(&funcDecl, inlinability);
976 return inlinability;
977}
978
979bool Inliner::candidateCanBeInlined(const InlineCandidate& candidate,
980 const ProgramUsage& usage,
981 InlinabilityCache* cache) {
982 // Check the cache to see if this function is safe to inline.
983 const FunctionDeclaration& funcDecl = candidate_func(candidate);
984 if (!this->functionCanBeInlined(funcDecl, usage, cache)) {
985 return false;
986 }
987
988 // Even if the function is safe, the arguments we are passing may not be. In particular, we
989 // can't make copies of opaque values, so we need to reject inline candidates that would need to
990 // do this. Every call has different arguments, so this part is not cacheable. (skia:13824)
991 const FunctionCall& call = candidate.fCandidateExpr->get()->as<FunctionCall>();
992 const ExpressionArray& arguments = call.arguments();
993 for (int i = 0; i < arguments.size(); ++i) {
994 const Expression* arg = arguments[i].get();
995 if (arg->type().isOpaque()) {
996 const Variable* param = funcDecl.parameters()[i];
997 if (argument_needs_scratch_variable(arg, param, usage)) {
998 return false;
999 }
1000 }
1001 }
1002
1003 return true;
1004}
1005
1006int Inliner::getFunctionSize(const FunctionDeclaration& funcDecl, FunctionSizeCache* cache) {
1007 if (const int* cachedSize = cache->find(&funcDecl)) {
1008 return *cachedSize;
1009 }
1010 int size = Analysis::NodeCountUpToLimit(*funcDecl.definition(),
1011 this->settings().fInlineThreshold);
1012 cache->set(&funcDecl, size);
1013 return size;
1014}
1015
1016void Inliner::buildCandidateList(const std::vector<std::unique_ptr<ProgramElement>>& elements,
1017 SymbolTable* symbols,
1018 ProgramUsage* usage,
1019 InlineCandidateList* candidateList) {
1020 // This is structured much like a ProgramVisitor, but does not actually use ProgramVisitor.
1021 // The analyzer needs to keep track of the `unique_ptr<T>*` of statements and expressions so
1022 // that they can later be replaced, and ProgramVisitor does not provide this; it only provides a
1023 // `const T&`.
1024 InlineCandidateAnalyzer analyzer;
1025 analyzer.visit(elements, symbols, candidateList);
1026
1027 // Early out if there are no inlining candidates.
1028 std::vector<InlineCandidate>& candidates = candidateList->fCandidates;
1029 if (candidates.empty()) {
1030 return;
1031 }
1032
1033 // Remove candidates that are not safe to inline.
1034 InlinabilityCache cache;
1035 candidates.erase(std::remove_if(candidates.begin(),
1036 candidates.end(),
1037 [&](const InlineCandidate& candidate) {
1038 return !this->candidateCanBeInlined(
1039 candidate, *usage, &cache);
1040 }),
1041 candidates.end());
1042
1043 // If the inline threshold is unlimited, or if we have no candidates left, our candidate list is
1044 // complete.
1045 if (this->settings().fInlineThreshold == INT_MAX || candidates.empty()) {
1046 return;
1047 }
1048
1049 // Remove candidates on a per-function basis if the effect of inlining would be to make more
1050 // than `inlineThreshold` nodes. (i.e. if Func() would be inlined six times and its size is
1051 // 10 nodes, it should be inlined if the inlineThreshold is 60 or higher.)
1052 FunctionSizeCache functionSizeCache;
1053 FunctionSizeCache candidateTotalCost;
1054 for (InlineCandidate& candidate : candidates) {
1055 const FunctionDeclaration& fnDecl = candidate_func(candidate);
1056 candidateTotalCost[&fnDecl] += this->getFunctionSize(fnDecl, &functionSizeCache);
1057 }
1058
1059 candidates.erase(std::remove_if(candidates.begin(), candidates.end(),
1060 [&](const InlineCandidate& candidate) {
1061 const FunctionDeclaration& fnDecl = candidate_func(candidate);
1062 if (fnDecl.modifierFlags().isInline()) {
1063 // Functions marked `inline` ignore size limitations.
1064 return false;
1065 }
1066 if (usage->get(fnDecl) == 1) {
1067 // If a function is only used once, it's cost-free to inline.
1068 return false;
1069 }
1070 if (candidateTotalCost[&fnDecl] <= this->settings().fInlineThreshold) {
1071 // We won't exceed the inline threshold by inlining this.
1072 return false;
1073 }
1074 // Inlining this function will add too many IRNodes.
1075 return true;
1076 }),
1077 candidates.end());
1078}
1079
1080bool Inliner::analyze(const std::vector<std::unique_ptr<ProgramElement>>& elements,
1083 // A threshold of zero indicates that the inliner is completely disabled, so we can just return.
1084 if (this->settings().fInlineThreshold <= 0) {
1085 return false;
1086 }
1087
1088 // Enforce a limit on inlining to avoid pathological cases. (inliner/ExponentialGrowth.sksl)
1089 if (fInlinedStatementCounter >= kInlinedStatementLimit) {
1090 return false;
1091 }
1092
1093 InlineCandidateList candidateList;
1094 this->buildCandidateList(elements, symbols, usage, &candidateList);
1095
1096 // Inline the candidates where we've determined that it's safe to do so.
1097 using StatementRemappingTable = THashMap<std::unique_ptr<Statement>*,
1098 std::unique_ptr<Statement>*>;
1099 StatementRemappingTable statementRemappingTable;
1100
1101 bool madeChanges = false;
1102 for (const InlineCandidate& candidate : candidateList.fCandidates) {
1103 const FunctionCall& funcCall = (*candidate.fCandidateExpr)->as<FunctionCall>();
1104
1105 // Convert the function call to its inlined equivalent.
1106 InlinedCall inlinedCall = this->inlineCall(funcCall, candidate.fSymbols, *usage,
1107 &candidate.fEnclosingFunction->declaration());
1108
1109 // Stop if an error was detected during the inlining process.
1110 if (!inlinedCall.fInlinedBody && !inlinedCall.fReplacementExpr) {
1111 break;
1112 }
1113
1114 // Ensure that the inlined body has a scope if it needs one.
1115 this->ensureScopedBlocks(inlinedCall.fInlinedBody.get(), candidate.fParentStmt->get());
1116
1117 // Add references within the inlined body
1118 usage->add(inlinedCall.fInlinedBody.get());
1119
1120 // Look up the enclosing statement; remap it if necessary.
1121 std::unique_ptr<Statement>* enclosingStmt = candidate.fEnclosingStmt;
1122 for (;;) {
1123 std::unique_ptr<Statement>** remappedStmt = statementRemappingTable.find(enclosingStmt);
1124 if (!remappedStmt) {
1125 break;
1126 }
1127 enclosingStmt = *remappedStmt;
1128 }
1129
1130 // Move the enclosing statement to the end of the unscoped Block containing the inlined
1131 // function, then replace the enclosing statement with that Block.
1132 // Before:
1133 // fInlinedBody = Block{ stmt1, stmt2, stmt3 }
1134 // fEnclosingStmt = stmt4
1135 // After:
1136 // fInlinedBody = null
1137 // fEnclosingStmt = Block{ stmt1, stmt2, stmt3, stmt4 }
1138 inlinedCall.fInlinedBody->children().push_back(std::move(*enclosingStmt));
1139 *enclosingStmt = std::move(inlinedCall.fInlinedBody);
1140
1141 // Replace the candidate function call with our replacement expression.
1142 usage->remove(candidate.fCandidateExpr->get());
1143 usage->add(inlinedCall.fReplacementExpr.get());
1144 *candidate.fCandidateExpr = std::move(inlinedCall.fReplacementExpr);
1145 madeChanges = true;
1146
1147 // If anything else pointed at our enclosing statement, it's now pointing at a Block
1148 // containing many other statements as well. Maintain a fix-up table to account for this.
1149 statementRemappingTable.set(enclosingStmt,&(*enclosingStmt)->as<Block>().children().back());
1150
1151 // Stop inlining if we've reached our hard cap on new statements.
1152 if (fInlinedStatementCounter >= kInlinedStatementLimit) {
1153 break;
1154 }
1155
1156 // Note that nothing was destroyed except for the FunctionCall. All other nodes should
1157 // remain valid.
1158 }
1159
1160 return madeChanges;
1161}
1162
1163} // namespace SkSL
1164
1165#endif // SK_ENABLE_OPTIMIZE_SIZE
SkPoint pos
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkDEBUGFAILF(fmt,...)
Definition: SkAssert.h:119
#define SkASSERT(cond)
Definition: SkAssert.h:116
virtual SkSpan< std::unique_ptr< Expression > > argumentSpan()=0
std::unique_ptr< Expression > & left()
std::unique_ptr< Expression > & right()
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > left, Operator op, std::unique_ptr< Expression > right)
Operator getOperator() const
const StatementArray & children() const
Definition: SkSLBlock.h:71
static std::unique_ptr< Statement > Make(Position pos, StatementArray statements, Kind kind=Kind::kBracedScope, std::unique_ptr< SymbolTable > symbols=nullptr)
Definition: SkSLBlock.cpp:14
static std::unique_ptr< Block > MakeBlock(Position pos, StatementArray statements, Kind kind=Kind::kBracedScope, std::unique_ptr< SymbolTable > symbols=nullptr)
Definition: SkSLBlock.cpp:59
static std::unique_ptr< Statement > Make(Position pos)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type *returnType, const Variable &child, ExpressionArray arguments)
ExpressionArray & arguments()
Definition: SkSLChildCall.h:51
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, ExpressionArray args)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, ExpressionArray args)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, std::unique_ptr< Expression > arg)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type &type, ExpressionArray args)
ErrorReporter * fErrors
Definition: SkSLContext.h:36
static std::unique_ptr< Statement > Make(Position pos)
static std::unique_ptr< Statement > Make(const Context &context, Position pos)
static std::unique_ptr< Statement > Make(const Context &context, Position pos, std::unique_ptr< Statement > stmt, std::unique_ptr< Expression > test)
std::unique_ptr< Statement > & statement()
static std::unique_ptr< Expression > Make(Position pos, const Context &context)
void error(Position position, std::string_view msg)
static std::unique_ptr< Statement > Make(const Context &context, std::unique_ptr< Expression > expr)
const std::unique_ptr< Expression > & expression() const
AnyConstructor & asAnyConstructor()
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > base, int fieldIndex, OwnerKind ownerKind=OwnerKind::kDefault)
std::unique_ptr< Statement > & statement()
static std::unique_ptr< Statement > Make(const Context &context, Position pos, ForLoopPositions forLoopPositions, std::unique_ptr< Statement > initializer, std::unique_ptr< Expression > test, std::unique_ptr< Expression > next, std::unique_ptr< Statement > statement, std::unique_ptr< LoopUnrollInfo > unrollInfo, std::unique_ptr< SymbolTable > symbolTable)
std::unique_ptr< Statement > & initializer()
ExpressionArray & arguments()
static std::unique_ptr< Expression > Make(const Context &context, Position pos, const Type *returnType, const FunctionDeclaration &function, ExpressionArray arguments)
SkSpan< Variable *const > parameters() const
std::unique_ptr< Statement > & body()
const FunctionDeclaration & declaration() const
const T & as() const
Definition: SkSLIRNode.h:133
static std::unique_ptr< Statement > Make(const Context &context, Position pos, std::unique_ptr< Expression > test, std::unique_ptr< Statement > ifTrue, std::unique_ptr< Statement > ifFalse)
std::unique_ptr< Expression > & test()
std::unique_ptr< Statement > & ifTrue()
std::unique_ptr< Statement > & ifFalse()
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > base, std::unique_ptr< Expression > index)
std::unique_ptr< Expression > & base()
std::unique_ptr< Expression > & index()
InlineCandidateList * fCandidateList
void addInlineCandidate(std::unique_ptr< Expression > *candidate)
std::vector< SymbolTable * > fSymbolTableStack
void visit(const std::vector< std::unique_ptr< ProgramElement > > &elements, SymbolTable *symbols, InlineCandidateList *candidateList)
FunctionDefinition * fEnclosingFunction
void visitStatement(std::unique_ptr< Statement > *stmt, bool isViableAsEnclosingStatement=true)
void visitProgramElement(ProgramElement *pe)
std::vector< std::unique_ptr< Statement > * > fEnclosingStmtStack
void visitExpression(std::unique_ptr< Expression > *expr)
std::string uniqueName(std::string_view baseName, SymbolTable *symbolTable)
Definition: SkSLMangler.cpp:21
static std::unique_ptr< Statement > Make()
Definition: SkSLNop.h:26
Kind kind() const
Definition: SkSLOperator.h:85
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > base, Operator op)
std::unique_ptr< Expression > & operand()
static std::unique_ptr< Expression > Make(const Context &context, Position pos, Operator op, std::unique_ptr< Expression > base)
std::unique_ptr< Expression > & operand()
std::unique_ptr< Expression > & expression()
static std::unique_ptr< Expression > Make(const Context &context, Position pos, CapsPtr capsPtr)
Definition: SkSLSetting.cpp:82
static std::unique_ptr< SwitchCase > MakeDefault(Position pos, std::unique_ptr< Statement > statement)
std::unique_ptr< Statement > & statement()
std::unique_ptr< Expression > & value()
static std::unique_ptr< Statement > Make(const Context &context, Position pos, std::unique_ptr< Expression > value, std::unique_ptr< Statement > caseBlock)
StatementArray & cases()
std::unique_ptr< Expression > & base()
Definition: SkSLSwizzle.h:82
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > expr, ComponentArray inComponents)
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > test, std::unique_ptr< Expression > ifTrue, std::unique_ptr< Expression > ifFalse)
std::unique_ptr< Expression > & test()
std::unique_ptr< Expression > & value()
static std::unique_ptr< VarDeclaration > Make(const Context &context, Variable *var, const Type *baseType, int arraySize, std::unique_ptr< Expression > value)
static std::unique_ptr< Expression > Make(Position pos, const Variable *variable, RefKind refKind=RefKind::kRead)
static std::unique_ptr< Variable > Make(Position pos, Position modifiersPosition, const Layout &layout, ModifierFlags flags, const Type *type, std::string_view name, std::string mangledName, bool builtin, Storage storage)
static ScratchVariable MakeScratchVariable(const Context &context, Mangler &mangler, std::string_view baseName, const Type *type, SymbolTable *symbolTable, std::unique_ptr< Expression > initialValue)
int size() const
Definition: SkTArray.h:421
void reserve_exact(int n)
Definition: SkTArray.h:181
V * find(const K &key) const
Definition: SkTHash.h:494
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
struct MyStruct s
if(end==-1)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
uint8_t value
GAsyncResult * result
Dart_NativeFunction function
Definition: fuchsia.cc:51
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
ReturnComplexity GetReturnComplexity(const FunctionDefinition &funcDef)
int NodeCountUpToLimit(const FunctionDefinition &function, int limit)
bool IsTrivialExpression(const Expression &expr)
bool HasSideEffects(const Expression &expr)
bool UpdateVariableRefKind(Expression *expr, VariableRefKind kind, ErrorReporter *errors=nullptr)
ModifierFlags AddConstToVarModifiers(const Variable &var, const Expression *initialValue, const ProgramUsage *usage)
static bool argument_needs_scratch_variable(const Expression *arg, const Variable *param, const ProgramUsage &usage)
static const FunctionDeclaration & candidate_func(const InlineCandidate &candidate)
skia_private::STArray< 2, std::unique_ptr< Statement > > StatementArray
Definition: SkSLDefines.h:32
def analyze()
Definition: dom.py:43
def call(args)
Definition: dom.py:159
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core counts
Definition: switches.h:239
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static void usage(char *argv0)
std::vector< InlineCandidate > fCandidates
FunctionDefinition * fEnclosingFunction
SymbolTable * fSymbols
std::unique_ptr< Expression > * fCandidateExpr
std::unique_ptr< Statement > * fEnclosingStmt
std::unique_ptr< Statement > * fParentStmt