| // 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 "source/opt/build_module.h" |
| #include "source/opt/value_number_table.h" |
| #include "test/opt/assembly_builder.h" |
| #include "test/opt/pass_fixture.h" |
| #include "test/opt/pass_utils.h" |
| |
| namespace spvtools { |
| namespace opt { |
| namespace { |
| |
| using ::testing::HasSubstr; |
| using ::testing::MatchesRegex; |
| using PrivateToLocalTest = PassTest<::testing::Test>; |
| |
| TEST_F(PrivateToLocalTest, ChangeToLocal) { |
| // Change the private variable to a local, and change the types accordingly. |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource GLSL 430 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| ; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32 |
| %5 = OpTypeFloat 32 |
| ; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] |
| %6 = OpTypePointer Private %5 |
| ; CHECK-NOT: OpVariable [[.+]] Private |
| %8 = OpVariable %6 Private |
| ; CHECK: OpFunction |
| %2 = OpFunction %3 None %4 |
| ; CHECK: OpLabel |
| %7 = OpLabel |
| ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function |
| ; CHECK: OpLoad [[float]] [[newvar]] |
| %9 = OpLoad %5 %8 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, false); |
| } |
| |
| TEST_F(PrivateToLocalTest, ReuseExistingType) { |
| // Change the private variable to a local, and change the types accordingly. |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource GLSL 430 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| ; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32 |
| %5 = OpTypeFloat 32 |
| %func_ptr = OpTypePointer Function %5 |
| ; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] |
| ; CHECK-NOT: [[%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] |
| %6 = OpTypePointer Private %5 |
| ; CHECK-NOT: OpVariable [[.+]] Private |
| %8 = OpVariable %6 Private |
| ; CHECK: OpFunction |
| %2 = OpFunction %3 None %4 |
| ; CHECK: OpLabel |
| %7 = OpLabel |
| ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function |
| ; CHECK: OpLoad [[float]] [[newvar]] |
| %9 = OpLoad %5 %8 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, false); |
| } |
| |
| TEST_F(PrivateToLocalTest, UpdateAccessChain) { |
| // Change the private variable to a local, and change the AccessChain. |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource GLSL 430 |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %void = OpTypeVoid |
| %6 = OpTypeFunction %void |
| ; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat |
| %float = OpTypeFloat 32 |
| ; CHECK: [[struct:%[a-zA-Z_\d]+]] = OpTypeStruct |
| %_struct_8 = OpTypeStruct %float |
| %_ptr_Private_float = OpTypePointer Private %float |
| ; CHECK: [[new_struct_type:%[a-zA-Z_\d]+]] = OpTypePointer Function [[struct]] |
| ; CHECK: [[new_float_type:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] |
| %_ptr_Private__struct_8 = OpTypePointer Private %_struct_8 |
| ; CHECK-NOT: OpVariable [[.+]] Private |
| %11 = OpVariable %_ptr_Private__struct_8 Private |
| ; CHECK: OpFunction |
| %2 = OpFunction %void None %6 |
| ; CHECK: OpLabel |
| %12 = OpLabel |
| ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[new_struct_type]] Function |
| ; CHECK: [[member:%[a-zA-Z_\d]+]] = OpAccessChain [[new_float_type]] [[newvar]] |
| %13 = OpAccessChain %_ptr_Private_float %11 %uint_0 |
| ; CHECK: OpLoad [[float]] [[member]] |
| %14 = OpLoad %float %13 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, false); |
| } |
| |
| TEST_F(PrivateToLocalTest, UseTexelPointer) { |
| // Change the private variable to a local, and change the OpImageTexelPointer. |
| const std::string text = R"( |
| OpCapability SampledBuffer |
| OpCapability StorageImageExtendedFormats |
| OpCapability ImageBuffer |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID |
| OpExecutionMode %2 LocalSize 64 1 1 |
| OpSource HLSL 600 |
| OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId |
| OpDecorate %4 DescriptorSet 4 |
| OpDecorate %4 Binding 70 |
| %uint = OpTypeInt 32 0 |
| %6 = OpTypeImage %uint Buffer 0 0 0 2 R32ui |
| %_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6 |
| %_ptr_Private_6 = OpTypePointer Private %6 |
| %void = OpTypeVoid |
| %10 = OpTypeFunction %void |
| %uint_0 = OpConstant %uint 0 |
| %uint_1 = OpConstant %uint 1 |
| %v3uint = OpTypeVector %uint 3 |
| %_ptr_Input_v3uint = OpTypePointer Input %v3uint |
| %_ptr_Image_uint = OpTypePointer Image %uint |
| %4 = OpVariable %_ptr_UniformConstant_6 UniformConstant |
| %16 = OpVariable %_ptr_Private_6 Private |
| %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input |
| %2 = OpFunction %void None %10 |
| %17 = OpLabel |
| ; Make sure the variable was moved. |
| ; CHECK: OpFunction |
| ; CHECK-NEXT: OpLabel |
| ; CHECK-NEXT: OpVariable %_ptr_Function_6 Function |
| %18 = OpLoad %6 %4 |
| OpStore %16 %18 |
| %19 = OpImageTexelPointer %_ptr_Image_uint %16 %uint_0 %uint_0 |
| %20 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %uint_1 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, false); |
| } |
| |
| TEST_F(PrivateToLocalTest, UsedInTwoFunctions) { |
| // Should not change because it is used in multiple functions. |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource GLSL 430 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeFloat 32 |
| %6 = OpTypePointer Private %5 |
| %8 = OpVariable %6 Private |
| %2 = OpFunction %3 None %4 |
| %7 = OpLabel |
| %9 = OpLoad %5 %8 |
| OpReturn |
| OpFunctionEnd |
| %10 = OpFunction %3 None %4 |
| %11 = OpLabel |
| %12 = OpLoad %5 %8 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto result = SinglePassRunAndDisassemble<StrengthReductionPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ false); |
| EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(PrivateToLocalTest, UsedInFunctionCall) { |
| // Should not change because it is used in a function call. Changing the |
| // signature of the function would require cloning the function, which is not |
| // worth it. |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource GLSL 430 |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %_ptr_Private_float = OpTypePointer Private %float |
| %7 = OpTypeFunction %void %_ptr_Private_float |
| %8 = OpVariable %_ptr_Private_float Private |
| %2 = OpFunction %void None %4 |
| %9 = OpLabel |
| %10 = OpFunctionCall %void %11 %8 |
| OpReturn |
| OpFunctionEnd |
| %11 = OpFunction %void None %7 |
| %12 = OpFunctionParameter %_ptr_Private_float |
| %13 = OpLabel |
| %14 = OpLoad %float %12 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto result = SinglePassRunAndDisassemble<StrengthReductionPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ false); |
| EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(PrivateToLocalTest, CreatePointerToAmbiguousStruct1) { |
| // Test that the correct pointer type is picked up. |
| const std::string text = R"( |
| ; CHECK: [[struct1:%[a-zA-Z_\d]+]] = OpTypeStruct |
| ; CHECK: [[struct2:%[a-zA-Z_\d]+]] = OpTypeStruct |
| ; CHECK: [[priv_ptr:%[\w]+]] = OpTypePointer Private [[struct1]] |
| ; CHECK: [[fuct_ptr2:%[\w]+]] = OpTypePointer Function [[struct2]] |
| ; CHECK: [[fuct_ptr1:%[\w]+]] = OpTypePointer Function [[struct1]] |
| ; CHECK: OpFunction |
| ; CHECK: OpLabel |
| ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[fuct_ptr1]] Function |
| ; CHECK: OpLoad [[struct1]] [[newvar]] |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource GLSL 430 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeFloat 32 |
| %struct1 = OpTypeStruct %5 |
| %struct2 = OpTypeStruct %5 |
| %6 = OpTypePointer Private %struct1 |
| %func_ptr2 = OpTypePointer Function %struct2 |
| %8 = OpVariable %6 Private |
| %2 = OpFunction %3 None %4 |
| %7 = OpLabel |
| %9 = OpLoad %struct1 %8 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, false); |
| } |
| |
| TEST_F(PrivateToLocalTest, CreatePointerToAmbiguousStruct2) { |
| // Test that the correct pointer type is picked up. |
| const std::string text = R"( |
| ; CHECK: [[struct1:%[a-zA-Z_\d]+]] = OpTypeStruct |
| ; CHECK: [[struct2:%[a-zA-Z_\d]+]] = OpTypeStruct |
| ; CHECK: [[priv_ptr:%[\w]+]] = OpTypePointer Private [[struct2]] |
| ; CHECK: [[fuct_ptr1:%[\w]+]] = OpTypePointer Function [[struct1]] |
| ; CHECK: [[fuct_ptr2:%[\w]+]] = OpTypePointer Function [[struct2]] |
| ; CHECK: OpFunction |
| ; CHECK: OpLabel |
| ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[fuct_ptr2]] Function |
| ; CHECK: OpLoad [[struct2]] [[newvar]] |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource GLSL 430 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeFloat 32 |
| %struct1 = OpTypeStruct %5 |
| %struct2 = OpTypeStruct %5 |
| %6 = OpTypePointer Private %struct2 |
| %func_ptr2 = OpTypePointer Function %struct1 |
| %8 = OpVariable %6 Private |
| %2 = OpFunction %3 None %4 |
| %7 = OpLabel |
| %9 = OpLoad %struct2 %8 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, false); |
| } |
| |
| TEST_F(PrivateToLocalTest, SPV14RemoveFromInterface) { |
| const std::string text = R"( |
| ; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv |
| ; CHECK: OpEntryPoint GLCompute %foo "foo" %in |
| ; CHECK: %priv = OpVariable {{%\w+}} Function |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %foo "foo" %in %priv |
| OpExecutionMode %foo LocalSize 1 1 1 |
| OpName %foo "foo" |
| OpName %in "in" |
| OpName %priv "priv" |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %ptr_ssbo_int = OpTypePointer StorageBuffer %int |
| %ptr_private_int = OpTypePointer Private %int |
| %in = OpVariable %ptr_ssbo_int StorageBuffer |
| %priv = OpVariable %ptr_private_int Private |
| %void_fn = OpTypeFunction %void |
| %foo = OpFunction %void None %void_fn |
| %entry = OpLabel |
| %ld = OpLoad %int %in |
| OpStore %priv %ld |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, true); |
| } |
| |
| TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleEntryPoints) { |
| const std::string text = R"( |
| ; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv |
| ; CHECK-NOT: OpEntryPoint GLCompute %foo "bar" %in %priv |
| ; CHECK: OpEntryPoint GLCompute %foo "foo" %in |
| ; CHECK: OpEntryPoint GLCompute %foo "bar" %in |
| ; CHECK: %priv = OpVariable {{%\w+}} Function |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %foo "foo" %in %priv |
| OpEntryPoint GLCompute %foo "bar" %in %priv |
| OpExecutionMode %foo LocalSize 1 1 1 |
| OpName %foo "foo" |
| OpName %in "in" |
| OpName %priv "priv" |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %ptr_ssbo_int = OpTypePointer StorageBuffer %int |
| %ptr_private_int = OpTypePointer Private %int |
| %in = OpVariable %ptr_ssbo_int StorageBuffer |
| %priv = OpVariable %ptr_private_int Private |
| %void_fn = OpTypeFunction %void |
| %foo = OpFunction %void None %void_fn |
| %entry = OpLabel |
| %ld = OpLoad %int %in |
| OpStore %priv %ld |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, true); |
| } |
| |
| TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleVariables) { |
| const std::string text = R"( |
| ; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2 |
| ; CHECK: OpEntryPoint GLCompute %foo "foo" %in |
| ; CHECK: %priv1 = OpVariable {{%\w+}} Function |
| ; CHECK: %priv2 = OpVariable {{%\w+}} Function |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2 |
| OpExecutionMode %foo LocalSize 1 1 1 |
| OpName %foo "foo" |
| OpName %in "in" |
| OpName %priv1 "priv1" |
| OpName %priv2 "priv2" |
| %void = OpTypeVoid |
| %int = OpTypeInt 32 0 |
| %ptr_ssbo_int = OpTypePointer StorageBuffer %int |
| %ptr_private_int = OpTypePointer Private %int |
| %in = OpVariable %ptr_ssbo_int StorageBuffer |
| %priv1 = OpVariable %ptr_private_int Private |
| %priv2 = OpVariable %ptr_private_int Private |
| %void_fn = OpTypeFunction %void |
| %foo = OpFunction %void None %void_fn |
| %entry = OpLabel |
| %1 = OpFunctionCall %void %bar1 |
| %2 = OpFunctionCall %void %bar2 |
| OpReturn |
| OpFunctionEnd |
| %bar1 = OpFunction %void None %void_fn |
| %3 = OpLabel |
| %ld1 = OpLoad %int %in |
| OpStore %priv1 %ld1 |
| OpReturn |
| OpFunctionEnd |
| %bar2 = OpFunction %void None %void_fn |
| %4 = OpLabel |
| %ld2 = OpLoad %int %in |
| OpStore %priv2 %ld2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, true); |
| } |
| |
| TEST_F(PrivateToLocalTest, IdBoundOverflow1) { |
| const std::string text = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" |
| OpExecutionMode %4 OriginLowerLeft |
| OpSource HLSL 84 |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeFloat 32 |
| %7 = OpTypeVector %6 4 |
| %8 = OpTypeStruct %7 |
| %4194302 = OpTypeStruct %8 %8 |
| %9 = OpTypeStruct %8 %8 |
| %11 = OpTypePointer Private %7 |
| %18 = OpTypeStruct %6 %9 |
| %12 = OpVariable %11 Private |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %13 = OpLoad %7 %12 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| |
| std::vector<Message> messages = { |
| {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; |
| SetMessageConsumer(GetTestMessageConsumer(messages)); |
| auto result = SinglePassRunToBinary<PrivateToLocalPass>(text, true); |
| EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); |
| } |
| |
| TEST_F(PrivateToLocalTest, DebugPrivateToLocal) { |
| // Debug instructions must not have any impact on changing the private |
| // variable to a local. |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| %10 = OpExtInstImport "OpenCL.DebugInfo.100" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| %11 = OpString "test" |
| OpSource GLSL 430 |
| %13 = OpTypeInt 32 0 |
| %14 = OpConstant %13 32 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| ; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32 |
| %5 = OpTypeFloat 32 |
| ; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] |
| %6 = OpTypePointer Private %5 |
| ; CHECK-NOT: OpVariable [[.+]] Private |
| %8 = OpVariable %6 Private |
| |
| %12 = OpExtInst %3 %10 DebugTypeBasic %11 %14 Float |
| %15 = OpExtInst %3 %10 DebugSource %11 |
| %16 = OpExtInst %3 %10 DebugCompilationUnit 1 4 %15 GLSL |
| ; CHECK-NOT: DebugGlobalVariable |
| ; CHECK: [[dbg_newvar:%[a-zA-Z_\d]+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable |
| %17 = OpExtInst %3 %10 DebugGlobalVariable %11 %12 %15 0 0 %16 %11 %8 FlagIsDefinition |
| |
| ; CHECK: OpFunction |
| %2 = OpFunction %3 None %4 |
| ; CHECK: OpLabel |
| %7 = OpLabel |
| ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function |
| ; CHECK-NEXT: DebugDeclare [[dbg_newvar]] [[newvar]] |
| ; CHECK: OpLoad [[float]] [[newvar]] |
| %9 = OpLoad %5 %8 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| SinglePassRunAndMatch<PrivateToLocalPass>(text, true); |
| } |
| |
| } // namespace |
| } // namespace opt |
| } // namespace spvtools |