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 "gmock/gmock.h"
16 #include "src/reader/spirv/function.h"
17 #include "src/reader/spirv/parser_impl_test_helper.h"
18 #include "src/reader/spirv/spirv_tools_helpers_test.h"
19
20 namespace tint {
21 namespace reader {
22 namespace spirv {
23 namespace {
24
25 using ::testing::Eq;
26 using ::testing::HasSubstr;
27
28 /// @returns a SPIR-V assembly segment which assigns debug names
29 /// to particular IDs.
Names(std::vector<std::string> ids)30 std::string Names(std::vector<std::string> ids) {
31 std::ostringstream outs;
32 for (auto& id : ids) {
33 outs << " OpName %" << id << " \"" << id << "\"\n";
34 }
35 return outs.str();
36 }
37
CommonTypes()38 std::string CommonTypes() {
39 return
40 R"(
41
42 %void = OpTypeVoid
43 %voidfn = OpTypeFunction %void
44
45 %bool = OpTypeBool
46 %float = OpTypeFloat 32
47 %uint = OpTypeInt 32 0
48 %int = OpTypeInt 32 1
49
50 %ptr_bool = OpTypePointer Function %bool
51 %ptr_float = OpTypePointer Function %float
52 %ptr_uint = OpTypePointer Function %uint
53 %ptr_int = OpTypePointer Function %int
54
55 %true = OpConstantTrue %bool
56 %false = OpConstantFalse %bool
57 %float_0 = OpConstant %float 0.0
58 %float_1p5 = OpConstant %float 1.5
59 %uint_0 = OpConstant %uint 0
60 %uint_1 = OpConstant %uint 1
61 %int_m1 = OpConstant %int -1
62 %int_0 = OpConstant %int 0
63 %int_1 = OpConstant %int 1
64 %int_3 = OpConstant %int 3
65 %uint_2 = OpConstant %uint 2
66 %uint_3 = OpConstant %uint 3
67 %uint_4 = OpConstant %uint 4
68 %uint_5 = OpConstant %uint 5
69
70 %v2int = OpTypeVector %int 2
71 %v2float = OpTypeVector %float 2
72 %m3v2float = OpTypeMatrix %v2float 3
73
74 %v2int_null = OpConstantNull %v2int
75
76 %arr2uint = OpTypeArray %uint %uint_2
77 %strct = OpTypeStruct %uint %float %arr2uint
78 )";
79 }
80
81 // Returns the SPIR-V assembly for capabilities, the memory model,
82 // a vertex shader entry point declaration, and name declarations
83 // for specified IDs.
Caps(std::vector<std::string> ids={})84 std::string Caps(std::vector<std::string> ids = {}) {
85 return R"(
86 OpCapability Shader
87 OpMemoryModel Logical Simple
88 OpEntryPoint Fragment %100 "main"
89 OpExecutionMode %100 OriginUpperLeft
90 )" + Names(ids);
91 }
92
93 // Returns the SPIR-V assembly for a vertex shader, optionally
94 // with OpName decorations for certain SPIR-V IDs
PreambleNames(std::vector<std::string> ids)95 std::string PreambleNames(std::vector<std::string> ids) {
96 return Caps(ids) + CommonTypes();
97 }
98
Preamble()99 std::string Preamble() {
100 return PreambleNames({});
101 }
102
103 using SpvParserFunctionVarTest = SpvParserTest;
104
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_AnonymousVars)105 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_AnonymousVars) {
106 auto p = parser(test::Assemble(Preamble() + R"(
107 %100 = OpFunction %void None %voidfn
108 %entry = OpLabel
109 %1 = OpVariable %ptr_uint Function
110 %2 = OpVariable %ptr_uint Function
111 %3 = OpVariable %ptr_uint Function
112 OpReturn
113 OpFunctionEnd
114 )"));
115 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
116 auto fe = p->function_emitter(100);
117 EXPECT_TRUE(fe.EmitFunctionVariables());
118
119 auto ast_body = fe.ast_body();
120 EXPECT_THAT(test::ToString(p->program(), ast_body),
121 HasSubstr(R"(var x_1 : u32;
122 var x_2 : u32;
123 var x_3 : u32;
124 )"));
125 }
126
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_NamedVars)127 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_NamedVars) {
128 auto p = parser(test::Assemble(PreambleNames({"a", "b", "c"}) + R"(
129 %100 = OpFunction %void None %voidfn
130 %entry = OpLabel
131 %a = OpVariable %ptr_uint Function
132 %b = OpVariable %ptr_uint Function
133 %c = OpVariable %ptr_uint Function
134 OpReturn
135 OpFunctionEnd
136 )"));
137 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
138 auto fe = p->function_emitter(100);
139 EXPECT_TRUE(fe.EmitFunctionVariables());
140
141 auto ast_body = fe.ast_body();
142 EXPECT_THAT(test::ToString(p->program(), ast_body), HasSubstr(R"(var a : u32;
143 var b : u32;
144 var c : u32;
145 )"));
146 }
147
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_MixedTypes)148 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_MixedTypes) {
149 auto p = parser(test::Assemble(PreambleNames({"a", "b", "c"}) + R"(
150 %100 = OpFunction %void None %voidfn
151 %entry = OpLabel
152 %a = OpVariable %ptr_uint Function
153 %b = OpVariable %ptr_int Function
154 %c = OpVariable %ptr_float Function
155 OpReturn
156 OpFunctionEnd
157 )"));
158 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
159 auto fe = p->function_emitter(100);
160 EXPECT_TRUE(fe.EmitFunctionVariables());
161
162 auto ast_body = fe.ast_body();
163 EXPECT_THAT(test::ToString(p->program(), ast_body), HasSubstr(R"(var a : u32;
164 var b : i32;
165 var c : f32;
166 )"));
167 }
168
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_ScalarInitializers)169 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_ScalarInitializers) {
170 auto p = parser(test::Assemble(PreambleNames({"a", "b", "c", "d", "e"}) + R"(
171 %100 = OpFunction %void None %voidfn
172 %entry = OpLabel
173 %a = OpVariable %ptr_bool Function %true
174 %b = OpVariable %ptr_bool Function %false
175 %c = OpVariable %ptr_int Function %int_m1
176 %d = OpVariable %ptr_uint Function %uint_1
177 %e = OpVariable %ptr_float Function %float_1p5
178 OpReturn
179 OpFunctionEnd
180 )"));
181 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
182 auto fe = p->function_emitter(100);
183 EXPECT_TRUE(fe.EmitFunctionVariables());
184
185 auto ast_body = fe.ast_body();
186 EXPECT_THAT(test::ToString(p->program(), ast_body),
187 HasSubstr(R"(var a : bool = true;
188 var b : bool = false;
189 var c : i32 = -1;
190 var d : u32 = 1u;
191 var e : f32 = 1.5;
192 )"));
193 }
194
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_ScalarNullInitializers)195 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_ScalarNullInitializers) {
196 auto p = parser(test::Assemble(PreambleNames({"a", "b", "c", "d"}) + R"(
197 %null_bool = OpConstantNull %bool
198 %null_int = OpConstantNull %int
199 %null_uint = OpConstantNull %uint
200 %null_float = OpConstantNull %float
201
202 %100 = OpFunction %void None %voidfn
203 %entry = OpLabel
204 %a = OpVariable %ptr_bool Function %null_bool
205 %b = OpVariable %ptr_int Function %null_int
206 %c = OpVariable %ptr_uint Function %null_uint
207 %d = OpVariable %ptr_float Function %null_float
208 OpReturn
209 OpFunctionEnd
210 )"));
211 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
212 auto fe = p->function_emitter(100);
213 EXPECT_TRUE(fe.EmitFunctionVariables());
214
215 auto ast_body = fe.ast_body();
216 EXPECT_THAT(test::ToString(p->program(), ast_body),
217 HasSubstr(R"(var a : bool = false;
218 var b : i32 = 0;
219 var c : u32 = 0u;
220 var d : f32 = 0.0;
221 )"));
222 }
223
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_VectorInitializer)224 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_VectorInitializer) {
225 auto p = parser(test::Assemble(Preamble() + R"(
226 %ptr = OpTypePointer Function %v2float
227 %two = OpConstant %float 2.0
228 %const = OpConstantComposite %v2float %float_1p5 %two
229
230 %100 = OpFunction %void None %voidfn
231 %entry = OpLabel
232 %200 = OpVariable %ptr Function %const
233 OpReturn
234 OpFunctionEnd
235 )"));
236 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
237 auto fe = p->function_emitter(100);
238 EXPECT_TRUE(fe.EmitFunctionVariables());
239
240 auto ast_body = fe.ast_body();
241 EXPECT_THAT(test::ToString(p->program(), ast_body),
242 HasSubstr("var x_200 : vec2<f32> = vec2<f32>(1.5, 2.0);"));
243 }
244
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_MatrixInitializer)245 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_MatrixInitializer) {
246 auto p = parser(test::Assemble(Preamble() + R"(
247 %ptr = OpTypePointer Function %m3v2float
248 %two = OpConstant %float 2.0
249 %three = OpConstant %float 3.0
250 %four = OpConstant %float 4.0
251 %v0 = OpConstantComposite %v2float %float_1p5 %two
252 %v1 = OpConstantComposite %v2float %two %three
253 %v2 = OpConstantComposite %v2float %three %four
254 %const = OpConstantComposite %m3v2float %v0 %v1 %v2
255
256 %100 = OpFunction %void None %voidfn
257 %entry = OpLabel
258 %200 = OpVariable %ptr Function %const
259 OpReturn
260 OpFunctionEnd
261 )"));
262 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
263 auto fe = p->function_emitter(100);
264 EXPECT_TRUE(fe.EmitFunctionVariables());
265
266 auto ast_body = fe.ast_body();
267 EXPECT_THAT(test::ToString(p->program(), ast_body),
268 HasSubstr("var x_200 : mat3x2<f32> = mat3x2<f32>("
269 "vec2<f32>(1.5, 2.0), "
270 "vec2<f32>(2.0, 3.0), "
271 "vec2<f32>(3.0, 4.0));"));
272 }
273
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_ArrayInitializer)274 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_ArrayInitializer) {
275 auto p = parser(test::Assemble(Preamble() + R"(
276 %ptr = OpTypePointer Function %arr2uint
277 %two = OpConstant %uint 2
278 %const = OpConstantComposite %arr2uint %uint_1 %two
279
280 %100 = OpFunction %void None %voidfn
281 %entry = OpLabel
282 %200 = OpVariable %ptr Function %const
283 OpReturn
284 OpFunctionEnd
285 )"));
286 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
287 auto fe = p->function_emitter(100);
288 EXPECT_TRUE(fe.EmitFunctionVariables());
289
290 auto ast_body = fe.ast_body();
291 EXPECT_THAT(
292 test::ToString(p->program(), ast_body),
293 HasSubstr("var x_200 : array<u32, 2u> = array<u32, 2u>(1u, 2u);"));
294 }
295
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_ArrayInitializer_Alias)296 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_ArrayInitializer_Alias) {
297 auto p = parser(test::Assemble(R"(
298 OpCapability Shader
299 OpMemoryModel Logical Simple
300 OpEntryPoint Fragment %100 "main"
301 OpExecutionMode %100 OriginUpperLeft
302 OpDecorate %arr2uint ArrayStride 16
303 )" + CommonTypes() + R"(
304 %ptr = OpTypePointer Function %arr2uint
305 %two = OpConstant %uint 2
306 %const = OpConstantComposite %arr2uint %uint_1 %two
307
308 %100 = OpFunction %void None %voidfn
309 %entry = OpLabel
310 %200 = OpVariable %ptr Function %const
311 OpReturn
312 OpFunctionEnd
313 )"));
314 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
315 auto fe = p->function_emitter(100);
316 EXPECT_TRUE(fe.EmitFunctionVariables());
317
318 auto ast_body = fe.ast_body();
319 auto got = test::ToString(p->program(), ast_body);
320 const char* expect = "var x_200 : Arr = Arr(1u, 2u);\n";
321 EXPECT_EQ(expect, got);
322 }
323
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_ArrayInitializer_Null)324 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_ArrayInitializer_Null) {
325 auto p = parser(test::Assemble(Preamble() + R"(
326 %ptr = OpTypePointer Function %arr2uint
327 %two = OpConstant %uint 2
328 %const = OpConstantNull %arr2uint
329
330 %100 = OpFunction %void None %voidfn
331 %entry = OpLabel
332 %200 = OpVariable %ptr Function %const
333 OpReturn
334 OpFunctionEnd
335 )"));
336 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
337 auto fe = p->function_emitter(100);
338 EXPECT_TRUE(fe.EmitFunctionVariables());
339
340 auto ast_body = fe.ast_body();
341 EXPECT_THAT(
342 test::ToString(p->program(), ast_body),
343 HasSubstr("var x_200 : array<u32, 2u> = array<u32, 2u>(0u, 0u);"));
344 }
345
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_ArrayInitializer_Alias_Null)346 TEST_F(SpvParserFunctionVarTest,
347 EmitFunctionVariables_ArrayInitializer_Alias_Null) {
348 auto p = parser(test::Assemble(R"(
349 OpCapability Shader
350 OpMemoryModel Logical Simple
351 OpEntryPoint Fragment %100 "main"
352 OpExecutionMode %100 OriginUpperLeft
353 OpDecorate %arr2uint ArrayStride 16
354 )" + CommonTypes() + R"(
355 %ptr = OpTypePointer Function %arr2uint
356 %two = OpConstant %uint 2
357 %const = OpConstantNull %arr2uint
358
359 %100 = OpFunction %void None %voidfn
360 %entry = OpLabel
361 %200 = OpVariable %ptr Function %const
362 OpReturn
363 OpFunctionEnd
364 )"));
365 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
366 auto fe = p->function_emitter(100);
367 EXPECT_TRUE(fe.EmitFunctionVariables());
368
369 auto ast_body = fe.ast_body();
370 EXPECT_THAT(test::ToString(p->program(), ast_body),
371 HasSubstr("var x_200 : Arr = Arr(0u, 0u);"));
372 }
373
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_StructInitializer)374 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_StructInitializer) {
375 auto p = parser(test::Assemble(Preamble() + R"(
376 %ptr = OpTypePointer Function %strct
377 %two = OpConstant %uint 2
378 %arrconst = OpConstantComposite %arr2uint %uint_1 %two
379 %const = OpConstantComposite %strct %uint_1 %float_1p5 %arrconst
380
381 %100 = OpFunction %void None %voidfn
382 %entry = OpLabel
383 %200 = OpVariable %ptr Function %const
384 OpReturn
385 OpFunctionEnd
386 )"));
387 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
388 auto fe = p->function_emitter(100);
389 EXPECT_TRUE(fe.EmitFunctionVariables());
390
391 auto ast_body = fe.ast_body();
392 EXPECT_THAT(test::ToString(p->program(), ast_body),
393 HasSubstr("var x_200 : S = S(1u, 1.5, array<u32, 2u>(1u, 2u));"));
394 }
395
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_StructInitializer_Null)396 TEST_F(SpvParserFunctionVarTest, EmitFunctionVariables_StructInitializer_Null) {
397 auto p = parser(test::Assemble(Preamble() + R"(
398 %ptr = OpTypePointer Function %strct
399 %two = OpConstant %uint 2
400 %arrconst = OpConstantComposite %arr2uint %uint_1 %two
401 %const = OpConstantNull %strct
402
403 %100 = OpFunction %void None %voidfn
404 %entry = OpLabel
405 %200 = OpVariable %ptr Function %const
406 OpReturn
407 OpFunctionEnd
408 )"));
409 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
410 auto fe = p->function_emitter(100);
411 EXPECT_TRUE(fe.EmitFunctionVariables());
412
413 auto ast_body = fe.ast_body();
414 EXPECT_THAT(test::ToString(p->program(), ast_body),
415 HasSubstr("var x_200 : S = S(0u, 0.0, array<u32, 2u>(0u, 0u));"));
416 }
417
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_Decorate_RelaxedPrecision)418 TEST_F(SpvParserFunctionVarTest,
419 EmitFunctionVariables_Decorate_RelaxedPrecision) {
420 // RelaxedPrecisionis dropped
421 const auto assembly = Caps({"myvar"}) + R"(
422 OpDecorate %myvar RelaxedPrecision
423
424 %float = OpTypeFloat 32
425 %ptr = OpTypePointer Function %float
426
427 %void = OpTypeVoid
428 %voidfn = OpTypeFunction %void
429
430 %100 = OpFunction %void None %voidfn
431 %entry = OpLabel
432 %myvar = OpVariable %ptr Function
433 OpReturn
434 OpFunctionEnd
435 )";
436 auto p = parser(test::Assemble(assembly));
437 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
438 auto fe = p->function_emitter(100);
439 EXPECT_TRUE(fe.EmitFunctionVariables());
440
441 auto ast_body = fe.ast_body();
442 const auto got = test::ToString(p->program(), ast_body);
443 EXPECT_EQ(got, "var myvar : f32;\n") << got;
444 }
445
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_MemberDecorate_RelaxedPrecision)446 TEST_F(SpvParserFunctionVarTest,
447 EmitFunctionVariables_MemberDecorate_RelaxedPrecision) {
448 // RelaxedPrecisionis dropped
449 const auto assembly = Caps({"myvar", "strct"}) + R"(
450 OpMemberDecorate %strct 0 RelaxedPrecision
451
452 %float = OpTypeFloat 32
453 %strct = OpTypeStruct %float
454 %ptr = OpTypePointer Function %strct
455
456 %void = OpTypeVoid
457 %voidfn = OpTypeFunction %void
458
459 %100 = OpFunction %void None %voidfn
460 %entry = OpLabel
461 %myvar = OpVariable %ptr Function
462 OpReturn
463 OpFunctionEnd
464 )";
465 auto p = parser(test::Assemble(assembly));
466 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
467 << assembly << p->error() << std::endl;
468 auto fe = p->function_emitter(100);
469 EXPECT_TRUE(fe.EmitFunctionVariables());
470
471 auto ast_body = fe.ast_body();
472 const auto got = test::ToString(p->program(), ast_body);
473 EXPECT_EQ(got, "var myvar : strct;\n") << got;
474 }
475
TEST_F(SpvParserFunctionVarTest,EmitFunctionVariables_StructDifferOnlyInMemberName)476 TEST_F(SpvParserFunctionVarTest,
477 EmitFunctionVariables_StructDifferOnlyInMemberName) {
478 auto p = parser(test::Assemble(R"(
479 OpCapability Shader
480 OpMemoryModel Logical Simple
481 OpEntryPoint Fragment %100 "main"
482 OpExecutionMode %100 OriginUpperLeft
483 OpName %_struct_5 "S"
484 OpName %_struct_6 "S"
485 OpMemberName %_struct_5 0 "algo"
486 OpMemberName %_struct_6 0 "rithm"
487
488 %void = OpTypeVoid
489 %voidfn = OpTypeFunction %void
490 %uint = OpTypeInt 32 0
491
492 %_struct_5 = OpTypeStruct %uint
493 %_struct_6 = OpTypeStruct %uint
494 %_ptr_Function__struct_5 = OpTypePointer Function %_struct_5
495 %_ptr_Function__struct_6 = OpTypePointer Function %_struct_6
496 %100 = OpFunction %void None %voidfn
497 %39 = OpLabel
498 %40 = OpVariable %_ptr_Function__struct_5 Function
499 %41 = OpVariable %_ptr_Function__struct_6 Function
500 OpReturn
501 OpFunctionEnd)"));
502 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
503 auto fe = p->function_emitter(100);
504 EXPECT_TRUE(fe.EmitFunctionVariables());
505
506 auto ast_body = fe.ast_body();
507 const auto got = test::ToString(p->program(), ast_body);
508 EXPECT_THAT(got, HasSubstr(R"(var x_40 : S;
509 var x_41 : S_1;
510 )"));
511 }
512
TEST_F(SpvParserFunctionVarTest,EmitStatement_CombinatorialValue_Defer_UsedOnceSameConstruct)513 TEST_F(SpvParserFunctionVarTest,
514 EmitStatement_CombinatorialValue_Defer_UsedOnceSameConstruct) {
515 auto assembly = Preamble() + R"(
516 %100 = OpFunction %void None %voidfn
517
518 %10 = OpLabel
519 %25 = OpVariable %ptr_uint Function
520 %2 = OpIAdd %uint %uint_1 %uint_1
521 OpStore %25 %uint_1 ; Do initial store to mark source location
522 OpBranch %20
523
524 %20 = OpLabel
525 OpStore %25 %2 ; defer emission of the addition until here.
526 OpReturn
527
528 OpFunctionEnd
529 )";
530 auto p = parser(test::Assemble(assembly));
531 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
532 auto fe = p->function_emitter(100);
533 EXPECT_TRUE(fe.EmitBody()) << p->error();
534
535 auto ast_body = fe.ast_body();
536 auto got = test::ToString(p->program(), ast_body);
537 auto* expect =
538 R"(var x_25 : u32;
539 x_25 = 1u;
540 x_25 = (1u + 1u);
541 return;
542 )";
543 EXPECT_EQ(expect, got);
544 }
545
TEST_F(SpvParserFunctionVarTest,EmitStatement_CombinatorialValue_Immediate_UsedTwice)546 TEST_F(SpvParserFunctionVarTest,
547 EmitStatement_CombinatorialValue_Immediate_UsedTwice) {
548 auto assembly = Preamble() + R"(
549 %100 = OpFunction %void None %voidfn
550
551 %10 = OpLabel
552 %25 = OpVariable %ptr_uint Function
553 %2 = OpIAdd %uint %uint_1 %uint_1
554 OpStore %25 %uint_1 ; Do initial store to mark source location
555 OpBranch %20
556
557 %20 = OpLabel
558 OpStore %25 %2
559 OpStore %25 %2
560 OpReturn
561
562 OpFunctionEnd
563 )";
564 auto p = parser(test::Assemble(assembly));
565 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
566 auto fe = p->function_emitter(100);
567 EXPECT_TRUE(fe.EmitBody()) << p->error();
568
569 auto ast_body = fe.ast_body();
570 auto got = test::ToString(p->program(), ast_body);
571 auto* expect = R"(var x_25 : u32;
572 let x_2 : u32 = (1u + 1u);
573 x_25 = 1u;
574 x_25 = x_2;
575 x_25 = x_2;
576 return;
577 )";
578 EXPECT_EQ(expect, got);
579 }
580
TEST_F(SpvParserFunctionVarTest,EmitStatement_CombinatorialValue_Immediate_UsedOnceDifferentConstruct)581 TEST_F(SpvParserFunctionVarTest,
582 EmitStatement_CombinatorialValue_Immediate_UsedOnceDifferentConstruct) {
583 // Translation should not sink expensive operations into or out of control
584 // flow. As a simple heuristic, don't move *any* combinatorial operation
585 // across any control flow.
586 auto assembly = Preamble() + R"(
587 %100 = OpFunction %void None %voidfn
588
589 %10 = OpLabel
590 %25 = OpVariable %ptr_uint Function
591 %2 = OpIAdd %uint %uint_1 %uint_1
592 OpStore %25 %uint_1 ; Do initial store to mark source location
593 OpBranch %20
594
595 %20 = OpLabel ; Introduce a new construct
596 OpLoopMerge %99 %80 None
597 OpBranch %80
598
599 %80 = OpLabel
600 OpStore %25 %2 ; store combinatorial value %2, inside the loop
601 OpBranch %20
602
603 %99 = OpLabel ; merge block
604 OpStore %25 %uint_2
605 OpReturn
606
607 OpFunctionEnd
608 )";
609 auto p = parser(test::Assemble(assembly));
610 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
611 auto fe = p->function_emitter(100);
612 EXPECT_TRUE(fe.EmitBody()) << p->error();
613
614 auto ast_body = fe.ast_body();
615 auto got = test::ToString(p->program(), ast_body);
616 auto* expect = R"(var x_25 : u32;
617 let x_2 : u32 = (1u + 1u);
618 x_25 = 1u;
619 loop {
620
621 continuing {
622 x_25 = x_2;
623 }
624 }
625 x_25 = 2u;
626 return;
627 )";
628 EXPECT_EQ(expect, got);
629 }
630
TEST_F(SpvParserFunctionVarTest,EmitStatement_CombinatorialNonPointer_DefConstruct_DoesNotEncloseAllUses)631 TEST_F(
632 SpvParserFunctionVarTest,
633 EmitStatement_CombinatorialNonPointer_DefConstruct_DoesNotEncloseAllUses) {
634 // Compensate for the difference between dominance and scoping.
635 // Exercise hoisting of the constant definition to before its natural
636 // location.
637 //
638 // The definition of %2 should be hoisted
639 auto assembly = Preamble() + R"(
640 %pty = OpTypePointer Private %uint
641 %1 = OpVariable %pty Private
642
643 %100 = OpFunction %void None %voidfn
644
645 %3 = OpLabel
646 OpStore %1 %uint_0
647 OpBranch %5
648
649 %5 = OpLabel
650 OpStore %1 %uint_1
651 OpLoopMerge %99 %80 None
652 OpBranchConditional %false %99 %20
653
654 %20 = OpLabel
655 OpStore %1 %uint_3
656 OpSelectionMerge %50 None
657 OpBranchConditional %true %30 %40
658
659 %30 = OpLabel
660 ; This combinatorial definition in nested control flow dominates
661 ; the use in the merge block in %50
662 %2 = OpIAdd %uint %uint_1 %uint_1
663 OpBranch %50
664
665 %40 = OpLabel
666 OpReturn
667
668 %50 = OpLabel ; merge block for if-selection
669 OpStore %1 %2
670 OpBranch %80
671
672 %80 = OpLabel ; merge block
673 OpStore %1 %uint_4
674 OpBranchConditional %false %99 %5 ; loop backedge
675
676 %99 = OpLabel
677 OpStore %1 %uint_5
678 OpReturn
679
680 OpFunctionEnd
681 )";
682 auto p = parser(test::Assemble(assembly));
683 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
684 auto fe = p->function_emitter(100);
685 EXPECT_TRUE(fe.EmitBody()) << p->error();
686
687 auto ast_body = fe.ast_body();
688 auto got = test::ToString(p->program(), ast_body);
689 auto* expect = R"(x_1 = 0u;
690 loop {
691 var x_2 : u32;
692 x_1 = 1u;
693 if (false) {
694 break;
695 }
696 x_1 = 3u;
697 if (true) {
698 x_2 = (1u + 1u);
699 } else {
700 return;
701 }
702 x_1 = x_2;
703
704 continuing {
705 x_1 = 4u;
706 if (false) {
707 break;
708 }
709 }
710 }
711 x_1 = 5u;
712 return;
713 )";
714 EXPECT_EQ(expect, got);
715 }
716
TEST_F(SpvParserFunctionVarTest,EmitStatement_CombinatorialNonPointer_Hoisting_DefFirstBlockIf_InFunction)717 TEST_F(
718 SpvParserFunctionVarTest,
719 EmitStatement_CombinatorialNonPointer_Hoisting_DefFirstBlockIf_InFunction) {
720 // This is a hoisting case, where the definition is in the first block
721 // of an if selection construct. In this case the definition should count
722 // as being in the parent (enclosing) construct.
723 //
724 // The definition of %1 is in an IfSelection construct and also the enclosing
725 // Function construct, both of which start at block %10. For the purpose of
726 // determining the construct containing %10, go to the parent construct of
727 // the IfSelection.
728 auto assembly = Preamble() + R"(
729 %pty = OpTypePointer Private %uint
730 %200 = OpVariable %pty Private
731 %cond = OpConstantTrue %bool
732
733 %100 = OpFunction %void None %voidfn
734
735 ; in IfSelection construct, nested in Function construct
736 %10 = OpLabel
737 %1 = OpCopyObject %uint %uint_1
738 OpSelectionMerge %99 None
739 OpBranchConditional %cond %20 %99
740
741 %20 = OpLabel ; in IfSelection construct
742 OpBranch %99
743
744 %99 = OpLabel
745 %3 = OpCopyObject %uint %1; in Function construct
746 OpStore %200 %3
747 OpReturn
748
749 OpFunctionEnd
750 )";
751 auto p = parser(test::Assemble(assembly));
752 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
753 auto fe = p->function_emitter(100);
754 EXPECT_TRUE(fe.EmitBody()) << p->error();
755
756 // We don't hoist x_1 into its own mutable variable. It is emitted as
757 // a const definition.
758 auto ast_body = fe.ast_body();
759 auto got = test::ToString(p->program(), ast_body);
760 auto* expect = R"(let x_1 : u32 = 1u;
761 if (true) {
762 }
763 let x_3 : u32 = x_1;
764 x_200 = x_3;
765 return;
766 )";
767 EXPECT_EQ(expect, got);
768 }
769
TEST_F(SpvParserFunctionVarTest,EmitStatement_CombinatorialNonPointer_Hoisting_DefFirstBlockIf_InIf)770 TEST_F(SpvParserFunctionVarTest,
771 EmitStatement_CombinatorialNonPointer_Hoisting_DefFirstBlockIf_InIf) {
772 // This is like the previous case, but the IfSelection is nested inside
773 // another IfSelection.
774 // This tests that the hoisting algorithm goes to only one parent of
775 // the definining if-selection block, and doesn't jump all the way out
776 // to the Function construct that encloses everything.
777 //
778 // We should not hoist %1 because its definition should count as being
779 // in the outer IfSelection, not the inner IfSelection.
780 auto assembly = Preamble() + R"(
781
782 %pty = OpTypePointer Private %uint
783 %200 = OpVariable %pty Private
784 %cond = OpConstantTrue %bool
785
786 %100 = OpFunction %void None %voidfn
787
788 ; outer IfSelection
789 %10 = OpLabel
790 OpSelectionMerge %99 None
791 OpBranchConditional %cond %20 %99
792
793 ; inner IfSelection
794 %20 = OpLabel
795 %1 = OpCopyObject %uint %uint_1
796 OpSelectionMerge %89 None
797 OpBranchConditional %cond %30 %89
798
799 %30 = OpLabel ; last block of inner IfSelection
800 OpBranch %89
801
802 ; in outer IfSelection
803 %89 = OpLabel
804 %3 = OpCopyObject %uint %1; Last use of %1, in outer IfSelection
805 OpStore %200 %3
806 OpBranch %99
807
808 %99 = OpLabel
809 OpReturn
810
811 OpFunctionEnd
812 )";
813 auto p = parser(test::Assemble(assembly));
814 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
815 auto fe = p->function_emitter(100);
816 EXPECT_TRUE(fe.EmitBody()) << p->error();
817
818 auto ast_body = fe.ast_body();
819 auto got = test::ToString(p->program(), ast_body);
820 auto* expect = R"(if (true) {
821 let x_1 : u32 = 1u;
822 if (true) {
823 }
824 let x_3 : u32 = x_1;
825 x_200 = x_3;
826 }
827 return;
828 )";
829 EXPECT_EQ(expect, got);
830 }
831
TEST_F(SpvParserFunctionVarTest,EmitStatement_CombinatorialNonPointer_Hoisting_DefFirstBlockSwitch_InIf)832 TEST_F(
833 SpvParserFunctionVarTest,
834 EmitStatement_CombinatorialNonPointer_Hoisting_DefFirstBlockSwitch_InIf) {
835 // This is like the previous case, but the definition is in a SwitchSelection
836 // inside another IfSelection.
837 // Tests that definitions in the first block of a switch count as being
838 // in the parent of the switch construct.
839 auto assembly = Preamble() + R"(
840 %pty = OpTypePointer Private %uint
841 %200 = OpVariable %pty Private
842 %cond = OpConstantTrue %bool
843
844 %100 = OpFunction %void None %voidfn
845
846 ; outer IfSelection
847 %10 = OpLabel
848 OpSelectionMerge %99 None
849 OpBranchConditional %cond %20 %99
850
851 ; inner SwitchSelection
852 %20 = OpLabel
853 %1 = OpCopyObject %uint %uint_1
854 OpSelectionMerge %89 None
855 OpSwitch %uint_1 %89 0 %30
856
857 %30 = OpLabel ; last block of inner SwitchSelection
858 OpBranch %89
859
860 ; in outer IfSelection
861 %89 = OpLabel
862 %3 = OpCopyObject %uint %1; Last use of %1, in outer IfSelection
863 OpStore %200 %3
864 OpBranch %99
865
866 %99 = OpLabel
867 OpReturn
868 OpFunctionEnd
869 )";
870 auto p = parser(test::Assemble(assembly));
871 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
872 auto fe = p->function_emitter(100);
873 EXPECT_TRUE(fe.EmitBody()) << p->error();
874
875 auto ast_body = fe.ast_body();
876 auto got = test::ToString(p->program(), ast_body);
877 auto* expect = R"(if (true) {
878 let x_1 : u32 = 1u;
879 switch(1u) {
880 case 0u: {
881 }
882 default: {
883 }
884 }
885 let x_3 : u32 = x_1;
886 x_200 = x_3;
887 }
888 return;
889 )";
890 EXPECT_EQ(expect, got);
891 }
892
TEST_F(SpvParserFunctionVarTest,EmitStatement_CombinatorialNonPointer_Hoisting_DefAndUseFirstBlockIf)893 TEST_F(SpvParserFunctionVarTest,
894 EmitStatement_CombinatorialNonPointer_Hoisting_DefAndUseFirstBlockIf) {
895 // In this test, both the defintion and the use are in the first block
896 // of an IfSelection. No hoisting occurs because hoisting is triggered
897 // on whether the defining construct contains the last use, rather than
898 // whether the two constructs are the same.
899 //
900 // This example has two SSA IDs which are tempting to hoist but should not:
901 // %1 is defined and used in the first block of an IfSelection.
902 // Do not hoist it.
903 auto assembly = Preamble() + R"(
904 %cond = OpConstantTrue %bool
905
906 %100 = OpFunction %void None %voidfn
907
908 ; in IfSelection construct, nested in Function construct
909 %10 = OpLabel
910 %1 = OpCopyObject %uint %uint_1
911 %2 = OpCopyObject %uint %1
912 OpSelectionMerge %99 None
913 OpBranchConditional %cond %20 %99
914
915 %20 = OpLabel ; in IfSelection construct
916 OpBranch %99
917
918 %99 = OpLabel
919 OpReturn
920
921 OpFunctionEnd
922 )";
923 auto p = parser(test::Assemble(assembly));
924 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
925 auto fe = p->function_emitter(100);
926 EXPECT_TRUE(fe.EmitBody()) << p->error();
927
928 // We don't hoist x_1 into its own mutable variable. It is emitted as
929 // a const definition.
930 auto ast_body = fe.ast_body();
931 auto got = test::ToString(p->program(), ast_body);
932 auto* expect = R"(let x_1 : u32 = 1u;
933 let x_2 : u32 = x_1;
934 if (true) {
935 }
936 return;
937 )";
938 EXPECT_EQ(expect, got);
939 }
940
TEST_F(SpvParserFunctionVarTest,EmitStatement_Phi_SingleBlockLoopIndex)941 TEST_F(SpvParserFunctionVarTest, EmitStatement_Phi_SingleBlockLoopIndex) {
942 auto assembly = Preamble() + R"(
943 %pty = OpTypePointer Private %uint
944 %1 = OpVariable %pty Private
945 %boolpty = OpTypePointer Private %bool
946 %7 = OpVariable %boolpty Private
947 %8 = OpVariable %boolpty Private
948
949 %100 = OpFunction %void None %voidfn
950
951 %5 = OpLabel
952 OpBranch %10
953
954 ; Use an outer loop to show we put the new variable in the
955 ; smallest enclosing scope.
956 %10 = OpLabel
957 %101 = OpLoad %bool %7
958 %102 = OpLoad %bool %8
959 OpLoopMerge %99 %89 None
960 OpBranchConditional %101 %99 %20
961
962 %20 = OpLabel
963 %2 = OpPhi %uint %uint_0 %10 %4 %20 ; gets computed value
964 %3 = OpPhi %uint %uint_1 %10 %3 %20 ; gets itself
965 %4 = OpIAdd %uint %2 %uint_1
966 OpLoopMerge %79 %20 None
967 OpBranchConditional %102 %79 %20
968
969 %79 = OpLabel
970 OpBranch %89
971
972 %89 = OpLabel
973 OpBranch %10
974
975 %99 = OpLabel
976 OpReturn
977
978 OpFunctionEnd
979 )";
980 auto p = parser(test::Assemble(assembly));
981 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
982 auto fe = p->function_emitter(100);
983 EXPECT_TRUE(fe.EmitBody()) << p->error();
984
985 auto ast_body = fe.ast_body();
986 auto got = test::ToString(p->program(), ast_body);
987 auto* expect = R"(loop {
988 var x_2_phi : u32;
989 var x_3_phi : u32;
990 let x_101 : bool = x_7;
991 let x_102 : bool = x_8;
992 x_2_phi = 0u;
993 x_3_phi = 1u;
994 if (x_101) {
995 break;
996 }
997 loop {
998 let x_2 : u32 = x_2_phi;
999 let x_3 : u32 = x_3_phi;
1000 x_2_phi = (x_2 + 1u);
1001 x_3_phi = x_3;
1002 if (x_102) {
1003 break;
1004 }
1005 }
1006 }
1007 return;
1008 )";
1009 EXPECT_EQ(expect, got);
1010 }
1011
TEST_F(SpvParserFunctionVarTest,EmitStatement_Phi_MultiBlockLoopIndex)1012 TEST_F(SpvParserFunctionVarTest, EmitStatement_Phi_MultiBlockLoopIndex) {
1013 auto assembly = Preamble() + R"(
1014 %pty = OpTypePointer Private %uint
1015 %1 = OpVariable %pty Private
1016 %boolpty = OpTypePointer Private %bool
1017 %7 = OpVariable %boolpty Private
1018 %8 = OpVariable %boolpty Private
1019
1020 %100 = OpFunction %void None %voidfn
1021
1022 %5 = OpLabel
1023 OpBranch %10
1024
1025 ; Use an outer loop to show we put the new variable in the
1026 ; smallest enclosing scope.
1027 %10 = OpLabel
1028 %101 = OpLoad %bool %7
1029 %102 = OpLoad %bool %8
1030 OpLoopMerge %99 %89 None
1031 OpBranchConditional %101 %99 %20
1032
1033 %20 = OpLabel
1034 %2 = OpPhi %uint %uint_0 %10 %4 %30 ; gets computed value
1035 %3 = OpPhi %uint %uint_1 %10 %3 %30 ; gets itself
1036 OpLoopMerge %79 %30 None
1037 OpBranchConditional %102 %79 %30
1038
1039 %30 = OpLabel
1040 %4 = OpIAdd %uint %2 %uint_1
1041 OpBranch %20
1042
1043 %79 = OpLabel
1044 OpBranch %89
1045
1046 %89 = OpLabel ; continue target for outer loop
1047 OpBranch %10
1048
1049 %99 = OpLabel
1050 OpReturn
1051
1052 OpFunctionEnd
1053 )";
1054 auto p = parser(test::Assemble(assembly));
1055 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
1056 auto fe = p->function_emitter(100);
1057 EXPECT_TRUE(fe.EmitBody()) << p->error();
1058
1059 auto ast_body = fe.ast_body();
1060 auto got = test::ToString(p->program(), ast_body);
1061 auto* expect = R"(loop {
1062 var x_2_phi : u32;
1063 var x_3_phi : u32;
1064 let x_101 : bool = x_7;
1065 let x_102 : bool = x_8;
1066 x_2_phi = 0u;
1067 x_3_phi = 1u;
1068 if (x_101) {
1069 break;
1070 }
1071 loop {
1072 var x_4 : u32;
1073 let x_2 : u32 = x_2_phi;
1074 let x_3 : u32 = x_3_phi;
1075 if (x_102) {
1076 break;
1077 }
1078
1079 continuing {
1080 x_4 = (x_2 + 1u);
1081 x_2_phi = x_4;
1082 x_3_phi = x_3;
1083 }
1084 }
1085 }
1086 return;
1087 )";
1088 EXPECT_EQ(expect, got);
1089 }
1090
TEST_F(SpvParserFunctionVarTest,EmitStatement_Phi_ValueFromLoopBodyAndContinuing)1091 TEST_F(SpvParserFunctionVarTest,
1092 EmitStatement_Phi_ValueFromLoopBodyAndContinuing) {
1093 auto assembly = Preamble() + R"(
1094 %pty = OpTypePointer Private %uint
1095 %1 = OpVariable %pty Private
1096 %boolpty = OpTypePointer Private %bool
1097 %17 = OpVariable %boolpty Private
1098
1099 %100 = OpFunction %void None %voidfn
1100
1101 %9 = OpLabel
1102 %101 = OpLoad %bool %17
1103 OpBranch %10
1104
1105 ; Use an outer loop to show we put the new variable in the
1106 ; smallest enclosing scope.
1107 %10 = OpLabel
1108 OpLoopMerge %99 %89 None
1109 OpBranch %20
1110
1111 %20 = OpLabel
1112 %2 = OpPhi %uint %uint_0 %10 %4 %30 ; gets computed value
1113 %5 = OpPhi %uint %uint_1 %10 %7 %30
1114 %4 = OpIAdd %uint %2 %uint_1 ; define %4
1115 %6 = OpIAdd %uint %4 %uint_1 ; use %4
1116 OpLoopMerge %79 %30 None
1117 OpBranchConditional %101 %79 %30
1118
1119 %30 = OpLabel
1120 %7 = OpIAdd %uint %4 %6 ; use %4 again
1121 OpBranch %20
1122
1123 %79 = OpLabel
1124 OpBranch %89
1125
1126 %89 = OpLabel
1127 OpBranch %10
1128
1129 %99 = OpLabel
1130 OpReturn
1131
1132 OpFunctionEnd
1133 )";
1134 auto p = parser(test::Assemble(assembly));
1135 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
1136 << assembly << p->error();
1137 auto fe = p->function_emitter(100);
1138 EXPECT_TRUE(fe.EmitBody()) << p->error();
1139
1140 auto ast_body = fe.ast_body();
1141 auto got = test::ToString(p->program(), ast_body);
1142 auto* expect = R"(let x_101 : bool = x_17;
1143 loop {
1144 var x_2_phi : u32;
1145 var x_5_phi : u32;
1146 x_2_phi = 0u;
1147 x_5_phi = 1u;
1148 loop {
1149 var x_7 : u32;
1150 let x_2 : u32 = x_2_phi;
1151 let x_5 : u32 = x_5_phi;
1152 let x_4 : u32 = (x_2 + 1u);
1153 let x_6 : u32 = (x_4 + 1u);
1154 if (x_101) {
1155 break;
1156 }
1157
1158 continuing {
1159 x_7 = (x_4 + x_6);
1160 x_2_phi = x_4;
1161 x_5_phi = x_7;
1162 }
1163 }
1164 }
1165 return;
1166 )";
1167 EXPECT_EQ(expect, got);
1168 }
1169
TEST_F(SpvParserFunctionVarTest,EmitStatement_Phi_FromElseAndThen)1170 TEST_F(SpvParserFunctionVarTest, EmitStatement_Phi_FromElseAndThen) {
1171 auto assembly = Preamble() + R"(
1172 %pty = OpTypePointer Private %uint
1173 %1 = OpVariable %pty Private
1174 %boolpty = OpTypePointer Private %bool
1175 %7 = OpVariable %boolpty Private
1176 %8 = OpVariable %boolpty Private
1177
1178 %100 = OpFunction %void None %voidfn
1179
1180 %5 = OpLabel
1181 %101 = OpLoad %bool %7
1182 %102 = OpLoad %bool %8
1183 OpBranch %10
1184
1185 ; Use an outer loop to show we put the new variable in the
1186 ; smallest enclosing scope.
1187 %10 = OpLabel
1188 OpLoopMerge %99 %89 None
1189 OpBranchConditional %101 %99 %20
1190
1191 %20 = OpLabel ; if seleciton
1192 OpSelectionMerge %79 None
1193 OpBranchConditional %102 %30 %40
1194
1195 %30 = OpLabel
1196 OpBranch %89
1197
1198 %40 = OpLabel
1199 OpBranch %89
1200
1201 %79 = OpLabel ; disconnected selection merge node
1202 OpBranch %89
1203
1204 %89 = OpLabel
1205 %2 = OpPhi %uint %uint_0 %30 %uint_1 %40 %uint_0 %79
1206 OpStore %1 %2
1207 OpBranch %10
1208
1209 %99 = OpLabel
1210 OpReturn
1211
1212 OpFunctionEnd
1213 )";
1214 auto p = parser(test::Assemble(assembly));
1215 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
1216 auto fe = p->function_emitter(100);
1217 EXPECT_TRUE(fe.EmitBody()) << p->error();
1218
1219 auto ast_body = fe.ast_body();
1220 auto got = test::ToString(p->program(), ast_body);
1221 auto* expect = R"(let x_101 : bool = x_7;
1222 let x_102 : bool = x_8;
1223 loop {
1224 var x_2_phi : u32;
1225 if (x_101) {
1226 break;
1227 }
1228 if (x_102) {
1229 x_2_phi = 0u;
1230 continue;
1231 } else {
1232 x_2_phi = 1u;
1233 continue;
1234 }
1235 x_2_phi = 0u;
1236
1237 continuing {
1238 let x_2 : u32 = x_2_phi;
1239 x_1 = x_2;
1240 }
1241 }
1242 return;
1243 )";
1244 EXPECT_EQ(expect, got) << got;
1245 }
1246
TEST_F(SpvParserFunctionVarTest,EmitStatement_Phi_FromHeaderAndThen)1247 TEST_F(SpvParserFunctionVarTest, EmitStatement_Phi_FromHeaderAndThen) {
1248 auto assembly = Preamble() + R"(
1249 %pty = OpTypePointer Private %uint
1250 %1 = OpVariable %pty Private
1251 %boolpty = OpTypePointer Private %bool
1252 %7 = OpVariable %boolpty Private
1253 %8 = OpVariable %boolpty Private
1254
1255 %100 = OpFunction %void None %voidfn
1256
1257 %5 = OpLabel
1258 %101 = OpLoad %bool %7
1259 %102 = OpLoad %bool %8
1260 OpBranch %10
1261
1262 ; Use an outer loop to show we put the new variable in the
1263 ; smallest enclosing scope.
1264 %10 = OpLabel
1265 OpLoopMerge %99 %89 None
1266 OpBranchConditional %101 %99 %20
1267
1268 %20 = OpLabel ; if seleciton
1269 OpSelectionMerge %79 None
1270 OpBranchConditional %102 %30 %89
1271
1272 %30 = OpLabel
1273 OpBranch %89
1274
1275 %79 = OpLabel ; disconnected selection merge node
1276 OpUnreachable
1277
1278 %89 = OpLabel
1279 %2 = OpPhi %uint %uint_0 %20 %uint_1 %30
1280 OpStore %1 %2
1281 OpBranch %10
1282
1283 %99 = OpLabel
1284 OpReturn
1285
1286 OpFunctionEnd
1287 )";
1288 auto p = parser(test::Assemble(assembly));
1289 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
1290 auto fe = p->function_emitter(100);
1291 EXPECT_TRUE(fe.EmitBody()) << p->error();
1292
1293 auto ast_body = fe.ast_body();
1294 auto got = test::ToString(p->program(), ast_body);
1295 auto* expect = R"(let x_101 : bool = x_7;
1296 let x_102 : bool = x_8;
1297 loop {
1298 var x_2_phi : u32;
1299 if (x_101) {
1300 break;
1301 }
1302 x_2_phi = 0u;
1303 if (x_102) {
1304 x_2_phi = 1u;
1305 continue;
1306 } else {
1307 continue;
1308 }
1309 return;
1310
1311 continuing {
1312 let x_2 : u32 = x_2_phi;
1313 x_1 = x_2;
1314 }
1315 }
1316 return;
1317 )";
1318 EXPECT_EQ(expect, got) << got;
1319 }
1320
TEST_F(SpvParserFunctionVarTest,EmitStatement_Phi_InMerge_PredecessorsDominatdByNestedSwitchCase)1321 TEST_F(SpvParserFunctionVarTest,
1322 EmitStatement_Phi_InMerge_PredecessorsDominatdByNestedSwitchCase) {
1323 // This is the essence of the bug report from crbug.com/tint/495
1324 auto assembly = Preamble() + R"(
1325 %cond = OpConstantTrue %bool
1326 %pty = OpTypePointer Private %uint
1327 %1 = OpVariable %pty Private
1328 %boolpty = OpTypePointer Private %bool
1329 %7 = OpVariable %boolpty Private
1330 %8 = OpVariable %boolpty Private
1331
1332 %100 = OpFunction %void None %voidfn
1333
1334 %10 = OpLabel
1335 OpSelectionMerge %99 None
1336 OpSwitch %uint_1 %20 0 %20 1 %30
1337
1338 %20 = OpLabel ; case 0
1339 OpBranch %30 ;; fall through
1340
1341 %30 = OpLabel ; case 1
1342 OpSelectionMerge %50 None
1343 OpBranchConditional %true %40 %45
1344
1345 %40 = OpLabel
1346 OpBranch %50
1347
1348 %45 = OpLabel
1349 OpBranch %99 ; break
1350
1351 %50 = OpLabel ; end the case
1352 OpBranch %99
1353
1354 %99 = OpLabel
1355 ; predecessors are all dominated by case construct head at %30
1356 %phi = OpPhi %uint %uint_0 %45 %uint_1 %50
1357 OpReturn
1358
1359 OpFunctionEnd
1360 )";
1361 auto p = parser(test::Assemble(assembly));
1362 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
1363 auto fe = p->function_emitter(100);
1364 EXPECT_TRUE(fe.EmitBody()) << p->error();
1365
1366 auto ast_body = fe.ast_body();
1367 auto got = test::ToString(p->program(), ast_body);
1368 auto* expect = R"(var x_41_phi : u32;
1369 switch(1u) {
1370 default: {
1371 fallthrough;
1372 }
1373 case 0u: {
1374 fallthrough;
1375 }
1376 case 1u: {
1377 if (true) {
1378 } else {
1379 x_41_phi = 0u;
1380 break;
1381 }
1382 x_41_phi = 1u;
1383 }
1384 }
1385 let x_41 : u32 = x_41_phi;
1386 return;
1387 )";
1388 EXPECT_EQ(expect, got) << got << assembly;
1389 }
1390
TEST_F(SpvParserFunctionVarTest,EmitStatement_UseInPhiCountsAsUse)1391 TEST_F(SpvParserFunctionVarTest, EmitStatement_UseInPhiCountsAsUse) {
1392 // From crbug.com/215
1393 // If the only use of a combinatorially computed ID is as the value
1394 // in an OpPhi, then we still have to emit it. The algorithm fix
1395 // is to always count uses in Phis.
1396 // This is the reduced case from the bug report.
1397 //
1398 // The only use of %12 is in the phi.
1399 // The only use of %11 is in %12.
1400 // Both definintions need to be emitted to the output.
1401 auto assembly = Preamble() + R"(
1402 %100 = OpFunction %void None %voidfn
1403
1404 %10 = OpLabel
1405 %11 = OpLogicalAnd %bool %true %true
1406 %12 = OpLogicalNot %bool %11 ;
1407 OpSelectionMerge %99 None
1408 OpBranchConditional %true %20 %99
1409
1410 %20 = OpLabel
1411 OpBranch %99
1412
1413 %99 = OpLabel
1414 %101 = OpPhi %bool %11 %10 %12 %20
1415 OpReturn
1416
1417 OpFunctionEnd
1418
1419 )";
1420 auto p = parser(test::Assemble(assembly));
1421 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
1422 auto fe = p->function_emitter(100);
1423 EXPECT_TRUE(fe.EmitBody()) << p->error();
1424
1425 auto ast_body = fe.ast_body();
1426 auto got = test::ToString(p->program(), ast_body);
1427 auto* expect = R"(var x_101_phi : bool;
1428 let x_11 : bool = (true & true);
1429 let x_12 : bool = !(x_11);
1430 x_101_phi = x_11;
1431 if (true) {
1432 x_101_phi = x_12;
1433 }
1434 let x_101 : bool = x_101_phi;
1435 return;
1436 )";
1437 EXPECT_EQ(expect, got);
1438 }
1439
TEST_F(SpvParserFunctionVarTest,EmitStatement_Phi_ValueFromBlockNotInBlockOrderIgnored)1440 TEST_F(SpvParserFunctionVarTest,
1441 EmitStatement_Phi_ValueFromBlockNotInBlockOrderIgnored) {
1442 // From crbug.com/tint/804
1443 const auto assembly = Preamble() + R"(
1444 %float_42 = OpConstant %float 42.0
1445 %cond = OpUndef %bool
1446
1447 %100 = OpFunction %void None %voidfn
1448 %10 = OpLabel
1449 OpBranch %30
1450
1451 ; unreachable
1452 %20 = OpLabel
1453 %499 = OpFAdd %float %float_42 %float_42
1454 %500 = OpFAdd %float %499 %float_42
1455 OpBranch %25
1456
1457 %25 = OpLabel
1458 OpBranch %80
1459
1460
1461 %30 = OpLabel
1462 OpLoopMerge %90 %80 None
1463 OpBranchConditional %cond %90 %40
1464
1465 %40 = OpLabel
1466 OpBranch %90
1467
1468 %80 = OpLabel ; unreachable continue target
1469 ; but "dominated" by %20 and %25
1470 %81 = OpPhi %float %500 %25
1471 OpBranch %30 ; backedge
1472
1473 %90 = OpLabel
1474 OpReturn
1475 OpFunctionEnd
1476 )";
1477 auto p = parser(test::Assemble(assembly));
1478 ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1479 auto fe = p->function_emitter(100);
1480 EXPECT_TRUE(fe.EmitBody()) << p->error();
1481
1482 const auto* expected = R"(loop {
1483 if (false) {
1484 break;
1485 }
1486 break;
1487
1488 continuing {
1489 var x_81_phi_1 : f32;
1490 let x_81 : f32 = x_81_phi_1;
1491 }
1492 }
1493 return;
1494 )";
1495 auto ast_body = fe.ast_body();
1496 const auto got = test::ToString(p->program(), ast_body);
1497 EXPECT_EQ(got, expected);
1498 }
1499
TEST_F(SpvParserFunctionVarTest,EmitStatement_Hoist_CompositeInsert)1500 TEST_F(SpvParserFunctionVarTest, EmitStatement_Hoist_CompositeInsert) {
1501 // From crbug.com/tint/804
1502 const auto assembly = Preamble() + R"(
1503 %100 = OpFunction %void None %voidfn
1504
1505 %10 = OpLabel
1506 OpSelectionMerge %50 None
1507 OpBranchConditional %true %20 %30
1508
1509 %20 = OpLabel
1510 %200 = OpCompositeInsert %v2int %int_0 %v2int_null 0
1511 OpBranch %50
1512
1513 %30 = OpLabel
1514 OpReturn
1515
1516 %50 = OpLabel ; dominated by %20, but %200 needs to be hoisted
1517 %201 = OpCopyObject %v2int %200
1518 OpReturn
1519 OpFunctionEnd
1520 )";
1521 auto p = parser(test::Assemble(assembly));
1522 ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1523 auto fe = p->function_emitter(100);
1524 EXPECT_TRUE(fe.EmitBody()) << p->error();
1525
1526 const auto* expected = R"(var x_200 : vec2<i32>;
1527 if (true) {
1528 x_200 = vec2<i32>(0, 0);
1529 x_200.x = 0;
1530 } else {
1531 return;
1532 }
1533 let x_201 : vec2<i32> = x_200;
1534 return;
1535 )";
1536 auto ast_body = fe.ast_body();
1537 const auto got = test::ToString(p->program(), ast_body);
1538 EXPECT_EQ(got, expected);
1539 }
1540
TEST_F(SpvParserFunctionVarTest,EmitStatement_Hoist_VectorInsertDynamic)1541 TEST_F(SpvParserFunctionVarTest, EmitStatement_Hoist_VectorInsertDynamic) {
1542 // Spawned from crbug.com/tint/804
1543 const auto assembly = Preamble() + R"(
1544 %100 = OpFunction %void None %voidfn
1545
1546 %10 = OpLabel
1547 OpSelectionMerge %50 None
1548 OpBranchConditional %true %20 %30
1549
1550 %20 = OpLabel
1551 %200 = OpVectorInsertDynamic %v2int %v2int_null %int_3 %int_1
1552 OpBranch %50
1553
1554 %30 = OpLabel
1555 OpReturn
1556
1557 %50 = OpLabel ; dominated by %20, but %200 needs to be hoisted
1558 %201 = OpCopyObject %v2int %200
1559 OpReturn
1560 OpFunctionEnd
1561 )";
1562 auto p = parser(test::Assemble(assembly));
1563 ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1564 auto fe = p->function_emitter(100);
1565 EXPECT_TRUE(fe.EmitBody()) << p->error();
1566
1567 auto ast_body = fe.ast_body();
1568 const auto got = test::ToString(p->program(), ast_body);
1569 const auto* expected = R"(var x_200 : vec2<i32>;
1570 if (true) {
1571 x_200 = vec2<i32>(0, 0);
1572 x_200[1] = 3;
1573 } else {
1574 return;
1575 }
1576 let x_201 : vec2<i32> = x_200;
1577 return;
1578 )";
1579 EXPECT_EQ(got, expected) << got;
1580 }
1581
TEST_F(SpvParserFunctionVarTest,EmitStatement_Hoist_UsedAsNonPtrArg)1582 TEST_F(SpvParserFunctionVarTest, EmitStatement_Hoist_UsedAsNonPtrArg) {
1583 // Spawned from crbug.com/tint/804
1584 const auto assembly = Preamble() + R"(
1585 %fn_int = OpTypeFunction %void %int
1586
1587 %500 = OpFunction %void None %fn_int
1588 %501 = OpFunctionParameter %int
1589 %502 = OpLabel
1590 OpReturn
1591 OpFunctionEnd
1592
1593 %100 = OpFunction %void None %voidfn
1594
1595 %10 = OpLabel
1596 OpSelectionMerge %50 None
1597 OpBranchConditional %true %20 %30
1598
1599 %20 = OpLabel
1600 %200 = OpCopyObject %int %int_1
1601 OpBranch %50
1602
1603 %30 = OpLabel
1604 OpReturn
1605
1606 %50 = OpLabel ; dominated by %20, but %200 needs to be hoisted
1607 %201 = OpFunctionCall %void %500 %200
1608 OpReturn
1609 OpFunctionEnd
1610 )";
1611 auto p = parser(test::Assemble(assembly));
1612 ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1613 auto fe = p->function_emitter(100);
1614 EXPECT_TRUE(fe.EmitBody()) << p->error();
1615
1616 auto ast_body = fe.ast_body();
1617 const auto got = test::ToString(p->program(), ast_body);
1618 const auto* expected = R"(var x_200 : i32;
1619 if (true) {
1620 x_200 = 1;
1621 } else {
1622 return;
1623 }
1624 x_500(x_200);
1625 return;
1626 )";
1627 EXPECT_EQ(got, expected) << got;
1628 }
1629
TEST_F(SpvParserFunctionVarTest,DISABLED_EmitStatement_Hoist_UsedAsPtrArg)1630 TEST_F(SpvParserFunctionVarTest, DISABLED_EmitStatement_Hoist_UsedAsPtrArg) {
1631 // Spawned from crbug.com/tint/804
1632 // Blocked by crbug.com/tint/98: hoisting pointer types
1633 const auto assembly = Preamble() + R"(
1634
1635 %fn_int = OpTypeFunction %void %ptr_int
1636
1637 %500 = OpFunction %void None %fn_int
1638 %501 = OpFunctionParameter %ptr_int
1639 %502 = OpLabel
1640 OpReturn
1641 OpFunctionEnd
1642
1643 %100 = OpFunction %void None %voidfn
1644
1645 %10 = OpLabel
1646 %199 = OpVariable %ptr_int Function
1647 OpSelectionMerge %50 None
1648 OpBranchConditional %true %20 %30
1649
1650 %20 = OpLabel
1651 %200 = OpCopyObject %ptr_int %199
1652 OpBranch %50
1653
1654 %30 = OpLabel
1655 OpReturn
1656
1657 %50 = OpLabel ; dominated by %20, but %200 needs to be hoisted
1658 %201 = OpFunctionCall %void %500 %200
1659 OpReturn
1660 OpFunctionEnd
1661 )";
1662 auto p = parser(test::Assemble(assembly));
1663 ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1664 auto fe = p->function_emitter(100);
1665 EXPECT_TRUE(fe.EmitBody()) << p->error();
1666
1667 auto ast_body = fe.ast_body();
1668 const auto got = test::ToString(p->program(), ast_body);
1669 const auto* expected = R"(xxxxxxxxxxxxxxxxxxxxx)";
1670 EXPECT_EQ(got, expected) << got;
1671 }
1672
1673 } // namespace
1674 } // namespace spirv
1675 } // namespace reader
1676 } // namespace tint
1677