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/ast/variable_decl_statement.h"
16 #include "src/writer/msl/test_helper.h"
17
18 namespace tint {
19 namespace writer {
20 namespace msl {
21 namespace {
22
23 using MslGeneratorImplTest = TestHelper;
24
TEST_F(MslGeneratorImplTest,Emit_Loop)25 TEST_F(MslGeneratorImplTest, Emit_Loop) {
26 auto* body = Block(create<ast::DiscardStatement>());
27 auto* continuing = Block();
28 auto* l = Loop(body, continuing);
29 WrapInFunction(l);
30
31 GeneratorImpl& gen = Build();
32
33 gen.increment_indent();
34
35 ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
36 EXPECT_EQ(gen.result(), R"( while (true) {
37 discard_fragment();
38 }
39 )");
40 }
41
TEST_F(MslGeneratorImplTest,Emit_LoopWithContinuing)42 TEST_F(MslGeneratorImplTest, Emit_LoopWithContinuing) {
43 Func("a_statement", {}, ty.void_(), {});
44
45 auto* body = Block(create<ast::DiscardStatement>());
46 auto* continuing = Block(CallStmt(Call("a_statement")));
47 auto* l = Loop(body, continuing);
48 WrapInFunction(l);
49
50 GeneratorImpl& gen = Build();
51
52 gen.increment_indent();
53
54 ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
55 EXPECT_EQ(gen.result(), R"( while (true) {
56 discard_fragment();
57 {
58 a_statement();
59 }
60 }
61 )");
62 }
63
TEST_F(MslGeneratorImplTest,Emit_LoopNestedWithContinuing)64 TEST_F(MslGeneratorImplTest, Emit_LoopNestedWithContinuing) {
65 Func("a_statement", {}, ty.void_(), {});
66
67 Global("lhs", ty.f32(), ast::StorageClass::kPrivate);
68 Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
69
70 auto* body = Block(create<ast::DiscardStatement>());
71 auto* continuing = Block(CallStmt(Call("a_statement")));
72 auto* inner = Loop(body, continuing);
73
74 body = Block(inner);
75
76 continuing = Block(Assign("lhs", "rhs"));
77
78 auto* outer = Loop(body, continuing);
79 WrapInFunction(outer);
80
81 GeneratorImpl& gen = Build();
82
83 gen.increment_indent();
84
85 ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
86 EXPECT_EQ(gen.result(), R"( while (true) {
87 while (true) {
88 discard_fragment();
89 {
90 a_statement();
91 }
92 }
93 {
94 lhs = rhs;
95 }
96 }
97 )");
98 }
99
TEST_F(MslGeneratorImplTest,Emit_LoopWithVarUsedInContinuing)100 TEST_F(MslGeneratorImplTest, Emit_LoopWithVarUsedInContinuing) {
101 // loop {
102 // var lhs : f32 = 2.4;
103 // var other : f32;
104 // continuing {
105 // lhs = rhs
106 // }
107 // }
108 //
109 // ->
110 // {
111 // float lhs;
112 // float other;
113 // for (;;) {
114 // if (continuing) {
115 // lhs = rhs;
116 // }
117 // lhs = 2.4f;
118 // other = 0.0f;
119 // }
120 // }
121
122 Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
123
124 auto* var = Var("lhs", ty.f32(), ast::StorageClass::kNone, Expr(2.4f));
125
126 auto* body = Block(Decl(var), Decl(Var("other", ty.f32())));
127
128 auto* continuing = Block(Assign("lhs", "rhs"));
129
130 auto* outer = Loop(body, continuing);
131 WrapInFunction(outer);
132
133 GeneratorImpl& gen = Build();
134
135 gen.increment_indent();
136
137 ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
138 EXPECT_EQ(gen.result(), R"( while (true) {
139 float lhs = 2.400000095f;
140 float other = 0.0f;
141 {
142 lhs = rhs;
143 }
144 }
145 )");
146 }
147
TEST_F(MslGeneratorImplTest,Emit_ForLoop)148 TEST_F(MslGeneratorImplTest, Emit_ForLoop) {
149 // for(; ; ) {
150 // return;
151 // }
152
153 Func("a_statement", {}, ty.void_(), {});
154
155 auto* f =
156 For(nullptr, nullptr, nullptr, Block(CallStmt(Call("a_statement"))));
157 WrapInFunction(f);
158
159 GeneratorImpl& gen = Build();
160
161 gen.increment_indent();
162
163 ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
164 EXPECT_EQ(gen.result(), R"( for(; ; ) {
165 a_statement();
166 }
167 )");
168 }
169
TEST_F(MslGeneratorImplTest,Emit_ForLoopWithSimpleInit)170 TEST_F(MslGeneratorImplTest, Emit_ForLoopWithSimpleInit) {
171 // for(var i : i32; ; ) {
172 // return;
173 // }
174
175 Func("a_statement", {}, ty.void_(), {});
176
177 auto* f = For(Decl(Var("i", ty.i32())), nullptr, nullptr,
178 Block(CallStmt(Call("a_statement"))));
179 WrapInFunction(f);
180
181 GeneratorImpl& gen = Build();
182
183 gen.increment_indent();
184
185 ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
186 EXPECT_EQ(gen.result(), R"( for(int i = 0; ; ) {
187 a_statement();
188 }
189 )");
190 }
191
TEST_F(MslGeneratorImplTest,Emit_ForLoopWithMultiStmtInit)192 TEST_F(MslGeneratorImplTest, Emit_ForLoopWithMultiStmtInit) {
193 // fn f(i : i32) {}
194 //
195 // var<workgroup> a : atomic<i32>;
196 // for({f(1); f(2);}; ; ) {
197 // return;
198 // }
199
200 Func("f", {Param("i", ty.i32())}, ty.void_(), {});
201 auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
202
203 Func("a_statement", {}, ty.void_(), {});
204
205 Global("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
206 auto* multi_stmt = Block(f(1), f(2));
207 auto* loop =
208 For(multi_stmt, nullptr, nullptr, Block(CallStmt(Call("a_statement"))));
209 WrapInFunction(loop);
210
211 GeneratorImpl& gen = Build();
212
213 gen.increment_indent();
214
215 ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
216 EXPECT_EQ(gen.result(), R"( {
217 {
218 f(1);
219 f(2);
220 }
221 for(; ; ) {
222 a_statement();
223 }
224 }
225 )");
226 }
227
TEST_F(MslGeneratorImplTest,Emit_ForLoopWithSimpleCond)228 TEST_F(MslGeneratorImplTest, Emit_ForLoopWithSimpleCond) {
229 // for(; true; ) {
230 // return;
231 // }
232
233 Func("a_statement", {}, ty.void_(), {});
234
235 auto* f = For(nullptr, true, nullptr, Block(CallStmt(Call("a_statement"))));
236 WrapInFunction(f);
237
238 GeneratorImpl& gen = Build();
239
240 gen.increment_indent();
241
242 ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
243 EXPECT_EQ(gen.result(), R"( for(; true; ) {
244 a_statement();
245 }
246 )");
247 }
248
TEST_F(MslGeneratorImplTest,Emit_ForLoopWithSimpleCont)249 TEST_F(MslGeneratorImplTest, Emit_ForLoopWithSimpleCont) {
250 // for(; ; i = i + 1) {
251 // return;
252 // }
253
254 Func("a_statement", {}, ty.void_(), {});
255
256 auto* v = Decl(Var("i", ty.i32()));
257 auto* f = For(nullptr, nullptr, Assign("i", Add("i", 1)),
258 Block(CallStmt(Call("a_statement"))));
259 WrapInFunction(v, f);
260
261 GeneratorImpl& gen = Build();
262
263 gen.increment_indent();
264
265 ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
266 EXPECT_EQ(
267 gen.result(),
268 R"( for(; ; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
269 a_statement();
270 }
271 )");
272 }
273
TEST_F(MslGeneratorImplTest,Emit_ForLoopWithMultiStmtCont)274 TEST_F(MslGeneratorImplTest, Emit_ForLoopWithMultiStmtCont) {
275 // fn f(i : i32) {}
276 //
277 // var<workgroup> a : atomic<i32>;
278 // for(; ; { f(1); f(2); }) {
279 // return;
280 // }
281
282 Func("f", {Param("i", ty.i32())}, ty.void_(), {});
283 auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
284
285 Func("a_statement", {}, ty.void_(), {});
286
287 Global("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
288 auto* multi_stmt = Block(f(1), f(2));
289 auto* loop =
290 For(nullptr, nullptr, multi_stmt, Block(CallStmt(Call("a_statement"))));
291 WrapInFunction(loop);
292
293 GeneratorImpl& gen = Build();
294
295 gen.increment_indent();
296
297 ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
298 EXPECT_EQ(gen.result(), R"( while (true) {
299 a_statement();
300 {
301 f(1);
302 f(2);
303 }
304 }
305 )");
306 }
307
TEST_F(MslGeneratorImplTest,Emit_ForLoopWithSimpleInitCondCont)308 TEST_F(MslGeneratorImplTest, Emit_ForLoopWithSimpleInitCondCont) {
309 // for(var i : i32; true; i = i + 1) {
310 // return;
311 // }
312
313 Func("a_statement", {}, ty.void_(), {});
314
315 auto* f = For(Decl(Var("i", ty.i32())), true, Assign("i", Add("i", 1)),
316 Block(CallStmt(Call("a_statement"))));
317 WrapInFunction(f);
318
319 GeneratorImpl& gen = Build();
320
321 gen.increment_indent();
322
323 ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
324 EXPECT_EQ(
325 gen.result(),
326 R"( for(int i = 0; true; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
327 a_statement();
328 }
329 )");
330 }
331
TEST_F(MslGeneratorImplTest,Emit_ForLoopWithMultiStmtInitCondCont)332 TEST_F(MslGeneratorImplTest, Emit_ForLoopWithMultiStmtInitCondCont) {
333 // fn f(i : i32) {}
334 //
335 // var<workgroup> a : atomic<i32>;
336 // for({ f(1); f(2); }; true; { f(3); f(4); }) {
337 // return;
338 // }
339
340 Func("f", {Param("i", ty.i32())}, ty.void_(), {});
341 auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
342
343 Func("a_statement", {}, ty.void_(), {});
344
345 Global("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
346 auto* multi_stmt_a = Block(f(1), f(2));
347 auto* multi_stmt_b = Block(f(3), f(4));
348 auto* loop = For(multi_stmt_a, Expr(true), multi_stmt_b,
349 Block(CallStmt(Call("a_statement"))));
350 WrapInFunction(loop);
351
352 GeneratorImpl& gen = Build();
353
354 gen.increment_indent();
355
356 ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
357 EXPECT_EQ(gen.result(), R"( {
358 {
359 f(1);
360 f(2);
361 }
362 while (true) {
363 if (!(true)) { break; }
364 a_statement();
365 {
366 f(3);
367 f(4);
368 }
369 }
370 }
371 )");
372 }
373
374 } // namespace
375 } // namespace msl
376 } // namespace writer
377 } // namespace tint
378