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/SkSLStatement.h"
9cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLFunctionDefinition.h"
10cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLIfStatement.h"
11cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLNop.h"
12cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLProgram.h"
13cb93a386Sopenharmony_ci#include "src/sksl/transform/SkSLProgramWriter.h"
14cb93a386Sopenharmony_ci#include "src/sksl/transform/SkSLTransform.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ci#include <stack>
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_cinamespace SkSL {
19cb93a386Sopenharmony_ci
20cb93a386Sopenharmony_civoid Transform::EliminateUnreachableCode(Program& program, ProgramUsage* usage) {
21cb93a386Sopenharmony_ci    class UnreachableCodeEliminator : public ProgramWriter {
22cb93a386Sopenharmony_ci    public:
23cb93a386Sopenharmony_ci        UnreachableCodeEliminator(ProgramUsage* usage)
24cb93a386Sopenharmony_ci                : fUsage(usage) {
25cb93a386Sopenharmony_ci            fFoundFunctionExit.push(false);
26cb93a386Sopenharmony_ci            fFoundLoopExit.push(false);
27cb93a386Sopenharmony_ci        }
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci        bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
30cb93a386Sopenharmony_ci            // We don't need to look inside expressions at all.
31cb93a386Sopenharmony_ci            return false;
32cb93a386Sopenharmony_ci        }
33cb93a386Sopenharmony_ci
34cb93a386Sopenharmony_ci        bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
35cb93a386Sopenharmony_ci            if (fFoundFunctionExit.top() || fFoundLoopExit.top()) {
36cb93a386Sopenharmony_ci                // If we already found an exit in this section, anything beyond it is dead code.
37cb93a386Sopenharmony_ci                if (!stmt->is<Nop>()) {
38cb93a386Sopenharmony_ci                    // Eliminate the dead statement by substituting a Nop.
39cb93a386Sopenharmony_ci                    if (fUsage) {
40cb93a386Sopenharmony_ci                        fUsage->remove(stmt.get());
41cb93a386Sopenharmony_ci                    }
42cb93a386Sopenharmony_ci                    stmt = Nop::Make();
43cb93a386Sopenharmony_ci                }
44cb93a386Sopenharmony_ci                return false;
45cb93a386Sopenharmony_ci            }
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ci            switch (stmt->kind()) {
48cb93a386Sopenharmony_ci                case Statement::Kind::kReturn:
49cb93a386Sopenharmony_ci                case Statement::Kind::kDiscard:
50cb93a386Sopenharmony_ci                    // We found a function exit on this path.
51cb93a386Sopenharmony_ci                    fFoundFunctionExit.top() = true;
52cb93a386Sopenharmony_ci                    break;
53cb93a386Sopenharmony_ci
54cb93a386Sopenharmony_ci                case Statement::Kind::kBreak:
55cb93a386Sopenharmony_ci                case Statement::Kind::kContinue:
56cb93a386Sopenharmony_ci                    // We found a loop exit on this path. Note that we skip over switch statements
57cb93a386Sopenharmony_ci                    // completely when eliminating code, so any `break` statement would be breaking
58cb93a386Sopenharmony_ci                    // out of a loop, not out of a switch.
59cb93a386Sopenharmony_ci                    fFoundLoopExit.top() = true;
60cb93a386Sopenharmony_ci                    break;
61cb93a386Sopenharmony_ci
62cb93a386Sopenharmony_ci                case Statement::Kind::kExpression:
63cb93a386Sopenharmony_ci                case Statement::Kind::kInlineMarker:
64cb93a386Sopenharmony_ci                case Statement::Kind::kNop:
65cb93a386Sopenharmony_ci                case Statement::Kind::kVarDeclaration:
66cb93a386Sopenharmony_ci                    // These statements don't affect control flow.
67cb93a386Sopenharmony_ci                    break;
68cb93a386Sopenharmony_ci
69cb93a386Sopenharmony_ci                case Statement::Kind::kBlock:
70cb93a386Sopenharmony_ci                    // Blocks are on the straight-line path and don't affect control flow.
71cb93a386Sopenharmony_ci                    return INHERITED::visitStatementPtr(stmt);
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_ci                case Statement::Kind::kDo: {
74cb93a386Sopenharmony_ci                    // Function-exits are allowed to propagate outside of a do-loop, because it
75cb93a386Sopenharmony_ci                    // always executes its body at least once.
76cb93a386Sopenharmony_ci                    fFoundLoopExit.push(false);
77cb93a386Sopenharmony_ci                    bool result = INHERITED::visitStatementPtr(stmt);
78cb93a386Sopenharmony_ci                    fFoundLoopExit.pop();
79cb93a386Sopenharmony_ci                    return result;
80cb93a386Sopenharmony_ci                }
81cb93a386Sopenharmony_ci                case Statement::Kind::kFor: {
82cb93a386Sopenharmony_ci                    // Function-exits are not allowed to propagate out, because a for-loop or while-
83cb93a386Sopenharmony_ci                    // loop could potentially run zero times.
84cb93a386Sopenharmony_ci                    fFoundFunctionExit.push(false);
85cb93a386Sopenharmony_ci                    fFoundLoopExit.push(false);
86cb93a386Sopenharmony_ci                    bool result = INHERITED::visitStatementPtr(stmt);
87cb93a386Sopenharmony_ci                    fFoundLoopExit.pop();
88cb93a386Sopenharmony_ci                    fFoundFunctionExit.pop();
89cb93a386Sopenharmony_ci                    return result;
90cb93a386Sopenharmony_ci                }
91cb93a386Sopenharmony_ci                case Statement::Kind::kIf: {
92cb93a386Sopenharmony_ci                    // This statement is conditional and encloses two inner sections of code.
93cb93a386Sopenharmony_ci                    // If both sides contain a function-exit or loop-exit, that exit is allowed to
94cb93a386Sopenharmony_ci                    // propagate out.
95cb93a386Sopenharmony_ci                    IfStatement& ifStmt = stmt->as<IfStatement>();
96cb93a386Sopenharmony_ci
97cb93a386Sopenharmony_ci                    fFoundFunctionExit.push(false);
98cb93a386Sopenharmony_ci                    fFoundLoopExit.push(false);
99cb93a386Sopenharmony_ci                    bool result = (ifStmt.ifTrue() && this->visitStatementPtr(ifStmt.ifTrue()));
100cb93a386Sopenharmony_ci                    bool foundFunctionExitOnTrue = fFoundFunctionExit.top();
101cb93a386Sopenharmony_ci                    bool foundLoopExitOnTrue = fFoundLoopExit.top();
102cb93a386Sopenharmony_ci                    fFoundFunctionExit.pop();
103cb93a386Sopenharmony_ci                    fFoundLoopExit.pop();
104cb93a386Sopenharmony_ci
105cb93a386Sopenharmony_ci                    fFoundFunctionExit.push(false);
106cb93a386Sopenharmony_ci                    fFoundLoopExit.push(false);
107cb93a386Sopenharmony_ci                    result |= (ifStmt.ifFalse() && this->visitStatementPtr(ifStmt.ifFalse()));
108cb93a386Sopenharmony_ci                    bool foundFunctionExitOnFalse = fFoundFunctionExit.top();
109cb93a386Sopenharmony_ci                    bool foundLoopExitOnFalse = fFoundLoopExit.top();
110cb93a386Sopenharmony_ci                    fFoundFunctionExit.pop();
111cb93a386Sopenharmony_ci                    fFoundLoopExit.pop();
112cb93a386Sopenharmony_ci
113cb93a386Sopenharmony_ci                    fFoundFunctionExit.top() |= foundFunctionExitOnTrue && foundFunctionExitOnFalse;
114cb93a386Sopenharmony_ci                    fFoundLoopExit.top() |= foundLoopExitOnTrue && foundLoopExitOnFalse;
115cb93a386Sopenharmony_ci                    return result;
116cb93a386Sopenharmony_ci                }
117cb93a386Sopenharmony_ci                case Statement::Kind::kSwitch:
118cb93a386Sopenharmony_ci                case Statement::Kind::kSwitchCase:
119cb93a386Sopenharmony_ci                    // We skip past switch statements entirely when scanning for dead code. Their
120cb93a386Sopenharmony_ci                    // control flow is quite complex and we already do a good job of flattening out
121cb93a386Sopenharmony_ci                    // switches on constant values.
122cb93a386Sopenharmony_ci                    break;
123cb93a386Sopenharmony_ci            }
124cb93a386Sopenharmony_ci
125cb93a386Sopenharmony_ci            return false;
126cb93a386Sopenharmony_ci        }
127cb93a386Sopenharmony_ci
128cb93a386Sopenharmony_ci        ProgramUsage* fUsage;
129cb93a386Sopenharmony_ci        std::stack<bool> fFoundFunctionExit;
130cb93a386Sopenharmony_ci        std::stack<bool> fFoundLoopExit;
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ci        using INHERITED = ProgramWriter;
133cb93a386Sopenharmony_ci    };
134cb93a386Sopenharmony_ci
135cb93a386Sopenharmony_ci    for (std::unique_ptr<ProgramElement>& pe : program.fOwnedElements) {
136cb93a386Sopenharmony_ci        if (pe->is<FunctionDefinition>()) {
137cb93a386Sopenharmony_ci            UnreachableCodeEliminator visitor{usage};
138cb93a386Sopenharmony_ci            visitor.visitStatementPtr(pe->as<FunctionDefinition>().body());
139cb93a386Sopenharmony_ci        }
140cb93a386Sopenharmony_ci    }
141cb93a386Sopenharmony_ci}
142cb93a386Sopenharmony_ci
143cb93a386Sopenharmony_ci}  // namespace SkSL
144