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