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