• 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 "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