1/*
2 * Copyright (C) 2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WHLSLStatementBehaviorChecker.h"
28
29#if ENABLE(WEBGPU)
30
31#include "WHLSLBlock.h"
32#include "WHLSLBreak.h"
33#include "WHLSLContinue.h"
34#include "WHLSLDoWhileLoop.h"
35#include "WHLSLEffectfulExpressionStatement.h"
36#include "WHLSLFallthrough.h"
37#include "WHLSLForLoop.h"
38#include "WHLSLIfStatement.h"
39#include "WHLSLInferTypes.h"
40#include "WHLSLProgram.h"
41#include "WHLSLReturn.h"
42#include "WHLSLSwitchCase.h"
43#include "WHLSLSwitchStatement.h"
44#include "WHLSLTrap.h"
45#include "WHLSLVariableDeclarationsStatement.h"
46#include "WHLSLVisitor.h"
47#include "WHLSLWhileLoop.h"
48#include <cstdint>
49#include <wtf/OptionSet.h>
50#include <wtf/Vector.h>
51
52namespace WebCore {
53
54namespace WHLSL {
55
56class StatementBehaviorChecker : public Visitor {
57public:
58 enum class Behavior : uint8_t {
59 Return = 1 << 0,
60 Break = 1 << 1,
61 Continue = 1 << 2,
62 Fallthrough = 1 << 3,
63 Nothing = 1 << 4
64 };
65
66 OptionSet<Behavior> takeFunctionBehavior()
67 {
68 ASSERT(m_stack.size() == 1);
69 return m_stack.takeLast();
70 }
71
72private:
73 void visit(AST::Break&) override
74 {
75 m_stack.append({ Behavior::Break });
76 }
77
78 void visit(AST::Fallthrough&) override
79 {
80 m_stack.append({ Behavior::Fallthrough });
81 }
82
83 void visit(AST::Continue&) override
84 {
85 m_stack.append({ Behavior::Continue });
86 }
87
88 void visit(AST::Return&) override
89 {
90 m_stack.append({ Behavior::Return });
91 }
92
93 void visit(AST::Trap&) override
94 {
95 m_stack.append({ Behavior::Return });
96 }
97
98 void visit(AST::DoWhileLoop& doWhileLoop) override
99 {
100 checkErrorAndVisit(doWhileLoop.body());
101 if (error())
102 return;
103 auto b = m_stack.takeLast();
104 b.remove(Behavior::Break);
105 b.remove(Behavior::Continue);
106 b.add(Behavior::Nothing);
107 m_stack.append(b);
108 }
109
110 void visit(AST::ForLoop& forLoop) override
111 {
112 // The initialization always has a behavior of Nothing, which we already add unconditionally below,
113 // so we can just ignore the initialization.
114
115 checkErrorAndVisit(forLoop.body());
116 if (error())
117 return;
118 auto b = m_stack.takeLast();
119 b.remove(Behavior::Break);
120 b.remove(Behavior::Continue);
121 b.add(Behavior::Nothing);
122 m_stack.append(b);
123 }
124
125 void visit(AST::WhileLoop& whileLoop) override
126 {
127 checkErrorAndVisit(whileLoop.body());
128 if (error())
129 return;
130 auto b = m_stack.takeLast();
131 b.remove(Behavior::Break);
132 b.remove(Behavior::Continue);
133 b.add(Behavior::Nothing);
134 m_stack.append(b);
135 }
136
137 void visit(AST::SwitchCase& switchCase) override
138 {
139 Visitor::visit(switchCase);
140 }
141
142 void visit(AST::SwitchStatement& switchStatement) override
143 {
144 if (switchStatement.switchCases().isEmpty()) {
145 m_stack.append({ Behavior::Nothing });
146 return;
147 }
148
149 OptionSet<Behavior> reduction = { };
150 for (auto& switchCase : switchStatement.switchCases()) {
151 checkErrorAndVisit(switchCase);
152 if (error())
153 return;
154 auto b = m_stack.takeLast();
155 reduction = reduction | b;
156 }
157 if (reduction.contains(Behavior::Nothing)) {
158 setError();
159 return;
160 }
161 reduction.remove(Behavior::Break);
162 reduction.remove(Behavior::Fallthrough);
163 reduction.add(Behavior::Nothing);
164 m_stack.append(reduction);
165 }
166
167 void visit(AST::IfStatement& ifStatement) override
168 {
169 checkErrorAndVisit(ifStatement.body());
170 if (error())
171 return;
172 auto b = m_stack.takeLast();
173 OptionSet<Behavior> bPrime;
174 if (ifStatement.elseBody()) {
175 checkErrorAndVisit(*ifStatement.elseBody());
176 if (error())
177 return;
178 bPrime = m_stack.takeLast();
179 } else
180 bPrime = { Behavior::Nothing };
181 m_stack.append(b | bPrime);
182 }
183
184 void visit(AST::EffectfulExpressionStatement&) override
185 {
186 m_stack.append({ Behavior::Nothing });
187 }
188
189 void visit(AST::Block& block) override
190 {
191 if (block.statements().isEmpty()) {
192 m_stack.append({ Behavior::Nothing });
193 return;
194 }
195
196 OptionSet<Behavior> reduction = { };
197 for (size_t i = 0; i < block.statements().size() - 1; ++i) {
198 checkErrorAndVisit(block.statements()[i]);
199 if (error())
200 return;
201 auto b = m_stack.takeLast();
202 if (!b.contains(Behavior::Nothing)) {
203 setError();
204 return;
205 }
206 b.remove(Behavior::Nothing);
207 if (b.contains(Behavior::Fallthrough)) {
208 setError();
209 return;
210 }
211 reduction = reduction | b;
212 }
213 checkErrorAndVisit(block.statements()[block.statements().size() - 1]);
214 if (error())
215 return;
216 auto b = m_stack.takeLast();
217 m_stack.append(reduction | b);
218 }
219
220 void visit(AST::VariableDeclarationsStatement&) override
221 {
222 m_stack.append({ Behavior::Nothing });
223 }
224
225 Vector<OptionSet<Behavior>> m_stack;
226};
227
228bool checkStatementBehavior(Program& program)
229{
230 StatementBehaviorChecker statementBehaviorChecker;
231 for (auto& functionDefinition : program.functionDefinitions()) {
232 statementBehaviorChecker.Visitor::visit(functionDefinition);
233 if (statementBehaviorChecker.error())
234 return false;
235 auto behavior = statementBehaviorChecker.takeFunctionBehavior();
236 if (matches(functionDefinition->type(), program.intrinsics().voidType())) {
237 behavior.remove(StatementBehaviorChecker::Behavior::Return);
238 behavior.remove(StatementBehaviorChecker::Behavior::Nothing);
239 if (behavior != OptionSet<StatementBehaviorChecker::Behavior>())
240 return false;
241 } else {
242 if (behavior != StatementBehaviorChecker::Behavior::Return)
243 return false;
244 }
245 }
246 return true;
247}
248
249} // namespace WHLSL
250
251} // namespace WebCore
252
253#endif // ENABLE(WEBGPU)
254