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/private/SkSLProgramElement.h"
9 #include "src/sksl/ir/SkSLExpressionStatement.h"
10 #include "src/sksl/ir/SkSLFunctionDefinition.h"
11 #include "src/sksl/ir/SkSLNop.h"
12 #include "src/sksl/ir/SkSLProgram.h"
13 #include "src/sksl/ir/SkSLVarDeclarations.h"
14 #include "src/sksl/transform/SkSLProgramWriter.h"
15 #include "src/sksl/transform/SkSLTransform.h"
16
17 namespace SkSL {
18
EliminateDeadLocalVariables(Program & program,ProgramUsage * usage)19 bool Transform::EliminateDeadLocalVariables(Program& program, ProgramUsage* usage) {
20 class DeadLocalVariableEliminator : public ProgramWriter {
21 public:
22 DeadLocalVariableEliminator(const Context& context, ProgramUsage* usage)
23 : fContext(context)
24 , fUsage(usage) {}
25
26 using ProgramWriter::visitProgramElement;
27
28 bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
29 // We don't need to look inside expressions at all.
30 return false;
31 }
32
33 bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
34 if (stmt->is<VarDeclaration>()) {
35 VarDeclaration& varDecl = stmt->as<VarDeclaration>();
36 const Variable* var = &varDecl.var();
37 ProgramUsage::VariableCounts* counts = fUsage->fVariableCounts.find(var);
38 SkASSERT(counts);
39 SkASSERT(counts->fDeclared);
40 if (CanEliminate(var, *counts)) {
41 if (var->initialValue()) {
42 // The variable has an initial-value expression, which might have side
43 // effects. ExpressionStatement::Make will preserve side effects, but
44 // replaces pure expressions with Nop.
45 fUsage->remove(stmt.get());
46 stmt = ExpressionStatement::Make(fContext, std::move(varDecl.value()));
47 fUsage->add(stmt.get());
48 } else {
49 // The variable has no initial-value and can be cleanly eliminated.
50 fUsage->remove(stmt.get());
51 stmt = Nop::Make();
52 }
53 fMadeChanges = true;
54 }
55 return false;
56 }
57 return INHERITED::visitStatementPtr(stmt);
58 }
59
60 static bool CanEliminate(const Variable* var, const ProgramUsage::VariableCounts& counts) {
61 if (!counts.fDeclared || counts.fRead || var->storage() != VariableStorage::kLocal) {
62 return false;
63 }
64 if (var->initialValue()) {
65 SkASSERT(counts.fWrite >= 1);
66 return counts.fWrite == 1;
67 } else {
68 return counts.fWrite == 0;
69 }
70 }
71
72 bool fMadeChanges = false;
73 const Context& fContext;
74 ProgramUsage* fUsage;
75
76 using INHERITED = ProgramWriter;
77 };
78
79 DeadLocalVariableEliminator visitor{*program.fContext, usage};
80
81 if (program.fConfig->fSettings.fRemoveDeadVariables) {
82 for (auto& [var, counts] : usage->fVariableCounts) {
83 if (DeadLocalVariableEliminator::CanEliminate(var, counts)) {
84 // This program contains at least one dead local variable.
85 // Scan the program for any dead local variables and eliminate them all.
86 for (std::unique_ptr<ProgramElement>& pe : program.fOwnedElements) {
87 if (pe->is<FunctionDefinition>()) {
88 visitor.visitProgramElement(*pe);
89 }
90 }
91 break;
92 }
93 }
94 }
95
96 return visitor.fMadeChanges;
97 }
98
99 } // namespace SkSL
100