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