1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/writer/spirv/spv_dump.h"
16 #include "src/writer/spirv/test_helper.h"
17
18 namespace tint {
19 namespace writer {
20 namespace spirv {
21 namespace {
22
23 using BuilderTest = TestHelper;
24
TEST_F(BuilderTest,Loop_Empty)25 TEST_F(BuilderTest, Loop_Empty) {
26 // loop {
27 // }
28
29 auto* loop = Loop(Block(), Block());
30 WrapInFunction(loop);
31
32 spirv::Builder& b = Build();
33
34 b.push_function(Function{});
35
36 EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
37 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
38 R"(OpBranch %1
39 %1 = OpLabel
40 OpLoopMerge %2 %3 None
41 OpBranch %4
42 %4 = OpLabel
43 OpBranch %3
44 %3 = OpLabel
45 OpBranch %1
46 %2 = OpLabel
47 )");
48 }
49
TEST_F(BuilderTest,Loop_WithoutContinuing)50 TEST_F(BuilderTest, Loop_WithoutContinuing) {
51 // loop {
52 // v = 2;
53 // }
54
55 auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
56 auto* body = Block(Assign("v", 2));
57
58 auto* loop = Loop(body, Block());
59 WrapInFunction(loop);
60
61 spirv::Builder& b = Build();
62
63 b.push_function(Function{});
64 ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
65
66 EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
67 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
68 %2 = OpTypePointer Private %3
69 %4 = OpConstantNull %3
70 %1 = OpVariable %2 Private %4
71 %9 = OpConstant %3 2
72 )");
73 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
74 R"(OpBranch %5
75 %5 = OpLabel
76 OpLoopMerge %6 %7 None
77 OpBranch %8
78 %8 = OpLabel
79 OpStore %1 %9
80 OpBranch %7
81 %7 = OpLabel
82 OpBranch %5
83 %6 = OpLabel
84 )");
85 }
86
TEST_F(BuilderTest,Loop_WithContinuing)87 TEST_F(BuilderTest, Loop_WithContinuing) {
88 // loop {
89 // a = 2;
90 // continuing {
91 // a = 3;
92 // }
93 // }
94
95 auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
96 auto* body = Block(Assign("v", 2));
97 auto* continuing = Block(Assign("v", 3));
98
99 auto* loop = Loop(body, continuing);
100 WrapInFunction(loop);
101
102 spirv::Builder& b = Build();
103
104 b.push_function(Function{});
105 ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
106
107 EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
108 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
109 %2 = OpTypePointer Private %3
110 %4 = OpConstantNull %3
111 %1 = OpVariable %2 Private %4
112 %9 = OpConstant %3 2
113 %10 = OpConstant %3 3
114 )");
115 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
116 R"(OpBranch %5
117 %5 = OpLabel
118 OpLoopMerge %6 %7 None
119 OpBranch %8
120 %8 = OpLabel
121 OpStore %1 %9
122 OpBranch %7
123 %7 = OpLabel
124 OpStore %1 %10
125 OpBranch %5
126 %6 = OpLabel
127 )");
128 }
129
TEST_F(BuilderTest,Loop_WithBodyVariableAccessInContinuing)130 TEST_F(BuilderTest, Loop_WithBodyVariableAccessInContinuing) {
131 // loop {
132 // var a : i32;
133 // continuing {
134 // a = 3;
135 // }
136 // }
137
138 auto* var = Var("a", ty.i32());
139 auto* var_decl = WrapInStatement(var);
140 auto* body = Block(var_decl);
141 auto* continuing = Block(Assign("a", 3));
142
143 auto* loop = Loop(body, continuing);
144 WrapInFunction(loop);
145
146 spirv::Builder& b = Build();
147
148 b.push_function(Function{});
149 EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
150
151 EXPECT_EQ(DumpInstructions(b.types()), R"(%7 = OpTypeInt 32 1
152 %6 = OpTypePointer Function %7
153 %8 = OpConstantNull %7
154 %9 = OpConstant %7 3
155 )");
156 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
157 R"(OpBranch %1
158 %1 = OpLabel
159 OpLoopMerge %2 %3 None
160 OpBranch %4
161 %4 = OpLabel
162 OpBranch %3
163 %3 = OpLabel
164 OpStore %5 %9
165 OpBranch %1
166 %2 = OpLabel
167 )");
168 }
169
TEST_F(BuilderTest,Loop_WithContinue)170 TEST_F(BuilderTest, Loop_WithContinue) {
171 // loop {
172 // continue;
173 // }
174 auto* body = Block(create<ast::ContinueStatement>());
175 auto* loop = Loop(body, Block());
176 WrapInFunction(loop);
177
178 spirv::Builder& b = Build();
179
180 b.push_function(Function{});
181
182 EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
183 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
184 R"(OpBranch %1
185 %1 = OpLabel
186 OpLoopMerge %2 %3 None
187 OpBranch %4
188 %4 = OpLabel
189 OpBranch %3
190 %3 = OpLabel
191 OpBranch %1
192 %2 = OpLabel
193 )");
194 }
195
TEST_F(BuilderTest,Loop_WithBreak)196 TEST_F(BuilderTest, Loop_WithBreak) {
197 // loop {
198 // break;
199 // }
200 auto* body = Block(create<ast::BreakStatement>());
201 auto* loop = Loop(body, Block());
202 WrapInFunction(loop);
203
204 spirv::Builder& b = Build();
205
206 b.push_function(Function{});
207
208 EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
209 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
210 R"(OpBranch %1
211 %1 = OpLabel
212 OpLoopMerge %2 %3 None
213 OpBranch %4
214 %4 = OpLabel
215 OpBranch %2
216 %3 = OpLabel
217 OpBranch %1
218 %2 = OpLabel
219 )");
220 }
221
TEST_F(BuilderTest,Loop_WithContinuing_BreakIf)222 TEST_F(BuilderTest, Loop_WithContinuing_BreakIf) {
223 // loop {
224 // continuing {
225 // if (true) { break; }
226 // }
227 // }
228
229 auto* if_stmt = create<ast::IfStatement>(Expr(true), Block(Break()),
230 ast::ElseStatementList{});
231 auto* continuing = Block(if_stmt);
232 auto* loop = Loop(Block(), continuing);
233 WrapInFunction(loop);
234
235 spirv::Builder& b = Build();
236
237 b.push_function(Function{});
238
239 EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
240 EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeBool
241 %6 = OpConstantTrue %5
242 )");
243 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
244 R"(OpBranch %1
245 %1 = OpLabel
246 OpLoopMerge %2 %3 None
247 OpBranch %4
248 %4 = OpLabel
249 OpBranch %3
250 %3 = OpLabel
251 OpBranchConditional %6 %2 %1
252 %2 = OpLabel
253 )");
254 }
255
TEST_F(BuilderTest,Loop_WithContinuing_BreakUnless)256 TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless) {
257 // loop {
258 // continuing {
259 // if (true) {} else { break; }
260 // }
261 // }
262 auto* if_stmt = create<ast::IfStatement>(
263 Expr(true), Block(),
264 ast::ElseStatementList{Else(nullptr, Block(Break()))});
265 auto* continuing = Block(if_stmt);
266 auto* loop = Loop(Block(), continuing);
267 WrapInFunction(loop);
268
269 spirv::Builder& b = Build();
270
271 b.push_function(Function{});
272
273 EXPECT_TRUE(b.GenerateLoopStatement(loop)) << b.error();
274 EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeBool
275 %6 = OpConstantTrue %5
276 )");
277 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
278 R"(OpBranch %1
279 %1 = OpLabel
280 OpLoopMerge %2 %3 None
281 OpBranch %4
282 %4 = OpLabel
283 OpBranch %3
284 %3 = OpLabel
285 OpBranchConditional %6 %1 %2
286 %2 = OpLabel
287 )");
288 }
289
TEST_F(BuilderTest,Loop_WithContinuing_BreakIf_Nested)290 TEST_F(BuilderTest, Loop_WithContinuing_BreakIf_Nested) {
291 // Make sure the right backedge and break target are used.
292 // loop {
293 // continuing {
294 // loop {
295 // continuing {
296 // if (true) { break; }
297 // }
298 // }
299 // if (true) { break; }
300 // }
301 // }
302
303 auto* inner_if_stmt = create<ast::IfStatement>(Expr(true), Block(Break()),
304 ast::ElseStatementList{});
305 auto* inner_continuing = Block(inner_if_stmt);
306 auto* inner_loop = Loop(Block(), inner_continuing);
307
308 auto* outer_if_stmt = create<ast::IfStatement>(Expr(true), Block(Break()),
309 ast::ElseStatementList{});
310 auto* outer_continuing = Block(inner_loop, outer_if_stmt);
311 auto* outer_loop = Loop(Block(), outer_continuing);
312
313 WrapInFunction(outer_loop);
314
315 spirv::Builder& b = Build();
316
317 b.push_function(Function{});
318
319 EXPECT_TRUE(b.GenerateLoopStatement(outer_loop)) << b.error();
320 EXPECT_EQ(DumpInstructions(b.types()), R"(%9 = OpTypeBool
321 %10 = OpConstantTrue %9
322 )");
323 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
324 R"(OpBranch %1
325 %1 = OpLabel
326 OpLoopMerge %2 %3 None
327 OpBranch %4
328 %4 = OpLabel
329 OpBranch %3
330 %3 = OpLabel
331 OpBranch %5
332 %5 = OpLabel
333 OpLoopMerge %6 %7 None
334 OpBranch %8
335 %8 = OpLabel
336 OpBranch %7
337 %7 = OpLabel
338 OpBranchConditional %10 %6 %5
339 %6 = OpLabel
340 OpBranchConditional %10 %2 %1
341 %2 = OpLabel
342 )");
343 }
344
TEST_F(BuilderTest,Loop_WithContinuing_BreakUnless_Nested)345 TEST_F(BuilderTest, Loop_WithContinuing_BreakUnless_Nested) {
346 // Make sure the right backedge and break target are used.
347 // loop {
348 // continuing {
349 // loop {
350 // continuing {
351 // if (true) {} else { break; }
352 // }
353 // }
354 // if (true) {} else { break; }
355 // }
356 // }
357
358 auto* inner_if_stmt = create<ast::IfStatement>(
359 Expr(true), Block(),
360 ast::ElseStatementList{Else(nullptr, Block(Break()))});
361 auto* inner_continuing = Block(inner_if_stmt);
362 auto* inner_loop = Loop(Block(), inner_continuing);
363
364 auto* outer_if_stmt = create<ast::IfStatement>(
365 Expr(true), Block(),
366 ast::ElseStatementList{Else(nullptr, Block(Break()))});
367 auto* outer_continuing = Block(inner_loop, outer_if_stmt);
368 auto* outer_loop = Loop(Block(), outer_continuing);
369
370 WrapInFunction(outer_loop);
371
372 spirv::Builder& b = Build();
373
374 b.push_function(Function{});
375
376 EXPECT_TRUE(b.GenerateLoopStatement(outer_loop)) << b.error();
377 EXPECT_EQ(DumpInstructions(b.types()), R"(%9 = OpTypeBool
378 %10 = OpConstantTrue %9
379 )");
380 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
381 R"(OpBranch %1
382 %1 = OpLabel
383 OpLoopMerge %2 %3 None
384 OpBranch %4
385 %4 = OpLabel
386 OpBranch %3
387 %3 = OpLabel
388 OpBranch %5
389 %5 = OpLabel
390 OpLoopMerge %6 %7 None
391 OpBranch %8
392 %8 = OpLabel
393 OpBranch %7
394 %7 = OpLabel
395 OpBranchConditional %10 %5 %6
396 %6 = OpLabel
397 OpBranchConditional %10 %1 %2
398 %2 = OpLabel
399 )");
400 }
401
402 } // namespace
403 } // namespace spirv
404 } // namespace writer
405 } // namespace tint
406