Flutter Engine
The Flutter Engine
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
49
50 bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
51 if (expr->is<BinaryExpression>()) {
52 // Search for expressions of the form `deadVar = anyExpression`.
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);
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,...)
Definition: SkRecorder.cpp:128
const Context & fContext
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
static std::unique_ptr< Statement > Make(const Context &context, std::unique_ptr< Expression > expr)
const std::unique_ptr< Expression > & expression() const
const T & as() const
Definition: SkSLIRNode.h:133
static std::unique_ptr< Statement > Make()
Definition: SkSLNop.h:26
virtual bool visitProgramElement(typename T::ProgramElement &programElement)
std::unique_ptr< Expression > & value()
Variable * var() const
const Variable * variable() const
Storage storage() const
Definition: SkSLVariable.h:103
GAsyncResult * result
bool HasSideEffects(const Expression &expr)
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)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core counts
Definition: switches.h:239
static void usage(char *argv0)
std::vector< std::unique_ptr< ProgramElement > > fElements
Definition: SkSLCompiler.h:59
std::vector< std::unique_ptr< ProgramElement > > fOwnedElements
Definition: SkSLProgram.h:161
std::shared_ptr< Context > fContext
Definition: SkSLProgram.h:154
std::unique_ptr< ProgramUsage > fUsage
Definition: SkSLProgram.h:155
std::unique_ptr< ProgramConfig > fConfig
Definition: SkSLProgram.h:153