Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkSLEliminateDeadLocalVariables.cpp
Go to the documentation of this file.
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
10#include "src/core/SkTHash.h"
19#include "src/sksl/ir/SkSLNop.h"
28
29#include <memory>
30#include <utility>
31#include <vector>
32
33using namespace skia_private;
34
35namespace SkSL {
36
37class Context;
38
39static bool eliminate_dead_local_variables(const Context& context,
40 SkSpan<std::unique_ptr<ProgramElement>> elements,
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
159 Module& module,
161 return eliminate_dead_local_variables(context, SkSpan(module.fElements), usage);
162}
163
165 return program.fConfig->fSettings.fRemoveDeadVariables
167 SkSpan(program.fOwnedElements),
168 program.fUsage.get())
169 : false;
170}
171
172} // namespace SkSL
#define SkASSERT(cond)
Definition SkAssert.h:116
#define INHERITED(method,...)
const Context & fContext
const std::unique_ptr< Expression > & expression() const
const T & as() const
Definition SkSLIRNode.h:133
std::unique_ptr< Expression > & value()
Variable * var() const
const Variable * variable() const
Storage storage() const
GAsyncResult * result
bool EliminateDeadLocalVariables(const Context &context, Module &module, ProgramUsage *usage)
static bool eliminate_dead_local_variables(const Context &context, SkSpan< std::unique_ptr< ProgramElement > > elements, ProgramUsage *usage)
static void usage(char *argv0)
std::vector< std::unique_ptr< ProgramElement > > fElements
std::vector< std::unique_ptr< ProgramElement > > fOwnedElements
std::shared_ptr< Context > fContext
std::unique_ptr< ProgramUsage > fUsage
std::unique_ptr< ProgramConfig > fConfig