| // Copyright (c) 2017 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <string> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "source/opt/ccp_pass.h" |
| #include "test/opt/pass_fixture.h" |
| #include "test/opt/pass_utils.h" |
| |
| namespace spvtools { |
| namespace opt { |
| namespace { |
| |
| using CCPTest = PassTest<::testing::Test>; |
| |
| TEST_F(CCPTest, PropagateThroughPhis) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %x %outparm |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| OpName %x "x" |
| OpName %outparm "outparm" |
| OpDecorate %x Flat |
| OpDecorate %x Location 0 |
| OpDecorate %outparm Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %bool = OpTypeBool |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_4 = OpConstant %int 4 |
| %int_3 = OpConstant %int 3 |
| %int_1 = OpConstant %int 1 |
| %_ptr_Input_int = OpTypePointer Input %int |
| %x = OpVariable %_ptr_Input_int Input |
| %_ptr_Output_int = OpTypePointer Output %int |
| %outparm = OpVariable %_ptr_Output_int Output |
| %main = OpFunction %void None %3 |
| %4 = OpLabel |
| %5 = OpLoad %int %x |
| %9 = OpIAdd %int %int_1 %int_3 |
| %6 = OpSGreaterThan %bool %5 %int_3 |
| OpSelectionMerge %25 None |
| OpBranchConditional %6 %22 %23 |
| %22 = OpLabel |
| |
| ; CHECK: OpCopyObject %int %int_4 |
| %7 = OpCopyObject %int %9 |
| |
| OpBranch %25 |
| %23 = OpLabel |
| %8 = OpCopyObject %int %int_4 |
| OpBranch %25 |
| %25 = OpLabel |
| |
| ; %int_4 should have propagated to both OpPhi operands. |
| ; CHECK: OpPhi %int %int_4 {{%\d+}} %int_4 {{%\d+}} |
| %35 = OpPhi %int %7 %22 %8 %23 |
| |
| ; This function always returns 4. DCE should get rid of everything else. |
| ; CHECK OpStore %outparm %int_4 |
| OpStore %outparm %35 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, SimplifyConditionals) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %outparm |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| OpName %outparm "outparm" |
| OpDecorate %outparm Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %bool = OpTypeBool |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_4 = OpConstant %int 4 |
| %int_3 = OpConstant %int 3 |
| %int_1 = OpConstant %int 1 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %outparm = OpVariable %_ptr_Output_int Output |
| %main = OpFunction %void None %3 |
| %4 = OpLabel |
| %9 = OpIAdd %int %int_4 %int_3 |
| %6 = OpSGreaterThan %bool %9 %int_3 |
| OpSelectionMerge %25 None |
| ; CHECK: OpBranchConditional %true [[bb_taken:%\d+]] [[bb_not_taken:%\d+]] |
| OpBranchConditional %6 %22 %23 |
| ; CHECK: [[bb_taken]] = OpLabel |
| %22 = OpLabel |
| ; CHECK: OpCopyObject %int %int_7 |
| %7 = OpCopyObject %int %9 |
| OpBranch %25 |
| ; CHECK: [[bb_not_taken]] = OpLabel |
| %23 = OpLabel |
| ; CHECK: [[id_not_evaluated:%\d+]] = OpCopyObject %int %int_4 |
| %8 = OpCopyObject %int %int_4 |
| OpBranch %25 |
| %25 = OpLabel |
| |
| ; %int_7 should have propagated to the first OpPhi operand. But the else branch |
| ; is not executable (conditional is always true), so no values should be |
| ; propagated there and the value of the OpPhi should always be %int_7. |
| ; CHECK: OpPhi %int %int_7 [[bb_taken]] [[id_not_evaluated]] [[bb_not_taken]] |
| %35 = OpPhi %int %7 %22 %8 %23 |
| |
| ; Only the true path of the conditional is ever executed. The output of this |
| ; function is always %int_7. |
| ; CHECK: OpStore %outparm %int_7 |
| OpStore %outparm %35 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, SimplifySwitches) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %outparm |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| OpName %outparm "outparm" |
| OpDecorate %outparm Location 0 |
| %void = OpTypeVoid |
| %6 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_23 = OpConstant %int 23 |
| %int_42 = OpConstant %int 42 |
| %int_14 = OpConstant %int 14 |
| %int_15 = OpConstant %int 15 |
| %int_4 = OpConstant %int 4 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %outparm = OpVariable %_ptr_Output_int Output |
| %main = OpFunction %void None %6 |
| %15 = OpLabel |
| OpSelectionMerge %17 None |
| OpSwitch %int_23 %17 10 %18 13 %19 23 %20 |
| %18 = OpLabel |
| OpBranch %17 |
| %19 = OpLabel |
| OpBranch %17 |
| %20 = OpLabel |
| OpBranch %17 |
| %17 = OpLabel |
| %24 = OpPhi %int %int_23 %15 %int_42 %18 %int_14 %19 %int_15 %20 |
| |
| ; The switch will always jump to label %20, which carries the value %int_15. |
| ; CHECK: OpIAdd %int %int_15 %int_4 |
| %22 = OpIAdd %int %24 %int_4 |
| |
| ; Consequently, the return value will always be %int_19. |
| ; CHECK: OpStore %outparm %int_19 |
| OpStore %outparm %22 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, SimplifySwitchesDefaultBranch) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %outparm |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| OpName %outparm "outparm" |
| OpDecorate %outparm Location 0 |
| %void = OpTypeVoid |
| %6 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_42 = OpConstant %int 42 |
| %int_4 = OpConstant %int 4 |
| %int_1 = OpConstant %int 1 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %outparm = OpVariable %_ptr_Output_int Output |
| %main = OpFunction %void None %6 |
| %13 = OpLabel |
| %15 = OpIAdd %int %int_42 %int_4 |
| OpSelectionMerge %16 None |
| |
| ; CHECK: OpSwitch %int_46 {{%\d+}} 10 {{%\d+}} |
| OpSwitch %15 %17 10 %18 |
| %18 = OpLabel |
| OpBranch %16 |
| %17 = OpLabel |
| OpBranch %16 |
| %16 = OpLabel |
| %22 = OpPhi %int %int_42 %18 %int_1 %17 |
| |
| ; The switch will always jump to the default label %17. This carries the value |
| ; %int_1. |
| ; CHECK: OpIAdd %int %int_1 %int_4 |
| %20 = OpIAdd %int %22 %int_4 |
| |
| ; Resulting in a return value of %int_5. |
| ; CHECK: OpStore %outparm %int_5 |
| OpStore %outparm %20 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, SimplifyIntVector) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %OutColor |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| OpName %v "v" |
| OpName %OutColor "OutColor" |
| OpDecorate %OutColor Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %v4int = OpTypeVector %int 4 |
| %_ptr_Function_v4int = OpTypePointer Function %v4int |
| %int_1 = OpConstant %int 1 |
| %int_2 = OpConstant %int 2 |
| %int_3 = OpConstant %int 3 |
| %int_4 = OpConstant %int 4 |
| %14 = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4 |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %_ptr_Output_v4int = OpTypePointer Output %v4int |
| %OutColor = OpVariable %_ptr_Output_v4int Output |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %v = OpVariable %_ptr_Function_v4int Function |
| OpStore %v %14 |
| %18 = OpAccessChain %_ptr_Function_int %v %uint_0 |
| %19 = OpLoad %int %18 |
| |
| ; The constant folder does not see through access chains. To get this, the |
| ; vector would have to be scalarized. |
| ; CHECK: [[result_id:%\d+]] = OpIAdd %int {{%\d+}} %int_1 |
| %20 = OpIAdd %int %19 %int_1 |
| %21 = OpAccessChain %_ptr_Function_int %v %uint_0 |
| |
| ; CHECK: OpStore {{%\d+}} [[result_id]] |
| OpStore %21 %20 |
| %24 = OpLoad %v4int %v |
| OpStore %OutColor %24 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, BadSimplifyFloatVector) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %OutColor |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| OpName %v "v" |
| OpName %OutColor "OutColor" |
| OpDecorate %OutColor Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Function_v4float = OpTypePointer Function %v4float |
| %float_1 = OpConstant %float 1 |
| %float_2 = OpConstant %float 2 |
| %float_3 = OpConstant %float 3 |
| %float_4 = OpConstant %float 4 |
| %14 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %_ptr_Function_float = OpTypePointer Function %float |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %OutColor = OpVariable %_ptr_Output_v4float Output |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %v = OpVariable %_ptr_Function_v4float Function |
| OpStore %v %14 |
| %18 = OpAccessChain %_ptr_Function_float %v %uint_0 |
| %19 = OpLoad %float %18 |
| |
| ; NOTE: This test should start failing once floating point folding is |
| ; implemented (https://github.com/KhronosGroup/SPIRV-Tools/issues/943). |
| ; This should be checking that we are adding %float_1 + %float_1. |
| ; CHECK: [[result_id:%\d+]] = OpFAdd %float {{%\d+}} %float_1 |
| %20 = OpFAdd %float %19 %float_1 |
| %21 = OpAccessChain %_ptr_Function_float %v %uint_0 |
| |
| ; This should be checkint that we are storing %float_2 instead of result_it. |
| ; CHECK: OpStore {{%\d+}} [[result_id]] |
| OpStore %21 %20 |
| %24 = OpLoad %v4float %v |
| OpStore %OutColor %24 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, NoLoadStorePropagation) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %outparm |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| OpName %x "x" |
| OpName %outparm "outparm" |
| OpDecorate %outparm Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_23 = OpConstant %int 23 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %outparm = OpVariable %_ptr_Output_int Output |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %x = OpVariable %_ptr_Function_int Function |
| OpStore %x %int_23 |
| |
| ; int_23 should not propagate into this load. |
| ; CHECK: [[load_id:%\d+]] = OpLoad %int %x |
| %12 = OpLoad %int %x |
| |
| ; Nor into this copy operation. |
| ; CHECK: [[copy_id:%\d+]] = OpCopyObject %int [[load_id]] |
| %13 = OpCopyObject %int %12 |
| |
| ; Likewise here. |
| ; CHECK: OpStore %outparm [[copy_id]] |
| OpStore %outparm %13 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, HandleAbortInstructions) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpSource HLSL 500 |
| OpName %main "main" |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %bool = OpTypeBool |
| ; CHECK: %true = OpConstantTrue %bool |
| %int_3 = OpConstant %int 3 |
| %int_1 = OpConstant %int 1 |
| %main = OpFunction %void None %3 |
| %4 = OpLabel |
| %9 = OpIAdd %int %int_3 %int_1 |
| %6 = OpSGreaterThan %bool %9 %int_3 |
| OpSelectionMerge %23 None |
| ; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}} |
| OpBranchConditional %6 %22 %23 |
| %22 = OpLabel |
| OpKill |
| %23 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, SSAWebCycles) { |
| // Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1159 |
| // When there is a cycle in the SSA def-use web, the propagator was getting |
| // into an infinite loop. SSA edges for Phi instructions should not be |
| // added to the edges to simulate. |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_0 = OpConstant %int 0 |
| %int_4 = OpConstant %int 4 |
| %bool = OpTypeBool |
| %int_1 = OpConstant %int 1 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| OpBranch %11 |
| %11 = OpLabel |
| %29 = OpPhi %int %int_0 %5 %22 %14 |
| %30 = OpPhi %int %int_0 %5 %25 %14 |
| OpLoopMerge %13 %14 None |
| OpBranch %15 |
| %15 = OpLabel |
| %19 = OpSLessThan %bool %30 %int_4 |
| ; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}} |
| OpBranchConditional %19 %12 %13 |
| %12 = OpLabel |
| ; CHECK: OpIAdd %int %int_0 %int_0 |
| %22 = OpIAdd %int %29 %30 |
| OpBranch %14 |
| %14 = OpLabel |
| ; CHECK: OpPhi %int %int_0 {{%\d+}} |
| %25 = OpPhi %int %30 %12 |
| OpBranch %11 |
| %13 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, LoopInductionVariables) { |
| // Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1143 |
| // We are failing to properly consider the induction variable for this loop |
| // as Varying. |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 430 |
| OpName %main "main" |
| %void = OpTypeVoid |
| %5 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_0 = OpConstant %int 0 |
| %int_10 = OpConstant %int 10 |
| %bool = OpTypeBool |
| %int_1 = OpConstant %int 1 |
| %main = OpFunction %void None %5 |
| %12 = OpLabel |
| OpBranch %13 |
| %13 = OpLabel |
| |
| ; This Phi should not have all constant arguments: |
| ; CHECK: [[phi_id:%\d+]] = OpPhi %int %int_0 {{%\d+}} {{%\d+}} {{%\d+}} |
| %22 = OpPhi %int %int_0 %12 %21 %15 |
| OpLoopMerge %14 %15 None |
| OpBranch %16 |
| %16 = OpLabel |
| |
| ; The Phi should never be considered to have the value %int_0. |
| ; CHECK: [[branch_selector:%\d+]] = OpSLessThan %bool [[phi_id]] %int_10 |
| %18 = OpSLessThan %bool %22 %int_10 |
| |
| ; This conditional was wrongly converted into an always-true jump due to the |
| ; bad meet evaluation of %22. |
| ; CHECK: OpBranchConditional [[branch_selector]] {{%\d+}} {{%\d+}} |
| OpBranchConditional %18 %19 %14 |
| %19 = OpLabel |
| OpBranch %15 |
| %15 = OpLabel |
| ; CHECK: OpIAdd %int [[phi_id]] %int_1 |
| %21 = OpIAdd %int %22 %int_1 |
| OpBranch %13 |
| %14 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| } |
| |
| TEST_F(CCPTest, HandleCompositeWithUndef) { |
| // Check to make sure that CCP does not crash when given a "constant" struct |
| // with an undef. If at a later time CCP is enhanced to optimize this case, |
| // it is not wrong. |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpSource HLSL 500 |
| OpName %main "main" |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %bool = OpTypeBool |
| %_struct_7 = OpTypeStruct %int %int |
| %int_1 = OpConstant %int 1 |
| %9 = OpUndef %int |
| %10 = OpConstantComposite %_struct_7 %int_1 %9 |
| %main = OpFunction %void None %4 |
| %11 = OpLabel |
| %12 = OpCompositeExtract %int %10 0 |
| %13 = OpCopyObject %int %12 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto res = SinglePassRunToBinary<CCPPass>(spv_asm, true); |
| EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange); |
| } |
| |
| TEST_F(CCPTest, SkipSpecConstantInstrucitons) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpSource HLSL 500 |
| OpName %main "main" |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %bool = OpTypeBool |
| %10 = OpSpecConstantFalse %bool |
| %main = OpFunction %void None %4 |
| %11 = OpLabel |
| OpBranchConditional %10 %L1 %L2 |
| %L1 = OpLabel |
| OpReturn |
| %L2 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto res = SinglePassRunToBinary<CCPPass>(spv_asm, true); |
| EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange); |
| } |
| |
| TEST_F(CCPTest, FoldConstantCompositeInstrucitonsWithSpecConst) { |
| const std::string spv_asm = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "main" |
| OpExecutionMode %1 OriginUpperLeft |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %bool = OpTypeBool |
| %v3bool = OpTypeVector %bool 3 |
| %_struct_8 = OpTypeStruct %v3bool |
| %true = OpConstantTrue %bool |
| ; CHECK: [[spec_const:%\w+]] = OpSpecConstantComposite %v3bool |
| %11 = OpSpecConstantComposite %v3bool %true %true %true |
| %12 = OpConstantComposite %_struct_8 %11 |
| ; CHECK: OpFunction |
| %1 = OpFunction %void None %4 |
| %29 = OpLabel |
| %31 = OpCompositeExtract %v3bool %12 0 |
| ; CHECK: OpCompositeExtract %bool [[spec_const]] 0 |
| %32 = OpCompositeExtract %bool %31 0 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndMatch<CCPPass>(spv_asm, true); |
| EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); |
| } |
| |
| TEST_F(CCPTest, UpdateSubsequentPhisToVarying) { |
| const std::string text = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" %in |
| OpExecutionMode %func OriginUpperLeft |
| %void = OpTypeVoid |
| %bool = OpTypeBool |
| %int = OpTypeInt 32 1 |
| %false = OpConstantFalse %bool |
| %int0 = OpConstant %int 0 |
| %int1 = OpConstant %int 1 |
| %int6 = OpConstant %int 6 |
| %int_ptr_Input = OpTypePointer Input %int |
| %in = OpVariable %int_ptr_Input Input |
| %undef = OpUndef %int |
| |
| ; Although no constants are propagated in this function, the propagator |
| ; generates a new %true value while visiting conditional statements. |
| ; CHECK: %true = OpConstantTrue %bool |
| |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpBranch %2 |
| %2 = OpLabel |
| %outer_phi = OpPhi %int %int0 %1 %outer_add %15 |
| %cond1 = OpSLessThanEqual %bool %outer_phi %int6 |
| OpLoopMerge %3 %15 None |
| OpBranchConditional %cond1 %4 %3 |
| %4 = OpLabel |
| %ld = OpLoad %int %in |
| %cond2 = OpSGreaterThanEqual %bool %int1 %ld |
| OpSelectionMerge %10 None |
| OpBranchConditional %cond2 %8 %9 |
| %8 = OpLabel |
| OpBranch %10 |
| %9 = OpLabel |
| OpBranch %10 |
| %10 = OpLabel |
| %extra_phi = OpPhi %int %outer_phi %8 %outer_phi %9 |
| OpBranch %11 |
| %11 = OpLabel |
| %inner_phi = OpPhi %int %int0 %10 %inner_add %13 |
| %cond3 = OpSLessThanEqual %bool %inner_phi %int6 |
| OpLoopMerge %14 %13 None |
| OpBranchConditional %cond3 %12 %14 |
| %12 = OpLabel |
| OpBranch %13 |
| %13 = OpLabel |
| %inner_add = OpIAdd %int %inner_phi %int1 |
| OpBranch %11 |
| %14 = OpLabel |
| OpBranch %15 |
| %15 = OpLabel |
| %outer_add = OpIAdd %int %extra_phi %int1 |
| OpBranch %2 |
| %3 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndMatch<CCPPass>(text, true); |
| EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); |
| } |
| |
| TEST_F(CCPTest, UndefInPhi) { |
| const std::string text = R"( |
| ; CHECK: [[uint1:%\w+]] = OpConstant {{%\w+}} 1 |
| ; CHECK: [[phi:%\w+]] = OpPhi |
| ; CHECK: OpIAdd {{%\w+}} [[phi]] [[uint1]] |
| OpCapability Kernel |
| OpCapability Linkage |
| OpMemoryModel Logical OpenCL |
| OpDecorate %1 LinkageAttributes "func" Export |
| %void = OpTypeVoid |
| %bool = OpTypeBool |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %uint_1 = OpConstant %uint 1 |
| %7 = OpUndef %uint |
| %8 = OpTypeFunction %void %bool |
| %1 = OpFunction %void None %8 |
| %9 = OpFunctionParameter %bool |
| %10 = OpLabel |
| OpBranchConditional %9 %11 %12 |
| %11 = OpLabel |
| OpBranch %13 |
| %12 = OpLabel |
| OpBranch %14 |
| %14 = OpLabel |
| OpBranchConditional %9 %13 %15 |
| %15 = OpLabel |
| OpBranch %13 |
| %13 = OpLabel |
| %16 = OpPhi %uint %uint_0 %11 %7 %14 %uint_1 %15 |
| %17 = OpIAdd %uint %16 %uint_1 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| // Just test to make sure the constant fold rules are being used. Will rely on |
| // the folding test for specific testing of specific rules. |
| TEST_F(CCPTest, UseConstantFoldingRules) { |
| const std::string text = R"( |
| ; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1 |
| ; CHECK: OpReturnValue [[float1]] |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpDecorate %1 LinkageAttributes "func" Export |
| %void = OpTypeVoid |
| %bool = OpTypeBool |
| %float = OpTypeFloat 32 |
| %float_0 = OpConstant %float 0 |
| %float_1 = OpConstant %float 1 |
| %8 = OpTypeFunction %float |
| %1 = OpFunction %float None %8 |
| %10 = OpLabel |
| %17 = OpFAdd %float %float_0 %float_1 |
| OpReturnValue %17 |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| // Test for #1300. Previously value for %5 would not settle during simulation. |
| TEST_F(CCPTest, SettlePhiLatticeValue) { |
| const std::string text = R"( |
| OpCapability Kernel |
| OpCapability Linkage |
| OpMemoryModel Logical OpenCL |
| OpDecorate %func LinkageAttributes "func" Export |
| %void = OpTypeVoid |
| %bool = OpTypeBool |
| %true = OpConstantTrue %bool |
| %false = OpConstantFalse %bool |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpBranchConditional %true %2 %3 |
| %3 = OpLabel |
| OpBranch %2 |
| %2 = OpLabel |
| %5 = OpPhi %bool %true %1 %false %3 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SinglePassRunToBinary<CCPPass>(text, true); |
| } |
| |
| TEST_F(CCPTest, NullBranchCondition) { |
| const std::string text = R"( |
| ; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1 |
| ; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2 |
| ; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]] |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %void = OpTypeVoid |
| %bool = OpTypeBool |
| %int = OpTypeInt 32 1 |
| %null = OpConstantNull %bool |
| %int_1 = OpConstant %int 1 |
| %int_2 = OpConstant %int 2 |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpSelectionMerge %2 None |
| OpBranchConditional %null %2 %3 |
| %3 = OpLabel |
| OpBranch %2 |
| %2 = OpLabel |
| %phi = OpPhi %int %int_1 %1 %int_2 %3 |
| %add = OpIAdd %int %int_1 %phi |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| TEST_F(CCPTest, UndefBranchCondition) { |
| const std::string text = R"( |
| ; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1 |
| ; CHECK: [[phi:%\w+]] = OpPhi |
| ; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]] |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %void = OpTypeVoid |
| %bool = OpTypeBool |
| %int = OpTypeInt 32 1 |
| %undef = OpUndef %bool |
| %int_1 = OpConstant %int 1 |
| %int_2 = OpConstant %int 2 |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpSelectionMerge %2 None |
| OpBranchConditional %undef %2 %3 |
| %3 = OpLabel |
| OpBranch %2 |
| %2 = OpLabel |
| %phi = OpPhi %int %int_1 %1 %int_2 %3 |
| %add = OpIAdd %int %int_1 %phi |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| TEST_F(CCPTest, NullSwitchCondition) { |
| const std::string text = R"( |
| ; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1 |
| ; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2 |
| ; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]] |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 1 |
| %null = OpConstantNull %int |
| %int_1 = OpConstant %int 1 |
| %int_2 = OpConstant %int 2 |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpSelectionMerge %2 None |
| OpSwitch %null %2 0 %3 |
| %3 = OpLabel |
| OpBranch %2 |
| %2 = OpLabel |
| %phi = OpPhi %int %int_1 %1 %int_2 %3 |
| %add = OpIAdd %int %int_1 %phi |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| TEST_F(CCPTest, UndefSwitchCondition) { |
| const std::string text = R"( |
| ; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1 |
| ; CHECK: [[phi:%\w+]] = OpPhi |
| ; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]] |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" |
| OpExecutionMode %func OriginUpperLeft |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 1 |
| %undef = OpUndef %int |
| %int_1 = OpConstant %int 1 |
| %int_2 = OpConstant %int 2 |
| %functy = OpTypeFunction %void |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpSelectionMerge %2 None |
| OpSwitch %undef %2 0 %3 |
| %3 = OpLabel |
| OpBranch %2 |
| %2 = OpLabel |
| %phi = OpPhi %int %int_1 %1 %int_2 %3 |
| %add = OpIAdd %int %int_1 %phi |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| // Test for #1361. |
| TEST_F(CCPTest, CompositeConstructOfGlobalValue) { |
| const std::string text = R"( |
| ; CHECK: [[phi:%\w+]] = OpPhi |
| ; CHECK-NEXT: OpCompositeExtract {{%\w+}} [[phi]] 0 |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %func "func" %in |
| OpExecutionMode %func OriginUpperLeft |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 1 |
| %bool = OpTypeBool |
| %functy = OpTypeFunction %void |
| %ptr_int_Input = OpTypePointer Input %int |
| %in = OpVariable %ptr_int_Input Input |
| %struct = OpTypeStruct %ptr_int_Input %ptr_int_Input |
| %struct_null = OpConstantNull %struct |
| %func = OpFunction %void None %functy |
| %1 = OpLabel |
| OpBranch %2 |
| %2 = OpLabel |
| %phi = OpPhi %struct %struct_null %1 %5 %4 |
| %extract = OpCompositeExtract %ptr_int_Input %phi 0 |
| OpLoopMerge %3 %4 None |
| OpBranch %4 |
| %4 = OpLabel |
| %5 = OpCompositeConstruct %struct %in %in |
| OpBranch %2 |
| %3 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| TEST_F(CCPTest, FoldWithDecoration) { |
| const std::string text = R"( |
| ; CHECK: OpCapability |
| ; CHECK-NOT: OpDecorate |
| ; CHECK: OpFunctionEnd |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| OpDecorate %3 RelaxedPrecision |
| %void = OpTypeVoid |
| %5 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %float_0 = OpConstant %float 0 |
| %v4float = OpTypeVector %float 4 |
| %10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 |
| %2 = OpFunction %void None %5 |
| %11 = OpLabel |
| %3 = OpVectorShuffle %v3float %10 %10 0 1 2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| TEST_F(CCPTest, DebugSimpleFoldConstant) { |
| const std::string text = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| %ext = OpExtInstImport "OpenCL.DebugInfo.100" |
| OpMemoryModel Logical GLSL450 |
| %file_name = OpString "test" |
| %float_name = OpString "float" |
| %main_name = OpString "main" |
| %f_name = OpString "f" |
| OpDecorate %1 LinkageAttributes "func" Export |
| %void = OpTypeVoid |
| %bool = OpTypeBool |
| %float = OpTypeFloat 32 |
| %float_0 = OpConstant %float 0 |
| |
| ; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1 |
| %float_1 = OpConstant %float 1 |
| %uint = OpTypeInt 32 0 |
| %uint_32 = OpConstant %uint 32 |
| %8 = OpTypeFunction %float |
| %null_expr = OpExtInst %void %ext DebugExpression |
| %src = OpExtInst %void %ext DebugSource %file_name |
| %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL |
| %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float |
| %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf |
| %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %1 |
| %dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal |
| %1 = OpFunction %float None %8 |
| %10 = OpLabel |
| |
| ; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope |
| ; CHECK: OpLine [[file:%\w+]] 1 0 |
| ; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %float_1 |
| %s0 = OpExtInst %void %ext DebugScope %dbg_main |
| OpLine %file_name 1 0 |
| %17 = OpFAdd %float %float_0 %float_1 |
| %val = OpExtInst %void %ext DebugValue %dbg_f %17 %null_expr |
| |
| ; CHECK: OpLine [[file]] 2 0 |
| ; CHECK: OpReturnValue [[float1]] |
| OpLine %file_name 2 0 |
| OpReturnValue %17 |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| TEST_F(CCPTest, DebugFoldMultipleForSingleConstant) { |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| %ext = OpExtInstImport "OpenCL.DebugInfo.100" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %outparm |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| %file_name = OpString "test" |
| %float_name = OpString "float" |
| %main_name = OpString "main" |
| %f_name = OpString "f" |
| OpName %main "main" |
| OpName %outparm "outparm" |
| OpDecorate %outparm Location 0 |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %bool = OpTypeBool |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_4 = OpConstant %int 4 |
| %int_3 = OpConstant %int 3 |
| %int_1 = OpConstant %int 1 |
| %uint = OpTypeInt 32 0 |
| %uint_32 = OpConstant %uint 32 |
| %_ptr_Output_int = OpTypePointer Output %int |
| %outparm = OpVariable %_ptr_Output_int Output |
| %null_expr = OpExtInst %void %ext DebugExpression |
| %src = OpExtInst %void %ext DebugSource %file_name |
| %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL |
| %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float |
| %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf |
| %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main |
| %bb0 = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main |
| %bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main |
| %bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main |
| %bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main |
| %dbg_f0 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal |
| %dbg_f1 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 1 0 %dbg_main FlagIsLocal |
| %dbg_f2 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 2 0 %dbg_main FlagIsLocal |
| %main = OpFunction %void None %3 |
| %4 = OpLabel |
| |
| ; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope |
| ; CHECK: OpLine [[file:%\w+]] 1 0 |
| ; CHECK: OpIAdd %int %int_4 %int_3 |
| ; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %int_7 |
| %s0 = OpExtInst %void %ext DebugScope %bb0 |
| OpLine %file_name 1 0 |
| %9 = OpIAdd %int %int_4 %int_3 |
| %val0 = OpExtInst %void %ext DebugValue %dbg_f0 %9 %null_expr |
| |
| ; CHECK: OpLine [[file]] 2 0 |
| ; CHECK: OpSGreaterThan %bool %int_7 %int_3 |
| ; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %true |
| OpLine %file_name 2 0 |
| %6 = OpSGreaterThan %bool %9 %int_3 |
| %val1 = OpExtInst %void %ext DebugValue %dbg_f1 %6 %null_expr |
| |
| OpSelectionMerge %25 None |
| OpBranchConditional %6 %22 %23 |
| %22 = OpLabel |
| %s1 = OpExtInst %void %ext DebugScope %bb1 |
| %7 = OpCopyObject %int %9 |
| %val2 = OpExtInst %void %ext DebugValue %dbg_f2 %7 %null_expr |
| OpBranch %25 |
| %23 = OpLabel |
| %s2 = OpExtInst %void %ext DebugScope %bb2 |
| %8 = OpCopyObject %int %int_4 |
| OpBranch %25 |
| %25 = OpLabel |
| %s3 = OpExtInst %void %ext DebugScope %bb3 |
| %35 = OpPhi %int %7 %22 %8 %23 |
| OpStore %outparm %35 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<CCPPass>(text, true); |
| } |
| |
| // Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3636 |
| TEST_F(CCPTest, CCPNoChangeFailure) { |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" |
| OpExecutionMode %4 OriginUpperLeft |
| OpSource ESSL 320 |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeInt 32 1 |
| %7 = OpConstant %6 2 |
| %13 = OpConstant %6 4 |
| %21 = OpConstant %6 1 |
| %10 = OpTypeBool |
| %17 = OpTypePointer Function %6 |
| |
| ; CCP is generating two new constants during propagation that end up being |
| ; dead because they cannot be replaced anywhere in the IR. CCP was wrongly |
| ; considering the IR to be unmodified because of this. |
| ; CHECK: %true = OpConstantTrue %bool |
| ; CHECK: %int_3 = OpConstant %int 3 |
| |
| %4 = OpFunction %2 None %3 |
| %11 = OpLabel |
| OpBranch %5 |
| %5 = OpLabel |
| %23 = OpPhi %6 %7 %11 %20 %15 |
| %9 = OpSLessThan %10 %23 %13 |
| OpLoopMerge %8 %15 None |
| OpBranchConditional %9 %15 %8 |
| %15 = OpLabel |
| %20 = OpIAdd %6 %23 %21 |
| OpBranch %5 |
| %8 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndMatch<CCPPass>(text, true); |
| EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); |
| } |
| |
| // Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3738 |
| // Similar to the previous one but more than one constant is generated in a |
| // single call to the instruction folder. |
| TEST_F(CCPTest, CCPNoChangeFailureSeveralConstantsDuringFolding) { |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %bool = OpTypeBool |
| %v3bool = OpTypeVector %bool 3 |
| %float_0 = OpConstant %float 0 |
| %12 = OpConstantComposite %v3float %float_0 %float_0 %float_0 |
| %float_0_300000012 = OpConstant %float 0.300000012 |
| %14 = OpConstantComposite %v3float %float_0_300000012 %float_0_300000012 %float_0_300000012 |
| |
| ; CCP is generating several constants during a single instruction evaluation. |
| ; When folding %19, it generates the constants %true and %24. They are dead |
| ; because they cannot be replaced anywhere in the IR. CCP was wrongly |
| ; considering the IR to be unmodified because of this. |
| ; |
| ; CHECK: %true = OpConstantTrue %bool |
| ; CHECK: %24 = OpConstantComposite %v3bool %true %true %true |
| ; CHECK: %float_1 = OpConstant %float 1 |
| ; CHECK: %float_0_699999988 = OpConstant %float 0.699999988 |
| |
| %2 = OpFunction %void None %4 |
| %15 = OpLabel |
| OpBranch %16 |
| %16 = OpLabel |
| %17 = OpPhi %v3float %12 %15 %14 %18 |
| %19 = OpFOrdLessThan %v3bool %17 %14 |
| %20 = OpAll %bool %19 |
| OpLoopMerge %21 %18 None |
| OpBranchConditional %20 %18 %21 |
| %18 = OpLabel |
| OpBranch %16 |
| %21 = OpLabel |
| %22 = OpExtInst %v3float %1 FMix %12 %17 %14 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndMatch<CCPPass>(text, true); |
| EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); |
| } |
| |
| // Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3991 |
| // Similar to the previous one but constants are created even when no |
| // instruction are ever folded during propagation. |
| TEST_F(CCPTest, CCPNoChangeFailureWithUnfoldableInstr) { |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v3float = OpTypeVector %float 3 |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %bool = OpTypeBool |
| %float_0 = OpConstant %float 0 |
| %11 = OpConstantComposite %v3float %float_0 %float_0 %float_0 |
| %float_0_300000012 = OpConstant %float 0.300000012 |
| %13 = OpConstantComposite %v3float %float_0_300000012 %float_0_300000012 %float_0_300000012 |
| |
| ; CCP generates two constants when trying to fold an instruction, which it |
| ; ultimately fails to fold. The instruction folder in CCP was only |
| ; checking for newly added constants if the instruction folds successfully. |
| ; |
| ; CHECK: %float_1 = OpConstant %float 1 |
| ; CHECK: %float_0_699999988 = OpConstant %float 0.69999998 |
| |
| %2 = OpFunction %void None %4 |
| %14 = OpLabel |
| %15 = OpBitcast %uint %float_0_300000012 |
| %16 = OpUGreaterThan %bool %15 %uint_0 |
| OpBranch %17 |
| %17 = OpLabel |
| %18 = OpPhi %v3float %11 %14 %13 %19 |
| OpLoopMerge %20 %19 None |
| OpBranchConditional %16 %19 %20 |
| %19 = OpLabel |
| OpBranch %17 |
| %20 = OpLabel |
| %21 = OpExtInst %v3float %1 FMix %11 %18 %13 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndMatch<CCPPass>(text, true); |
| EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); |
| } |
| |
| TEST_F(CCPTest, FunctionDeclaration) { |
| // Make sure the pass works with a function declaration that is called. |
| const std::string text = R"(OpCapability Addresses |
| OpCapability Linkage |
| OpCapability Kernel |
| OpCapability Int8 |
| %1 = OpExtInstImport "OpenCL.std" |
| OpMemoryModel Physical64 OpenCL |
| OpEntryPoint Kernel %2 "_Z23julia__1166_kernel_77094Bool" |
| OpExecutionMode %2 ContractionOff |
| OpSource Unknown 0 |
| OpDecorate %3 LinkageAttributes "julia_error_7712" Import |
| %void = OpTypeVoid |
| %5 = OpTypeFunction %void |
| %3 = OpFunction %void None %5 |
| OpFunctionEnd |
| %2 = OpFunction %void None %5 |
| %6 = OpLabel |
| %7 = OpFunctionCall %void %3 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndCheck<CCPPass>(text, text, false); |
| } |
| |
| // Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/4462. |
| // The test was causing a lateral movement in the constant lattice, which was |
| // not being detected as varying by CCP. In this test, FClamp is evaluated |
| // twice. On the first evaluation, if computes FClamp(0.5, 0.5, -1) which |
| // returns -1. On the second evaluation, it computes FClamp(0.5, 0.5, VARYING) |
| // which returns 0.5. |
| // |
| // Both fold() computations are correct given the semantics of FClamp() but |
| // this causes a lateral transition in the constant lattice which was not being |
| // considered VARYING by CCP. |
| TEST_F(CCPTest, LateralLatticeTransition) { |
| const std::string text = R"(OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %gl_FragCoord %outColor |
| OpExecutionMode %main OriginUpperLeft |
| OpSource ESSL 310 |
| OpName %main "main" |
| OpName %gl_FragCoord "gl_FragCoord" |
| OpName %outColor "outColor" |
| OpDecorate %gl_FragCoord BuiltIn FragCoord |
| OpDecorate %outColor Location 0 |
| %void = OpTypeVoid |
| %6 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %float_0_5 = OpConstant %float 0.5 |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Input_v4float = OpTypePointer Input %v4float |
| %gl_FragCoord = OpVariable %_ptr_Input_v4float Input |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %_ptr_Input_float = OpTypePointer Input %float |
| %float_0 = OpConstant %float 0 |
| %bool = OpTypeBool |
| %float_n1 = OpConstant %float -1 |
| %float_1 = OpConstant %float 1 |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %outColor = OpVariable %_ptr_Output_v4float Output |
| |
| ; This constant is created during the first evaluation of the CompositeConstruct |
| ; CHECK: [[new_constant:%\d+]] = OpConstantComposite %v4float %float_n1 %float_0_5 %float_0 %float_1 |
| |
| %main = OpFunction %void None %6 |
| %19 = OpLabel |
| %20 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 |
| %21 = OpLoad %float %20 |
| %22 = OpFOrdLessThan %bool %21 %float_0 |
| OpSelectionMerge %23 None |
| OpBranchConditional %22 %24 %25 |
| %24 = OpLabel |
| OpBranch %23 |
| %25 = OpLabel |
| OpBranch %26 |
| %26 = OpLabel |
| OpBranch %23 |
| %23 = OpLabel |
| %27 = OpPhi %float %float_n1 %24 %float_0_5 %26 |
| %28 = OpExtInst %float %1 FClamp %float_0_5 %float_0_5 %27 |
| |
| ; On first evaluation, the result from FClamp will return 0.5. |
| ; But on second evaluation, FClamp should return VARYING. Check |
| ; that CCP is not keeping the first result. |
| ; CHECK-NOT: %29 = OpCompositeConstruct %v4float %float_0_5 %float_0_5 %float_0 %float_1 |
| %29 = OpCompositeConstruct %v4float %28 %float_0_5 %float_0 %float_1 |
| |
| ; CHECK-NOT: OpCopyObject %v4float [[new_constant]] |
| %42 = OpCopyObject %v4float %29 |
| |
| ; CHECK-NOT: OpStore %outColor [[new_constant]] |
| OpStore %outColor %42 |
| |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndMatch<CCPPass>(text, true); |
| EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); |
| } |
| |
| } // namespace |
| } // namespace opt |
| } // namespace spvtools |