xref: /aosp_15_r20/external/skia/src/sksl/transform/SkSLEliminateUnnecessaryBraces.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2024 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLDefines.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLModule.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPosition.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBlock.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLDoStatement.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpression.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLForStatement.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDefinition.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIRNode.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIfStatement.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLNop.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgramElement.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLStatement.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/transform/SkSLProgramWriter.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/transform/SkSLTransform.h"
23*c8dee2aaSAndroid Build Coastguard Worker 
24*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
25*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
26*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
29*c8dee2aaSAndroid Build Coastguard Worker 
30*c8dee2aaSAndroid Build Coastguard Worker class Context;
31*c8dee2aaSAndroid Build Coastguard Worker 
EliminateUnnecessaryBraces(const Context & context,Module & module)32*c8dee2aaSAndroid Build Coastguard Worker void Transform::EliminateUnnecessaryBraces(const Context& context, Module& module) {
33*c8dee2aaSAndroid Build Coastguard Worker     class UnnecessaryBraceEliminator : public ProgramWriter {
34*c8dee2aaSAndroid Build Coastguard Worker     public:
35*c8dee2aaSAndroid Build Coastguard Worker         bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
36*c8dee2aaSAndroid Build Coastguard Worker             // We don't need to look inside expressions at all.
37*c8dee2aaSAndroid Build Coastguard Worker             return false;
38*c8dee2aaSAndroid Build Coastguard Worker         }
39*c8dee2aaSAndroid Build Coastguard Worker 
40*c8dee2aaSAndroid Build Coastguard Worker         bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
41*c8dee2aaSAndroid Build Coastguard Worker             // Work from the innermost blocks to the outermost.
42*c8dee2aaSAndroid Build Coastguard Worker             INHERITED::visitStatementPtr(stmt);
43*c8dee2aaSAndroid Build Coastguard Worker 
44*c8dee2aaSAndroid Build Coastguard Worker             switch (stmt->kind()) {
45*c8dee2aaSAndroid Build Coastguard Worker                 case StatementKind::kIf: {
46*c8dee2aaSAndroid Build Coastguard Worker                     IfStatement& ifStmt = stmt->as<IfStatement>();
47*c8dee2aaSAndroid Build Coastguard Worker                     EliminateBracesFrom(ifStmt.ifTrue());
48*c8dee2aaSAndroid Build Coastguard Worker                     EliminateBracesFrom(ifStmt.ifFalse());
49*c8dee2aaSAndroid Build Coastguard Worker                     break;
50*c8dee2aaSAndroid Build Coastguard Worker                 }
51*c8dee2aaSAndroid Build Coastguard Worker                 case StatementKind::kFor: {
52*c8dee2aaSAndroid Build Coastguard Worker                     ForStatement& forStmt = stmt->as<ForStatement>();
53*c8dee2aaSAndroid Build Coastguard Worker                     EliminateBracesFrom(forStmt.statement());
54*c8dee2aaSAndroid Build Coastguard Worker                     break;
55*c8dee2aaSAndroid Build Coastguard Worker                 }
56*c8dee2aaSAndroid Build Coastguard Worker                 case StatementKind::kDo: {
57*c8dee2aaSAndroid Build Coastguard Worker                     DoStatement& doStmt = stmt->as<DoStatement>();
58*c8dee2aaSAndroid Build Coastguard Worker                     EliminateBracesFrom(doStmt.statement());
59*c8dee2aaSAndroid Build Coastguard Worker                     break;
60*c8dee2aaSAndroid Build Coastguard Worker                 }
61*c8dee2aaSAndroid Build Coastguard Worker                 default:
62*c8dee2aaSAndroid Build Coastguard Worker                     break;
63*c8dee2aaSAndroid Build Coastguard Worker             }
64*c8dee2aaSAndroid Build Coastguard Worker 
65*c8dee2aaSAndroid Build Coastguard Worker             // We always check the entire program.
66*c8dee2aaSAndroid Build Coastguard Worker             return false;
67*c8dee2aaSAndroid Build Coastguard Worker         }
68*c8dee2aaSAndroid Build Coastguard Worker 
69*c8dee2aaSAndroid Build Coastguard Worker         static void EliminateBracesFrom(std::unique_ptr<Statement>& stmt) {
70*c8dee2aaSAndroid Build Coastguard Worker             if (!stmt || !stmt->is<Block>()) {
71*c8dee2aaSAndroid Build Coastguard Worker                 return;
72*c8dee2aaSAndroid Build Coastguard Worker             }
73*c8dee2aaSAndroid Build Coastguard Worker             Block& block = stmt->as<Block>();
74*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<Statement>* usefulStmt = nullptr;
75*c8dee2aaSAndroid Build Coastguard Worker             for (std::unique_ptr<Statement>& childStmt : block.children()) {
76*c8dee2aaSAndroid Build Coastguard Worker                 if (childStmt->isEmpty()) {
77*c8dee2aaSAndroid Build Coastguard Worker                     continue;
78*c8dee2aaSAndroid Build Coastguard Worker                 }
79*c8dee2aaSAndroid Build Coastguard Worker                 if (usefulStmt) {
80*c8dee2aaSAndroid Build Coastguard Worker                     // We found two non-empty statements. We can't eliminate braces from
81*c8dee2aaSAndroid Build Coastguard Worker                     // this block.
82*c8dee2aaSAndroid Build Coastguard Worker                     return;
83*c8dee2aaSAndroid Build Coastguard Worker                 }
84*c8dee2aaSAndroid Build Coastguard Worker                 // We found one non-empty statement.
85*c8dee2aaSAndroid Build Coastguard Worker                 usefulStmt = &childStmt;
86*c8dee2aaSAndroid Build Coastguard Worker             }
87*c8dee2aaSAndroid Build Coastguard Worker 
88*c8dee2aaSAndroid Build Coastguard Worker             if (!usefulStmt) {
89*c8dee2aaSAndroid Build Coastguard Worker                 // This block held zero useful statements. Replace the block with a nop.
90*c8dee2aaSAndroid Build Coastguard Worker                 stmt = Nop::Make();
91*c8dee2aaSAndroid Build Coastguard Worker             } else {
92*c8dee2aaSAndroid Build Coastguard Worker                 // This block held one useful statement. Replace the block with that statement.
93*c8dee2aaSAndroid Build Coastguard Worker                 stmt = std::move(*usefulStmt);
94*c8dee2aaSAndroid Build Coastguard Worker             }
95*c8dee2aaSAndroid Build Coastguard Worker         }
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker         using INHERITED = ProgramWriter;
98*c8dee2aaSAndroid Build Coastguard Worker     };
99*c8dee2aaSAndroid Build Coastguard Worker 
100*c8dee2aaSAndroid Build Coastguard Worker     class RequiredBraceWriter : public ProgramWriter {
101*c8dee2aaSAndroid Build Coastguard Worker     public:
102*c8dee2aaSAndroid Build Coastguard Worker         RequiredBraceWriter(const Context& ctx) : fContext(ctx) {}
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker         bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
105*c8dee2aaSAndroid Build Coastguard Worker             // We don't need to look inside expressions at all.
106*c8dee2aaSAndroid Build Coastguard Worker             return false;
107*c8dee2aaSAndroid Build Coastguard Worker         }
108*c8dee2aaSAndroid Build Coastguard Worker 
109*c8dee2aaSAndroid Build Coastguard Worker         bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
110*c8dee2aaSAndroid Build Coastguard Worker             // Look for the following structure:
111*c8dee2aaSAndroid Build Coastguard Worker             //
112*c8dee2aaSAndroid Build Coastguard Worker             //    if (...)
113*c8dee2aaSAndroid Build Coastguard Worker             //      if (...)
114*c8dee2aaSAndroid Build Coastguard Worker             //        any statement;
115*c8dee2aaSAndroid Build Coastguard Worker             //    else
116*c8dee2aaSAndroid Build Coastguard Worker             //      any statement;
117*c8dee2aaSAndroid Build Coastguard Worker             //
118*c8dee2aaSAndroid Build Coastguard Worker             // This structure isn't correct if we emit it textually, because the else-clause would
119*c8dee2aaSAndroid Build Coastguard Worker             // be interpreted as if it were bound to the inner if-statement, like this:
120*c8dee2aaSAndroid Build Coastguard Worker             //
121*c8dee2aaSAndroid Build Coastguard Worker             //    if (...) {
122*c8dee2aaSAndroid Build Coastguard Worker             //      if (...)
123*c8dee2aaSAndroid Build Coastguard Worker             //        any statement;
124*c8dee2aaSAndroid Build Coastguard Worker             //      else
125*c8dee2aaSAndroid Build Coastguard Worker             //        any statement;
126*c8dee2aaSAndroid Build Coastguard Worker             //    }
127*c8dee2aaSAndroid Build Coastguard Worker             //
128*c8dee2aaSAndroid Build Coastguard Worker             // If we find such a structure, we must disambiguate the else-clause by adding braces:
129*c8dee2aaSAndroid Build Coastguard Worker             //    if (...) {
130*c8dee2aaSAndroid Build Coastguard Worker             //      if (...)
131*c8dee2aaSAndroid Build Coastguard Worker             //        any statement;
132*c8dee2aaSAndroid Build Coastguard Worker             //    } else
133*c8dee2aaSAndroid Build Coastguard Worker             //      any statement;
134*c8dee2aaSAndroid Build Coastguard Worker 
135*c8dee2aaSAndroid Build Coastguard Worker             // Work from the innermost blocks to the outermost.
136*c8dee2aaSAndroid Build Coastguard Worker             INHERITED::visitStatementPtr(stmt);
137*c8dee2aaSAndroid Build Coastguard Worker 
138*c8dee2aaSAndroid Build Coastguard Worker             // We are looking for an if-statement.
139*c8dee2aaSAndroid Build Coastguard Worker             if (stmt->is<IfStatement>()) {
140*c8dee2aaSAndroid Build Coastguard Worker                 IfStatement& outer = stmt->as<IfStatement>();
141*c8dee2aaSAndroid Build Coastguard Worker 
142*c8dee2aaSAndroid Build Coastguard Worker                 // It should have an else clause, and directly wrap another if-statement (no Block).
143*c8dee2aaSAndroid Build Coastguard Worker                 if (outer.ifFalse() && outer.ifTrue()->is<IfStatement>()) {
144*c8dee2aaSAndroid Build Coastguard Worker                     const IfStatement& inner = outer.ifTrue()->as<IfStatement>();
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker                     // The inner if statement shouldn't have an else clause.
147*c8dee2aaSAndroid Build Coastguard Worker                     if (!inner.ifFalse()) {
148*c8dee2aaSAndroid Build Coastguard Worker                         // This structure is ambiguous; the else clause on the outer if-statement
149*c8dee2aaSAndroid Build Coastguard Worker                         // will bind to the inner if-statement if we don't add braces. We must wrap
150*c8dee2aaSAndroid Build Coastguard Worker                         // the outer if-statement's true-clause in braces.
151*c8dee2aaSAndroid Build Coastguard Worker                         StatementArray blockStmts;
152*c8dee2aaSAndroid Build Coastguard Worker                         blockStmts.push_back(std::move(outer.ifTrue()));
153*c8dee2aaSAndroid Build Coastguard Worker                         Position stmtPosition = blockStmts.front()->position();
154*c8dee2aaSAndroid Build Coastguard Worker                         std::unique_ptr<Statement> bracedIfTrue =
155*c8dee2aaSAndroid Build Coastguard Worker                                 Block::MakeBlock(stmtPosition, std::move(blockStmts));
156*c8dee2aaSAndroid Build Coastguard Worker                         stmt = IfStatement::Make(fContext,
157*c8dee2aaSAndroid Build Coastguard Worker                                                  outer.position(),
158*c8dee2aaSAndroid Build Coastguard Worker                                                  std::move(outer.test()),
159*c8dee2aaSAndroid Build Coastguard Worker                                                  std::move(bracedIfTrue),
160*c8dee2aaSAndroid Build Coastguard Worker                                                  std::move(outer.ifFalse()));
161*c8dee2aaSAndroid Build Coastguard Worker                     }
162*c8dee2aaSAndroid Build Coastguard Worker                 }
163*c8dee2aaSAndroid Build Coastguard Worker             }
164*c8dee2aaSAndroid Build Coastguard Worker 
165*c8dee2aaSAndroid Build Coastguard Worker             // We always check the entire program.
166*c8dee2aaSAndroid Build Coastguard Worker             return false;
167*c8dee2aaSAndroid Build Coastguard Worker         }
168*c8dee2aaSAndroid Build Coastguard Worker 
169*c8dee2aaSAndroid Build Coastguard Worker         const Context& fContext;
170*c8dee2aaSAndroid Build Coastguard Worker         using INHERITED = ProgramWriter;
171*c8dee2aaSAndroid Build Coastguard Worker     };
172*c8dee2aaSAndroid Build Coastguard Worker 
173*c8dee2aaSAndroid Build Coastguard Worker     for (std::unique_ptr<ProgramElement>& pe : module.fElements) {
174*c8dee2aaSAndroid Build Coastguard Worker         if (pe->is<FunctionDefinition>()) {
175*c8dee2aaSAndroid Build Coastguard Worker             // First, we eliminate braces around single-statement child blocks wherever possible.
176*c8dee2aaSAndroid Build Coastguard Worker             UnnecessaryBraceEliminator eliminator;
177*c8dee2aaSAndroid Build Coastguard Worker             eliminator.visitStatementPtr(pe->as<FunctionDefinition>().body());
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker             // The first pass can be overzealous, since it can remove so many braces that else-
180*c8dee2aaSAndroid Build Coastguard Worker             // clauses are bound to the wrong if-statement. Search for this case and fix it up
181*c8dee2aaSAndroid Build Coastguard Worker             // if we find it.
182*c8dee2aaSAndroid Build Coastguard Worker             RequiredBraceWriter writer(context);
183*c8dee2aaSAndroid Build Coastguard Worker             writer.visitStatementPtr(pe->as<FunctionDefinition>().body());
184*c8dee2aaSAndroid Build Coastguard Worker         }
185*c8dee2aaSAndroid Build Coastguard Worker     }
186*c8dee2aaSAndroid Build Coastguard Worker }
187*c8dee2aaSAndroid Build Coastguard Worker 
188*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
189