xref: /aosp_15_r20/external/skia/src/sksl/transform/SkSLHoistSwitchVarDeclarationsAtTopLevel.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2023 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 "include/private/base/SkAssert.h"
9 #include "include/private/base/SkTArray.h"
10 #include "src/sksl/SkSLAnalysis.h"
11 #include "src/sksl/SkSLDefines.h"
12 #include "src/sksl/SkSLPosition.h"
13 #include "src/sksl/ir/SkSLBlock.h"
14 #include "src/sksl/ir/SkSLExpression.h"
15 #include "src/sksl/ir/SkSLIRHelpers.h"
16 #include "src/sksl/ir/SkSLIRNode.h"
17 #include "src/sksl/ir/SkSLModifierFlags.h"
18 #include "src/sksl/ir/SkSLNop.h"
19 #include "src/sksl/ir/SkSLStatement.h"
20 #include "src/sksl/ir/SkSLSymbolTable.h"
21 #include "src/sksl/ir/SkSLVarDeclarations.h"
22 #include "src/sksl/ir/SkSLVariable.h"
23 #include "src/sksl/transform/SkSLProgramWriter.h"
24 #include "src/sksl/transform/SkSLTransform.h"
25 
26 #include <memory>
27 #include <utility>
28 
29 using namespace skia_private;
30 
31 namespace SkSL {
32 
33 class Context;
34 
HoistSwitchVarDeclarationsAtTopLevel(const Context & context,StatementArray & cases,SymbolTable & switchSymbols,Position pos)35 std::unique_ptr<Block> Transform::HoistSwitchVarDeclarationsAtTopLevel(const Context& context,
36                                                                        StatementArray& cases,
37                                                                        SymbolTable& switchSymbols,
38                                                                        Position pos) {
39     struct HoistSwitchVarDeclsVisitor : public ProgramWriter {
40         HoistSwitchVarDeclsVisitor(const Context& c) : fContext(c) {}
41 
42         bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
43             // We don't need to recurse into expressions.
44             return false;
45         }
46 
47         bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
48             switch (stmt->kind()) {
49                 case StatementKind::kSwitchCase:
50                     // Recurse inward from the switch and its inner switch-cases.
51                     return INHERITED::visitStatementPtr(stmt);
52 
53                 case StatementKind::kBlock:
54                     if (!stmt->as<Block>().isScope()) {
55                         // Recurse inward from unscoped blocks.
56                         return INHERITED::visitStatementPtr(stmt);
57                     }
58                     break;
59 
60                 case StatementKind::kVarDeclaration:
61                     // Keep track of variable declarations.
62                     fVarDeclarations.push_back(&stmt);
63                     break;
64 
65                 default:
66                     break;
67             }
68 
69             // We don't need to recurse into other statement types; we're only interested in the top
70             // level of the switch statement.
71             return false;
72         }
73 
74         const Context& fContext;
75         TArray<std::unique_ptr<Statement>*> fVarDeclarations;
76 
77         using INHERITED = ProgramWriter;
78     };
79 
80     // Visit every switch-case in the switch, looking for hoistable var-declarations.
81     HoistSwitchVarDeclsVisitor visitor(context);
82     for (std::unique_ptr<Statement>& sc : cases) {
83         visitor.visitStatementPtr(sc);
84     }
85 
86     // If no declarations were found, the switch can stay as-is.
87     if (visitor.fVarDeclarations.empty()) {
88         return nullptr;
89     }
90 
91     // Move all of the var-declaration statements into a separate block.
92     std::unique_ptr<SymbolTable> blockSymbols = switchSymbols.insertNewParent();
93 
94     StatementArray blockStmts;
95     blockStmts.reserve_exact(visitor.fVarDeclarations.size() + 1);
96     for (std::unique_ptr<Statement>* innerDeclaration : visitor.fVarDeclarations) {
97         VarDeclaration& decl = (*innerDeclaration)->as<VarDeclaration>();
98         Variable* var = decl.var();
99         bool isConst = var->modifierFlags().isConst();
100 
101         std::unique_ptr<Statement> replacementStmt;
102         if (decl.value() && !isConst) {
103             // The inner variable-declaration has an initial-value; we must replace the declaration
104             // with an assignment to the variable. This also has the helpful effect of stripping off
105             // the initial-value from the declaration.
106             struct AssignmentHelper : public IRHelpers {
107                 using IRHelpers::IRHelpers;
108 
109                 std::unique_ptr<Statement> makeAssignmentStmt(VarDeclaration& decl) const {
110                     return Assign(Ref(decl.var()), std::move(decl.value()));
111                 }
112             };
113 
114             AssignmentHelper helper(context);
115             replacementStmt = helper.makeAssignmentStmt(decl);
116         } else {
117             // The inner variable-declaration has no initial-value, or it's const and has a constant
118             // value; we can move it upwards as-is and replace its statement with a no-op.
119             SkASSERT(!isConst || Analysis::IsConstantExpression(*decl.value()));
120 
121             replacementStmt = Nop::Make();
122         }
123 
124         // Move the var-declaration into its own block, and replace the existing statement with
125         // either an assignment (if there was an initial-value) or a no-op (if there wasn't one).
126         blockStmts.push_back(std::move(*innerDeclaration));
127         *innerDeclaration = std::move(replacementStmt);
128 
129         // Hoist the variable's symbol outside of the switch's symbol table, and into the enclosing
130         // block's symbol table.
131         switchSymbols.moveSymbolTo(blockSymbols.get(), var, context);
132     }
133 
134     // Return a scoped Block holding the variable declarations.
135     return Block::MakeBlock(pos, std::move(blockStmts), Block::Kind::kBracedScope,
136                             std::move(blockSymbols));
137 }
138 
139 }  // namespace SkSL
140