1// Copyright (c) 2019-2022 Valve Corporation
2// Copyright (c) 2019-2022 LunarG Inc.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16// Bindless Check Instrumentation Tests.
17// Tests ending with V2 use version 2 record format.
18
19#include <string>
20#include <vector>
21
22#include "test/opt/pass_fixture.h"
23#include "test/opt/pass_utils.h"
24
25namespace spvtools {
26namespace opt {
27namespace {
28
29static const std::string kFuncName = "inst_buff_addr_search_and_test";
30static const std::string kImportDeco = R"(
31;CHECK: OpDecorate %)" + kFuncName + R"( LinkageAttributes ")" +
32                                       kFuncName + R"(" Import
33)";
34static const std::string kImportStub = R"(
35;CHECK: %)" + kFuncName + R"( = OpFunction %bool None {{%\w+}}
36;CHECK: OpFunctionEnd
37)";
38// clang-format on
39
40using InstBuffAddrTest = PassTest<::testing::Test>;
41
42TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) {
43  // #version 450
44  // #extension GL_EXT_buffer_reference : enable
45  //
46  // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
47  //
48  // layout(set = 0, binding = 0) uniform ufoo {
49  //     bufStruct data;
50  //     uint offset;
51  // } u_info;
52  //
53  // layout(buffer_reference, std140) buffer bufStruct {
54  //     layout(offset = 0) int a[2];
55  //     layout(offset = 32) int b;
56  // };
57  //
58  // void main() {
59  //     u_info.data.b = 0xca7;
60  // }
61
62  const std::string defs = R"(
63OpCapability Shader
64OpCapability PhysicalStorageBufferAddresses
65;CHECK: OpCapability Int64
66OpExtension "SPV_EXT_physical_storage_buffer"
67;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
68%1 = OpExtInstImport "GLSL.std.450"
69OpMemoryModel PhysicalStorageBuffer64 GLSL450
70OpEntryPoint GLCompute %main "main"
71;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
72OpExecutionMode %main LocalSize 1 1 1
73OpSource GLSL 450
74OpSourceExtension "GL_EXT_buffer_reference"
75OpName %main "main"
76OpName %ufoo "ufoo"
77OpMemberName %ufoo 0 "data"
78OpMemberName %ufoo 1 "offset"
79OpName %bufStruct "bufStruct"
80OpMemberName %bufStruct 0 "a"
81OpMemberName %bufStruct 1 "b"
82OpName %u_info "u_info"
83)";
84
85  // clang-format off
86  const std::string decorates = R"(
87OpMemberDecorate %ufoo 0 Offset 0
88OpMemberDecorate %ufoo 1 Offset 8
89OpDecorate %ufoo Block
90OpDecorate %_arr_int_uint_2 ArrayStride 16
91OpMemberDecorate %bufStruct 0 Offset 0
92OpMemberDecorate %bufStruct 1 Offset 32
93OpDecorate %bufStruct Block
94OpDecorate %u_info DescriptorSet 0
95OpDecorate %u_info Binding 0
96)" + kImportDeco + R"(
97;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
98)";
99
100  const std::string globals = R"(
101%void = OpTypeVoid
102%3 = OpTypeFunction %void
103OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
104%uint = OpTypeInt 32 0
105%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint
106%int = OpTypeInt 32 1
107%uint_2 = OpConstant %uint 2
108%_arr_int_uint_2 = OpTypeArray %int %uint_2
109%bufStruct = OpTypeStruct %_arr_int_uint_2 %int
110%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct
111%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
112%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
113%int_0 = OpConstant %int 0
114%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
115%int_1 = OpConstant %int 1
116%int_3239 = OpConstant %int 3239
117%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
118;CHECK: %ulong = OpTypeInt 64 0
119;CHECK: %bool = OpTypeBool
120;CHECK: %v3uint = OpTypeVector %uint 3
121;CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
122;CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
123)";
124// clang-format off
125
126  const std::string main_func = R"(
127%main = OpFunction %void None %3
128%5 = OpLabel
129%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
130%18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
131%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
132;CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
133;CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
134;CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
135;CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
136;CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20
137;CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1
138;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %22
139;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
140;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
141;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
142;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
143;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
144;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} {{%\w+}} %uint_4
145;CHECK: OpSelectionMerge {{%\w+}} None
146;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
147;CHECK: {{%\w+}} = OpLabel
148OpStore %22 %int_3239 Aligned 16
149;CHECK: OpStore %22 %int_3239 Aligned 16
150;CHECK: OpBranch {{%\w+}}
151;CHECK: {{%\w+}} = OpLabel
152;CHECK: OpBranch {{%\w+}}
153;CHECK: {{%\w+}} = OpLabel
154OpReturn
155OpFunctionEnd
156)";
157
158  // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
159  SinglePassRunAndMatch<InstBuffAddrCheckPass>(
160      defs + decorates + globals + kImportStub + main_func, true, 23u);
161}
162
163TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
164  // #version 450
165  // #extension GL_EXT_buffer_reference : enable
166
167  // // forward reference
168  // layout(buffer_reference) buffer blockType;
169
170  // layout(buffer_reference, std430, buffer_reference_align = 16) buffer
171  // blockType {
172  //   int x;
173  //   blockType next;
174  // };
175
176  // layout(std430) buffer rootBlock {
177  //   blockType root;
178  // } r;
179
180  // void main()
181  // {
182  //   blockType b = r.root;
183  //   b = b.next;
184  //   b.x = 531;
185  // }
186
187  const std::string defs = R"(
188OpCapability Shader
189OpCapability PhysicalStorageBufferAddresses
190;CHECK: OpCapability Int64
191OpExtension "SPV_EXT_physical_storage_buffer"
192OpExtension "SPV_KHR_storage_buffer_storage_class"
193%1 = OpExtInstImport "GLSL.std.450"
194OpMemoryModel PhysicalStorageBuffer64 GLSL450
195OpEntryPoint GLCompute %main "main"
196OpExecutionMode %main LocalSize 1 1 1
197OpSource GLSL 450
198OpSourceExtension "GL_EXT_buffer_reference"
199OpName %main "main"
200;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
201OpName %blockType "blockType"
202OpMemberName %blockType 0 "x"
203OpMemberName %blockType 1 "next"
204OpName %rootBlock "rootBlock"
205OpMemberName %rootBlock 0 "root"
206OpName %r "r"
207)";
208
209// clang-format off
210  const std::string decorates = R"(
211OpMemberDecorate %blockType 0 Offset 0
212OpMemberDecorate %blockType 1 Offset 8
213OpDecorate %blockType Block
214OpMemberDecorate %rootBlock 0 Offset 0
215OpDecorate %rootBlock Block
216OpDecorate %r DescriptorSet 0
217OpDecorate %r Binding 0
218)" + kImportDeco + R"(
219;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
220)";
221
222  const std::string globals = R"(
223%void = OpTypeVoid
224%3 = OpTypeFunction %void
225OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
226%int = OpTypeInt 32 1
227%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType
228%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType
229%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType
230%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock
231%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer
232%int_0 = OpConstant %int 0
233%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType
234%int_1 = OpConstant %int 1
235%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType
236%int_531 = OpConstant %int 531
237%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
238)";
239
240  const std::string main_func = R"(
241%main = OpFunction %void None %3
242%5 = OpLabel
243%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0
244%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16
245%21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1
246%22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
247%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
248OpStore %26 %int_531 Aligned 16
249;CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
250;CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
251;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %21
252;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
253;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
254;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
255;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
256;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
257;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_45 {{%\w+}} {{%\w+}} %uint_8
258;CHECK: OpSelectionMerge {{%\w+}} None
259;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
260;CHECK: {{%\w+}} = OpLabel
261;CHECK: {{%\w+}} = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
262;CHECK: OpBranch {{%\w+}}
263;CHECK: {{%\w+}} = OpLabel
264;CHECK: {{%\w+}} = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %52
265;CHECK: OpBranch {{%\w+}}
266;CHECK: {{%\w+}} = OpLabel
267;CHECK: {{%\w+}} = OpPhi %_ptr_PhysicalStorageBuffer_blockType {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
268;CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int {{%\w+}} %int_0
269;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %26
270;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
271;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
272;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
273;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
274;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
275;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_47 {{%\w+}} {{%\w+}} %uint_4
276;CHECK: OpSelectionMerge {{%\w+}} None
277;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
278;CHECK: {{%\w+}} = OpLabel
279;CHECK: OpStore %26 %int_531 Aligned 16
280;CHECK: OpBranch {{%\w+}}
281;CHECK: {{%\w+}} = OpLabel
282;CHECK: OpBranch {{%\w+}}
283;CHECK: {{%\w+}} = OpLabel
284OpReturn
285OpFunctionEnd
286)";
287  // clang-format on
288
289  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
290  SinglePassRunAndMatch<InstBuffAddrCheckPass>(
291      defs + decorates + globals + kImportStub + main_func, true, 23u);
292}
293
294TEST_F(InstBuffAddrTest, StructLoad) {
295  // #version 450
296  //   #extension GL_EXT_buffer_reference : enable
297  //   #extension GL_ARB_gpu_shader_int64 : enable
298  //   struct Test {
299  //   float a;
300  // };
301  //
302  // layout(buffer_reference, std430, buffer_reference_align = 16) buffer
303  // TestBuffer { Test test; };
304  //
305  // Test GetTest(uint64_t ptr) {
306  //   return TestBuffer(ptr).test;
307  // }
308  //
309  // void main() {
310  //   GetTest(0xe0000000);
311  // }
312
313  const std::string defs =
314      R"(
315OpCapability Shader
316OpCapability Int64
317OpCapability PhysicalStorageBufferAddresses
318;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
319%1 = OpExtInstImport "GLSL.std.450"
320OpMemoryModel PhysicalStorageBuffer64 GLSL450
321OpEntryPoint Fragment %main "main"
322;CHECK: OpEntryPoint Fragment %main "main" %gl_FragCoord
323OpExecutionMode %main OriginUpperLeft
324OpSource GLSL 450
325OpSourceExtension "GL_ARB_gpu_shader_int64"
326OpSourceExtension "GL_EXT_buffer_reference"
327OpName %main "main"
328OpName %Test "Test"
329OpMemberName %Test 0 "a"
330OpName %Test_0 "Test"
331OpMemberName %Test_0 0 "a"
332OpName %TestBuffer "TestBuffer"
333OpMemberName %TestBuffer 0 "test"
334)";
335
336  // clang-format off
337  const std::string decorates = R"(
338OpMemberDecorate %Test_0 0 Offset 0
339OpMemberDecorate %TestBuffer 0 Offset 0
340OpDecorate %TestBuffer Block
341)" + kImportDeco + R"(
342;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
343)";
344
345  const std::string globals = R"(
346%void = OpTypeVoid
347%3 = OpTypeFunction %void
348%ulong = OpTypeInt 64 0
349%float = OpTypeFloat 32
350%Test = OpTypeStruct %float
351OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffer
352%Test_0 = OpTypeStruct %float
353%TestBuffer = OpTypeStruct %Test_0
354%_ptr_PhysicalStorageBuffer_TestBuffer = OpTypePointer PhysicalStorageBuffer %TestBuffer
355%int = OpTypeInt 32 1
356%int_0 = OpConstant %int 0
357%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0
358%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704
359;CHECK: {{%\w+}} = OpConstantNull %Test_0
360)";
361
362  const std::string main_func = R"(
363%main = OpFunction %void None %3
364%5 = OpLabel
365%37 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %ulong_18446744073172680704
366%38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0
367%39 = OpLoad %Test_0 %38 Aligned 16
368;CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16
369;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %38
370;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
371;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
372;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
373;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
374;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
375;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_38 {{%\w+}} {{%\w+}} %uint_4
376;CHECK: OpSelectionMerge {{%\w+}} None
377;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
378;CHECK: {{%\w+}} = OpLabel
379;CHECK: {{%\w+}} = OpLoad %Test_0 %38 Aligned 16
380;CHECK: OpBranch {{%\w+}}
381;CHECK: {{%\w+}} = OpLabel
382;CHECK: OpBranch {{%\w+}}
383;CHECK: {{%\w+}} = OpLabel
384;CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
385%40 = OpCopyLogical %Test %39
386;CHECK-NOT: %40 = OpCopyLogical %Test %39
387;CHECK: %40 = OpCopyLogical %Test [[phi_result]]
388OpReturn
389OpFunctionEnd
390)";
391  // clang-format on
392
393  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
394  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
395  SinglePassRunAndMatch<InstBuffAddrCheckPass>(
396      defs + decorates + globals + kImportStub + main_func, true);
397}
398
399TEST_F(InstBuffAddrTest, PaddedStructLoad) {
400  // #version 450
401  // #extension GL_EXT_buffer_reference : enable
402  // #extension GL_ARB_gpu_shader_int64 : enable
403  // struct Test {
404  //   uvec3 pad_1;  // Offset 0 Size 12
405  //   double pad_2; // Offset 16 Size 8 (alignment requirement)
406  //   float a;      // Offset 24 Size 4
407  // }; // Total Size 28
408  //
409  // layout(buffer_reference, std430, buffer_reference_align = 16) buffer
410  // TestBuffer { Test test; };
411  //
412  // Test GetTest(uint64_t ptr) {
413  //   return TestBuffer(ptr).test;
414  // }
415  //
416  // void main() {
417  //   GetTest(0xe0000000);
418  // }
419
420  const std::string defs =
421      R"(
422OpCapability Shader
423OpCapability Float64
424OpCapability Int64
425OpCapability PhysicalStorageBufferAddresses
426%1 = OpExtInstImport "GLSL.std.450"
427OpMemoryModel PhysicalStorageBuffer64 GLSL450
428OpEntryPoint Vertex %main "main"
429OpSource GLSL 450
430OpSourceExtension "GL_ARB_gpu_shader_int64"
431OpSourceExtension "GL_EXT_buffer_reference"
432OpName %main "main"
433OpName %Test "Test"
434OpMemberName %Test 0 "pad_1"
435OpMemberName %Test 1 "pad_2"
436OpMemberName %Test 2 "a"
437OpName %GetTest_u641_ "GetTest(u641;"
438OpName %ptr "ptr"
439OpName %Test_0 "Test"
440OpMemberName %Test_0 0 "pad_1"
441OpMemberName %Test_0 1 "pad_2"
442OpMemberName %Test_0 2 "a"
443OpName %TestBuffer "TestBuffer"
444OpMemberName %TestBuffer 0 "test"
445OpName %param "param"
446)";
447
448  // clang-format off
449  const std::string decorates = R"(
450OpDecorate %TestBuffer Block
451OpMemberDecorate %Test_0 0 Offset 0
452OpMemberDecorate %Test_0 1 Offset 16
453OpMemberDecorate %Test_0 2 Offset 24
454OpMemberDecorate %TestBuffer 0 Offset 0
455)" + kImportDeco + R"(
456;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
457;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
458)";
459
460  const std::string globals = R"(
461%void = OpTypeVoid
462%3 = OpTypeFunction %void
463%ulong = OpTypeInt 64 0
464%_ptr_Function_ulong = OpTypePointer Function %ulong
465%uint = OpTypeInt 32 0
466%v3uint = OpTypeVector %uint 3
467%double = OpTypeFloat 64
468%float = OpTypeFloat 32
469%Test = OpTypeStruct %v3uint %double %float
470%13 = OpTypeFunction %Test %_ptr_Function_ulong
471OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffer
472%Test_0 = OpTypeStruct %v3uint %double %float
473%TestBuffer = OpTypeStruct %Test_0
474%_ptr_PhysicalStorageBuffer_TestBuffer = OpTypePointer PhysicalStorageBuffer %TestBuffer
475%int = OpTypeInt 32 1
476%int_0 = OpConstant %int 0
477%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0
478%_ptr_Function_Test = OpTypePointer Function %Test
479%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704
480;CHECK: {{%\w+}} = OpConstantNull %Test_0
481)";
482
483  const std::string main_func = R"(
484%main = OpFunction %void None %3
485%5 = OpLabel
486%param = OpVariable %_ptr_Function_ulong Function
487OpStore %param %ulong_18446744073172680704
488%35 = OpFunctionCall %Test %GetTest_u641_ %param
489OpReturn
490OpFunctionEnd
491%GetTest_u641_ = OpFunction %Test None %13
492%ptr = OpFunctionParameter %_ptr_Function_ulong
493%16 = OpLabel
494%28 = OpVariable %_ptr_Function_Test Function
495%17 = OpLoad %ulong %ptr
496%21 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %17
497%25 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %21 %int_0
498%26 = OpLoad %Test_0 %25 Aligned 16
499%29 = OpCopyLogical %Test %26
500;CHECK-NOT: %30 = OpLoad %Test %28
501;CHECK-NOT: %26 = OpLoad %Test_0 %25 Aligned 16
502;CHECK-NOT: %29 = OpCopyLogical %Test %26
503;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %25
504;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
505;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
506;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
507;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_63 {{%\w+}} {{%\w+}} %uint_28
508;CHECK: OpSelectionMerge {{%\w+}} None
509;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
510;CHECK: {{%\w+}} = OpLabel
511;CHECK: {{%\w+}} = OpLoad %Test_0 %25 Aligned 16
512;CHECK: OpBranch {{%\w+}}
513;CHECK: {{%\w+}} = OpLabel
514;CHECK: OpBranch {{%\w+}}
515;CHECK: {{%\w+}} = OpLabel
516;CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
517;CHECK: %29 = OpCopyLogical %Test [[phi_result]]
518OpStore %28 %29
519%30 = OpLoad %Test %28
520OpReturnValue %30
521OpFunctionEnd
522)";
523  // clang-format on
524
525  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
526  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
527  SinglePassRunAndMatch<InstBuffAddrCheckPass>(
528      defs + decorates + globals + kImportStub + main_func, true);
529}
530
531TEST_F(InstBuffAddrTest, DeviceBufferAddressOOB) {
532  // #version 450
533  // #extension GL_EXT_buffer_reference : enable
534  //  layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;
535  // layout(set = 0, binding = 0) uniform ufoo {
536  //     bufStruct data;
537  //     int nWrites;
538  // } u_info;
539  // layout(buffer_reference, std140) buffer bufStruct {
540  //     int a[4];
541  // };
542  // void main() {
543  //     for (int i=0; i < u_info.nWrites; ++i) {
544  //         u_info.data.a[i] = 0xdeadca71;
545  //     }
546  // }
547
548  // clang-format off
549  const std::string text = R"(
550OpCapability Shader
551OpCapability PhysicalStorageBufferAddresses
552%1 = OpExtInstImport "GLSL.std.450"
553OpMemoryModel PhysicalStorageBuffer64 GLSL450
554OpEntryPoint Vertex %main "main" %u_info
555;CHECK: OpEntryPoint Vertex %main "main" %u_info %gl_VertexIndex %gl_InstanceIndex
556OpSource GLSL 450
557OpSourceExtension "GL_EXT_buffer_reference"
558OpName %main "main"
559OpName %i "i"
560OpName %ufoo "ufoo"
561OpMemberName %ufoo 0 "data"
562OpMemberName %ufoo 1 "nWrites"
563OpName %bufStruct "bufStruct"
564OpMemberName %bufStruct 0 "a"
565OpName %u_info "u_info"
566OpMemberDecorate %ufoo 0 Offset 0
567OpMemberDecorate %ufoo 1 Offset 8
568OpDecorate %ufoo Block
569OpDecorate %_arr_int_uint_4 ArrayStride 16
570OpMemberDecorate %bufStruct 0 Offset 0
571OpDecorate %bufStruct Block
572OpDecorate %u_info DescriptorSet 0
573OpDecorate %u_info Binding 0
574)" + kImportDeco + R"(
575%void = OpTypeVoid
576%3 = OpTypeFunction %void
577%int = OpTypeInt 32 1
578%_ptr_Function_int = OpTypePointer Function %int
579%int_0 = OpConstant %int 0
580OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
581%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %int
582%uint = OpTypeInt 32 0
583%uint_4 = OpConstant %uint 4
584%_arr_int_uint_4 = OpTypeArray %int %uint_4
585%bufStruct = OpTypeStruct %_arr_int_uint_4
586%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct
587%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
588%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
589%int_1 = OpConstant %int 1
590%_ptr_Uniform_int = OpTypePointer Uniform %int
591%bool = OpTypeBool
592%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
593%int_n559035791 = OpConstant %int -559035791
594%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
595)" + kImportStub + R"(
596%main = OpFunction %void None %3
597%5 = OpLabel
598%i = OpVariable %_ptr_Function_int Function
599OpStore %i %int_0
600OpBranch %10
601%10 = OpLabel
602OpLoopMerge %12 %13 None
603OpBranch %14
604%14 = OpLabel
605%15 = OpLoad %int %i
606%26 = OpAccessChain %_ptr_Uniform_int %u_info %int_1
607%27 = OpLoad %int %26
608%29 = OpSLessThan %bool %15 %27
609OpBranchConditional %29 %11 %12
610%11 = OpLabel
611%31 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
612%32 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31
613%33 = OpLoad %int %i
614%36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %32 %int_0 %33
615OpStore %36 %int_n559035791 Aligned 16
616;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %36
617;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
618;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
619;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
620;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_63 {{%\w+}} {{%\w+}} %uint_4
621;CHECK: OpSelectionMerge {{%\w+}} None
622;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
623;CHECK: {{%\w+}} = OpLabel
624;CHECK: OpStore %36 %int_n559035791 Aligned 16
625;CHECK: OpBranch {{%\w+}}
626;CHECK: {{%\w+}} = OpLabel
627;CHECK: OpBranch {{%\w+}}
628;CHECK: {{%\w+}} = OpLabel
629OpBranch %13
630%13 = OpLabel
631%37 = OpLoad %int %i
632%38 = OpIAdd %int %37 %int_1
633OpStore %i %38
634OpBranch %10
635%12 = OpLabel
636OpReturn
637OpFunctionEnd)";
638  // clang-format on
639
640  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
641  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
642  SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 23);
643}
644
645TEST_F(InstBuffAddrTest, UVec3ScalarAddressOOB) {
646  // clang-format off
647  // #version 450
648  //    #extension GL_EXT_buffer_reference : enable
649  //    #extension GL_EXT_scalar_block_layout : enable
650  //    layout(buffer_reference, std430, scalar) readonly buffer IndexBuffer
651  //    {
652  //        uvec3 indices[];
653  //    };
654  //    layout(set = 0, binding = 0) uniform ufoo {
655  //        IndexBuffer data;
656  //        int nReads;
657  //    } u_info;
658  //    void main() {
659  //        uvec3 readvec;
660  //        for (int i=0; i < u_info.nReads; ++i) {
661  //            readvec = u_info.data.indices[i];
662  //        }
663  //    }
664  const std::string text = R"(
665OpCapability Shader
666OpCapability PhysicalStorageBufferAddresses
667%1 = OpExtInstImport "GLSL.std.450"
668OpMemoryModel PhysicalStorageBuffer64 GLSL450
669OpEntryPoint Vertex %main "main" %u_info
670OpSource GLSL 450
671OpSourceExtension "GL_EXT_buffer_reference"
672OpSourceExtension "GL_EXT_scalar_block_layout"
673OpName %main "main"
674OpName %i "i"
675OpName %ufoo "ufoo"
676OpMemberName %ufoo 0 "data"
677OpMemberName %ufoo 1 "nReads"
678OpName %IndexBuffer "IndexBuffer"
679OpMemberName %IndexBuffer 0 "indices"
680OpName %u_info "u_info"
681OpName %readvec "readvec"
682OpMemberDecorate %ufoo 0 Offset 0
683OpMemberDecorate %ufoo 1 Offset 8
684OpDecorate %ufoo Block
685OpDecorate %_runtimearr_v3uint ArrayStride 12
686OpMemberDecorate %IndexBuffer 0 NonWritable
687OpMemberDecorate %IndexBuffer 0 Offset 0
688OpDecorate %IndexBuffer Block
689OpDecorate %u_info DescriptorSet 0
690OpDecorate %u_info Binding 0
691)" + kImportDeco + R"(
692%void = OpTypeVoid
693%3 = OpTypeFunction %void
694%int = OpTypeInt 32 1
695%_ptr_Function_int = OpTypePointer Function %int
696%int_0 = OpConstant %int 0
697OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_IndexBuffer PhysicalStorageBuffer
698%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_IndexBuffer %int
699%uint = OpTypeInt 32 0
700%v3uint = OpTypeVector %uint 3
701%_runtimearr_v3uint = OpTypeRuntimeArray %v3uint
702%IndexBuffer = OpTypeStruct %_runtimearr_v3uint
703%_ptr_PhysicalStorageBuffer_IndexBuffer = OpTypePointer PhysicalStorageBuffer %IndexBuffer
704%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo
705%u_info = OpVariable %_ptr_Uniform_ufoo Uniform
706%int_1 = OpConstant %int 1
707%_ptr_Uniform_int = OpTypePointer Uniform %int
708%bool = OpTypeBool
709)" + kImportStub + R"(
710%_ptr_Function_v3uint = OpTypePointer Function %v3uint
711%_ptr_Uniform__ptr_PhysicalStorageBuffer_IndexBuffer = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_IndexBuffer
712%_ptr_PhysicalStorageBuffer_v3uint = OpTypePointer PhysicalStorageBuffer %v3uint
713%main = OpFunction %void None %3
714%5 = OpLabel
715%i = OpVariable %_ptr_Function_int Function
716%readvec = OpVariable %_ptr_Function_v3uint Function
717OpStore %i %int_0
718OpBranch %10
719%10 = OpLabel
720OpLoopMerge %12 %13 None
721OpBranch %14
722%14 = OpLabel
723%15 = OpLoad %int %i
724%26 = OpAccessChain %_ptr_Uniform_int %u_info %int_1
725%27 = OpLoad %int %26
726%29 = OpSLessThan %bool %15 %27
727OpBranchConditional %29 %11 %12
728%11 = OpLabel
729%33 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_IndexBuffer %u_info %int_0
730%34 = OpLoad %_ptr_PhysicalStorageBuffer_IndexBuffer %33
731%35 = OpLoad %int %i
732%37 = OpAccessChain %_ptr_PhysicalStorageBuffer_v3uint %34 %int_0 %35
733%38 = OpLoad %v3uint %37 Aligned 4
734OpStore %readvec %38
735;CHECK-NOT: %38 = OpLoad %v3uint %37 Aligned 4
736;CHECK-NOT: OpStore %readvec %38
737;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %37
738;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
739;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
740;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
741;CHECK: [[test_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_67 {{%\w+}} {{%\w+}} %uint_12
742;CHECK: OpSelectionMerge {{%\w+}} None
743;CHECK: OpBranchConditional [[test_result]] {{%\w+}} {{%\w+}}
744;CHECK: {{%\w+}} = OpLabel
745;CHECK: {{%\w+}} = OpLoad %v3uint %37 Aligned 4
746;CHECK: OpBranch {{%\w+}}
747;CHECK: {{%\w+}} = OpLabel
748;CHECK: OpBranch {{%\w+}}
749;CHECK: {{%\w+}} = OpLabel
750;CHECK: [[phi_result:%\w+]] = OpPhi %v3uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
751;CHECK: OpStore %readvec [[phi_result]]
752OpBranch %13
753%13 = OpLabel
754%39 = OpLoad %int %i
755%40 = OpIAdd %int %39 %int_1
756OpStore %i %40
757OpBranch %10
758%12 = OpLabel
759OpReturn
760OpFunctionEnd
761)";
762  // clang-format on
763
764  SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
765  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
766  ValidatorOptions()->scalar_block_layout = true;
767  SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 23);
768}
769
770}  // namespace
771}  // namespace opt
772}  // namespace spvtools
773