1 // Copyright (c) 2017 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 <string>
16
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
19 #include "source/opt/ccp_pass.h"
20 #include "test/opt/pass_fixture.h"
21 #include "test/opt/pass_utils.h"
22
23 namespace spvtools {
24 namespace opt {
25 namespace {
26
27 using CCPTest = PassTest<::testing::Test>;
28
TEST_F(CCPTest,PropagateThroughPhis)29 TEST_F(CCPTest, PropagateThroughPhis) {
30 const std::string spv_asm = R"(
31 OpCapability Shader
32 %1 = OpExtInstImport "GLSL.std.450"
33 OpMemoryModel Logical GLSL450
34 OpEntryPoint Fragment %main "main" %x %outparm
35 OpExecutionMode %main OriginUpperLeft
36 OpSource GLSL 450
37 OpName %main "main"
38 OpName %x "x"
39 OpName %outparm "outparm"
40 OpDecorate %x Flat
41 OpDecorate %x Location 0
42 OpDecorate %outparm Location 0
43 %void = OpTypeVoid
44 %3 = OpTypeFunction %void
45 %int = OpTypeInt 32 1
46 %bool = OpTypeBool
47 %_ptr_Function_int = OpTypePointer Function %int
48 %int_4 = OpConstant %int 4
49 %int_3 = OpConstant %int 3
50 %int_1 = OpConstant %int 1
51 %_ptr_Input_int = OpTypePointer Input %int
52 %x = OpVariable %_ptr_Input_int Input
53 %_ptr_Output_int = OpTypePointer Output %int
54 %outparm = OpVariable %_ptr_Output_int Output
55 %main = OpFunction %void None %3
56 %4 = OpLabel
57 %5 = OpLoad %int %x
58 %9 = OpIAdd %int %int_1 %int_3
59 %6 = OpSGreaterThan %bool %5 %int_3
60 OpSelectionMerge %25 None
61 OpBranchConditional %6 %22 %23
62 %22 = OpLabel
63
64 ; CHECK: OpCopyObject %int %int_4
65 %7 = OpCopyObject %int %9
66
67 OpBranch %25
68 %23 = OpLabel
69 %8 = OpCopyObject %int %int_4
70 OpBranch %25
71 %25 = OpLabel
72
73 ; %int_4 should have propagated to both OpPhi operands.
74 ; CHECK: OpPhi %int %int_4 {{%\d+}} %int_4 {{%\d+}}
75 %35 = OpPhi %int %7 %22 %8 %23
76
77 ; This function always returns 4. DCE should get rid of everything else.
78 ; CHECK OpStore %outparm %int_4
79 OpStore %outparm %35
80 OpReturn
81 OpFunctionEnd
82 )";
83
84 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
85 }
86
TEST_F(CCPTest,SimplifyConditionals)87 TEST_F(CCPTest, SimplifyConditionals) {
88 const std::string spv_asm = R"(
89 OpCapability Shader
90 %1 = OpExtInstImport "GLSL.std.450"
91 OpMemoryModel Logical GLSL450
92 OpEntryPoint Fragment %main "main" %outparm
93 OpExecutionMode %main OriginUpperLeft
94 OpSource GLSL 450
95 OpName %main "main"
96 OpName %outparm "outparm"
97 OpDecorate %outparm Location 0
98 %void = OpTypeVoid
99 %3 = OpTypeFunction %void
100 %int = OpTypeInt 32 1
101 %bool = OpTypeBool
102 %_ptr_Function_int = OpTypePointer Function %int
103 %int_4 = OpConstant %int 4
104 %int_3 = OpConstant %int 3
105 %int_1 = OpConstant %int 1
106 %_ptr_Output_int = OpTypePointer Output %int
107 %outparm = OpVariable %_ptr_Output_int Output
108 %main = OpFunction %void None %3
109 %4 = OpLabel
110 %9 = OpIAdd %int %int_4 %int_3
111 %6 = OpSGreaterThan %bool %9 %int_3
112 OpSelectionMerge %25 None
113 ; CHECK: OpBranchConditional %true [[bb_taken:%\d+]] [[bb_not_taken:%\d+]]
114 OpBranchConditional %6 %22 %23
115 ; CHECK: [[bb_taken]] = OpLabel
116 %22 = OpLabel
117 ; CHECK: OpCopyObject %int %int_7
118 %7 = OpCopyObject %int %9
119 OpBranch %25
120 ; CHECK: [[bb_not_taken]] = OpLabel
121 %23 = OpLabel
122 ; CHECK: [[id_not_evaluated:%\d+]] = OpCopyObject %int %int_4
123 %8 = OpCopyObject %int %int_4
124 OpBranch %25
125 %25 = OpLabel
126
127 ; %int_7 should have propagated to the first OpPhi operand. But the else branch
128 ; is not executable (conditional is always true), so no values should be
129 ; propagated there and the value of the OpPhi should always be %int_7.
130 ; CHECK: OpPhi %int %int_7 [[bb_taken]] [[id_not_evaluated]] [[bb_not_taken]]
131 %35 = OpPhi %int %7 %22 %8 %23
132
133 ; Only the true path of the conditional is ever executed. The output of this
134 ; function is always %int_7.
135 ; CHECK: OpStore %outparm %int_7
136 OpStore %outparm %35
137 OpReturn
138 OpFunctionEnd
139 )";
140
141 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
142 }
143
TEST_F(CCPTest,SimplifySwitches)144 TEST_F(CCPTest, SimplifySwitches) {
145 const std::string spv_asm = R"(
146 OpCapability Shader
147 %1 = OpExtInstImport "GLSL.std.450"
148 OpMemoryModel Logical GLSL450
149 OpEntryPoint Fragment %main "main" %outparm
150 OpExecutionMode %main OriginUpperLeft
151 OpSource GLSL 450
152 OpName %main "main"
153 OpName %outparm "outparm"
154 OpDecorate %outparm Location 0
155 %void = OpTypeVoid
156 %6 = OpTypeFunction %void
157 %int = OpTypeInt 32 1
158 %_ptr_Function_int = OpTypePointer Function %int
159 %int_23 = OpConstant %int 23
160 %int_42 = OpConstant %int 42
161 %int_14 = OpConstant %int 14
162 %int_15 = OpConstant %int 15
163 %int_4 = OpConstant %int 4
164 %_ptr_Output_int = OpTypePointer Output %int
165 %outparm = OpVariable %_ptr_Output_int Output
166 %main = OpFunction %void None %6
167 %15 = OpLabel
168 OpSelectionMerge %17 None
169 OpSwitch %int_23 %17 10 %18 13 %19 23 %20
170 %18 = OpLabel
171 OpBranch %17
172 %19 = OpLabel
173 OpBranch %17
174 %20 = OpLabel
175 OpBranch %17
176 %17 = OpLabel
177 %24 = OpPhi %int %int_23 %15 %int_42 %18 %int_14 %19 %int_15 %20
178
179 ; The switch will always jump to label %20, which carries the value %int_15.
180 ; CHECK: OpIAdd %int %int_15 %int_4
181 %22 = OpIAdd %int %24 %int_4
182
183 ; Consequently, the return value will always be %int_19.
184 ; CHECK: OpStore %outparm %int_19
185 OpStore %outparm %22
186 OpReturn
187 OpFunctionEnd
188 )";
189
190 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
191 }
192
TEST_F(CCPTest,SimplifySwitchesDefaultBranch)193 TEST_F(CCPTest, SimplifySwitchesDefaultBranch) {
194 const std::string spv_asm = R"(
195 OpCapability Shader
196 %1 = OpExtInstImport "GLSL.std.450"
197 OpMemoryModel Logical GLSL450
198 OpEntryPoint Fragment %main "main" %outparm
199 OpExecutionMode %main OriginUpperLeft
200 OpSource GLSL 450
201 OpName %main "main"
202 OpName %outparm "outparm"
203 OpDecorate %outparm Location 0
204 %void = OpTypeVoid
205 %6 = OpTypeFunction %void
206 %int = OpTypeInt 32 1
207 %_ptr_Function_int = OpTypePointer Function %int
208 %int_42 = OpConstant %int 42
209 %int_4 = OpConstant %int 4
210 %int_1 = OpConstant %int 1
211 %_ptr_Output_int = OpTypePointer Output %int
212 %outparm = OpVariable %_ptr_Output_int Output
213 %main = OpFunction %void None %6
214 %13 = OpLabel
215 %15 = OpIAdd %int %int_42 %int_4
216 OpSelectionMerge %16 None
217
218 ; CHECK: OpSwitch %int_46 {{%\d+}} 10 {{%\d+}}
219 OpSwitch %15 %17 10 %18
220 %18 = OpLabel
221 OpBranch %16
222 %17 = OpLabel
223 OpBranch %16
224 %16 = OpLabel
225 %22 = OpPhi %int %int_42 %18 %int_1 %17
226
227 ; The switch will always jump to the default label %17. This carries the value
228 ; %int_1.
229 ; CHECK: OpIAdd %int %int_1 %int_4
230 %20 = OpIAdd %int %22 %int_4
231
232 ; Resulting in a return value of %int_5.
233 ; CHECK: OpStore %outparm %int_5
234 OpStore %outparm %20
235 OpReturn
236 OpFunctionEnd
237 )";
238
239 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
240 }
241
TEST_F(CCPTest,SimplifyIntVector)242 TEST_F(CCPTest, SimplifyIntVector) {
243 const std::string spv_asm = R"(
244 OpCapability Shader
245 %1 = OpExtInstImport "GLSL.std.450"
246 OpMemoryModel Logical GLSL450
247 OpEntryPoint Fragment %main "main" %OutColor
248 OpExecutionMode %main OriginUpperLeft
249 OpSource GLSL 450
250 OpName %main "main"
251 OpName %v "v"
252 OpName %OutColor "OutColor"
253 OpDecorate %OutColor Location 0
254 %void = OpTypeVoid
255 %3 = OpTypeFunction %void
256 %int = OpTypeInt 32 1
257 %v4int = OpTypeVector %int 4
258 %_ptr_Function_v4int = OpTypePointer Function %v4int
259 %int_1 = OpConstant %int 1
260 %int_2 = OpConstant %int 2
261 %int_3 = OpConstant %int 3
262 %int_4 = OpConstant %int 4
263 %14 = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
264 %uint = OpTypeInt 32 0
265 %uint_0 = OpConstant %uint 0
266 %_ptr_Function_int = OpTypePointer Function %int
267 %_ptr_Output_v4int = OpTypePointer Output %v4int
268 %OutColor = OpVariable %_ptr_Output_v4int Output
269 %main = OpFunction %void None %3
270 %5 = OpLabel
271 %v = OpVariable %_ptr_Function_v4int Function
272 OpStore %v %14
273 %18 = OpAccessChain %_ptr_Function_int %v %uint_0
274 %19 = OpLoad %int %18
275
276 ; The constant folder does not see through access chains. To get this, the
277 ; vector would have to be scalarized.
278 ; CHECK: [[result_id:%\d+]] = OpIAdd %int {{%\d+}} %int_1
279 %20 = OpIAdd %int %19 %int_1
280 %21 = OpAccessChain %_ptr_Function_int %v %uint_0
281
282 ; CHECK: OpStore {{%\d+}} [[result_id]]
283 OpStore %21 %20
284 %24 = OpLoad %v4int %v
285 OpStore %OutColor %24
286 OpReturn
287 OpFunctionEnd
288 )";
289
290 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
291 }
292
TEST_F(CCPTest,BadSimplifyFloatVector)293 TEST_F(CCPTest, BadSimplifyFloatVector) {
294 const std::string spv_asm = R"(
295 OpCapability Shader
296 %1 = OpExtInstImport "GLSL.std.450"
297 OpMemoryModel Logical GLSL450
298 OpEntryPoint Fragment %main "main" %OutColor
299 OpExecutionMode %main OriginUpperLeft
300 OpSource GLSL 450
301 OpName %main "main"
302 OpName %v "v"
303 OpName %OutColor "OutColor"
304 OpDecorate %OutColor Location 0
305 %void = OpTypeVoid
306 %3 = OpTypeFunction %void
307 %float = OpTypeFloat 32
308 %v4float = OpTypeVector %float 4
309 %_ptr_Function_v4float = OpTypePointer Function %v4float
310 %float_1 = OpConstant %float 1
311 %float_2 = OpConstant %float 2
312 %float_3 = OpConstant %float 3
313 %float_4 = OpConstant %float 4
314 %14 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
315 %uint = OpTypeInt 32 0
316 %uint_0 = OpConstant %uint 0
317 %_ptr_Function_float = OpTypePointer Function %float
318 %_ptr_Output_v4float = OpTypePointer Output %v4float
319 %OutColor = OpVariable %_ptr_Output_v4float Output
320 %main = OpFunction %void None %3
321 %5 = OpLabel
322 %v = OpVariable %_ptr_Function_v4float Function
323 OpStore %v %14
324 %18 = OpAccessChain %_ptr_Function_float %v %uint_0
325 %19 = OpLoad %float %18
326
327 ; NOTE: This test should start failing once floating point folding is
328 ; implemented (https://github.com/KhronosGroup/SPIRV-Tools/issues/943).
329 ; This should be checking that we are adding %float_1 + %float_1.
330 ; CHECK: [[result_id:%\d+]] = OpFAdd %float {{%\d+}} %float_1
331 %20 = OpFAdd %float %19 %float_1
332 %21 = OpAccessChain %_ptr_Function_float %v %uint_0
333
334 ; This should be checkint that we are storing %float_2 instead of result_it.
335 ; CHECK: OpStore {{%\d+}} [[result_id]]
336 OpStore %21 %20
337 %24 = OpLoad %v4float %v
338 OpStore %OutColor %24
339 OpReturn
340 OpFunctionEnd
341 )";
342
343 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
344 }
345
TEST_F(CCPTest,NoLoadStorePropagation)346 TEST_F(CCPTest, NoLoadStorePropagation) {
347 const std::string spv_asm = R"(
348 OpCapability Shader
349 %1 = OpExtInstImport "GLSL.std.450"
350 OpMemoryModel Logical GLSL450
351 OpEntryPoint Fragment %main "main" %outparm
352 OpExecutionMode %main OriginUpperLeft
353 OpSource GLSL 450
354 OpName %main "main"
355 OpName %x "x"
356 OpName %outparm "outparm"
357 OpDecorate %outparm Location 0
358 %void = OpTypeVoid
359 %3 = OpTypeFunction %void
360 %int = OpTypeInt 32 1
361 %_ptr_Function_int = OpTypePointer Function %int
362 %int_23 = OpConstant %int 23
363 %_ptr_Output_int = OpTypePointer Output %int
364 %outparm = OpVariable %_ptr_Output_int Output
365 %main = OpFunction %void None %3
366 %5 = OpLabel
367 %x = OpVariable %_ptr_Function_int Function
368 OpStore %x %int_23
369
370 ; int_23 should not propagate into this load.
371 ; CHECK: [[load_id:%\d+]] = OpLoad %int %x
372 %12 = OpLoad %int %x
373
374 ; Nor into this copy operation.
375 ; CHECK: [[copy_id:%\d+]] = OpCopyObject %int [[load_id]]
376 %13 = OpCopyObject %int %12
377
378 ; Likewise here.
379 ; CHECK: OpStore %outparm [[copy_id]]
380 OpStore %outparm %13
381 OpReturn
382 OpFunctionEnd
383 )";
384
385 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
386 }
387
TEST_F(CCPTest,HandleAbortInstructions)388 TEST_F(CCPTest, HandleAbortInstructions) {
389 const std::string spv_asm = R"(
390 OpCapability Shader
391 %1 = OpExtInstImport "GLSL.std.450"
392 OpMemoryModel Logical GLSL450
393 OpEntryPoint Fragment %main "main"
394 OpExecutionMode %main OriginUpperLeft
395 OpSource HLSL 500
396 OpName %main "main"
397 %void = OpTypeVoid
398 %3 = OpTypeFunction %void
399 %int = OpTypeInt 32 1
400 %bool = OpTypeBool
401 ; CHECK: %true = OpConstantTrue %bool
402 %int_3 = OpConstant %int 3
403 %int_1 = OpConstant %int 1
404 %main = OpFunction %void None %3
405 %4 = OpLabel
406 %9 = OpIAdd %int %int_3 %int_1
407 %6 = OpSGreaterThan %bool %9 %int_3
408 OpSelectionMerge %23 None
409 ; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}}
410 OpBranchConditional %6 %22 %23
411 %22 = OpLabel
412 OpKill
413 %23 = OpLabel
414 OpReturn
415 OpFunctionEnd
416 )";
417
418 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
419 }
420
TEST_F(CCPTest,SSAWebCycles)421 TEST_F(CCPTest, SSAWebCycles) {
422 // Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1159
423 // When there is a cycle in the SSA def-use web, the propagator was getting
424 // into an infinite loop. SSA edges for Phi instructions should not be
425 // added to the edges to simulate.
426 const std::string spv_asm = R"(
427 OpCapability Shader
428 %1 = OpExtInstImport "GLSL.std.450"
429 OpMemoryModel Logical GLSL450
430 OpEntryPoint Fragment %main "main"
431 OpExecutionMode %main OriginUpperLeft
432 OpSource GLSL 450
433 OpName %main "main"
434 %void = OpTypeVoid
435 %3 = OpTypeFunction %void
436 %int = OpTypeInt 32 1
437 %_ptr_Function_int = OpTypePointer Function %int
438 %int_0 = OpConstant %int 0
439 %int_4 = OpConstant %int 4
440 %bool = OpTypeBool
441 %int_1 = OpConstant %int 1
442 %_ptr_Output_int = OpTypePointer Output %int
443 %main = OpFunction %void None %3
444 %5 = OpLabel
445 OpBranch %11
446 %11 = OpLabel
447 %29 = OpPhi %int %int_0 %5 %22 %14
448 %30 = OpPhi %int %int_0 %5 %25 %14
449 OpLoopMerge %13 %14 None
450 OpBranch %15
451 %15 = OpLabel
452 %19 = OpSLessThan %bool %30 %int_4
453 ; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}}
454 OpBranchConditional %19 %12 %13
455 %12 = OpLabel
456 ; CHECK: OpIAdd %int %int_0 %int_0
457 %22 = OpIAdd %int %29 %30
458 OpBranch %14
459 %14 = OpLabel
460 ; CHECK: OpPhi %int %int_0 {{%\d+}}
461 %25 = OpPhi %int %30 %12
462 OpBranch %11
463 %13 = OpLabel
464 OpReturn
465 OpFunctionEnd
466 )";
467
468 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
469 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
470 }
471
TEST_F(CCPTest,LoopInductionVariables)472 TEST_F(CCPTest, LoopInductionVariables) {
473 // Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1143
474 // We are failing to properly consider the induction variable for this loop
475 // as Varying.
476 const std::string spv_asm = R"(
477 OpCapability Shader
478 %1 = OpExtInstImport "GLSL.std.450"
479 OpMemoryModel Logical GLSL450
480 OpEntryPoint Fragment %main "main"
481 OpExecutionMode %main OriginUpperLeft
482 OpSource GLSL 430
483 OpName %main "main"
484 %void = OpTypeVoid
485 %5 = OpTypeFunction %void
486 %int = OpTypeInt 32 1
487 %_ptr_Function_int = OpTypePointer Function %int
488 %int_0 = OpConstant %int 0
489 %int_10 = OpConstant %int 10
490 %bool = OpTypeBool
491 %int_1 = OpConstant %int 1
492 %main = OpFunction %void None %5
493 %12 = OpLabel
494 OpBranch %13
495 %13 = OpLabel
496
497 ; This Phi should not have all constant arguments:
498 ; CHECK: [[phi_id:%\d+]] = OpPhi %int %int_0 {{%\d+}} {{%\d+}} {{%\d+}}
499 %22 = OpPhi %int %int_0 %12 %21 %15
500 OpLoopMerge %14 %15 None
501 OpBranch %16
502 %16 = OpLabel
503
504 ; The Phi should never be considered to have the value %int_0.
505 ; CHECK: [[branch_selector:%\d+]] = OpSLessThan %bool [[phi_id]] %int_10
506 %18 = OpSLessThan %bool %22 %int_10
507
508 ; This conditional was wrongly converted into an always-true jump due to the
509 ; bad meet evaluation of %22.
510 ; CHECK: OpBranchConditional [[branch_selector]] {{%\d+}} {{%\d+}}
511 OpBranchConditional %18 %19 %14
512 %19 = OpLabel
513 OpBranch %15
514 %15 = OpLabel
515 ; CHECK: OpIAdd %int [[phi_id]] %int_1
516 %21 = OpIAdd %int %22 %int_1
517 OpBranch %13
518 %14 = OpLabel
519 OpReturn
520 OpFunctionEnd
521 )";
522
523 SinglePassRunAndMatch<CCPPass>(spv_asm, true);
524 }
525
TEST_F(CCPTest,HandleCompositeWithUndef)526 TEST_F(CCPTest, HandleCompositeWithUndef) {
527 // Check to make sure that CCP does not crash when given a "constant" struct
528 // with an undef. If at a later time CCP is enhanced to optimize this case,
529 // it is not wrong.
530 const std::string spv_asm = R"(
531 OpCapability Shader
532 %1 = OpExtInstImport "GLSL.std.450"
533 OpMemoryModel Logical GLSL450
534 OpEntryPoint Fragment %main "main"
535 OpExecutionMode %main OriginUpperLeft
536 OpSource HLSL 500
537 OpName %main "main"
538 %void = OpTypeVoid
539 %4 = OpTypeFunction %void
540 %int = OpTypeInt 32 1
541 %bool = OpTypeBool
542 %_struct_7 = OpTypeStruct %int %int
543 %int_1 = OpConstant %int 1
544 %9 = OpUndef %int
545 %10 = OpConstantComposite %_struct_7 %int_1 %9
546 %main = OpFunction %void None %4
547 %11 = OpLabel
548 %12 = OpCompositeExtract %int %10 0
549 %13 = OpCopyObject %int %12
550 OpReturn
551 OpFunctionEnd
552 )";
553
554 auto res = SinglePassRunToBinary<CCPPass>(spv_asm, true);
555 EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange);
556 }
557
TEST_F(CCPTest,SkipSpecConstantInstrucitons)558 TEST_F(CCPTest, SkipSpecConstantInstrucitons) {
559 const std::string spv_asm = R"(
560 OpCapability Shader
561 %1 = OpExtInstImport "GLSL.std.450"
562 OpMemoryModel Logical GLSL450
563 OpEntryPoint Fragment %main "main"
564 OpExecutionMode %main OriginUpperLeft
565 OpSource HLSL 500
566 OpName %main "main"
567 %void = OpTypeVoid
568 %4 = OpTypeFunction %void
569 %bool = OpTypeBool
570 %10 = OpSpecConstantFalse %bool
571 %main = OpFunction %void None %4
572 %11 = OpLabel
573 OpBranchConditional %10 %L1 %L2
574 %L1 = OpLabel
575 OpReturn
576 %L2 = OpLabel
577 OpReturn
578 OpFunctionEnd
579 )";
580
581 auto res = SinglePassRunToBinary<CCPPass>(spv_asm, true);
582 EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange);
583 }
584
TEST_F(CCPTest,FoldConstantCompositeInstrucitonsWithSpecConst)585 TEST_F(CCPTest, FoldConstantCompositeInstrucitonsWithSpecConst) {
586 const std::string spv_asm = R"(
587 OpCapability Shader
588 OpMemoryModel Logical GLSL450
589 OpEntryPoint Fragment %1 "main"
590 OpExecutionMode %1 OriginUpperLeft
591 %void = OpTypeVoid
592 %4 = OpTypeFunction %void
593 %bool = OpTypeBool
594 %v3bool = OpTypeVector %bool 3
595 %_struct_8 = OpTypeStruct %v3bool
596 %true = OpConstantTrue %bool
597 ; CHECK: [[spec_const:%\w+]] = OpSpecConstantComposite %v3bool
598 %11 = OpSpecConstantComposite %v3bool %true %true %true
599 %12 = OpConstantComposite %_struct_8 %11
600 ; CHECK: OpFunction
601 %1 = OpFunction %void None %4
602 %29 = OpLabel
603 %31 = OpCompositeExtract %v3bool %12 0
604 ; CHECK: OpCompositeExtract %bool [[spec_const]] 0
605 %32 = OpCompositeExtract %bool %31 0
606 OpReturn
607 OpFunctionEnd
608 )";
609
610 auto result = SinglePassRunAndMatch<CCPPass>(spv_asm, true);
611 EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
612 }
613
TEST_F(CCPTest,UpdateSubsequentPhisToVarying)614 TEST_F(CCPTest, UpdateSubsequentPhisToVarying) {
615 const std::string text = R"(
616 OpCapability Shader
617 OpMemoryModel Logical GLSL450
618 OpEntryPoint Fragment %func "func" %in
619 OpExecutionMode %func OriginUpperLeft
620 %void = OpTypeVoid
621 %bool = OpTypeBool
622 %int = OpTypeInt 32 1
623 %false = OpConstantFalse %bool
624 %int0 = OpConstant %int 0
625 %int1 = OpConstant %int 1
626 %int6 = OpConstant %int 6
627 %int_ptr_Input = OpTypePointer Input %int
628 %in = OpVariable %int_ptr_Input Input
629 %undef = OpUndef %int
630
631 ; Although no constants are propagated in this function, the propagator
632 ; generates a new %true value while visiting conditional statements.
633 ; CHECK: %true = OpConstantTrue %bool
634
635 %functy = OpTypeFunction %void
636 %func = OpFunction %void None %functy
637 %1 = OpLabel
638 OpBranch %2
639 %2 = OpLabel
640 %outer_phi = OpPhi %int %int0 %1 %outer_add %15
641 %cond1 = OpSLessThanEqual %bool %outer_phi %int6
642 OpLoopMerge %3 %15 None
643 OpBranchConditional %cond1 %4 %3
644 %4 = OpLabel
645 %ld = OpLoad %int %in
646 %cond2 = OpSGreaterThanEqual %bool %int1 %ld
647 OpSelectionMerge %10 None
648 OpBranchConditional %cond2 %8 %9
649 %8 = OpLabel
650 OpBranch %10
651 %9 = OpLabel
652 OpBranch %10
653 %10 = OpLabel
654 %extra_phi = OpPhi %int %outer_phi %8 %outer_phi %9
655 OpBranch %11
656 %11 = OpLabel
657 %inner_phi = OpPhi %int %int0 %10 %inner_add %13
658 %cond3 = OpSLessThanEqual %bool %inner_phi %int6
659 OpLoopMerge %14 %13 None
660 OpBranchConditional %cond3 %12 %14
661 %12 = OpLabel
662 OpBranch %13
663 %13 = OpLabel
664 %inner_add = OpIAdd %int %inner_phi %int1
665 OpBranch %11
666 %14 = OpLabel
667 OpBranch %15
668 %15 = OpLabel
669 %outer_add = OpIAdd %int %extra_phi %int1
670 OpBranch %2
671 %3 = OpLabel
672 OpReturn
673 OpFunctionEnd
674 )";
675
676 auto result = SinglePassRunAndMatch<CCPPass>(text, true);
677 EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
678 }
679
TEST_F(CCPTest,UndefInPhi)680 TEST_F(CCPTest, UndefInPhi) {
681 const std::string text = R"(
682 ; CHECK: [[uint1:%\w+]] = OpConstant {{%\w+}} 1
683 ; CHECK: [[phi:%\w+]] = OpPhi
684 ; CHECK: OpIAdd {{%\w+}} [[phi]] [[uint1]]
685 OpCapability Kernel
686 OpCapability Linkage
687 OpMemoryModel Logical OpenCL
688 OpDecorate %1 LinkageAttributes "func" Export
689 %void = OpTypeVoid
690 %bool = OpTypeBool
691 %uint = OpTypeInt 32 0
692 %uint_0 = OpConstant %uint 0
693 %uint_1 = OpConstant %uint 1
694 %7 = OpUndef %uint
695 %8 = OpTypeFunction %void %bool
696 %1 = OpFunction %void None %8
697 %9 = OpFunctionParameter %bool
698 %10 = OpLabel
699 OpBranchConditional %9 %11 %12
700 %11 = OpLabel
701 OpBranch %13
702 %12 = OpLabel
703 OpBranch %14
704 %14 = OpLabel
705 OpBranchConditional %9 %13 %15
706 %15 = OpLabel
707 OpBranch %13
708 %13 = OpLabel
709 %16 = OpPhi %uint %uint_0 %11 %7 %14 %uint_1 %15
710 %17 = OpIAdd %uint %16 %uint_1
711 OpReturn
712 OpFunctionEnd
713 )";
714
715 SinglePassRunAndMatch<CCPPass>(text, true);
716 }
717
718 // Just test to make sure the constant fold rules are being used. Will rely on
719 // the folding test for specific testing of specific rules.
TEST_F(CCPTest,UseConstantFoldingRules)720 TEST_F(CCPTest, UseConstantFoldingRules) {
721 const std::string text = R"(
722 ; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1
723 ; CHECK: OpReturnValue [[float1]]
724 OpCapability Shader
725 OpCapability Linkage
726 OpMemoryModel Logical GLSL450
727 OpDecorate %1 LinkageAttributes "func" Export
728 %void = OpTypeVoid
729 %bool = OpTypeBool
730 %float = OpTypeFloat 32
731 %float_0 = OpConstant %float 0
732 %float_1 = OpConstant %float 1
733 %8 = OpTypeFunction %float
734 %1 = OpFunction %float None %8
735 %10 = OpLabel
736 %17 = OpFAdd %float %float_0 %float_1
737 OpReturnValue %17
738 OpFunctionEnd
739 )";
740
741 SinglePassRunAndMatch<CCPPass>(text, true);
742 }
743
744 // Test for #1300. Previously value for %5 would not settle during simulation.
TEST_F(CCPTest,SettlePhiLatticeValue)745 TEST_F(CCPTest, SettlePhiLatticeValue) {
746 const std::string text = R"(
747 OpCapability Kernel
748 OpCapability Linkage
749 OpMemoryModel Logical OpenCL
750 OpDecorate %func LinkageAttributes "func" Export
751 %void = OpTypeVoid
752 %bool = OpTypeBool
753 %true = OpConstantTrue %bool
754 %false = OpConstantFalse %bool
755 %functy = OpTypeFunction %void
756 %func = OpFunction %void None %functy
757 %1 = OpLabel
758 OpBranchConditional %true %2 %3
759 %3 = OpLabel
760 OpBranch %2
761 %2 = OpLabel
762 %5 = OpPhi %bool %true %1 %false %3
763 OpReturn
764 OpFunctionEnd
765 )";
766
767 SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
768 SinglePassRunToBinary<CCPPass>(text, true);
769 }
770
TEST_F(CCPTest,NullBranchCondition)771 TEST_F(CCPTest, NullBranchCondition) {
772 const std::string text = R"(
773 ; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
774 ; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2
775 ; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]]
776 OpCapability Shader
777 OpMemoryModel Logical GLSL450
778 OpEntryPoint Fragment %func "func"
779 OpExecutionMode %func OriginUpperLeft
780 %void = OpTypeVoid
781 %bool = OpTypeBool
782 %int = OpTypeInt 32 1
783 %null = OpConstantNull %bool
784 %int_1 = OpConstant %int 1
785 %int_2 = OpConstant %int 2
786 %functy = OpTypeFunction %void
787 %func = OpFunction %void None %functy
788 %1 = OpLabel
789 OpSelectionMerge %2 None
790 OpBranchConditional %null %2 %3
791 %3 = OpLabel
792 OpBranch %2
793 %2 = OpLabel
794 %phi = OpPhi %int %int_1 %1 %int_2 %3
795 %add = OpIAdd %int %int_1 %phi
796 OpReturn
797 OpFunctionEnd
798 )";
799
800 SinglePassRunAndMatch<CCPPass>(text, true);
801 }
802
TEST_F(CCPTest,UndefBranchCondition)803 TEST_F(CCPTest, UndefBranchCondition) {
804 const std::string text = R"(
805 ; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
806 ; CHECK: [[phi:%\w+]] = OpPhi
807 ; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]]
808 OpCapability Shader
809 OpMemoryModel Logical GLSL450
810 OpEntryPoint Fragment %func "func"
811 OpExecutionMode %func OriginUpperLeft
812 %void = OpTypeVoid
813 %bool = OpTypeBool
814 %int = OpTypeInt 32 1
815 %undef = OpUndef %bool
816 %int_1 = OpConstant %int 1
817 %int_2 = OpConstant %int 2
818 %functy = OpTypeFunction %void
819 %func = OpFunction %void None %functy
820 %1 = OpLabel
821 OpSelectionMerge %2 None
822 OpBranchConditional %undef %2 %3
823 %3 = OpLabel
824 OpBranch %2
825 %2 = OpLabel
826 %phi = OpPhi %int %int_1 %1 %int_2 %3
827 %add = OpIAdd %int %int_1 %phi
828 OpReturn
829 OpFunctionEnd
830 )";
831
832 SinglePassRunAndMatch<CCPPass>(text, true);
833 }
834
TEST_F(CCPTest,NullSwitchCondition)835 TEST_F(CCPTest, NullSwitchCondition) {
836 const std::string text = R"(
837 ; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
838 ; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2
839 ; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]]
840 OpCapability Shader
841 OpMemoryModel Logical GLSL450
842 OpEntryPoint Fragment %func "func"
843 OpExecutionMode %func OriginUpperLeft
844 %void = OpTypeVoid
845 %int = OpTypeInt 32 1
846 %null = OpConstantNull %int
847 %int_1 = OpConstant %int 1
848 %int_2 = OpConstant %int 2
849 %functy = OpTypeFunction %void
850 %func = OpFunction %void None %functy
851 %1 = OpLabel
852 OpSelectionMerge %2 None
853 OpSwitch %null %2 0 %3
854 %3 = OpLabel
855 OpBranch %2
856 %2 = OpLabel
857 %phi = OpPhi %int %int_1 %1 %int_2 %3
858 %add = OpIAdd %int %int_1 %phi
859 OpReturn
860 OpFunctionEnd
861 )";
862
863 SinglePassRunAndMatch<CCPPass>(text, true);
864 }
865
TEST_F(CCPTest,UndefSwitchCondition)866 TEST_F(CCPTest, UndefSwitchCondition) {
867 const std::string text = R"(
868 ; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
869 ; CHECK: [[phi:%\w+]] = OpPhi
870 ; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]]
871 OpCapability Shader
872 OpMemoryModel Logical GLSL450
873 OpEntryPoint Fragment %func "func"
874 OpExecutionMode %func OriginUpperLeft
875 %void = OpTypeVoid
876 %int = OpTypeInt 32 1
877 %undef = OpUndef %int
878 %int_1 = OpConstant %int 1
879 %int_2 = OpConstant %int 2
880 %functy = OpTypeFunction %void
881 %func = OpFunction %void None %functy
882 %1 = OpLabel
883 OpSelectionMerge %2 None
884 OpSwitch %undef %2 0 %3
885 %3 = OpLabel
886 OpBranch %2
887 %2 = OpLabel
888 %phi = OpPhi %int %int_1 %1 %int_2 %3
889 %add = OpIAdd %int %int_1 %phi
890 OpReturn
891 OpFunctionEnd
892 )";
893
894 SinglePassRunAndMatch<CCPPass>(text, true);
895 }
896
897 // Test for #1361.
TEST_F(CCPTest,CompositeConstructOfGlobalValue)898 TEST_F(CCPTest, CompositeConstructOfGlobalValue) {
899 const std::string text = R"(
900 ; CHECK: [[phi:%\w+]] = OpPhi
901 ; CHECK-NEXT: OpCompositeExtract {{%\w+}} [[phi]] 0
902 OpCapability Shader
903 OpMemoryModel Logical GLSL450
904 OpEntryPoint Fragment %func "func" %in
905 OpExecutionMode %func OriginUpperLeft
906 %void = OpTypeVoid
907 %int = OpTypeInt 32 1
908 %bool = OpTypeBool
909 %functy = OpTypeFunction %void
910 %ptr_int_Input = OpTypePointer Input %int
911 %in = OpVariable %ptr_int_Input Input
912 %struct = OpTypeStruct %ptr_int_Input %ptr_int_Input
913 %struct_null = OpConstantNull %struct
914 %func = OpFunction %void None %functy
915 %1 = OpLabel
916 OpBranch %2
917 %2 = OpLabel
918 %phi = OpPhi %struct %struct_null %1 %5 %4
919 %extract = OpCompositeExtract %ptr_int_Input %phi 0
920 OpLoopMerge %3 %4 None
921 OpBranch %4
922 %4 = OpLabel
923 %5 = OpCompositeConstruct %struct %in %in
924 OpBranch %2
925 %3 = OpLabel
926 OpReturn
927 OpFunctionEnd
928 )";
929
930 SinglePassRunAndMatch<CCPPass>(text, true);
931 }
932
TEST_F(CCPTest,FoldWithDecoration)933 TEST_F(CCPTest, FoldWithDecoration) {
934 const std::string text = R"(
935 ; CHECK: OpCapability
936 ; CHECK-NOT: OpDecorate
937 ; CHECK: OpFunctionEnd
938 OpCapability Shader
939 %1 = OpExtInstImport "GLSL.std.450"
940 OpMemoryModel Logical GLSL450
941 OpEntryPoint Fragment %2 "main"
942 OpExecutionMode %2 OriginUpperLeft
943 OpSource ESSL 310
944 OpDecorate %3 RelaxedPrecision
945 %void = OpTypeVoid
946 %5 = OpTypeFunction %void
947 %float = OpTypeFloat 32
948 %v3float = OpTypeVector %float 3
949 %float_0 = OpConstant %float 0
950 %v4float = OpTypeVector %float 4
951 %10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
952 %2 = OpFunction %void None %5
953 %11 = OpLabel
954 %3 = OpVectorShuffle %v3float %10 %10 0 1 2
955 OpReturn
956 OpFunctionEnd
957 )";
958
959 SinglePassRunAndMatch<CCPPass>(text, true);
960 }
961
TEST_F(CCPTest,DebugSimpleFoldConstant)962 TEST_F(CCPTest, DebugSimpleFoldConstant) {
963 const std::string text = R"(
964 OpCapability Shader
965 OpCapability Linkage
966 %ext = OpExtInstImport "OpenCL.DebugInfo.100"
967 OpMemoryModel Logical GLSL450
968 %file_name = OpString "test"
969 %float_name = OpString "float"
970 %main_name = OpString "main"
971 %f_name = OpString "f"
972 OpDecorate %1 LinkageAttributes "func" Export
973 %void = OpTypeVoid
974 %bool = OpTypeBool
975 %float = OpTypeFloat 32
976 %float_0 = OpConstant %float 0
977
978 ; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1
979 %float_1 = OpConstant %float 1
980 %uint = OpTypeInt 32 0
981 %uint_32 = OpConstant %uint 32
982 %8 = OpTypeFunction %float
983 %null_expr = OpExtInst %void %ext DebugExpression
984 %src = OpExtInst %void %ext DebugSource %file_name
985 %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
986 %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
987 %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
988 %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %1
989 %dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
990 %1 = OpFunction %float None %8
991 %10 = OpLabel
992
993 ; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope
994 ; CHECK: OpLine [[file:%\w+]] 1 0
995 ; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %float_1
996 %s0 = OpExtInst %void %ext DebugScope %dbg_main
997 OpLine %file_name 1 0
998 %17 = OpFAdd %float %float_0 %float_1
999 %val = OpExtInst %void %ext DebugValue %dbg_f %17 %null_expr
1000
1001 ; CHECK: OpLine [[file]] 2 0
1002 ; CHECK: OpReturnValue [[float1]]
1003 OpLine %file_name 2 0
1004 OpReturnValue %17
1005 OpFunctionEnd
1006 )";
1007
1008 SinglePassRunAndMatch<CCPPass>(text, true);
1009 }
1010
TEST_F(CCPTest,DebugFoldMultipleForSingleConstant)1011 TEST_F(CCPTest, DebugFoldMultipleForSingleConstant) {
1012 const std::string text = R"(
1013 OpCapability Shader
1014 %1 = OpExtInstImport "GLSL.std.450"
1015 %ext = OpExtInstImport "OpenCL.DebugInfo.100"
1016 OpMemoryModel Logical GLSL450
1017 OpEntryPoint Fragment %main "main" %outparm
1018 OpExecutionMode %main OriginUpperLeft
1019 OpSource GLSL 450
1020 %file_name = OpString "test"
1021 %float_name = OpString "float"
1022 %main_name = OpString "main"
1023 %f_name = OpString "f"
1024 OpName %main "main"
1025 OpName %outparm "outparm"
1026 OpDecorate %outparm Location 0
1027 %void = OpTypeVoid
1028 %3 = OpTypeFunction %void
1029 %int = OpTypeInt 32 1
1030 %bool = OpTypeBool
1031 %_ptr_Function_int = OpTypePointer Function %int
1032 %int_4 = OpConstant %int 4
1033 %int_3 = OpConstant %int 3
1034 %int_1 = OpConstant %int 1
1035 %uint = OpTypeInt 32 0
1036 %uint_32 = OpConstant %uint 32
1037 %_ptr_Output_int = OpTypePointer Output %int
1038 %outparm = OpVariable %_ptr_Output_int Output
1039 %null_expr = OpExtInst %void %ext DebugExpression
1040 %src = OpExtInst %void %ext DebugSource %file_name
1041 %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
1042 %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
1043 %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
1044 %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
1045 %bb0 = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main
1046 %bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main
1047 %bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main
1048 %bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main
1049 %dbg_f0 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
1050 %dbg_f1 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 1 0 %dbg_main FlagIsLocal
1051 %dbg_f2 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 2 0 %dbg_main FlagIsLocal
1052 %main = OpFunction %void None %3
1053 %4 = OpLabel
1054
1055 ; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope
1056 ; CHECK: OpLine [[file:%\w+]] 1 0
1057 ; CHECK: OpIAdd %int %int_4 %int_3
1058 ; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %int_7
1059 %s0 = OpExtInst %void %ext DebugScope %bb0
1060 OpLine %file_name 1 0
1061 %9 = OpIAdd %int %int_4 %int_3
1062 %val0 = OpExtInst %void %ext DebugValue %dbg_f0 %9 %null_expr
1063
1064 ; CHECK: OpLine [[file]] 2 0
1065 ; CHECK: OpSGreaterThan %bool %int_7 %int_3
1066 ; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %true
1067 OpLine %file_name 2 0
1068 %6 = OpSGreaterThan %bool %9 %int_3
1069 %val1 = OpExtInst %void %ext DebugValue %dbg_f1 %6 %null_expr
1070
1071 OpSelectionMerge %25 None
1072 OpBranchConditional %6 %22 %23
1073 %22 = OpLabel
1074 %s1 = OpExtInst %void %ext DebugScope %bb1
1075 %7 = OpCopyObject %int %9
1076 %val2 = OpExtInst %void %ext DebugValue %dbg_f2 %7 %null_expr
1077 OpBranch %25
1078 %23 = OpLabel
1079 %s2 = OpExtInst %void %ext DebugScope %bb2
1080 %8 = OpCopyObject %int %int_4
1081 OpBranch %25
1082 %25 = OpLabel
1083 %s3 = OpExtInst %void %ext DebugScope %bb3
1084 %35 = OpPhi %int %7 %22 %8 %23
1085 OpStore %outparm %35
1086 OpReturn
1087 OpFunctionEnd
1088 )";
1089
1090 SinglePassRunAndMatch<CCPPass>(text, true);
1091 }
1092
1093 // Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3636
TEST_F(CCPTest,CCPNoChangeFailure)1094 TEST_F(CCPTest, CCPNoChangeFailure) {
1095 const std::string text = R"(
1096 OpCapability Shader
1097 %1 = OpExtInstImport "GLSL.std.450"
1098 OpMemoryModel Logical GLSL450
1099 OpEntryPoint Fragment %4 "main"
1100 OpExecutionMode %4 OriginUpperLeft
1101 OpSource ESSL 320
1102 %2 = OpTypeVoid
1103 %3 = OpTypeFunction %2
1104 %6 = OpTypeInt 32 1
1105 %7 = OpConstant %6 2
1106 %13 = OpConstant %6 4
1107 %21 = OpConstant %6 1
1108 %10 = OpTypeBool
1109 %17 = OpTypePointer Function %6
1110
1111 ; CCP is generating two new constants during propagation that end up being
1112 ; dead because they cannot be replaced anywhere in the IR. CCP was wrongly
1113 ; considering the IR to be unmodified because of this.
1114 ; CHECK: %true = OpConstantTrue %bool
1115 ; CHECK: %int_3 = OpConstant %int 3
1116
1117 %4 = OpFunction %2 None %3
1118 %11 = OpLabel
1119 OpBranch %5
1120 %5 = OpLabel
1121 %23 = OpPhi %6 %7 %11 %20 %15
1122 %9 = OpSLessThan %10 %23 %13
1123 OpLoopMerge %8 %15 None
1124 OpBranchConditional %9 %15 %8
1125 %15 = OpLabel
1126 %20 = OpIAdd %6 %23 %21
1127 OpBranch %5
1128 %8 = OpLabel
1129 OpReturn
1130 OpFunctionEnd
1131 )";
1132
1133 auto result = SinglePassRunAndMatch<CCPPass>(text, true);
1134 EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
1135 }
1136
1137 // Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3738
1138 // Similar to the previous one but more than one constant is generated in a
1139 // single call to the instruction folder.
TEST_F(CCPTest,CCPNoChangeFailureSeveralConstantsDuringFolding)1140 TEST_F(CCPTest, CCPNoChangeFailureSeveralConstantsDuringFolding) {
1141 const std::string text = R"(
1142 OpCapability Shader
1143 %1 = OpExtInstImport "GLSL.std.450"
1144 OpMemoryModel Logical GLSL450
1145 OpEntryPoint Fragment %2 "main"
1146 OpExecutionMode %2 OriginUpperLeft
1147 %void = OpTypeVoid
1148 %4 = OpTypeFunction %void
1149 %float = OpTypeFloat 32
1150 %v3float = OpTypeVector %float 3
1151 %uint = OpTypeInt 32 0
1152 %uint_0 = OpConstant %uint 0
1153 %bool = OpTypeBool
1154 %v3bool = OpTypeVector %bool 3
1155 %float_0 = OpConstant %float 0
1156 %12 = OpConstantComposite %v3float %float_0 %float_0 %float_0
1157 %float_0_300000012 = OpConstant %float 0.300000012
1158 %14 = OpConstantComposite %v3float %float_0_300000012 %float_0_300000012 %float_0_300000012
1159
1160 ; CCP is generating several constants during a single instruction evaluation.
1161 ; When folding %19, it generates the constants %true and %24. They are dead
1162 ; because they cannot be replaced anywhere in the IR. CCP was wrongly
1163 ; considering the IR to be unmodified because of this.
1164 ;
1165 ; CHECK: %true = OpConstantTrue %bool
1166 ; CHECK: %24 = OpConstantComposite %v3bool %true %true %true
1167 ; CHECK: %float_1 = OpConstant %float 1
1168 ; CHECK: %float_0_699999988 = OpConstant %float 0.699999988
1169
1170 %2 = OpFunction %void None %4
1171 %15 = OpLabel
1172 OpBranch %16
1173 %16 = OpLabel
1174 %17 = OpPhi %v3float %12 %15 %14 %18
1175 %19 = OpFOrdLessThan %v3bool %17 %14
1176 %20 = OpAll %bool %19
1177 OpLoopMerge %21 %18 None
1178 OpBranchConditional %20 %18 %21
1179 %18 = OpLabel
1180 OpBranch %16
1181 %21 = OpLabel
1182 %22 = OpExtInst %v3float %1 FMix %12 %17 %14
1183 OpReturn
1184 OpFunctionEnd
1185 )";
1186
1187 auto result = SinglePassRunAndMatch<CCPPass>(text, true);
1188 EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
1189 }
1190
1191 // Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3991
1192 // Similar to the previous one but constants are created even when no
1193 // instruction are ever folded during propagation.
TEST_F(CCPTest,CCPNoChangeFailureWithUnfoldableInstr)1194 TEST_F(CCPTest, CCPNoChangeFailureWithUnfoldableInstr) {
1195 const std::string text = R"(
1196 OpCapability Shader
1197 %1 = OpExtInstImport "GLSL.std.450"
1198 OpMemoryModel Logical GLSL450
1199 OpEntryPoint Fragment %2 "main"
1200 OpExecutionMode %2 OriginUpperLeft
1201 %void = OpTypeVoid
1202 %4 = OpTypeFunction %void
1203 %float = OpTypeFloat 32
1204 %v3float = OpTypeVector %float 3
1205 %uint = OpTypeInt 32 0
1206 %uint_0 = OpConstant %uint 0
1207 %bool = OpTypeBool
1208 %float_0 = OpConstant %float 0
1209 %11 = OpConstantComposite %v3float %float_0 %float_0 %float_0
1210 %float_0_300000012 = OpConstant %float 0.300000012
1211 %13 = OpConstantComposite %v3float %float_0_300000012 %float_0_300000012 %float_0_300000012
1212
1213 ; CCP generates two constants when trying to fold an instruction, which it
1214 ; ultimately fails to fold. The instruction folder in CCP was only
1215 ; checking for newly added constants if the instruction folds successfully.
1216 ;
1217 ; CHECK: %float_1 = OpConstant %float 1
1218 ; CHECK: %float_0_699999988 = OpConstant %float 0.69999998
1219
1220 %2 = OpFunction %void None %4
1221 %14 = OpLabel
1222 %15 = OpBitcast %uint %float_0_300000012
1223 %16 = OpUGreaterThan %bool %15 %uint_0
1224 OpBranch %17
1225 %17 = OpLabel
1226 %18 = OpPhi %v3float %11 %14 %13 %19
1227 OpLoopMerge %20 %19 None
1228 OpBranchConditional %16 %19 %20
1229 %19 = OpLabel
1230 OpBranch %17
1231 %20 = OpLabel
1232 %21 = OpExtInst %v3float %1 FMix %11 %18 %13
1233 OpReturn
1234 OpFunctionEnd
1235 )";
1236
1237 auto result = SinglePassRunAndMatch<CCPPass>(text, true);
1238 EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
1239 }
1240
TEST_F(CCPTest,FunctionDeclaration)1241 TEST_F(CCPTest, FunctionDeclaration) {
1242 // Make sure the pass works with a function declaration that is called.
1243 const std::string text = R"(OpCapability Addresses
1244 OpCapability Linkage
1245 OpCapability Kernel
1246 OpCapability Int8
1247 %1 = OpExtInstImport "OpenCL.std"
1248 OpMemoryModel Physical64 OpenCL
1249 OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool"
1250 OpExecutionMode %2 ContractionOff
1251 OpSource Unknown 0
1252 OpDecorate %3 LinkageAttributes "julia_error_7712" Import
1253 %void = OpTypeVoid
1254 %5 = OpTypeFunction %void
1255 %3 = OpFunction %void None %5
1256 OpFunctionEnd
1257 %2 = OpFunction %void None %5
1258 %6 = OpLabel
1259 %7 = OpFunctionCall %void %3
1260 OpReturn
1261 OpFunctionEnd
1262 )";
1263
1264 SinglePassRunAndCheck<CCPPass>(text, text, false);
1265 }
1266
1267 // Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/4462.
1268 // The test was causing a lateral movement in the constant lattice, which was
1269 // not being detected as varying by CCP. In this test, FClamp is evaluated
1270 // twice. On the first evaluation, if computes FClamp(0.5, 0.5, -1) which
1271 // returns -1. On the second evaluation, it computes FClamp(0.5, 0.5, VARYING)
1272 // which returns 0.5.
1273 //
1274 // Both fold() computations are correct given the semantics of FClamp() but
1275 // this causes a lateral transition in the constant lattice which was not being
1276 // considered VARYING by CCP.
TEST_F(CCPTest,LateralLatticeTransition)1277 TEST_F(CCPTest, LateralLatticeTransition) {
1278 const std::string text = R"(OpCapability Shader
1279 %1 = OpExtInstImport "GLSL.std.450"
1280 OpMemoryModel Logical GLSL450
1281 OpEntryPoint Fragment %main "main" %gl_FragCoord %outColor
1282 OpExecutionMode %main OriginUpperLeft
1283 OpSource ESSL 310
1284 OpName %main "main"
1285 OpName %gl_FragCoord "gl_FragCoord"
1286 OpName %outColor "outColor"
1287 OpDecorate %gl_FragCoord BuiltIn FragCoord
1288 OpDecorate %outColor Location 0
1289 %void = OpTypeVoid
1290 %6 = OpTypeFunction %void
1291 %float = OpTypeFloat 32
1292 %float_0_5 = OpConstant %float 0.5
1293 %v4float = OpTypeVector %float 4
1294 %_ptr_Input_v4float = OpTypePointer Input %v4float
1295 %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
1296 %uint = OpTypeInt 32 0
1297 %uint_0 = OpConstant %uint 0
1298 %_ptr_Input_float = OpTypePointer Input %float
1299 %float_0 = OpConstant %float 0
1300 %bool = OpTypeBool
1301 %float_n1 = OpConstant %float -1
1302 %float_1 = OpConstant %float 1
1303 %_ptr_Output_v4float = OpTypePointer Output %v4float
1304 %outColor = OpVariable %_ptr_Output_v4float Output
1305
1306 ; This constant is created during the first evaluation of the CompositeConstruct
1307 ; CHECK: [[new_constant:%\d+]] = OpConstantComposite %v4float %float_n1 %float_0_5 %float_0 %float_1
1308
1309 %main = OpFunction %void None %6
1310 %19 = OpLabel
1311 %20 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0
1312 %21 = OpLoad %float %20
1313 %22 = OpFOrdLessThan %bool %21 %float_0
1314 OpSelectionMerge %23 None
1315 OpBranchConditional %22 %24 %25
1316 %24 = OpLabel
1317 OpBranch %23
1318 %25 = OpLabel
1319 OpBranch %26
1320 %26 = OpLabel
1321 OpBranch %23
1322 %23 = OpLabel
1323 %27 = OpPhi %float %float_n1 %24 %float_0_5 %26
1324 %28 = OpExtInst %float %1 FClamp %float_0_5 %float_0_5 %27
1325
1326 ; On first evaluation, the result from FClamp will return 0.5.
1327 ; But on second evaluation, FClamp should return VARYING. Check
1328 ; that CCP is not keeping the first result.
1329 ; CHECK-NOT: %29 = OpCompositeConstruct %v4float %float_0_5 %float_0_5 %float_0 %float_1
1330 %29 = OpCompositeConstruct %v4float %28 %float_0_5 %float_0 %float_1
1331
1332 ; CHECK-NOT: OpCopyObject %v4float [[new_constant]]
1333 %42 = OpCopyObject %v4float %29
1334
1335 ; CHECK-NOT: OpStore %outColor [[new_constant]]
1336 OpStore %outColor %42
1337
1338 OpReturn
1339 OpFunctionEnd
1340 )";
1341
1342 auto result = SinglePassRunAndMatch<CCPPass>(text, true);
1343 EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
1344 }
1345
1346 } // namespace
1347 } // namespace opt
1348 } // namespace spvtools
1349