1 // Copyright (c) 2023 Google Inc.
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 "spirv-tools/optimizer.hpp"
16 #include "test/opt/pass_fixture.h"
17 #include "test/opt/pass_utils.h"
18 
19 namespace spvtools {
20 namespace opt {
21 namespace {
22 
23 using InterlockInvocationPlacementTest = PassTest<::testing::Test>;
24 
TEST_F(InterlockInvocationPlacementTest,CheckUnchangedIfNotFragment)25 TEST_F(InterlockInvocationPlacementTest, CheckUnchangedIfNotFragment) {
26   const std::string kTest = R"(
27                OpCapability Shader
28                OpCapability FragmentShaderSampleInterlockEXT
29                OpExtension "SPV_EXT_fragment_shader_interlock"
30                OpMemoryModel Logical GLSL450
31                OpEntryPoint Vertex %main "main"
32                OpExecutionMode %main SampleInterlockOrderedEXT
33                OpName %main "main"
34        %void = OpTypeVoid
35           %1 = OpTypeFunction %void
36        %main = OpFunction %void None %1
37           %2 = OpLabel
38                OpBeginInvocationInterlockEXT
39                OpBeginInvocationInterlockEXT
40 	             OpEndInvocationInterlockEXT
41 	             OpBeginInvocationInterlockEXT
42                OpEndInvocationInterlockEXT
43                OpReturn
44                OpFunctionEnd
45   )";
46   SetTargetEnv(SPV_ENV_VULKAN_1_3);
47   EXPECT_EQ(
48       Pass::Status::SuccessWithoutChange,
49       std::get<1>(SinglePassRunAndDisassemble<InvocationInterlockPlacementPass>(
50           kTest, /* skip_nop= */ false, /* do_validation= */ false)));
51 }
52 
TEST_F(InterlockInvocationPlacementTest,CheckUnchangedWithoutCapability)53 TEST_F(InterlockInvocationPlacementTest, CheckUnchangedWithoutCapability) {
54   const std::string kTest = R"(
55                OpCapability Shader
56                OpExtension "SPV_EXT_fragment_shader_interlock"
57                OpMemoryModel Logical GLSL450
58                OpEntryPoint Fragment %main "main"
59                OpExecutionMode %main OriginUpperLeft
60                OpExecutionMode %main SampleInterlockOrderedEXT
61                OpName %main "main"
62        %void = OpTypeVoid
63           %1 = OpTypeFunction %void
64        %main = OpFunction %void None %1
65           %2 = OpLabel
66                OpBeginInvocationInterlockEXT
67                OpBeginInvocationInterlockEXT
68 	             OpEndInvocationInterlockEXT
69 	             OpBeginInvocationInterlockEXT
70                OpEndInvocationInterlockEXT
71                OpReturn
72                OpFunctionEnd
73   )";
74   SetTargetEnv(SPV_ENV_VULKAN_1_3);
75   EXPECT_EQ(
76       Pass::Status::SuccessWithoutChange,
77       std::get<1>(SinglePassRunAndDisassemble<InvocationInterlockPlacementPass>(
78           kTest, /* skip_nop= */ false, /* do_validation= */ false)));
79 }
80 
TEST_F(InterlockInvocationPlacementTest,CheckSingleBasicBlock)81 TEST_F(InterlockInvocationPlacementTest, CheckSingleBasicBlock) {
82   // We're using OpNoLine as a generic standin for any other instruction, to
83   // test that begin and end aren't moved.
84   const std::string kTest = R"(
85                OpCapability Shader
86                OpCapability FragmentShaderSampleInterlockEXT
87                OpExtension "SPV_EXT_fragment_shader_interlock"
88                OpMemoryModel Logical GLSL450
89                OpEntryPoint Fragment %main "main"
90                OpExecutionMode %main OriginUpperLeft
91                OpExecutionMode %main SampleInterlockOrderedEXT
92                OpName %main "main"
93        %void = OpTypeVoid
94           %1 = OpTypeFunction %void
95        %main = OpFunction %void None %1
96 ; CHECK: OpLabel
97           %2 = OpLabel
98 ; CHECK-NEXT: OpNoLine
99                OpNoLine
100 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
101                OpBeginInvocationInterlockEXT
102                OpBeginInvocationInterlockEXT
103 	             OpEndInvocationInterlockEXT
104 	             OpBeginInvocationInterlockEXT
105 ; CHECK-NEXT: OpNoLine
106                OpNoLine
107 ; CHECK-NEXT: OpEndInvocationInterlockEXT
108                OpEndInvocationInterlockEXT
109 ; CHECK-NEXT: OpNoLine
110                OpNoLine
111 ; CHECK-NEXT: OpReturn
112                OpReturn
113                OpFunctionEnd
114   )";
115   SetTargetEnv(SPV_ENV_VULKAN_1_3);
116   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
117       kTest, /* skip_nop= */ false);
118   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
119 }
120 
TEST_F(InterlockInvocationPlacementTest,CheckFunctionCallExtractionBegin)121 TEST_F(InterlockInvocationPlacementTest, CheckFunctionCallExtractionBegin) {
122   const std::string kTest = R"(
123                OpCapability Shader
124                OpCapability FragmentShaderSampleInterlockEXT
125                OpExtension "SPV_EXT_fragment_shader_interlock"
126                OpMemoryModel Logical GLSL450
127                OpEntryPoint Fragment %main "main"
128                OpExecutionMode %main OriginUpperLeft
129                OpExecutionMode %main SampleInterlockOrderedEXT
130                OpName %main "main"
131        %void = OpTypeVoid
132           %1 = OpTypeFunction %void
133         %foo = OpFunction %void None %1
134 ; CHECK: OpLabel
135 ; CHECK-NOT: OpBeginInvocationInterlockEXT
136           %2 = OpLabel
137                OpBeginInvocationInterlockEXT
138                OpBeginInvocationInterlockEXT
139                OpReturn
140 ; CHECK: OpFunctionEnd
141                OpFunctionEnd
142        %main = OpFunction %void None %1
143 ; CHECK: OpLabel
144           %3 = OpLabel
145 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
146 ; CHECK-NEXT: OpFunctionCall
147           %4 = OpFunctionCall %void %foo
148 ; CHECK-NEXT: OpReturn
149                OpReturn
150                OpFunctionEnd
151   )";
152   SetTargetEnv(SPV_ENV_VULKAN_1_3);
153   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
154       kTest, /* skip_nop= */ false);
155   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
156 }
157 
TEST_F(InterlockInvocationPlacementTest,CheckFunctionCallExtractionEnd)158 TEST_F(InterlockInvocationPlacementTest, CheckFunctionCallExtractionEnd) {
159   const std::string kTest = R"(
160                OpCapability Shader
161                OpCapability FragmentShaderSampleInterlockEXT
162                OpExtension "SPV_EXT_fragment_shader_interlock"
163                OpMemoryModel Logical GLSL450
164                OpEntryPoint Fragment %main "main"
165                OpExecutionMode %main OriginUpperLeft
166                OpExecutionMode %main SampleInterlockOrderedEXT
167                OpName %main "main"
168        %void = OpTypeVoid
169           %1 = OpTypeFunction %void
170         %foo = OpFunction %void None %1
171 ; CHECK: OpLabel
172 ; CHECK-NOT: OpEndInvocationInterlockEXT
173           %2 = OpLabel
174                OpEndInvocationInterlockEXT
175                OpEndInvocationInterlockEXT
176                OpReturn
177 ; CHECK: OpFunctionEnd
178                OpFunctionEnd
179        %main = OpFunction %void None %1
180 ; CHECK: OpLabel
181           %3 = OpLabel
182 ; CHECK-NEXT: OpFunctionCall
183           %4 = OpFunctionCall %void %foo
184 ; CHECK-NEXT: OpEndInvocationInterlockEXT
185 ; CHECK-NEXT: OpReturn
186                OpReturn
187                OpFunctionEnd
188   )";
189   SetTargetEnv(SPV_ENV_VULKAN_1_3);
190   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
191       kTest, /* skip_nop= */ false);
192   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
193 }
194 
TEST_F(InterlockInvocationPlacementTest,CheckFunctionCallExtractionRepeatedCall)195 TEST_F(InterlockInvocationPlacementTest,
196        CheckFunctionCallExtractionRepeatedCall) {
197   const std::string kTest = R"(
198                OpCapability Shader
199                OpCapability FragmentShaderSampleInterlockEXT
200                OpExtension "SPV_EXT_fragment_shader_interlock"
201                OpMemoryModel Logical GLSL450
202                OpEntryPoint Fragment %main "main"
203                OpExecutionMode %main OriginUpperLeft
204                OpExecutionMode %main SampleInterlockOrderedEXT
205                OpName %main "main"
206        %void = OpTypeVoid
207           %1 = OpTypeFunction %void
208         %foo = OpFunction %void None %1
209 ; CHECK: OpLabel
210 ; CHECK-NOT: OpBeginInvocationInterlockEXT
211 ; CHECK-NOT: OpEndInvocationInterlockEXT
212           %2 = OpLabel
213                OpBeginInvocationInterlockEXT
214                OpEndInvocationInterlockEXT
215                OpReturn
216 ; CHECK: OpFunctionEnd
217                OpFunctionEnd
218        %main = OpFunction %void None %1
219 ; CHECK: OpLabel
220           %3 = OpLabel
221 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
222 ; CHECK-NEXT: OpFunctionCall
223           %4 = OpFunctionCall %void %foo
224 ; CHECK-NEXT: OpFunctionCall
225           %5 = OpFunctionCall %void %foo
226 ; CHECK-NEXT: OpEndInvocationInterlockEXT
227 ; CHECK-NEXT: OpReturn
228                OpReturn
229                OpFunctionEnd
230   )";
231   SetTargetEnv(SPV_ENV_VULKAN_1_3);
232   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
233       kTest, /* skip_nop= */ false);
234   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
235 }
236 
TEST_F(InterlockInvocationPlacementTest,CheckFunctionCallExtractionNestedCall)237 TEST_F(InterlockInvocationPlacementTest,
238        CheckFunctionCallExtractionNestedCall) {
239   const std::string kTest = R"(
240                OpCapability Shader
241                OpCapability FragmentShaderSampleInterlockEXT
242                OpExtension "SPV_EXT_fragment_shader_interlock"
243                OpMemoryModel Logical GLSL450
244                OpEntryPoint Fragment %main "main"
245                OpExecutionMode %main OriginUpperLeft
246                OpExecutionMode %main SampleInterlockOrderedEXT
247                OpName %main "main"
248        %void = OpTypeVoid
249           %1 = OpTypeFunction %void
250         %foo = OpFunction %void None %1
251 ; CHECK: OpLabel
252 ; CHECK-NOT: OpBeginInvocationInterlockEXT
253 ; CHECK-NOT: OpEndInvocationInterlockEXT
254           %2 = OpLabel
255                OpBeginInvocationInterlockEXT
256                OpEndInvocationInterlockEXT
257                OpReturn
258 ; CHECK: OpFunctionEnd
259                OpFunctionEnd
260         %bar = OpFunction %void None %1
261 ; CHECK: OpLabel
262 ; CHECK-NOT: OpBeginInvocationInterlockEXT
263 ; CHECK-NOT: OpEndInvocationInterlockEXT
264           %3 = OpLabel
265           %4 = OpFunctionCall %void %foo
266                OpReturn
267 ; CHECK: OpFunctionEnd
268                OpFunctionEnd
269        %main = OpFunction %void None %1
270 ; CHECK: OpLabel
271           %5 = OpLabel
272 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
273 ; CHECK-NEXT: OpFunctionCall
274           %6 = OpFunctionCall %void %bar
275 ; CHECK-NEXT: OpEndInvocationInterlockEXT
276 ; CHECK-NEXT: OpReturn
277                OpReturn
278                OpFunctionEnd
279   )";
280   SetTargetEnv(SPV_ENV_VULKAN_1_3);
281   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
282       kTest, /* skip_nop= */ false);
283   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
284 }
285 
TEST_F(InterlockInvocationPlacementTest,CheckLoopExtraction)286 TEST_F(InterlockInvocationPlacementTest, CheckLoopExtraction) {
287   // Tests that any begin or end instructions in a loop are moved outside of the
288   // loop.
289   const std::string kTest = R"(
290                OpCapability Shader
291                OpCapability FragmentShaderSampleInterlockEXT
292                OpExtension "SPV_EXT_fragment_shader_interlock"
293                OpMemoryModel Logical GLSL450
294                OpEntryPoint Fragment %main "main"
295                OpExecutionMode %main OriginUpperLeft
296                OpExecutionMode %main SampleInterlockOrderedEXT
297        %void = OpTypeVoid
298        %bool = OpTypeBool
299        %true = OpConstantTrue %bool
300            %1 = OpTypeFunction %void
301        %main = OpFunction %void None %1
302 
303           %2 = OpLabel
304 ; CHECK: OpBeginInvocationInterlockEXT
305 ; CHECK-NOT: OpBeginInvocationInterlockEXT
306 ; CHECK-NOT: OpEndInvocationInterlockEXT
307                OpBranch %3
308 
309           %3 = OpLabel
310                OpLoopMerge %3 %4 None
311 ; CHECK: OpBranchConditional
312 ; CHECK-NOT: OpBeginInvocationInterlockEXT
313 ; CHECK-NOT: OpEndInvocationInterlockEXT
314                OpBranchConditional %true %4 %5
315 
316           %4 = OpLabel
317                OpBeginInvocationInterlockEXT
318                OpEndInvocationInterlockEXT
319 ; CHECK: OpBranch
320                OpBranch %3
321 
322 ; CHECK-NEXT: OpLabel
323           %5 = OpLabel
324 ; CHECK-NEXT: OpEndInvocationInterlockEXT
325 ; CHECK-NOT: OpEndInvocationInterlockEXT
326                OpEndInvocationInterlockEXT
327                OpReturn
328                OpFunctionEnd
329   )";
330   SetTargetEnv(SPV_ENV_VULKAN_1_3);
331   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
332       kTest, /* skip_nop= */ false);
333   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
334 }
335 
TEST_F(InterlockInvocationPlacementTest,CheckAddBeginToElse)336 TEST_F(InterlockInvocationPlacementTest, CheckAddBeginToElse) {
337   // Test that if there is a begin in a single branch of a conditional, begin
338   // will be added to the other branch.
339   const std::string kTest = R"(
340                OpCapability Shader
341                OpCapability FragmentShaderSampleInterlockEXT
342 	             OpExtension "SPV_EXT_fragment_shader_interlock"
343                OpMemoryModel Logical GLSL450
344                OpEntryPoint Fragment %main "main"
345                OpExecutionMode %main OriginUpperLeft
346                OpExecutionMode %main SampleInterlockOrderedEXT
347                OpName %main "main"
348        %void = OpTypeVoid
349        %bool = OpTypeBool
350        %true = OpConstantTrue %bool
351            %1 = OpTypeFunction %void
352        %main = OpFunction %void None %1
353 
354           %2 = OpLabel
355 ; CHECK-NOT: OpBeginInvocationInterlockEXT
356                OpSelectionMerge %5 None
357 ; CHECK: OpBranchConditional
358                OpBranchConditional %true %3 %4
359 
360 ; CHECK-NEXT: OpLabel
361           %3 = OpLabel
362 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
363                OpBeginInvocationInterlockEXT
364                OpEndInvocationInterlockEXT
365 ; CHECK-NEXT: OpBranch
366                OpBranch %5
367 
368           %4 = OpLabel
369 ; CHECK: OpBeginInvocationInterlockEXT
370 ; CHECK-NEXT: OpBranch
371                OpBranch %5
372 
373 ; CHECK-NEXT: OpLabel
374           %5 = OpLabel
375                OpBeginInvocationInterlockEXT
376 ; CHECK-NEXT: OpEndInvocationInterlockEXT
377                OpEndInvocationInterlockEXT
378                OpReturn
379                OpFunctionEnd
380   )";
381   SetTargetEnv(SPV_ENV_VULKAN_1_3);
382   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
383       kTest, /* skip_nop= */ false);
384   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
385 }
386 
TEST_F(InterlockInvocationPlacementTest,CheckAddEndToElse)387 TEST_F(InterlockInvocationPlacementTest, CheckAddEndToElse) {
388   const std::string kTest = R"(
389                OpCapability Shader
390                OpCapability FragmentShaderSampleInterlockEXT
391 	             OpExtension "SPV_EXT_fragment_shader_interlock"
392                OpMemoryModel Logical GLSL450
393                OpEntryPoint Fragment %main "main"
394                OpExecutionMode %main OriginUpperLeft
395                OpExecutionMode %main SampleInterlockOrderedEXT
396                OpName %main "main"
397        %void = OpTypeVoid
398        %bool = OpTypeBool
399        %true = OpConstantTrue %bool
400            %1 = OpTypeFunction %void
401        %main = OpFunction %void None %1
402 
403           %2 = OpLabel
404 ; CHECK: OpBeginInvocationInterlockEXT
405                OpBeginInvocationInterlockEXT
406 ; CHECK-NOT: OpEndInvocationInterlockEXT
407                OpEndInvocationInterlockEXT
408                OpSelectionMerge %5 None
409 ; CHECK: OpBranchConditional
410                OpBranchConditional %true %3 %4
411 
412 ; CHECK-NEXT: OpLabel
413           %3 = OpLabel
414                OpBeginInvocationInterlockEXT
415 ; CHECK-NEXT: OpEndInvocationInterlockEXT
416                OpEndInvocationInterlockEXT
417 ; CHECK-NEXT: OpBranch
418                OpBranch %5
419 
420           %4 = OpLabel
421 ; CHECK: OpEndInvocationInterlockEXT
422 ; CHECK-NEXT: OpBranch
423                OpBranch %5
424 
425 ; CHECK-NEXT: OpLabel
426           %5 = OpLabel
427 ; CHECK-NOT: OpEndInvocationInterlockEXT
428                OpReturn
429                OpFunctionEnd
430   )";
431   SetTargetEnv(SPV_ENV_VULKAN_1_3);
432   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
433       kTest, /* skip_nop= */ false);
434   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
435 }
436 
TEST_F(InterlockInvocationPlacementTest,CheckSplitIfWithoutElseBegin)437 TEST_F(InterlockInvocationPlacementTest, CheckSplitIfWithoutElseBegin) {
438   // Test that if there is a begin in the then branch of a conditional, and no
439   // else branch, an else branch with a begin will created.
440   const std::string kTest = R"(
441                OpCapability Shader
442                OpCapability FragmentShaderSampleInterlockEXT
443 	             OpExtension "SPV_EXT_fragment_shader_interlock"
444                OpMemoryModel Logical GLSL450
445                OpEntryPoint Fragment %main "main"
446                OpExecutionMode %main OriginUpperLeft
447                OpExecutionMode %main SampleInterlockOrderedEXT
448                OpName %main "main"
449        %void = OpTypeVoid
450        %bool = OpTypeBool
451        %true = OpConstantTrue %bool
452            %1 = OpTypeFunction %void
453        %main = OpFunction %void None %1
454 
455           %2 = OpLabel
456 ; CHECK-NOT: OpBeginInvocationInterlockEXT
457                OpSelectionMerge %5 None
458 ; CHECK: OpBranchConditional
459                OpBranchConditional %true %3 %5
460 
461 ; CHECK-NEXT: OpLabel
462 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
463 ; CHECK-NEXT: OpBranch
464 
465 ; CHECK-NEXT: OpLabel
466           %3 = OpLabel
467 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
468 ; CHECK-NOT: OpEndInvocationInterlockEXT
469                OpBeginInvocationInterlockEXT
470                OpEndInvocationInterlockEXT
471                OpBranch %5
472 
473 ; CHECK: OpLabel
474           %5 = OpLabel
475 ; CHECK-NOT: OpBeginInvocationInterlockEXT
476                OpBeginInvocationInterlockEXT
477 ; CHECK-NEXT: OpEndInvocationInterlockEXT
478                OpEndInvocationInterlockEXT
479                OpReturn
480                OpFunctionEnd
481   )";
482   SetTargetEnv(SPV_ENV_VULKAN_1_3);
483   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
484       kTest, /* skip_nop= */ false);
485   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
486 }
487 
TEST_F(InterlockInvocationPlacementTest,CheckSplitIfWithoutElseEnd)488 TEST_F(InterlockInvocationPlacementTest, CheckSplitIfWithoutElseEnd) {
489   const std::string kTest = R"(
490                OpCapability Shader
491                OpCapability FragmentShaderSampleInterlockEXT
492 	             OpExtension "SPV_EXT_fragment_shader_interlock"
493                OpMemoryModel Logical GLSL450
494                OpEntryPoint Fragment %main "main"
495                OpExecutionMode %main OriginUpperLeft
496                OpExecutionMode %main SampleInterlockOrderedEXT
497                OpName %main "main"
498        %void = OpTypeVoid
499        %bool = OpTypeBool
500        %true = OpConstantTrue %bool
501            %1 = OpTypeFunction %void
502        %main = OpFunction %void None %1
503 
504           %2 = OpLabel
505 
506 ; CHECK: OpBeginInvocationInterlockEXT
507                OpBeginInvocationInterlockEXT
508 ; CHECK-NOT: OpEndInvocationInterlockEXT
509                OpEndInvocationInterlockEXT
510 ; CHECK-NEXT: OpSelectionMerge [[merge:%\d+]]
511                OpSelectionMerge %5 None
512 ; CHECK-NEXT: OpBranchConditional %true [[then:%\d+]] [[else:%\d+]]
513                OpBranchConditional %true %3 %5
514 
515 ; CHECK-NEXT: [[else]] = OpLabel
516 ; CHECK-NEXT: OpEndInvocationInterlockEXT
517 ; CHECK-NEXT: OpBranch [[merge]]
518 
519 ; CHECK-NEXT: [[then]] = OpLabel
520           %3 = OpLabel
521 ; CHECK-NEXT: OpEndInvocationInterlockEXT
522                OpBeginInvocationInterlockEXT
523                OpEndInvocationInterlockEXT
524 ; CHECK-NEXT: OpBranch [[merge]]
525                OpBranch %5
526 
527 ; CHECK-NEXT: [[merge]] = OpLabel
528           %5 = OpLabel
529 ; CHECK-NEXT: OpReturn
530                OpReturn
531                OpFunctionEnd
532   )";
533   SetTargetEnv(SPV_ENV_VULKAN_1_3);
534   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
535       kTest, /* skip_nop= */ false);
536   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
537 }
538 
TEST_F(InterlockInvocationPlacementTest,CheckSplitSwitch)539 TEST_F(InterlockInvocationPlacementTest, CheckSplitSwitch) {
540   // Test that if there is a begin or end in a single branch of a switch, begin
541   // or end will be added to all the other branches.
542   const std::string kTest = R"(
543                OpCapability Shader
544                OpCapability FragmentShaderSampleInterlockEXT
545 	             OpExtension "SPV_EXT_fragment_shader_interlock"
546                OpMemoryModel Logical GLSL450
547                OpEntryPoint Fragment %main "main"
548                OpExecutionMode %main OriginUpperLeft
549                OpExecutionMode %main SampleInterlockOrderedEXT
550                OpName %main "main"
551        %void = OpTypeVoid
552        %uint = OpTypeInt 32 0
553      %uint_1 = OpConstant %uint 1
554            %1 = OpTypeFunction %void
555        %main = OpFunction %void None %1
556 
557 ; CHECK: OpLabel
558           %2 = OpLabel
559 ; CHECK-NEXT: OpSelectionMerge [[merge:%\d+]]
560                OpSelectionMerge %8 None
561 ; CHECK-NEXT: OpSwitch %uint_1 [[default:%\d+]] 0 [[case_0:%\d+]] 1 [[case_1:%\d+]] 2 [[case_2:%\d+]]
562                OpSwitch %uint_1 %8 0 %4 1 %5 2 %8
563 
564 ; CHECK-NEXT: [[case_2]] = OpLabel
565 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
566 ; CHECK-NEXT: OpBranch [[merge]]
567 
568 ; CHECK-NEXT: [[default]] = OpLabel
569 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
570 ; CHECK-NEXT: OpBranch [[merge]]
571 
572 ; CHECK-NEXT: [[case_0]] = OpLabel
573           %4 = OpLabel
574 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
575 ; CHECK-NOT: OpEndInvocationInterlockEXT
576                OpBeginInvocationInterlockEXT
577                OpEndInvocationInterlockEXT
578 ; CHECK-NEXT: OpNoLine
579                OpNoLine
580 ; CHECK-NEXT: OpBranch [[merge]]
581                OpBranch %8
582 
583 ; CHECK-NEXT: [[case_1]] = OpLabel
584           %5 = OpLabel
585 ; CHECK-NEXT: OpBeginInvocationInterlockEXT
586 ; CHECK-NOT: OpEndInvocationInterlockEXT
587                OpBeginInvocationInterlockEXT
588                OpEndInvocationInterlockEXT
589 ; CHECK-NEXT: OpNoLine
590                OpNoLine
591 ; CHECK-NEXT: OpNoLine
592                OpNoLine
593 ; CHECK-NEXT: OpBranch [[merge]]
594                OpBranch %8
595 
596 ; CHECK-NEXT: [[merge]] = OpLabel
597           %8 = OpLabel
598 ; CHECK-NOT: OpBeginInvocationInterlockEXT
599                OpBeginInvocationInterlockEXT
600 ; CHECK-NEXT: OpEndInvocationInterlockEXT
601                OpEndInvocationInterlockEXT
602                OpReturn
603                OpFunctionEnd
604   )";
605   SetTargetEnv(SPV_ENV_VULKAN_1_3);
606   const auto result = SinglePassRunAndMatch<InvocationInterlockPlacementPass>(
607       kTest, /* skip_nop= */ false);
608   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
609 }
610 
611 }  // namespace
612 }  // namespace opt
613 }  // namespace spvtools
614