• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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