@@ -520,15 +520,15 @@ std::unique_ptr<Statement> Inliner::inlineStatement(int offset,
520
520
StatementArray block;
521
521
block.reserve_back (2 );
522
522
block.push_back (std::move (assignment));
523
- block.push_back (std::make_unique<BreakStatement >(offset));
524
- return std::make_unique<Block>(offset, std::move (block), /* symbols= */ nullptr ,
525
- /* isScope=*/ true );
523
+ block.push_back (std::make_unique<ContinueStatement >(offset));
524
+ return std::make_unique<Block>(offset, std::move (block),
525
+ /* symbols= */ nullptr , /* isScope=*/ true );
526
526
} else {
527
527
return std::move (assignment);
528
528
}
529
529
} else {
530
530
if (haveEarlyReturns) {
531
- return std::make_unique<BreakStatement >(offset);
531
+ return std::make_unique<ContinueStatement >(offset);
532
532
} else {
533
533
return std::make_unique<Nop>();
534
534
}
@@ -577,6 +577,51 @@ std::unique_ptr<Statement> Inliner::inlineStatement(int offset,
577
577
}
578
578
}
579
579
580
+ Inliner::InlineVariable Inliner::makeInlineVariable (const String& baseName,
581
+ const Type* type,
582
+ SymbolTable* symbolTable,
583
+ Modifiers modifiers,
584
+ bool isBuiltinCode,
585
+ std::unique_ptr<Expression>* initialValue) {
586
+ // $floatLiteral or $intLiteral aren't real types that we can use for scratch variables, so
587
+ // replace them if they ever appear here. If this happens, we likely forgot to coerce a type
588
+ // somewhere during compilation.
589
+ if (type == fContext ->fFloatLiteral_Type .get ()) {
590
+ SkDEBUGFAIL (" found a $floatLiteral type while inlining" );
591
+ type = fContext ->fFloat_Type .get ();
592
+ } else if (type == fContext ->fIntLiteral_Type .get ()) {
593
+ SkDEBUGFAIL (" found an $intLiteral type while inlining" );
594
+ type = fContext ->fInt_Type .get ();
595
+ }
596
+
597
+ // Provide our new variable with a unique name, and add it to our symbol table.
598
+ const String* namePtr = symbolTable->takeOwnershipOfString (
599
+ std::make_unique<String>(this ->uniqueNameForInlineVar (baseName, symbolTable)));
600
+ StringFragment nameFrag{namePtr->c_str (), namePtr->length ()};
601
+
602
+ // Create our new variable and add it to the symbol table.
603
+ InlineVariable result;
604
+ result.fVarSymbol =
605
+ symbolTable->add (std::make_unique<Variable>(/* offset=*/ -1 ,
606
+ fModifiers ->addToPool (Modifiers ()),
607
+ nameFrag,
608
+ type,
609
+ isBuiltinCode,
610
+ Variable::Storage::kLocal ,
611
+ initialValue->get ()));
612
+
613
+ // Prepare the variable declaration (taking extra care with `out` params to not clobber any
614
+ // initial value).
615
+ if (*initialValue && (modifiers.fFlags & Modifiers::kOut_Flag )) {
616
+ result.fVarDecl = std::make_unique<VarDeclaration>(result.fVarSymbol , type, /* arraySize=*/ 0 ,
617
+ (*initialValue)->clone ());
618
+ } else {
619
+ result.fVarDecl = std::make_unique<VarDeclaration>(result.fVarSymbol , type, /* arraySize=*/ 0 ,
620
+ std::move (*initialValue));
621
+ }
622
+ return result;
623
+ }
624
+
580
625
Inliner::InlinedCall Inliner::inlineCall (FunctionCall* call,
581
626
std::shared_ptr<SymbolTable> symbolTable,
582
627
const FunctionDeclaration* caller) {
@@ -610,59 +655,20 @@ Inliner::InlinedCall Inliner::inlineCall(FunctionCall* call,
610
655
1 + // Result variable
611
656
arguments.size () + // Function arguments (passing in)
612
657
arguments.size () + // Function arguments (copy out-params back)
613
- 1 ); // Inlined code ( Block or do-while loop)
658
+ 1 ); // Block for inlined code
614
659
615
660
inlinedBody.children ().push_back (std::make_unique<InlineMarker>(&call->function ()));
616
661
617
- auto makeInlineVar =
618
- [&](const String& baseName, const Type* type, Modifiers modifiers,
619
- std::unique_ptr<Expression>* initialValue) -> std::unique_ptr<Expression> {
620
- // $floatLiteral or $intLiteral aren't real types that we can use for scratch variables, so
621
- // replace them if they ever appear here. If this happens, we likely forgot to coerce a type
622
- // somewhere during compilation.
623
- if (type == fContext ->fFloatLiteral_Type .get ()) {
624
- SkDEBUGFAIL (" found a $floatLiteral type while inlining" );
625
- type = fContext ->fFloat_Type .get ();
626
- } else if (type == fContext ->fIntLiteral_Type .get ()) {
627
- SkDEBUGFAIL (" found an $intLiteral type while inlining" );
628
- type = fContext ->fInt_Type .get ();
629
- }
630
-
631
- // Provide our new variable with a unique name, and add it to our symbol table.
632
- const String* namePtr = symbolTable->takeOwnershipOfString (std::make_unique<String>(
633
- this ->uniqueNameForInlineVar (baseName, symbolTable.get ())));
634
- StringFragment nameFrag{namePtr->c_str (), namePtr->length ()};
635
-
636
- // Add our new variable to the symbol table.
637
- const Variable* variableSymbol = symbolTable->add (std::make_unique<Variable>(
638
- /* offset=*/ -1 , fModifiers ->addToPool (Modifiers ()),
639
- nameFrag, type, caller->isBuiltin (),
640
- Variable::Storage::kLocal , initialValue->get ()));
641
-
642
- // Prepare the variable declaration (taking extra care with `out` params to not clobber any
643
- // initial value).
644
- std::unique_ptr<Statement> variable;
645
- if (initialValue && (modifiers.fFlags & Modifiers::kOut_Flag )) {
646
- variable = std::make_unique<VarDeclaration>(variableSymbol, type, /* arraySize=*/ 0 ,
647
- (*initialValue)->clone ());
648
- } else {
649
- variable = std::make_unique<VarDeclaration>(variableSymbol, type, /* arraySize=*/ 0 ,
650
- std::move (*initialValue));
651
- }
652
-
653
- // Add the new variable-declaration statement to our block of extra statements.
654
- inlinedBody.children ().push_back (std::move (variable));
655
-
656
- return std::make_unique<VariableReference>(offset, variableSymbol);
657
- };
658
-
659
662
// Create a variable to hold the result in the extra statements (excepting void).
660
663
std::unique_ptr<Expression> resultExpr;
661
664
if (function.declaration ().returnType () != *fContext ->fVoid_Type ) {
662
665
std::unique_ptr<Expression> noInitialValue;
663
- resultExpr = makeInlineVar (String (function.declaration ().name ()),
664
- &function.declaration ().returnType (),
665
- Modifiers{}, &noInitialValue);
666
+ InlineVariable var = this ->makeInlineVariable (function.declaration ().name (),
667
+ &function.declaration ().returnType (),
668
+ symbolTable.get (), Modifiers{},
669
+ caller->isBuiltin (), &noInitialValue);
670
+ inlinedBody.children ().push_back (std::move (var.fVarDecl ));
671
+ resultExpr = std::make_unique<VariableReference>(/* offset=*/ -1 , var.fVarSymbol );
666
672
}
667
673
668
674
// Create variables in the extra statements to hold the arguments, and assign the arguments to
@@ -682,44 +688,81 @@ Inliner::InlinedCall Inliner::inlineCall(FunctionCall* call,
682
688
continue ;
683
689
}
684
690
}
685
-
686
691
if (isOutParam) {
687
692
argsToCopyBack.push_back (i);
688
693
}
689
-
690
- varMap[param] = makeInlineVar (String (param->name ()), &arguments[i]->type (),
691
- param->modifiers (), &arguments[i]);
694
+ InlineVariable var = this ->makeInlineVariable (param->name (), &arguments[i]->type (),
695
+ symbolTable.get (), param->modifiers (),
696
+ caller->isBuiltin (), &arguments[i]);
697
+ inlinedBody.children ().push_back (std::move (var.fVarDecl ));
698
+ varMap[param] = std::make_unique<VariableReference>(/* offset=*/ -1 , var.fVarSymbol );
692
699
}
693
700
694
701
const Block& body = function.body ()->as <Block>();
695
- auto inlineBlock = std::make_unique<Block>(offset, StatementArray{},
696
- /* symbols=*/ nullptr , /* isScope=*/ hasEarlyReturn);
697
- inlineBlock->children ().reserve_back (body.children ().size ());
698
- for (const std::unique_ptr<Statement>& stmt : body.children ()) {
699
- inlineBlock->children ().push_back (this ->inlineStatement (offset, &varMap, symbolTable.get (),
700
- resultExpr.get (), hasEarlyReturn,
701
- *stmt, caller->isBuiltin ()));
702
- }
702
+ StatementArray* inlineStatements;
703
+
703
704
if (hasEarlyReturn) {
704
705
// Since we output to backends that don't have a goto statement (which would normally be
705
- // used to perform an early return), we fake it by wrapping the function in a
706
- // do { } while (false); and then use break statements to jump to the end in order to
707
- // emulate a goto.
708
- inlinedBody.children ().push_back (std::make_unique<DoStatement>(
706
+ // used to perform an early return), we fake it by wrapping the function in a single-
707
+ // iteration for loop, and use a continue statement to jump to the end of the loop
708
+ // prematurely.
709
+
710
+ // int _1_loop = 0;
711
+ symbolTable = std::make_shared<SymbolTable>(std::move (symbolTable), caller->isBuiltin ());
712
+ const Type* intType = fContext ->fInt_Type .get ();
713
+ std::unique_ptr<Expression> initialValue = std::make_unique<IntLiteral>(/* offset=*/ -1 ,
714
+ /* value=*/ 0 ,
715
+ intType);
716
+ InlineVariable loopVar = this ->makeInlineVariable (" loop" , intType, symbolTable.get (),
717
+ Modifiers{}, caller->isBuiltin (),
718
+ &initialValue);
719
+
720
+ // _1_loop < 1;
721
+ std::unique_ptr<Expression> test = std::make_unique<BinaryExpression>(
709
722
/* offset=*/ -1 ,
710
- std::move (inlineBlock),
711
- std::make_unique<BoolLiteral>(*fContext , offset, /* value=*/ false )));
723
+ std::make_unique<VariableReference>(/* offset=*/ -1 , loopVar.fVarSymbol ),
724
+ Token::Kind::TK_LT,
725
+ std::make_unique<IntLiteral>(/* offset=*/ -1 , /* value=*/ 1 , intType),
726
+ fContext ->fBool_Type .get ());
727
+
728
+ // _1_loop++
729
+ std::unique_ptr<Expression> increment = std::make_unique<PostfixExpression>(
730
+ std::make_unique<VariableReference>(/* offset=*/ -1 , loopVar.fVarSymbol ,
731
+ VariableReference::RefKind::kReadWrite ),
732
+ Token::Kind::TK_PLUSPLUS);
733
+
734
+ // {...}
735
+ auto innerBlock = std::make_unique<Block>(offset, StatementArray{},
736
+ /* symbols=*/ nullptr , /* isScope=*/ true );
737
+ inlineStatements = &innerBlock->children ();
738
+
739
+ // for (int _1_loop = 0; _1_loop < 1; _1_loop++) {...}
740
+ inlinedBody.children ().push_back (std::make_unique<ForStatement>(/* offset=*/ -1 ,
741
+ std::move (loopVar.fVarDecl ),
742
+ std::move (test),
743
+ std::move (increment),
744
+ std::move (innerBlock),
745
+ symbolTable));
712
746
} else {
713
- // No early returns, so we can just dump the code in. We still need to keep the block so we
714
- // don't get name conflicts with locals.
715
- inlinedBody.children ().push_back (std::move (inlineBlock));
747
+ // No early returns, so we can just dump the code into a scopeless block.
748
+ auto innerBlock = std::make_unique<Block>(offset, StatementArray{},
749
+ /* symbols=*/ nullptr , /* isScope=*/ false );
750
+ inlineStatements = &innerBlock->children ();
751
+ inlinedBody.children ().push_back (std::move (innerBlock));
752
+ }
753
+
754
+ inlineStatements->reserve_back (body.children ().size () + argsToCopyBack.size ());
755
+ for (const std::unique_ptr<Statement>& stmt : body.children ()) {
756
+ inlineStatements->push_back (this ->inlineStatement (offset, &varMap, symbolTable.get (),
757
+ resultExpr.get (), hasEarlyReturn, *stmt,
758
+ caller->isBuiltin ()));
716
759
}
717
760
718
761
// Copy back the values of `out` parameters into their real destinations.
719
762
for (int i : argsToCopyBack) {
720
763
const Variable* p = function.declaration ().parameters ()[i];
721
764
SkASSERT (varMap.find (p) != varMap.end ());
722
- inlinedBody. children (). push_back (
765
+ inlineStatements-> push_back (
723
766
std::make_unique<ExpressionStatement>(std::make_unique<BinaryExpression>(
724
767
offset,
725
768
clone_with_ref_kind (*arguments[i], VariableReference::RefKind::kWrite ),
@@ -762,18 +805,8 @@ bool Inliner::isSafeToInline(const FunctionDefinition* functionDef) {
762
805
return false ;
763
806
}
764
807
765
- if (!fCaps || !fCaps ->canUseDoLoops ()) {
766
- // We don't have do-while loops. We use do-while loops to simulate early returns, so we
767
- // can't inline functions that have an early return.
768
- bool hasEarlyReturn = has_early_return (*functionDef);
769
-
770
- // If we didn't detect an early return, there shouldn't be any returns in breakable
771
- // constructs either.
772
- SkASSERT (hasEarlyReturn || count_returns_in_breakable_constructs (*functionDef) == 0 );
773
- return !hasEarlyReturn;
774
- }
775
- // We have do-while loops, but we don't have any mechanism to simulate early returns within a
776
- // breakable construct (switch/for/do/while), so we can't inline if there's a return inside one.
808
+ // We don't have any mechanism to simulate early returns within a breakable construct
809
+ // (switch/for/do/while), so we can't inline if there's a return inside one.
777
810
bool hasReturnInBreakableConstruct = (count_returns_in_breakable_constructs (*functionDef) > 0 );
778
811
779
812
// If we detected returns in breakable constructs, we should also detect an early return.
0 commit comments