| // Copyright (c) 2019-2022 Valve Corporation |
| // Copyright (c) 2019-2022 LunarG 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. |
| |
| // Bindless Check Instrumentation Tests. |
| // Tests ending with V2 use version 2 record format. |
| |
| #include <string> |
| #include <vector> |
| |
| #include "test/opt/assembly_builder.h" |
| #include "test/opt/pass_fixture.h" |
| #include "test/opt/pass_utils.h" |
| |
| namespace spvtools { |
| namespace opt { |
| namespace { |
| |
| static const std::string kOutputDecorations = R"( |
| ; CHECK: OpDecorate [[output_buffer_type:%inst_buff_addr_OutputBuffer]] Block |
| ; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0 |
| ; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4 |
| ; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7 |
| ; CHECK: OpDecorate [[output_buffer_var]] Binding 0 |
| )"; |
| |
| static const std::string kOutputGlobals = R"( |
| ; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %uint %_runtimearr_uint |
| ; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]] |
| ; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer |
| )"; |
| |
| static const std::string kStreamWrite4Begin = R"( |
| ; CHECK: {{%\w+}} = OpFunction %void None {{%\w+}} |
| ; CHECK: [[param_1:%\w+]] = OpFunctionParameter %uint |
| ; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint |
| ; CHECK: [[param_3:%\w+]] = OpFunctionParameter %uint |
| ; CHECK: [[param_4:%\w+]] = OpFunctionParameter %uint |
| ; CHECK: {{%\w+}} = OpLabel |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1 |
| ; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10 |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10 |
| ; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 2 |
| ; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} |
| ; CHECK: OpSelectionMerge {{%\w+}} None |
| ; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} |
| ; CHECK: {{%\w+}} = OpLabel |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} %uint_10 |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} %uint_23 |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} [[param_1]] |
| )"; |
| |
| static const std::string kStreamWrite4End = R"( |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} [[param_2]] |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} [[param_3]] |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} [[param_4]] |
| ; CHECK: OpBranch {{%\w+}} |
| ; CHECK: {{%\w+}} = OpLabel |
| ; CHECK: OpReturn |
| ; CHECK: OpFunctionEnd |
| )"; |
| |
| // clang-format off |
| static const std::string kStreamWrite4Frag = kStreamWrite4Begin + R"( |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} %uint_4 |
| ; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord |
| ; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}} |
| ; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} {{%\w+}} |
| ; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} {{%\w+}} |
| )" + kStreamWrite4End; |
| |
| static const std::string kStreamWrite4Compute = kStreamWrite4Begin + R"( |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} %uint_5 |
| ; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID |
| ; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0 |
| ; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1 |
| ; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2 |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} {{%\w+}} |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} {{%\w+}} |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} {{%\w+}} |
| )" + kStreamWrite4End; |
| // clang-format on |
| |
| // clang-format off |
| static const std::string kStreamWrite4Vert = kStreamWrite4Begin + R"( |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} %uint_0 |
| ; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} {{%\w+}} |
| ; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}} |
| ; CHECK: OpStore {{%\w+}} {{%\w+}} |
| )" + kStreamWrite4End; |
| // clang-format on |
| |
| static const std::string kInputDecorations = R"( |
| ; CHECK: OpDecorate [[input_buffer_type:%inst_buff_addr_InputBuffer]] Block |
| ; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0 |
| ; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7 |
| ; CHECK: OpDecorate [[input_buffer_var]] Binding 2 |
| )"; |
| |
| static const std::string kInputGlobals = R"( |
| ; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_ulong |
| ; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]] |
| ; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer |
| )"; |
| |
| static const std::string kSearchAndTest = R"( |
| ; CHECK: {{%\w+}} = OpFunction %bool None {{%\w+}} |
| ; CHECK: [[param_1:%\w+]] = OpFunctionParameter %ulong |
| ; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint |
| ; CHECK: {{%\w+}} = OpLabel |
| ; CHECK: OpBranch {{%\w+}} |
| ; CHECK: {{%\w+}} = OpLabel |
| ; CHECK: {{%\w+}} = OpPhi %uint %uint_1 {{%\w+}} {{%\w+}} {{%\w+}} |
| ; CHECK: OpLoopMerge {{%\w+}} {{%\w+}} None |
| ; CHECK: OpBranch {{%\w+}} |
| ; CHECK: {{%\w+}} = OpLabel |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}} |
| ; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} |
| ; CHECK: {{%\w+}} = OpUGreaterThan %bool {{%\w+}} [[param_1]] |
| ; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}} |
| ; CHECK: {{%\w+}} = OpLabel |
| ; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1 |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}} |
| ; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} |
| ; CHECK: {{%\w+}} = OpISub %ulong [[param_1]] {{%\w+}} |
| ; CHECK: {{%\w+}} = OpUConvert %ulong [[param_2]] |
| ; CHECK: {{%\w+}} = OpIAdd %ulong {{%\w+}} {{%\w+}} |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 %uint_0 |
| ; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} |
| ; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}} |
| ; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1 |
| ; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}} |
| ; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}} |
| ; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}} |
| ; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}} |
| ; CHECK: OpReturnValue {{%\w+}} |
| ; CHECK: OpFunctionEnd |
| )"; |
| // clang-format on |
| |
| using InstBuffAddrTest = PassTest<::testing::Test>; |
| |
| TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) { |
| // #version 450 |
| // #extension GL_EXT_buffer_reference : enable |
| // |
| // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct; |
| // |
| // layout(set = 0, binding = 0) uniform ufoo { |
| // bufStruct data; |
| // uint offset; |
| // } u_info; |
| // |
| // layout(buffer_reference, std140) buffer bufStruct { |
| // layout(offset = 0) int a[2]; |
| // layout(offset = 32) int b; |
| // }; |
| // |
| // void main() { |
| // u_info.data.b = 0xca7; |
| // } |
| |
| const std::string defs = R"( |
| OpCapability Shader |
| OpCapability PhysicalStorageBufferAddresses |
| ; CHECK: OpCapability Int64 |
| OpExtension "SPV_EXT_physical_storage_buffer" |
| ; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| ; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 450 |
| OpSourceExtension "GL_EXT_buffer_reference" |
| OpName %main "main" |
| OpName %ufoo "ufoo" |
| OpMemberName %ufoo 0 "data" |
| OpMemberName %ufoo 1 "offset" |
| OpName %bufStruct "bufStruct" |
| OpMemberName %bufStruct 0 "a" |
| OpMemberName %bufStruct 1 "b" |
| OpName %u_info "u_info" |
| )"; |
| |
| // clang-format off |
| const std::string decorates = R"( |
| OpMemberDecorate %ufoo 0 Offset 0 |
| OpMemberDecorate %ufoo 1 Offset 8 |
| OpDecorate %ufoo Block |
| OpDecorate %_arr_int_uint_2 ArrayStride 16 |
| OpMemberDecorate %bufStruct 0 Offset 0 |
| OpMemberDecorate %bufStruct 1 Offset 32 |
| OpDecorate %bufStruct Block |
| OpDecorate %u_info DescriptorSet 0 |
| OpDecorate %u_info Binding 0 |
| ; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 |
| )" + kInputDecorations + R"( |
| ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 |
| )" + kOutputDecorations + R"( |
| ; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId |
| )"; |
| |
| const std::string globals = R"( |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer |
| %uint = OpTypeInt 32 0 |
| %ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint |
| %int = OpTypeInt 32 1 |
| %uint_2 = OpConstant %uint 2 |
| %_arr_int_uint_2 = OpTypeArray %int %uint_2 |
| %bufStruct = OpTypeStruct %_arr_int_uint_2 %int |
| %_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct |
| %_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo |
| %u_info = OpVariable %_ptr_Uniform_ufoo Uniform |
| %int_0 = OpConstant %int 0 |
| %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct |
| %int_1 = OpConstant %int 1 |
| %int_3239 = OpConstant %int 3239 |
| %_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int |
| ; CHECK: %ulong = OpTypeInt 64 0 |
| ; CHECK: %bool = OpTypeBool |
| ; CHECK: %28 = OpTypeFunction %bool %ulong %uint |
| ; CHECK: %_runtimearr_ulong = OpTypeRuntimeArray %ulong |
| )" + kInputGlobals + R"( |
| ; CHECK: %_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong |
| ; CHECK: {{%\w+}} = OpTypeFunction %void %uint %uint %uint %uint |
| ; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint |
| )" + kOutputGlobals + R"( |
| ; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint |
| ; CHECK: %v3uint = OpTypeVector %uint 3 |
| ; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint |
| ; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input |
| )"; |
| // clang-format off |
| |
| const std::string main_func = R"( |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 |
| %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 |
| %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 |
| ; CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 |
| ; CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 |
| ; CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 |
| ; CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 |
| ; CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20 |
| ; CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1 |
| ; CHECK: %24 = OpConvertPtrToU %ulong %22 |
| ; CHECK: %61 = OpFunctionCall %bool %inst_buff_addr_search_and_test %24 %uint_4 |
| ; CHECK: OpSelectionMerge %62 None |
| ; CHECK: OpBranchConditional %61 %63 %64 |
| ; CHECK: %63 = OpLabel |
| OpStore %22 %int_3239 Aligned 16 |
| ; CHECK: OpStore %22 %int_3239 Aligned 16 |
| ; CHECK: OpBranch %62 |
| ; CHECK: %64 = OpLabel |
| ; CHECK: %65 = OpUConvert %uint %24 |
| ; CHECK: %67 = OpShiftRightLogical %ulong %24 %uint_32 |
| ; CHECK: %68 = OpUConvert %uint %67 |
| ; CHECK: %124 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_48 %uint_2 %65 %68 |
| ; CHECK: OpBranch %62 |
| ; CHECK: %62 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute; |
| |
| // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SinglePassRunAndMatch<InstBuffAddrCheckPass>( |
| defs + decorates + globals + main_func + output_funcs, true, 7u, 23u); |
| } |
| |
| TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) { |
| // #version 450 |
| // #extension GL_EXT_buffer_reference : enable |
| |
| // // forward reference |
| // layout(buffer_reference) buffer blockType; |
| |
| // layout(buffer_reference, std430, buffer_reference_align = 16) buffer |
| // blockType { |
| // int x; |
| // blockType next; |
| // }; |
| |
| // layout(std430) buffer rootBlock { |
| // blockType root; |
| // } r; |
| |
| // void main() |
| // { |
| // blockType b = r.root; |
| // b = b.next; |
| // b.x = 531; |
| // } |
| |
| const std::string defs = R"( |
| OpCapability Shader |
| OpCapability PhysicalStorageBufferAddresses |
| ; CHECK: OpCapability Int64 |
| OpExtension "SPV_EXT_physical_storage_buffer" |
| OpExtension "SPV_KHR_storage_buffer_storage_class" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint GLCompute %main "main" |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpSource GLSL 450 |
| OpSourceExtension "GL_EXT_buffer_reference" |
| OpName %main "main" |
| ; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID |
| OpName %blockType "blockType" |
| OpMemberName %blockType 0 "x" |
| OpMemberName %blockType 1 "next" |
| OpName %rootBlock "rootBlock" |
| OpMemberName %rootBlock 0 "root" |
| OpName %r "r" |
| )"; |
| |
| // clang-format off |
| const std::string decorates = R"( |
| OpMemberDecorate %blockType 0 Offset 0 |
| OpMemberDecorate %blockType 1 Offset 8 |
| OpDecorate %blockType Block |
| OpMemberDecorate %rootBlock 0 Offset 0 |
| OpDecorate %rootBlock Block |
| OpDecorate %r DescriptorSet 0 |
| OpDecorate %r Binding 0 |
| ; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 |
| )" + kInputDecorations + R"( |
| ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 |
| )" + kOutputDecorations + R"( |
| ; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId |
| )"; |
| // clang-format on |
| |
| const std::string globals = R"( |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer |
| %int = OpTypeInt 32 1 |
| %blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType |
| %_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType |
| %rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType |
| %_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock |
| %r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer |
| %int_0 = OpConstant %int 0 |
| %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType |
| %int_1 = OpConstant %int 1 |
| %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType |
| %int_531 = OpConstant %int 531 |
| %_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int |
| )" + kInputGlobals + kOutputGlobals; |
| |
| const std::string main_func = R"( |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0 |
| %17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16 |
| %21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1 |
| %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 |
| %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0 |
| OpStore %26 %int_531 Aligned 16 |
| ; CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 |
| ; CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0 |
| ; CHECK: %30 = OpConvertPtrToU %ulong %21 |
| ; CHECK: %67 = OpFunctionCall %bool %inst_buff_addr_search_and_test %30 %uint_8 |
| ; CHECK: OpSelectionMerge %68 None |
| ; CHECK: OpBranchConditional %67 %69 %70 |
| ; CHECK: %69 = OpLabel |
| ; CHECK: %71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 |
| ; CHECK: OpBranch %68 |
| ; CHECK: %70 = OpLabel |
| ; CHECK: %72 = OpUConvert %uint %30 |
| ; CHECK: %74 = OpShiftRightLogical %ulong %30 %uint_32 |
| ; CHECK: %75 = OpUConvert %uint %74 |
| ; CHECK: %131 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_44 %uint_2 %72 %75 |
| ; CHECK: %133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132 |
| ; CHECK: OpBranch %68 |
| ; CHECK: %68 = OpLabel |
| ; CHECK: %134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70 |
| ; CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0 |
| ; CHECK: %135 = OpConvertPtrToU %ulong %26 |
| ; CHECK: %136 = OpFunctionCall %bool %inst_buff_addr_search_and_test %135 %uint_4 |
| ; CHECK: OpSelectionMerge %137 None |
| ; CHECK: OpBranchConditional %136 %138 %139 |
| ; CHECK: %138 = OpLabel |
| ; CHECK: OpStore %26 %int_531 Aligned 16 |
| ; CHECK: OpBranch %137 |
| ; CHECK: %139 = OpLabel |
| ; CHECK: %140 = OpUConvert %uint %135 |
| ; CHECK: %141 = OpShiftRightLogical %ulong %135 %uint_32 |
| ; CHECK: %142 = OpUConvert %uint %141 |
| ; CHECK: %144 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_46 %uint_2 %140 %142 |
| ; CHECK: OpBranch %137 |
| ; CHECK: %137 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const std::string output_funcs = kSearchAndTest + kStreamWrite4Compute; |
| |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SinglePassRunAndMatch<InstBuffAddrCheckPass>( |
| defs + decorates + globals + main_func + output_funcs, true, 7u, 23u); |
| } |
| |
| TEST_F(InstBuffAddrTest, StructLoad) { |
| // #version 450 |
| // #extension GL_EXT_buffer_reference : enable |
| // #extension GL_ARB_gpu_shader_int64 : enable |
| // struct Test { |
| // float a; |
| // }; |
| // |
| // layout(buffer_reference, std430, buffer_reference_align = 16) buffer |
| // TestBuffer { Test test; }; |
| // |
| // Test GetTest(uint64_t ptr) { |
| // return TestBuffer(ptr).test; |
| // } |
| // |
| // void main() { |
| // GetTest(0xe0000000); |
| // } |
| |
| const std::string defs = |
| R"( |
| OpCapability Shader |
| OpCapability Int64 |
| OpCapability PhysicalStorageBufferAddresses |
| ; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint Fragment %main "main" |
| ; CHECK: OpEntryPoint Fragment %main "main" %inst_buff_addr_input_buffer %inst_buff_addr_output_buffer %gl_FragCoord |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpSourceExtension "GL_ARB_gpu_shader_int64" |
| OpSourceExtension "GL_EXT_buffer_reference" |
| OpName %main "main" |
| OpName %Test "Test" |
| OpMemberName %Test 0 "a" |
| OpName %Test_0 "Test" |
| OpMemberName %Test_0 0 "a" |
| OpName %TestBuffer "TestBuffer" |
| OpMemberName %TestBuffer 0 "test" |
| )"; |
| |
| // clang-format off |
| const std::string decorates = R"( |
| OpMemberDecorate %Test_0 0 Offset 0 |
| OpMemberDecorate %TestBuffer 0 Offset 0 |
| OpDecorate %TestBuffer Block |
| ; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8 |
| )" + kInputDecorations + R"( |
| ; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 |
| )" + kOutputDecorations + R"( |
| ; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord |
| )"; |
| |
| const std::string globals = R"( |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %ulong = OpTypeInt 64 0 |
| %float = OpTypeFloat 32 |
| %Test = OpTypeStruct %float |
| OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffer |
| %Test_0 = OpTypeStruct %float |
| %TestBuffer = OpTypeStruct %Test_0 |
| %_ptr_PhysicalStorageBuffer_TestBuffer = OpTypePointer PhysicalStorageBuffer %TestBuffer |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0 |
| %ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704 |
| ; CHECK: %47 = OpTypeFunction %bool %ulong %uint |
| )" + kInputGlobals + R"( |
| ; CHECK: {{%\w+}} = OpTypeFunction %void %uint %uint %uint %uint |
| )" + kOutputGlobals + R"( |
| ; CHECK: %143 = OpConstantNull %Test_0 |
| )"; |
| // clang-format on |
| |
| const std::string main_func = |
| R"( |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %37 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %ulong_18446744073172680704 |
| %38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0 |
| %39 = OpLoad %Test_0 %38 Aligned 16 |
| ; CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16 |
| ; CHECK: %43 = OpConvertPtrToU %ulong %38 |
| ; CHECK: %80 = OpFunctionCall %bool %inst_buff_addr_search_and_test %43 %uint_4 |
| ; CHECK: OpSelectionMerge %81 None |
| ; CHECK: OpBranchConditional %80 %82 %83 |
| ; CHECK: %82 = OpLabel |
| ; CHECK: %84 = OpLoad %Test_0 %38 Aligned 16 |
| ; CHECK: OpBranch %81 |
| ; CHECK: %83 = OpLabel |
| ; CHECK: %85 = OpUConvert %uint %43 |
| ; CHECK: %87 = OpShiftRightLogical %ulong %43 %uint_32 |
| ; CHECK: %88 = OpUConvert %uint %87 |
| ; CHECK: %142 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_37 %uint_2 %85 %88 |
| ; CHECK: OpBranch %81 |
| ; CHECK: %81 = OpLabel |
| ; CHECK: %144 = OpPhi %Test_0 %84 %82 %143 %83 |
| %40 = OpCopyLogical %Test %39 |
| ; CHECK-NOT: %40 = OpCopyLogical %Test %39 |
| ; CHECK: %40 = OpCopyLogical %Test %144 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const std::string output_funcs = kSearchAndTest + kStreamWrite4Frag; |
| |
| SetTargetEnv(SPV_ENV_VULKAN_1_2); |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SinglePassRunAndMatch<InstBuffAddrCheckPass>( |
| defs + decorates + globals + main_func + output_funcs, true); |
| } |
| |
| TEST_F(InstBuffAddrTest, DeviceBufferAddressOOB) { |
| // #version 450 |
| // #extension GL_EXT_buffer_reference : enable |
| // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct; |
| // layout(set = 0, binding = 0) uniform ufoo { |
| // bufStruct data; |
| // int nWrites; |
| // } u_info; |
| // layout(buffer_reference, std140) buffer bufStruct { |
| // int a[4]; |
| // }; |
| // void main() { |
| // for (int i=0; i < u_info.nWrites; ++i) { |
| // u_info.data.a[i] = 0xdeadca71; |
| // } |
| // } |
| |
| // clang-format off |
| const std::string text = R"( |
| OpCapability Shader |
| OpCapability PhysicalStorageBufferAddresses |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel PhysicalStorageBuffer64 GLSL450 |
| OpEntryPoint Vertex %main "main" %u_info |
| ;CHECK: OpEntryPoint Vertex %main "main" %u_info %inst_buff_addr_input_buffer %inst_buff_addr_output_buffer %gl_VertexIndex %gl_InstanceIndex |
| OpSource GLSL 450 |
| OpSourceExtension "GL_EXT_buffer_reference" |
| OpName %main "main" |
| OpName %i "i" |
| OpName %ufoo "ufoo" |
| OpMemberName %ufoo 0 "data" |
| OpMemberName %ufoo 1 "nWrites" |
| OpName %bufStruct "bufStruct" |
| OpMemberName %bufStruct 0 "a" |
| OpName %u_info "u_info" |
| OpMemberDecorate %ufoo 0 Offset 0 |
| OpMemberDecorate %ufoo 1 Offset 8 |
| OpDecorate %ufoo Block |
| OpDecorate %_arr_int_uint_4 ArrayStride 16 |
| OpMemberDecorate %bufStruct 0 Offset 0 |
| OpDecorate %bufStruct Block |
| OpDecorate %u_info DescriptorSet 0 |
| OpDecorate %u_info Binding 0)" + kInputDecorations + kOutputDecorations + R"( |
| %void = OpTypeVoid |
| %3 = OpTypeFunction %void |
| %int = OpTypeInt 32 1 |
| %_ptr_Function_int = OpTypePointer Function %int |
| %int_0 = OpConstant %int 0 |
| OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer |
| %ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %int |
| %uint = OpTypeInt 32 0 |
| %uint_4 = OpConstant %uint 4 |
| %_arr_int_uint_4 = OpTypeArray %int %uint_4 |
| %bufStruct = OpTypeStruct %_arr_int_uint_4 |
| %_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct |
| %_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo |
| %u_info = OpVariable %_ptr_Uniform_ufoo Uniform |
| %int_1 = OpConstant %int 1 |
| %_ptr_Uniform_int = OpTypePointer Uniform %int |
| %bool = OpTypeBool |
| %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct |
| %int_n559035791 = OpConstant %int -559035791 |
| %_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int)" + kInputGlobals + kOutputGlobals + R"( |
| %main = OpFunction %void None %3 |
| %5 = OpLabel |
| %i = OpVariable %_ptr_Function_int Function |
| OpStore %i %int_0 |
| OpBranch %10 |
| %10 = OpLabel |
| OpLoopMerge %12 %13 None |
| OpBranch %14 |
| %14 = OpLabel |
| %15 = OpLoad %int %i |
| %26 = OpAccessChain %_ptr_Uniform_int %u_info %int_1 |
| %27 = OpLoad %int %26 |
| %29 = OpSLessThan %bool %15 %27 |
| OpBranchConditional %29 %11 %12 |
| %11 = OpLabel |
| %31 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 |
| %32 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31 |
| %33 = OpLoad %int %i |
| %36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %32 %int_0 %33 |
| ;CHECK: %41 = OpConvertPtrToU %ulong %36 |
| ;CHECK: %76 = OpFunctionCall %bool %inst_buff_addr_search_and_test %41 %uint_4 |
| ;CHECK: OpSelectionMerge %77 None |
| ;CHECK: OpBranchConditional %76 %78 %79 |
| ;CHECK: %78 = OpLabel |
| OpStore %36 %int_n559035791 Aligned 16 |
| ;CHECK: OpBranch %77 |
| ;CHECK: 79 = OpLabel |
| ;CHECK: 80 = OpUConvert %uint %41 |
| ;CHECK: 82 = OpShiftRightLogical %ulong %41 %uint_32 |
| ;CHECK: 83 = OpUConvert %uint %82 |
| ;CHECK: 134 = OpFunctionCall %void %inst_buff_addr_stream_write_4 %uint_62 %uint_2 %80 %83 |
| ;CHECK: OpBranch %77 |
| ;CHECK: 77 = OpLabel |
| OpBranch %13 |
| %13 = OpLabel |
| %37 = OpLoad %int %i |
| %38 = OpIAdd %int %37 %int_1 |
| OpStore %i %38 |
| OpBranch %10 |
| %12 = OpLabel |
| OpReturn |
| OpFunctionEnd)" + kSearchAndTest + kStreamWrite4Vert; |
| // clang-format on |
| |
| SetTargetEnv(SPV_ENV_VULKAN_1_2); |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 7, 23); |
| } |
| |
| } // namespace |
| } // namespace opt |
| } // namespace spvtools |