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