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