1 // Copyright (c) 2019 Google LLC
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 <string>
16
17 #include "gmock/gmock.h"
18 #include "test/opt/assembly_builder.h"
19 #include "test/opt/pass_fixture.h"
20 #include "test/opt/pass_utils.h"
21
22 namespace spvtools {
23 namespace opt {
24 namespace {
25
26 using CodeSinkTest = PassTest<::testing::Test>;
27
TEST_F(CodeSinkTest,MoveToNextBlock)28 TEST_F(CodeSinkTest, MoveToNextBlock) {
29 const std::string text = R"(
30 ;CHECK: OpFunction
31 ;CHECK: OpLabel
32 ;CHECK: OpLabel
33 ;CHECK: [[ac:%\w+]] = OpAccessChain
34 ;CHECK: [[ld:%\w+]] = OpLoad %uint [[ac]]
35 ;CHECK: OpCopyObject %uint [[ld]]
36 OpCapability Shader
37 OpMemoryModel Logical GLSL450
38 OpEntryPoint GLCompute %1 "main"
39 %void = OpTypeVoid
40 %uint = OpTypeInt 32 0
41 %uint_0 = OpConstant %uint 0
42 %uint_4 = OpConstant %uint 4
43 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
44 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
45 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
46 %9 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
47 %10 = OpTypeFunction %void
48 %1 = OpFunction %void None %10
49 %11 = OpLabel
50 %12 = OpAccessChain %_ptr_Uniform_uint %9 %uint_0
51 %13 = OpLoad %uint %12
52 OpBranch %14
53 %14 = OpLabel
54 %15 = OpCopyObject %uint %13
55 OpReturn
56 OpFunctionEnd
57 )";
58
59 SinglePassRunAndMatch<CodeSinkingPass>(text, true);
60 }
61
TEST_F(CodeSinkTest,MovePastSelection)62 TEST_F(CodeSinkTest, MovePastSelection) {
63 const std::string text = R"(
64 ;CHECK: OpFunction
65 ;CHECK: OpLabel
66 ;CHECK: OpSelectionMerge [[merge_bb:%\w+]]
67 ;CHECK: [[merge_bb]] = OpLabel
68 ;CHECK: [[ac:%\w+]] = OpAccessChain
69 ;CHECK: [[ld:%\w+]] = OpLoad %uint [[ac]]
70 ;CHECK: OpCopyObject %uint [[ld]]
71 OpCapability Shader
72 OpMemoryModel Logical GLSL450
73 OpEntryPoint GLCompute %1 "main"
74 %void = OpTypeVoid
75 %bool = OpTypeBool
76 %true = OpConstantTrue %bool
77 %uint = OpTypeInt 32 0
78 %uint_0 = OpConstant %uint 0
79 %uint_4 = OpConstant %uint 4
80 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
81 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
82 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
83 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
84 %12 = OpTypeFunction %void
85 %1 = OpFunction %void None %12
86 %13 = OpLabel
87 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
88 %15 = OpLoad %uint %14
89 OpSelectionMerge %16 None
90 OpBranchConditional %true %17 %16
91 %17 = OpLabel
92 OpBranch %16
93 %16 = OpLabel
94 %18 = OpCopyObject %uint %15
95 OpReturn
96 OpFunctionEnd
97 )";
98
99 SinglePassRunAndMatch<CodeSinkingPass>(text, true);
100 }
101
TEST_F(CodeSinkTest,MoveIntoSelection)102 TEST_F(CodeSinkTest, MoveIntoSelection) {
103 const std::string text = R"(
104 ;CHECK: OpFunction
105 ;CHECK: OpLabel
106 ;CHECK: OpSelectionMerge [[merge_bb:%\w+]]
107 ;CHECK-NEXT: OpBranchConditional %true [[bb:%\w+]] [[merge_bb]]
108 ;CHECK: [[bb]] = OpLabel
109 ;CHECK-NEXT: [[ac:%\w+]] = OpAccessChain
110 ;CHECK-NEXT: [[ld:%\w+]] = OpLoad %uint [[ac]]
111 ;CHECK-NEXT: OpCopyObject %uint [[ld]]
112 OpCapability Shader
113 OpMemoryModel Logical GLSL450
114 OpEntryPoint GLCompute %1 "main"
115 %void = OpTypeVoid
116 %bool = OpTypeBool
117 %true = OpConstantTrue %bool
118 %uint = OpTypeInt 32 0
119 %uint_0 = OpConstant %uint 0
120 %uint_4 = OpConstant %uint 4
121 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
122 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
123 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
124 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
125 %12 = OpTypeFunction %void
126 %1 = OpFunction %void None %12
127 %13 = OpLabel
128 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
129 %15 = OpLoad %uint %14
130 OpSelectionMerge %16 None
131 OpBranchConditional %true %17 %16
132 %17 = OpLabel
133 %18 = OpCopyObject %uint %15
134 OpBranch %16
135 %16 = OpLabel
136 OpReturn
137 OpFunctionEnd
138 )";
139
140 SinglePassRunAndMatch<CodeSinkingPass>(text, true);
141 }
142
TEST_F(CodeSinkTest,LeaveBeforeSelection)143 TEST_F(CodeSinkTest, LeaveBeforeSelection) {
144 const std::string text = R"(
145 OpCapability Shader
146 OpMemoryModel Logical GLSL450
147 OpEntryPoint GLCompute %1 "main"
148 %void = OpTypeVoid
149 %bool = OpTypeBool
150 %true = OpConstantTrue %bool
151 %uint = OpTypeInt 32 0
152 %uint_0 = OpConstant %uint 0
153 %uint_4 = OpConstant %uint 4
154 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
155 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
156 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
157 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
158 %12 = OpTypeFunction %void
159 %1 = OpFunction %void None %12
160 %13 = OpLabel
161 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
162 %15 = OpLoad %uint %14
163 OpSelectionMerge %16 None
164 OpBranchConditional %true %17 %20
165 %20 = OpLabel
166 OpBranch %16
167 %17 = OpLabel
168 %18 = OpCopyObject %uint %15
169 OpBranch %16
170 %16 = OpLabel
171 %19 = OpCopyObject %uint %15
172 OpReturn
173 OpFunctionEnd
174 )";
175
176 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
177 text, /* skip_nop = */ true, /* do_validation = */ true);
178 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
179 }
180
TEST_F(CodeSinkTest,LeaveAloneUseInSameBlock)181 TEST_F(CodeSinkTest, LeaveAloneUseInSameBlock) {
182 const std::string text = R"(
183 OpCapability Shader
184 OpMemoryModel Logical GLSL450
185 OpEntryPoint GLCompute %1 "main"
186 %void = OpTypeVoid
187 %bool = OpTypeBool
188 %true = OpConstantTrue %bool
189 %uint = OpTypeInt 32 0
190 %uint_0 = OpConstant %uint 0
191 %uint_4 = OpConstant %uint 4
192 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
193 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
194 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
195 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
196 %12 = OpTypeFunction %void
197 %1 = OpFunction %void None %12
198 %13 = OpLabel
199 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
200 %15 = OpLoad %uint %14
201 %cond = OpIEqual %bool %15 %uint_0
202 OpSelectionMerge %16 None
203 OpBranchConditional %cond %17 %16
204 %17 = OpLabel
205 OpBranch %16
206 %16 = OpLabel
207 %19 = OpCopyObject %uint %15
208 OpReturn
209 OpFunctionEnd
210 )";
211
212 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
213 text, /* skip_nop = */ true, /* do_validation = */ true);
214 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
215 }
216
TEST_F(CodeSinkTest,DontMoveIntoLoop)217 TEST_F(CodeSinkTest, DontMoveIntoLoop) {
218 const std::string text = R"(
219 OpCapability Shader
220 OpMemoryModel Logical GLSL450
221 OpEntryPoint GLCompute %1 "main"
222 %void = OpTypeVoid
223 %bool = OpTypeBool
224 %true = OpConstantTrue %bool
225 %uint = OpTypeInt 32 0
226 %uint_0 = OpConstant %uint 0
227 %uint_4 = OpConstant %uint 4
228 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
229 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
230 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
231 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
232 %12 = OpTypeFunction %void
233 %1 = OpFunction %void None %12
234 %13 = OpLabel
235 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
236 %15 = OpLoad %uint %14
237 OpBranch %17
238 %17 = OpLabel
239 OpLoopMerge %merge %cont None
240 OpBranch %cont
241 %cont = OpLabel
242 %cond = OpIEqual %bool %15 %uint_0
243 OpBranchConditional %cond %merge %17
244 %merge = OpLabel
245 OpReturn
246 OpFunctionEnd
247 )";
248
249 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
250 text, /* skip_nop = */ true, /* do_validation = */ true);
251 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
252 }
253
TEST_F(CodeSinkTest,DontMoveIntoLoop2)254 TEST_F(CodeSinkTest, DontMoveIntoLoop2) {
255 const std::string text = R"(
256 OpCapability Shader
257 OpMemoryModel Logical GLSL450
258 OpEntryPoint GLCompute %1 "main"
259 %void = OpTypeVoid
260 %bool = OpTypeBool
261 %true = OpConstantTrue %bool
262 %uint = OpTypeInt 32 0
263 %uint_0 = OpConstant %uint 0
264 %uint_4 = OpConstant %uint 4
265 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
266 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
267 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
268 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
269 %12 = OpTypeFunction %void
270 %1 = OpFunction %void None %12
271 %13 = OpLabel
272 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
273 %15 = OpLoad %uint %14
274 OpSelectionMerge %16 None
275 OpBranchConditional %true %17 %16
276 %17 = OpLabel
277 OpLoopMerge %merge %cont None
278 OpBranch %cont
279 %cont = OpLabel
280 %cond = OpIEqual %bool %15 %uint_0
281 OpBranchConditional %cond %merge %17
282 %merge = OpLabel
283 OpBranch %16
284 %16 = OpLabel
285 OpReturn
286 OpFunctionEnd
287 )";
288
289 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
290 text, /* skip_nop = */ true, /* do_validation = */ true);
291 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
292 }
293
TEST_F(CodeSinkTest,DontMoveSelectionUsedInBothSides)294 TEST_F(CodeSinkTest, DontMoveSelectionUsedInBothSides) {
295 const std::string text = R"(
296 OpCapability Shader
297 OpMemoryModel Logical GLSL450
298 OpEntryPoint GLCompute %1 "main"
299 %void = OpTypeVoid
300 %bool = OpTypeBool
301 %true = OpConstantTrue %bool
302 %uint = OpTypeInt 32 0
303 %uint_0 = OpConstant %uint 0
304 %uint_4 = OpConstant %uint 4
305 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
306 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
307 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
308 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
309 %12 = OpTypeFunction %void
310 %1 = OpFunction %void None %12
311 %13 = OpLabel
312 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
313 %15 = OpLoad %uint %14
314 OpSelectionMerge %16 None
315 OpBranchConditional %true %17 %20
316 %20 = OpLabel
317 %19 = OpCopyObject %uint %15
318 OpBranch %16
319 %17 = OpLabel
320 %18 = OpCopyObject %uint %15
321 OpBranch %16
322 %16 = OpLabel
323 OpReturn
324 OpFunctionEnd
325 )";
326
327 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
328 text, /* skip_nop = */ true, /* do_validation = */ true);
329 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
330 }
331
TEST_F(CodeSinkTest,DontMoveBecauseOfStore)332 TEST_F(CodeSinkTest, DontMoveBecauseOfStore) {
333 const std::string text = R"(
334 OpCapability Shader
335 OpMemoryModel Logical GLSL450
336 OpEntryPoint GLCompute %1 "main"
337 %void = OpTypeVoid
338 %bool = OpTypeBool
339 %true = OpConstantTrue %bool
340 %uint = OpTypeInt 32 0
341 %uint_0 = OpConstant %uint 0
342 %uint_4 = OpConstant %uint 4
343 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
344 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
345 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
346 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
347 %12 = OpTypeFunction %void
348 %1 = OpFunction %void None %12
349 %13 = OpLabel
350 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
351 %15 = OpLoad %uint %14
352 OpStore %14 %15
353 OpSelectionMerge %16 None
354 OpBranchConditional %true %17 %20
355 %20 = OpLabel
356 OpBranch %16
357 %17 = OpLabel
358 %18 = OpCopyObject %uint %15
359 OpBranch %16
360 %16 = OpLabel
361 OpReturn
362 OpFunctionEnd
363 )";
364
365 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
366 text, /* skip_nop = */ true, /* do_validation = */ true);
367 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
368 }
369
TEST_F(CodeSinkTest,MoveReadOnlyLoadWithSync)370 TEST_F(CodeSinkTest, MoveReadOnlyLoadWithSync) {
371 const std::string text = R"(
372 OpCapability Shader
373 OpMemoryModel Logical GLSL450
374 OpEntryPoint GLCompute %1 "main"
375 %void = OpTypeVoid
376 %bool = OpTypeBool
377 %true = OpConstantTrue %bool
378 %uint = OpTypeInt 32 0
379 %uint_0 = OpConstant %uint 0
380 %uint_4 = OpConstant %uint 4
381 %mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire
382 %_arr_uint_uint_4 = OpTypeArray %uint %uint_4
383 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
384 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
385 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
386 %12 = OpTypeFunction %void
387 %1 = OpFunction %void None %12
388 %13 = OpLabel
389 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
390 %15 = OpLoad %uint %14
391 OpMemoryBarrier %uint_4 %mem_semantics
392 OpSelectionMerge %16 None
393 OpBranchConditional %true %17 %20
394 %20 = OpLabel
395 OpBranch %16
396 %17 = OpLabel
397 %18 = OpCopyObject %uint %15
398 OpBranch %16
399 %16 = OpLabel
400 OpReturn
401 OpFunctionEnd
402 )";
403
404 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
405 text, /* skip_nop = */ true, /* do_validation = */ true);
406 EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
407 }
408
TEST_F(CodeSinkTest,DontMoveBecauseOfSync)409 TEST_F(CodeSinkTest, DontMoveBecauseOfSync) {
410 const std::string text = R"(
411 OpCapability Shader
412 OpMemoryModel Logical GLSL450
413 OpEntryPoint GLCompute %1 "main"
414 OpDecorate %_arr_uint_uint_4 BufferBlock
415 OpMemberDecorate %_arr_uint_uint_4 0 Offset 0
416 %void = OpTypeVoid
417 %bool = OpTypeBool
418 %true = OpConstantTrue %bool
419 %uint = OpTypeInt 32 0
420 %uint_0 = OpConstant %uint 0
421 %uint_4 = OpConstant %uint 4
422 %mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire
423 %_arr_uint_uint_4 = OpTypeStruct %uint
424 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
425 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
426 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
427 %12 = OpTypeFunction %void
428 %1 = OpFunction %void None %12
429 %13 = OpLabel
430 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
431 %15 = OpLoad %uint %14
432 OpMemoryBarrier %uint_4 %mem_semantics
433 OpSelectionMerge %16 None
434 OpBranchConditional %true %17 %20
435 %20 = OpLabel
436 OpBranch %16
437 %17 = OpLabel
438 %18 = OpCopyObject %uint %15
439 OpBranch %16
440 %16 = OpLabel
441 OpReturn
442 OpFunctionEnd
443 )";
444
445 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
446 text, /* skip_nop = */ true, /* do_validation = */ true);
447 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
448 }
449
TEST_F(CodeSinkTest,DontMoveBecauseOfAtomicWithSync)450 TEST_F(CodeSinkTest, DontMoveBecauseOfAtomicWithSync) {
451 const std::string text = R"(
452 OpCapability Shader
453 OpMemoryModel Logical GLSL450
454 OpEntryPoint GLCompute %1 "main"
455 OpDecorate %_arr_uint_uint_4 BufferBlock
456 OpMemberDecorate %_arr_uint_uint_4 0 Offset 0
457 %void = OpTypeVoid
458 %bool = OpTypeBool
459 %true = OpConstantTrue %bool
460 %uint = OpTypeInt 32 0
461 %uint_0 = OpConstant %uint 0
462 %uint_4 = OpConstant %uint 4
463 %mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire
464 %_arr_uint_uint_4 = OpTypeStruct %uint
465 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
466 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
467 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
468 %12 = OpTypeFunction %void
469 %1 = OpFunction %void None %12
470 %13 = OpLabel
471 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
472 %15 = OpLoad %uint %14
473 %al = OpAtomicLoad %uint %14 %uint_4 %mem_semantics
474 OpSelectionMerge %16 None
475 OpBranchConditional %true %17 %20
476 %20 = OpLabel
477 OpBranch %16
478 %17 = OpLabel
479 %18 = OpCopyObject %uint %15
480 OpBranch %16
481 %16 = OpLabel
482 OpReturn
483 OpFunctionEnd
484 )";
485
486 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
487 text, /* skip_nop = */ true, /* do_validation = */ true);
488 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
489 }
490
TEST_F(CodeSinkTest,MoveWithAtomicWithoutSync)491 TEST_F(CodeSinkTest, MoveWithAtomicWithoutSync) {
492 const std::string text = R"(
493 OpCapability Shader
494 OpMemoryModel Logical GLSL450
495 OpEntryPoint GLCompute %1 "main"
496 OpDecorate %_arr_uint_uint_4 BufferBlock
497 OpMemberDecorate %_arr_uint_uint_4 0 Offset 0
498 %void = OpTypeVoid
499 %bool = OpTypeBool
500 %true = OpConstantTrue %bool
501 %uint = OpTypeInt 32 0
502 %uint_0 = OpConstant %uint 0
503 %uint_4 = OpConstant %uint 4
504 %_arr_uint_uint_4 = OpTypeStruct %uint
505 %_ptr_Uniform_uint = OpTypePointer Uniform %uint
506 %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4
507 %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform
508 %12 = OpTypeFunction %void
509 %1 = OpFunction %void None %12
510 %13 = OpLabel
511 %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0
512 %15 = OpLoad %uint %14
513 %al = OpAtomicLoad %uint %14 %uint_4 %uint_0
514 OpSelectionMerge %16 None
515 OpBranchConditional %true %17 %20
516 %20 = OpLabel
517 OpBranch %16
518 %17 = OpLabel
519 %18 = OpCopyObject %uint %15
520 OpBranch %16
521 %16 = OpLabel
522 OpReturn
523 OpFunctionEnd
524 )";
525
526 auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
527 text, /* skip_nop = */ true, /* do_validation = */ true);
528 EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
529 }
530
531 } // namespace
532 } // namespace opt
533 } // namespace spvtools
534