blob: 2acc668099313c4b226a4bd2293819c0131caeab [file] [log] [blame]
John Stilesa7827d62021-10-06 16:51:57 -04001/*
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
17namespace SkSL {
18
19bool 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.
John Stilesbcaacec2021-10-06 20:06:55 -040086 for (std::unique_ptr<ProgramElement>& pe : program.fOwnedElements) {
John Stilesa7827d62021-10-06 16:51:57 -040087 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