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