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
19namespace spvtools {
20namespace opt {
21namespace {
22
23using InterlockInvocationPlacementTest = PassTest<::testing::Test>;
24
25TEST_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
53TEST_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
81TEST_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
121TEST_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
158TEST_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
195TEST_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
237TEST_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
286TEST_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
336TEST_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
387TEST_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
437TEST_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
488TEST_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
539TEST_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