xref: /aosp_15_r20/external/skia/src/sksl/transform/SkSLEliminateDeadLocalVariables.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2021 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/core/SkSpan.h"
9 #include "include/core/SkTypes.h"
10 #include "src/core/SkTHash.h"
11 #include "src/sksl/SkSLAnalysis.h"
12 #include "src/sksl/SkSLModule.h"
13 #include "src/sksl/SkSLProgramSettings.h"
14 #include "src/sksl/analysis/SkSLProgramUsage.h"
15 #include "src/sksl/ir/SkSLBinaryExpression.h"
16 #include "src/sksl/ir/SkSLExpression.h"
17 #include "src/sksl/ir/SkSLExpressionStatement.h"
18 #include "src/sksl/ir/SkSLFunctionDefinition.h"
19 #include "src/sksl/ir/SkSLNop.h"
20 #include "src/sksl/ir/SkSLProgram.h"
21 #include "src/sksl/ir/SkSLProgramElement.h"
22 #include "src/sksl/ir/SkSLStatement.h"
23 #include "src/sksl/ir/SkSLVarDeclarations.h"
24 #include "src/sksl/ir/SkSLVariable.h"
25 #include "src/sksl/ir/SkSLVariableReference.h"
26 #include "src/sksl/transform/SkSLProgramWriter.h"
27 #include "src/sksl/transform/SkSLTransform.h"
28 
29 #include <memory>
30 #include <utility>
31 #include <vector>
32 
33 using namespace skia_private;
34 
35 namespace SkSL {
36 
37 class Context;
38 
eliminate_dead_local_variables(const Context & context,SkSpan<std::unique_ptr<ProgramElement>> elements,ProgramUsage * usage)39 static bool eliminate_dead_local_variables(const Context& context,
40                                            SkSpan<std::unique_ptr<ProgramElement>> elements,
41                                            ProgramUsage* usage) {
42     class DeadLocalVariableEliminator : public ProgramWriter {
43     public:
44         DeadLocalVariableEliminator(const Context& context, ProgramUsage* usage)
45                 : fContext(context)
46                 , fUsage(usage) {}
47 
48         using ProgramWriter::visitProgramElement;
49 
50         bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
51             if (expr->is<BinaryExpression>()) {
52                 // Search for expressions of the form `deadVar = anyExpression`.
53                 BinaryExpression& binary = expr->as<BinaryExpression>();
54                 if (VariableReference* assignedVar = binary.isAssignmentIntoVariable()) {
55                     if (fDeadVariables.contains(assignedVar->variable())) {
56                         // Replace `deadVar = anyExpression` with `anyExpression`.
57                         fUsage->remove(expr.get());
58                         expr = std::move(binary.right());
59                         fUsage->add(expr.get());
60 
61                         // If `anyExpression` is now a lone ExpressionStatement, it's highly likely
62                         // that we can eliminate it entirely. This flag will let us know to check.
63                         fAssignmentWasEliminated = true;
64 
65                         // Re-process the newly cleaned-up expression. This lets us fully clean up
66                         // gnarly assignments like `a = b = 123;` where both `a` and `b` are dead,
67                         // or silly double-assignments like `a = a = 123;`.
68                         return this->visitExpressionPtr(expr);
69                     }
70                 }
71             }
72             if (expr->is<VariableReference>()) {
73                 SkASSERT(!fDeadVariables.contains(expr->as<VariableReference>().variable()));
74             }
75             return INHERITED::visitExpressionPtr(expr);
76         }
77 
78         bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
79             if (stmt->is<VarDeclaration>()) {
80                 VarDeclaration& varDecl = stmt->as<VarDeclaration>();
81                 const Variable* var = varDecl.var();
82                 ProgramUsage::VariableCounts* counts = fUsage->fVariableCounts.find(var);
83                 SkASSERT(counts);
84                 SkASSERT(counts->fVarExists);
85                 if (CanEliminate(var, *counts)) {
86                     fDeadVariables.add(var);
87                     if (var->initialValue()) {
88                         // The variable has an initial-value expression, which might have side
89                         // effects. ExpressionStatement::Make will preserve side effects, but
90                         // replaces pure expressions with Nop.
91                         fUsage->remove(stmt.get());
92                         stmt = ExpressionStatement::Make(fContext, std::move(varDecl.value()));
93                         fUsage->add(stmt.get());
94                     } else {
95                         // The variable has no initial-value and can be cleanly eliminated.
96                         fUsage->remove(stmt.get());
97                         stmt = Nop::Make();
98                     }
99                     fMadeChanges = true;
100 
101                     // Re-process the newly cleaned-up statement. This lets us fully clean up
102                     // gnarly assignments like `a = b = 123;` where both `a` and `b` are dead,
103                     // or silly double-assignments like `a = a = 123;`.
104                     return this->visitStatementPtr(stmt);
105                 }
106             }
107 
108             bool result = INHERITED::visitStatementPtr(stmt);
109 
110             // If we eliminated an assignment above, we may have left behind an inert
111             // ExpressionStatement.
112             if (fAssignmentWasEliminated) {
113                 fAssignmentWasEliminated = false;
114                 if (stmt->is<ExpressionStatement>()) {
115                     ExpressionStatement& exprStmt = stmt->as<ExpressionStatement>();
116                     if (!Analysis::HasSideEffects(*exprStmt.expression())) {
117                         // The expression-statement was inert; eliminate it entirely.
118                         fUsage->remove(&exprStmt);
119                         stmt = Nop::Make();
120                     }
121                 }
122             }
123 
124             return result;
125         }
126 
127         static bool CanEliminate(const Variable* var, const ProgramUsage::VariableCounts& counts) {
128             return counts.fVarExists && !counts.fRead && var->storage() == VariableStorage::kLocal;
129         }
130 
131         bool fMadeChanges = false;
132         const Context& fContext;
133         ProgramUsage* fUsage;
134         THashSet<const Variable*> fDeadVariables;
135         bool fAssignmentWasEliminated = false;
136 
137         using INHERITED = ProgramWriter;
138     };
139 
140     DeadLocalVariableEliminator visitor{context, usage};
141 
142     for (auto& [var, counts] : usage->fVariableCounts) {
143         if (DeadLocalVariableEliminator::CanEliminate(var, counts)) {
144             // This program contains at least one dead local variable.
145             // Scan the program for any dead local variables and eliminate them all.
146             for (std::unique_ptr<ProgramElement>& pe : elements) {
147                 if (pe->is<FunctionDefinition>()) {
148                     visitor.visitProgramElement(*pe);
149                 }
150             }
151             break;
152         }
153     }
154 
155     return visitor.fMadeChanges;
156 }
157 
EliminateDeadLocalVariables(const Context & context,Module & module,ProgramUsage * usage)158 bool Transform::EliminateDeadLocalVariables(const Context& context,
159                                             Module& module,
160                                             ProgramUsage* usage) {
161     return eliminate_dead_local_variables(context, SkSpan(module.fElements), usage);
162 }
163 
EliminateDeadLocalVariables(Program & program)164 bool Transform::EliminateDeadLocalVariables(Program& program) {
165     return program.fConfig->fSettings.fRemoveDeadVariables
166                    ? eliminate_dead_local_variables(*program.fContext,
167                                                     SkSpan(program.fOwnedElements),
168                                                     program.fUsage.get())
169                    : false;
170 }
171 
172 }  // namespace SkSL
173