1 // Copyright (c) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <array>
16 #include <sstream>
17 #include <string>
18 #include <vector>
19
20 #include "gmock/gmock.h"
21 #include "pass_fixture.h"
22 #include "pass_utils.h"
23 #include "source/opt/graphics_robust_access_pass.h"
24
25 namespace {
26
27 using namespace spvtools;
28
29 using opt::GraphicsRobustAccessPass;
30 using GraphicsRobustAccessTest = opt::PassTest<::testing::Test>;
31
32 // Test incompatible module, determined at module-level.
33
TEST_F(GraphicsRobustAccessTest,FailNotShader)34 TEST_F(GraphicsRobustAccessTest, FailNotShader) {
35 const std::string text = R"(
36 ; CHECK: Can only process Shader modules
37 OpCapability Kernel
38 )";
39
40 SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
41 }
42
TEST_F(GraphicsRobustAccessTest,FailCantProcessVariablePointers)43 TEST_F(GraphicsRobustAccessTest, FailCantProcessVariablePointers) {
44 const std::string text = R"(
45 ; CHECK: Can't process modules with VariablePointers capability
46 OpCapability VariablePointers
47 )";
48
49 SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
50 }
51
TEST_F(GraphicsRobustAccessTest,FailCantProcessVariablePointersStorageBuffer)52 TEST_F(GraphicsRobustAccessTest, FailCantProcessVariablePointersStorageBuffer) {
53 const std::string text = R"(
54 ; CHECK: Can't process modules with VariablePointersStorageBuffer capability
55 OpCapability VariablePointersStorageBuffer
56 )";
57
58 SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
59 }
60
TEST_F(GraphicsRobustAccessTest,FailCantProcessRuntimeDescriptorArrayEXT)61 TEST_F(GraphicsRobustAccessTest, FailCantProcessRuntimeDescriptorArrayEXT) {
62 const std::string text = R"(
63 ; CHECK: Can't process modules with RuntimeDescriptorArrayEXT capability
64 OpCapability RuntimeDescriptorArrayEXT
65 )";
66
67 SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
68 }
69
TEST_F(GraphicsRobustAccessTest,FailCantProcessPhysical32AddressingModel)70 TEST_F(GraphicsRobustAccessTest, FailCantProcessPhysical32AddressingModel) {
71 const std::string text = R"(
72 ; CHECK: Addressing model must be Logical. Found OpMemoryModel Physical32 OpenCL
73 OpCapability Shader
74 OpMemoryModel Physical32 OpenCL
75 )";
76
77 SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
78 }
79
TEST_F(GraphicsRobustAccessTest,FailCantProcessPhysical64AddressingModel)80 TEST_F(GraphicsRobustAccessTest, FailCantProcessPhysical64AddressingModel) {
81 const std::string text = R"(
82 ; CHECK: Addressing model must be Logical. Found OpMemoryModel Physical64 OpenCL
83 OpCapability Shader
84 OpMemoryModel Physical64 OpenCL
85 )";
86
87 SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
88 }
89
TEST_F(GraphicsRobustAccessTest,FailCantProcessPhysicalStorageBuffer64EXTAddressingModel)90 TEST_F(GraphicsRobustAccessTest,
91 FailCantProcessPhysicalStorageBuffer64EXTAddressingModel) {
92 const std::string text = R"(
93 ; CHECK: Addressing model must be Logical. Found OpMemoryModel PhysicalStorageBuffer64 GLSL450
94 OpCapability Shader
95 OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
96 )";
97
98 SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
99 }
100
101 // Test access chains
102
103 // Returns the names of access chain instructions handled by the pass.
104 // For the purposes of this pass, regular and in-bounds access chains are the
105 // same.)
AccessChains()106 std::vector<const char*> AccessChains() {
107 return {"OpAccessChain", "OpInBoundsAccessChain"};
108 }
109
ShaderPreamble()110 std::string ShaderPreamble() {
111 return R"(
112 OpCapability Shader
113 OpMemoryModel Logical Simple
114 OpEntryPoint GLCompute %main "main"
115 )";
116 }
117
ShaderPreamble(const std::vector<std::string> & names)118 std::string ShaderPreamble(const std::vector<std::string>& names) {
119 std::ostringstream os;
120 os << ShaderPreamble();
121 for (auto& name : names) {
122 os << " OpName %" << name << " \"" << name << "\"\n";
123 }
124 return os.str();
125 }
126
ShaderPreambleAC()127 std::string ShaderPreambleAC() {
128 return ShaderPreamble({"ac", "ptr_ty", "var"});
129 }
130
ShaderPreambleAC(const std::vector<std::string> & names)131 std::string ShaderPreambleAC(const std::vector<std::string>& names) {
132 auto names2 = names;
133 names2.push_back("ac");
134 names2.push_back("ptr_ty");
135 names2.push_back("var");
136 return ShaderPreamble(names2);
137 }
138
DecoSSBO()139 std::string DecoSSBO() {
140 return R"(
141 OpDecorate %ssbo_s BufferBlock
142 OpMemberDecorate %ssbo_s 0 Offset 0
143 OpMemberDecorate %ssbo_s 1 Offset 4
144 OpMemberDecorate %ssbo_s 2 Offset 16
145 OpDecorate %var DescriptorSet 0
146 OpDecorate %var Binding 0
147 )";
148 }
149
TypesVoid()150 std::string TypesVoid() {
151 return R"(
152 %void = OpTypeVoid
153 %void_fn = OpTypeFunction %void
154 )";
155 }
156
TypesInt()157 std::string TypesInt() {
158 return R"(
159 %uint = OpTypeInt 32 0
160 %int = OpTypeInt 32 1
161 )";
162 }
163
TypesFloat()164 std::string TypesFloat() {
165 return R"(
166 %float = OpTypeFloat 32
167 )";
168 }
169
TypesShort()170 std::string TypesShort() {
171 return R"(
172 %ushort = OpTypeInt 16 0
173 %short = OpTypeInt 16 1
174 )";
175 }
176
TypesLong()177 std::string TypesLong() {
178 return R"(
179 %ulong = OpTypeInt 64 0
180 %long = OpTypeInt 64 1
181 )";
182 }
183
MainPrefix()184 std::string MainPrefix() {
185 return R"(
186 %main = OpFunction %void None %void_fn
187 %entry = OpLabel
188 )";
189 }
190
MainSuffix()191 std::string MainSuffix() {
192 return R"(
193 OpReturn
194 OpFunctionEnd
195 )";
196 }
197
ACCheck(const std::string & access_chain_inst,const std::string & original,const std::string & transformed)198 std::string ACCheck(const std::string& access_chain_inst,
199 const std::string& original,
200 const std::string& transformed) {
201 return "\n ; CHECK: %ac = " + access_chain_inst + " %ptr_ty %var" +
202 (transformed.empty() ? "" : " ") + transformed +
203 "\n ; CHECK-NOT: " + access_chain_inst +
204 "\n ; CHECK-NEXT: OpReturn"
205 "\n %ac = " +
206 access_chain_inst + " %ptr_ty %var " + (original.empty() ? "" : " ") +
207 original + "\n";
208 }
209
ACCheckFail(const std::string & access_chain_inst,const std::string & original,const std::string & transformed)210 std::string ACCheckFail(const std::string& access_chain_inst,
211 const std::string& original,
212 const std::string& transformed) {
213 return "\n ; CHECK: %ac = " + access_chain_inst + " %ptr_ty %var" +
214 (transformed.empty() ? "" : " ") + transformed +
215 "\n ; CHECK-NOT: " + access_chain_inst +
216 "\n ; CHECK-NOT: OpReturn"
217 "\n %ac = " +
218 access_chain_inst + " %ptr_ty %var " + (original.empty() ? "" : " ") +
219 original + "\n";
220 }
221
222 // Access chain into:
223 // Vector
224 // Vector sizes 2, 3, 4
225 // Matrix
226 // Matrix columns 2, 4
227 // Component is vector 2, 4
228 // Array
229 // Struct
230 // TODO(dneto): RuntimeArray
231
TEST_F(GraphicsRobustAccessTest,ACVectorLeastInboundConstantUntouched)232 TEST_F(GraphicsRobustAccessTest, ACVectorLeastInboundConstantUntouched) {
233 for (auto* ac : AccessChains()) {
234 std::ostringstream shaders;
235 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
236 %uvec2 = OpTypeVector %uint 2
237 %var_ty = OpTypePointer Function %uvec2
238 %ptr_ty = OpTypePointer Function %uint
239 %uint_0 = OpConstant %uint 0
240 )"
241 << MainPrefix() << R"(
242 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_0", "%uint_0")
243 << MainSuffix();
244 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
245 }
246 }
247
TEST_F(GraphicsRobustAccessTest,ACVectorMostInboundConstantUntouched)248 TEST_F(GraphicsRobustAccessTest, ACVectorMostInboundConstantUntouched) {
249 for (auto* ac : AccessChains()) {
250 std::ostringstream shaders;
251 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
252 %v4uint = OpTypeVector %uint 4
253 %var_ty = OpTypePointer Function %v4uint
254 %ptr_ty = OpTypePointer Function %uint
255 %uint_3 = OpConstant %uint 3
256 )"
257 << MainPrefix() << R"(
258 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_3", "%uint_3")
259 << MainSuffix();
260 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
261 }
262 }
263
TEST_F(GraphicsRobustAccessTest,ACVectorExcessConstantClamped)264 TEST_F(GraphicsRobustAccessTest, ACVectorExcessConstantClamped) {
265 for (auto* ac : AccessChains()) {
266 std::ostringstream shaders;
267 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
268 %v4uint = OpTypeVector %uint 4
269 %var_ty = OpTypePointer Function %v4uint
270 %ptr_ty = OpTypePointer Function %uint
271 %uint_4 = OpConstant %uint 4
272 )"
273 << MainPrefix() << R"(
274 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%uint_3")
275 << MainSuffix();
276 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
277 }
278 }
279
TEST_F(GraphicsRobustAccessTest,ACVectorNegativeConstantClamped)280 TEST_F(GraphicsRobustAccessTest, ACVectorNegativeConstantClamped) {
281 for (auto* ac : AccessChains()) {
282 std::ostringstream shaders;
283 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
284 %v4uint = OpTypeVector %uint 4
285 %var_ty = OpTypePointer Function %v4uint
286 %ptr_ty = OpTypePointer Function %uint
287 %int_n1 = OpConstant %int -1
288 )"
289 << MainPrefix() << R"(
290 ; CHECK: %int_0 = OpConstant %int 0
291 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%int_n1", "%int_0")
292 << MainSuffix();
293 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
294 }
295 }
296
297 // Like the previous test, but ensures the pass knows how to modify an index
298 // which does not come first in the access chain.
TEST_F(GraphicsRobustAccessTest,ACVectorInArrayNegativeConstantClamped)299 TEST_F(GraphicsRobustAccessTest, ACVectorInArrayNegativeConstantClamped) {
300 for (auto* ac : AccessChains()) {
301 std::ostringstream shaders;
302 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
303 %v4uint = OpTypeVector %uint 4
304 %uint_1 = OpConstant %uint 1
305 %uint_2 = OpConstant %uint 2
306 %arr = OpTypeArray %v4uint %uint_2
307 %var_ty = OpTypePointer Function %arr
308 %ptr_ty = OpTypePointer Function %uint
309 %int_n1 = OpConstant %int -1
310 )"
311 << MainPrefix() << R"(
312 ; CHECK: %int_0 = OpConstant %int 0
313 %var = OpVariable %var_ty Function)"
314 << ACCheck(ac, "%uint_1 %int_n1", "%uint_1 %int_0") << MainSuffix();
315 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
316 }
317 }
318
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralClamped)319 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralClamped) {
320 for (auto* ac : AccessChains()) {
321 std::ostringstream shaders;
322 shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() << R"(
323 %v4uint = OpTypeVector %uint 4
324 %var_ty = OpTypePointer Function %v4uint
325 %ptr_ty = OpTypePointer Function %uint
326 %i = OpUndef %int)"
327 << MainPrefix() << R"(
328 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
329 ; CHECK-DAG: %int_0 = OpConstant %int 0
330 ; CHECK-DAG: %int_3 = OpConstant %int 3
331 ; CHECK: OpLabel
332 ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3
333 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
334 << MainSuffix();
335 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
336 }
337 }
338
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralShortClamped)339 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralShortClamped) {
340 // Show that signed 16 bit integers are clamped as well.
341 for (auto* ac : AccessChains()) {
342 std::ostringstream shaders;
343 shaders << "OpCapability Int16\n"
344 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort() <<
345 R"(
346 %v4short = OpTypeVector %short 4
347 %var_ty = OpTypePointer Function %v4short
348 %ptr_ty = OpTypePointer Function %short
349 %i = OpUndef %short)"
350 << MainPrefix() << R"(
351 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
352 ; CHECK-NOT: = OpTypeInt 32
353 ; CHECK-DAG: %short_0 = OpConstant %short 0
354 ; CHECK-DAG: %short_3 = OpConstant %short 3
355 ; CHECK-NOT: = OpTypeInt 32
356 ; CHECK: OpLabel
357 ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] UClamp %i %short_0 %short_3
358 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
359 << MainSuffix();
360 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
361 }
362 }
363
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralUShortClamped)364 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralUShortClamped) {
365 // Show that unsigned 16 bit integers are clamped as well.
366 for (auto* ac : AccessChains()) {
367 std::ostringstream shaders;
368 shaders << "OpCapability Int16\n"
369 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort() <<
370 R"(
371 %v4ushort = OpTypeVector %ushort 4
372 %var_ty = OpTypePointer Function %v4ushort
373 %ptr_ty = OpTypePointer Function %ushort
374 %i = OpUndef %ushort)"
375 << MainPrefix() << R"(
376 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
377 ; CHECK-NOT: = OpTypeInt 32
378 ; CHECK-DAG: %ushort_0 = OpConstant %ushort 0
379 ; CHECK-DAG: %ushort_3 = OpConstant %ushort 3
380 ; CHECK-NOT: = OpTypeInt 32
381 ; CHECK: OpLabel
382 ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] UClamp %i %ushort_0 %ushort_3
383 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
384 << MainSuffix();
385 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
386 }
387 }
388
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralLongClamped)389 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralLongClamped) {
390 // Show that signed 64 bit integers are clamped as well.
391 for (auto* ac : AccessChains()) {
392 std::ostringstream shaders;
393 shaders << "OpCapability Int64\n"
394 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesLong() <<
395 R"(
396 %v4long = OpTypeVector %long 4
397 %var_ty = OpTypePointer Function %v4long
398 %ptr_ty = OpTypePointer Function %long
399 %i = OpUndef %long)"
400 << MainPrefix() << R"(
401 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
402 ; CHECK-NOT: = OpTypeInt 32
403 ; CHECK-DAG: %long_0 = OpConstant %long 0
404 ; CHECK-DAG: %long_3 = OpConstant %long 3
405 ; CHECK-NOT: = OpTypeInt 32
406 ; CHECK: OpLabel
407 ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_3
408 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
409 << MainSuffix();
410 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
411 }
412 }
413
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralULongClamped)414 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralULongClamped) {
415 // Show that unsigned 64 bit integers are clamped as well.
416 for (auto* ac : AccessChains()) {
417 std::ostringstream shaders;
418 shaders << "OpCapability Int64\n"
419 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesLong() <<
420 R"(
421 %v4ulong = OpTypeVector %ulong 4
422 %var_ty = OpTypePointer Function %v4ulong
423 %ptr_ty = OpTypePointer Function %ulong
424 %i = OpUndef %ulong)"
425 << MainPrefix() << R"(
426 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
427 ; CHECK-NOT: = OpTypeInt 32
428 ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
429 ; CHECK-DAG: %ulong_3 = OpConstant %ulong 3
430 ; CHECK-NOT: = OpTypeInt 32
431 ; CHECK: OpLabel
432 ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_3
433 %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
434 << MainSuffix();
435 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
436 }
437 }
438
TEST_F(GraphicsRobustAccessTest,ACMatrixLeastInboundConstantUntouched)439 TEST_F(GraphicsRobustAccessTest, ACMatrixLeastInboundConstantUntouched) {
440 for (auto* ac : AccessChains()) {
441 std::ostringstream shaders;
442 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
443 << TypesFloat() << R"(
444 %v2float = OpTypeVector %float 2
445 %mat4v2float = OpTypeMatrix %v2float 4
446 %var_ty = OpTypePointer Function %mat4v2float
447 %ptr_ty = OpTypePointer Function %float
448 %uint_0 = OpConstant %uint 0
449 %uint_1 = OpConstant %uint 1
450 )" << MainPrefix() << R"(
451 %var = OpVariable %var_ty Function)"
452 << ACCheck(ac, "%uint_0 %uint_1", "%uint_0 %uint_1")
453 << MainSuffix();
454 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
455 }
456 }
457
TEST_F(GraphicsRobustAccessTest,ACMatrixMostInboundConstantUntouched)458 TEST_F(GraphicsRobustAccessTest, ACMatrixMostInboundConstantUntouched) {
459 for (auto* ac : AccessChains()) {
460 std::ostringstream shaders;
461 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
462 << TypesFloat() << R"(
463 %v2float = OpTypeVector %float 2
464 %mat4v2float = OpTypeMatrix %v2float 4
465 %var_ty = OpTypePointer Function %mat4v2float
466 %ptr_ty = OpTypePointer Function %float
467 %uint_1 = OpConstant %uint 1
468 %uint_3 = OpConstant %uint 3
469 )" << MainPrefix() << R"(
470 %var = OpVariable %var_ty Function)"
471 << ACCheck(ac, "%uint_3 %uint_1", "%uint_3 %uint_1")
472 << MainSuffix();
473 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
474 }
475 }
476
TEST_F(GraphicsRobustAccessTest,ACMatrixExcessConstantClamped)477 TEST_F(GraphicsRobustAccessTest, ACMatrixExcessConstantClamped) {
478 for (auto* ac : AccessChains()) {
479 std::ostringstream shaders;
480 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
481 << TypesFloat() << R"(
482 %v2float = OpTypeVector %float 2
483 %mat4v2float = OpTypeMatrix %v2float 4
484 %var_ty = OpTypePointer Function %mat4v2float
485 %ptr_ty = OpTypePointer Function %float
486 %uint_1 = OpConstant %uint 1
487 %uint_4 = OpConstant %uint 4
488 )" << MainPrefix() << R"(
489 ; CHECK: %uint_3 = OpConstant %uint 3
490 %var = OpVariable %var_ty Function)"
491 << ACCheck(ac, "%uint_4 %uint_1", "%uint_3 %uint_1")
492 << MainSuffix();
493 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
494 }
495 }
496
TEST_F(GraphicsRobustAccessTest,ACMatrixNegativeConstantClamped)497 TEST_F(GraphicsRobustAccessTest, ACMatrixNegativeConstantClamped) {
498 for (auto* ac : AccessChains()) {
499 std::ostringstream shaders;
500 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
501 << TypesFloat() << R"(
502 %v2float = OpTypeVector %float 2
503 %mat4v2float = OpTypeMatrix %v2float 4
504 %var_ty = OpTypePointer Function %mat4v2float
505 %ptr_ty = OpTypePointer Function %float
506 %uint_1 = OpConstant %uint 1
507 %int_n1 = OpConstant %int -1
508 )" << MainPrefix() << R"(
509 ; CHECK: %int_0 = OpConstant %int 0
510 %var = OpVariable %var_ty Function)"
511 << ACCheck(ac, "%int_n1 %uint_1", "%int_0 %uint_1") << MainSuffix();
512 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
513 }
514 }
515
TEST_F(GraphicsRobustAccessTest,ACMatrixGeneralClamped)516 TEST_F(GraphicsRobustAccessTest, ACMatrixGeneralClamped) {
517 for (auto* ac : AccessChains()) {
518 std::ostringstream shaders;
519 shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
520 << TypesFloat() << R"(
521 %v2float = OpTypeVector %float 2
522 %mat4v2float = OpTypeMatrix %v2float 4
523 %var_ty = OpTypePointer Function %mat4v2float
524 %ptr_ty = OpTypePointer Function %float
525 %uint_1 = OpConstant %uint 1
526 %i = OpUndef %int
527 )" << MainPrefix() << R"(
528 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
529 ; CHECK-DAG: %int_0 = OpConstant %int 0
530 ; CHECK-DAG: %int_3 = OpConstant %int 3
531 ; CHECK: OpLabel
532 ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_3
533 %var = OpVariable %var_ty Function)"
534 << ACCheck(ac, "%i %uint_1", "%[[clamp]] %uint_1") << MainSuffix();
535 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
536 }
537 }
538
TEST_F(GraphicsRobustAccessTest,ACArrayLeastInboundConstantUntouched)539 TEST_F(GraphicsRobustAccessTest, ACArrayLeastInboundConstantUntouched) {
540 for (auto* ac : AccessChains()) {
541 std::ostringstream shaders;
542 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
543 << TypesFloat() << R"(
544 %uint_200 = OpConstant %uint 200
545 %arr = OpTypeArray %float %uint_200
546 %var_ty = OpTypePointer Function %arr
547 %ptr_ty = OpTypePointer Function %float
548 %int_0 = OpConstant %int 0
549 )" << MainPrefix() << R"(
550 %var = OpVariable %var_ty Function)"
551 << ACCheck(ac, "%int_0", "%int_0") << MainSuffix();
552 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
553 }
554 }
555
TEST_F(GraphicsRobustAccessTest,ACArrayMostInboundConstantUntouched)556 TEST_F(GraphicsRobustAccessTest, ACArrayMostInboundConstantUntouched) {
557 for (auto* ac : AccessChains()) {
558 std::ostringstream shaders;
559 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
560 << TypesFloat() << R"(
561 %uint_200 = OpConstant %uint 200
562 %arr = OpTypeArray %float %uint_200
563 %var_ty = OpTypePointer Function %arr
564 %ptr_ty = OpTypePointer Function %float
565 %int_199 = OpConstant %int 199
566 )" << MainPrefix() << R"(
567 %var = OpVariable %var_ty Function)"
568 << ACCheck(ac, "%int_199", "%int_199") << MainSuffix();
569 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
570 }
571 }
572
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralClamped)573 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralClamped) {
574 for (auto* ac : AccessChains()) {
575 std::ostringstream shaders;
576 shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
577 << TypesFloat() << R"(
578 %uint_200 = OpConstant %uint 200
579 %arr = OpTypeArray %float %uint_200
580 %var_ty = OpTypePointer Function %arr
581 %ptr_ty = OpTypePointer Function %float
582 %i = OpUndef %int
583 )" << MainPrefix() << R"(
584 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
585 ; CHECK-DAG: %int_0 = OpConstant %int 0
586 ; CHECK-DAG: %int_199 = OpConstant %int 199
587 ; CHECK: OpLabel
588 ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199
589 %var = OpVariable %var_ty Function)"
590 << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
591 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
592 }
593 }
594
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralShortIndexUIntBoundsClamped)595 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralShortIndexUIntBoundsClamped) {
596 // Index is signed short, array bounds overflows the index type.
597 for (auto* ac : AccessChains()) {
598 std::ostringstream shaders;
599 shaders << "OpCapability Int16\n"
600 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
601 << TypesShort() << TypesFloat() << R"(
602 %uint_70000 = OpConstant %uint 70000 ; overflows 16bits
603 %arr = OpTypeArray %float %uint_70000
604 %var_ty = OpTypePointer Function %arr
605 %ptr_ty = OpTypePointer Function %float
606 %i = OpUndef %short
607 )" << MainPrefix() << R"(
608 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
609 ; CHECK-DAG: %uint_0 = OpConstant %uint 0
610 ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999
611 ; CHECK: OpLabel
612 ; CHECK: %[[i_ext:\w+]] = OpSConvert %uint %i
613 ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999
614 %var = OpVariable %var_ty Function)"
615 << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
616 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
617 }
618 }
619
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralUShortIndexIntBoundsClamped)620 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUShortIndexIntBoundsClamped) {
621 // Index is unsigned short, array bounds overflows the index type.
622 for (auto* ac : AccessChains()) {
623 std::ostringstream shaders;
624 shaders << "OpCapability Int16\n"
625 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
626 << TypesShort() << TypesFloat() << R"(
627 %int_70000 = OpConstant %int 70000 ; overflows 16bits
628 %arr = OpTypeArray %float %int_70000
629 %var_ty = OpTypePointer Function %arr
630 %ptr_ty = OpTypePointer Function %float
631 %i = OpUndef %ushort
632 )" << MainPrefix() << R"(
633 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
634 ; CHECK-DAG: %uint_0 = OpConstant %uint 0
635 ; CHECK-DAG: %uint_69999 = OpConstant %uint 69999
636 ; CHECK: OpLabel
637 ; CHECK: %[[i_ext:\w+]] = OpUConvert %uint %i
638 ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %uint_69999
639 %var = OpVariable %var_ty Function)"
640 << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
641 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
642 }
643 }
644
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralUIntIndexShortBoundsClamped)645 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUIntIndexShortBoundsClamped) {
646 // Signed int index i is wider than the array bounds type.
647 for (auto* ac : AccessChains()) {
648 std::ostringstream shaders;
649 shaders << "OpCapability Int16\n"
650 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
651 << TypesShort() << TypesFloat() << R"(
652 %short_200 = OpConstant %short 200
653 %arr = OpTypeArray %float %short_200
654 %var_ty = OpTypePointer Function %arr
655 %ptr_ty = OpTypePointer Function %float
656 %i = OpUndef %uint
657 )" << MainPrefix() << R"(
658 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
659 ; CHECK-DAG: %uint_0 = OpConstant %uint 0
660 ; CHECK-DAG: %uint_199 = OpConstant %uint 199
661 ; CHECK: OpLabel
662 ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %uint_199
663 %var = OpVariable %var_ty Function)"
664 << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
665 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
666 }
667 }
668
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralIntIndexUShortBoundsClamped)669 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralIntIndexUShortBoundsClamped) {
670 // Unsigned int index i is wider than the array bounds type.
671 for (auto* ac : AccessChains()) {
672 std::ostringstream shaders;
673 shaders << "OpCapability Int16\n"
674 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
675 << TypesShort() << TypesFloat() << R"(
676 %ushort_200 = OpConstant %ushort 200
677 %arr = OpTypeArray %float %ushort_200
678 %var_ty = OpTypePointer Function %arr
679 %ptr_ty = OpTypePointer Function %float
680 %i = OpUndef %int
681 )" << MainPrefix() << R"(
682 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
683 ; CHECK-DAG: %int_0 = OpConstant %int 0
684 ; CHECK-DAG: %int_199 = OpConstant %int 199
685 ; CHECK: OpLabel
686 ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_199
687 %var = OpVariable %var_ty Function)"
688 << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
689 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
690 }
691 }
692
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralLongIndexUIntBoundsClamped)693 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralLongIndexUIntBoundsClamped) {
694 // Signed long index i is wider than the array bounds type.
695 for (auto* ac : AccessChains()) {
696 std::ostringstream shaders;
697 shaders << "OpCapability Int64\n"
698 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
699 << TypesLong() << TypesFloat() << R"(
700 %uint_200 = OpConstant %uint 200
701 %arr = OpTypeArray %float %uint_200
702 %var_ty = OpTypePointer Function %arr
703 %ptr_ty = OpTypePointer Function %float
704 %i = OpUndef %long
705 )" << MainPrefix() << R"(
706 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
707 ; CHECK-DAG: %long_0 = OpConstant %long 0
708 ; CHECK-DAG: %long_199 = OpConstant %long 199
709 ; CHECK: OpLabel
710 ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %long_199
711 %var = OpVariable %var_ty Function)"
712 << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
713 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
714 }
715 }
716
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralULongIndexIntBoundsClamped)717 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralULongIndexIntBoundsClamped) {
718 // Unsigned long index i is wider than the array bounds type.
719 for (auto* ac : AccessChains()) {
720 std::ostringstream shaders;
721 shaders << "OpCapability Int64\n"
722 << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
723 << TypesLong() << TypesFloat() << R"(
724 %int_200 = OpConstant %int 200
725 %arr = OpTypeArray %float %int_200
726 %var_ty = OpTypePointer Function %arr
727 %ptr_ty = OpTypePointer Function %float
728 %i = OpUndef %ulong
729 )" << MainPrefix() << R"(
730 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
731 ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
732 ; CHECK-DAG: %ulong_199 = OpConstant %ulong 199
733 ; CHECK: OpLabel
734 ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %ulong_199
735 %var = OpVariable %var_ty Function)"
736 << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
737 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
738 }
739 }
740
TEST_F(GraphicsRobustAccessTest,ACArraySpecIdSizedAlwaysClamped)741 TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) {
742 for (auto* ac : AccessChains()) {
743 std::ostringstream shaders;
744 shaders << ShaderPreambleAC({"spec200"}) << R"(
745 OpDecorate %spec200 SpecId 0 )" << TypesVoid() << TypesInt()
746 << TypesFloat() << R"(
747 %spec200 = OpSpecConstant %int 200
748 %arr = OpTypeArray %float %spec200
749 %var_ty = OpTypePointer Function %arr
750 %ptr_ty = OpTypePointer Function %float
751 %uint_5 = OpConstant %uint 5
752 )" << MainPrefix() << R"(
753 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
754 ; CHECK-DAG: %uint_0 = OpConstant %uint 0
755 ; CHECK-DAG: %uint_1 = OpConstant %uint 1
756 ; CHECK: OpLabel
757 ; CHECK: %[[max:\w+]] = OpISub %uint %spec200 %uint_1
758 ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %uint_5 %uint_0 %[[max]]
759 %var = OpVariable %var_ty Function)"
760 << ACCheck(ac, "%uint_5", "%[[clamp]]") << MainSuffix();
761 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
762 }
763 }
764
TEST_F(GraphicsRobustAccessTest,ACStructLeastUntouched)765 TEST_F(GraphicsRobustAccessTest, ACStructLeastUntouched) {
766 for (auto* ac : AccessChains()) {
767 std::ostringstream shaders;
768 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
769 << TypesFloat() << R"(
770 %struct = OpTypeStruct %float %float %float
771 %var_ty = OpTypePointer Function %struct
772 %ptr_ty = OpTypePointer Function %float
773 %int_0 = OpConstant %int 0
774 )" << MainPrefix() << R"(
775 %var = OpVariable %var_ty Function)"
776 << ACCheck(ac, "%int_0", "%int_0") << MainSuffix();
777 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
778 }
779 }
780
TEST_F(GraphicsRobustAccessTest,ACStructMostUntouched)781 TEST_F(GraphicsRobustAccessTest, ACStructMostUntouched) {
782 for (auto* ac : AccessChains()) {
783 std::ostringstream shaders;
784 shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
785 << TypesFloat() << R"(
786 %struct = OpTypeStruct %float %float %float
787 %var_ty = OpTypePointer Function %struct
788 %ptr_ty = OpTypePointer Function %float
789 %int_2 = OpConstant %int 2
790 )" << MainPrefix() << R"(
791 %var = OpVariable %var_ty Function)"
792 << ACCheck(ac, "%int_2", "%int_2") << MainSuffix();
793 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
794 }
795 }
796
TEST_F(GraphicsRobustAccessTest,ACStructSpecConstantFail)797 TEST_F(GraphicsRobustAccessTest, ACStructSpecConstantFail) {
798 for (auto* ac : AccessChains()) {
799 std::ostringstream shaders;
800 shaders << ShaderPreambleAC({"struct", "spec200"})
801 << "OpDecorate %spec200 SpecId 0\n"
802 <<
803
804 TypesVoid() << TypesInt() << TypesFloat() << R"(
805 %spec200 = OpSpecConstant %int 200
806 %struct = OpTypeStruct %float %float %float
807 %var_ty = OpTypePointer Function %struct
808 %ptr_ty = OpTypePointer Function %float
809 )" << MainPrefix() << R"(
810 %var = OpVariable %var_ty Function
811 ; CHECK: Member index into struct is not a constant integer
812 ; CHECK-SAME: %spec200 = OpSpecConstant %int 200
813 )"
814 << ACCheckFail(ac, "%spec200", "%spec200") << MainSuffix();
815 SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
816 }
817 }
818
TEST_F(GraphicsRobustAccessTest,ACStructFloatConstantFail)819 TEST_F(GraphicsRobustAccessTest, ACStructFloatConstantFail) {
820 for (auto* ac : AccessChains()) {
821 std::ostringstream shaders;
822 shaders << ShaderPreambleAC({"struct"}) <<
823
824 TypesVoid() << TypesInt() << TypesFloat() << R"(
825 %float_2 = OpConstant %float 2
826 %struct = OpTypeStruct %float %float %float
827 %var_ty = OpTypePointer Function %struct
828 %ptr_ty = OpTypePointer Function %float
829 )" << MainPrefix() << R"(
830 %var = OpVariable %var_ty Function
831 ; CHECK: Member index into struct is not a constant integer
832 ; CHECK-SAME: %float_2 = OpConstant %float 2
833 )"
834 << ACCheckFail(ac, "%float_2", "%float_2") << MainSuffix();
835 SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
836 }
837 }
838
TEST_F(GraphicsRobustAccessTest,ACStructNonConstantFail)839 TEST_F(GraphicsRobustAccessTest, ACStructNonConstantFail) {
840 for (auto* ac : AccessChains()) {
841 std::ostringstream shaders;
842 shaders << ShaderPreambleAC({"struct", "i"}) <<
843
844 TypesVoid() << TypesInt() << TypesFloat() << R"(
845 %float_2 = OpConstant %float 2
846 %struct = OpTypeStruct %float %float %float
847 %var_ty = OpTypePointer Function %struct
848 %ptr_ty = OpTypePointer Function %float
849 %i = OpUndef %int
850 )" << MainPrefix() << R"(
851 %var = OpVariable %var_ty Function
852 ; CHECK: Member index into struct is not a constant integer
853 ; CHECK-SAME: %i = OpUndef %int
854 )"
855 << ACCheckFail(ac, "%i", "%i") << MainSuffix();
856 SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
857 }
858 }
859
TEST_F(GraphicsRobustAccessTest,ACStructExcessFail)860 TEST_F(GraphicsRobustAccessTest, ACStructExcessFail) {
861 for (auto* ac : AccessChains()) {
862 std::ostringstream shaders;
863 shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt()
864 << TypesFloat() << R"(
865 %struct = OpTypeStruct %float %float %float
866 %var_ty = OpTypePointer Function %struct
867 %ptr_ty = OpTypePointer Function %float
868 %i = OpConstant %int 4
869 )" << MainPrefix() << R"(
870 %var = OpVariable %var_ty Function
871 ; CHECK: Member index 4 is out of bounds for struct type:
872 ; CHECK-SAME: %struct = OpTypeStruct %float %float %float
873 )"
874 << ACCheckFail(ac, "%i", "%i") << MainSuffix();
875 SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
876 }
877 }
878
TEST_F(GraphicsRobustAccessTest,ACStructNegativeFail)879 TEST_F(GraphicsRobustAccessTest, ACStructNegativeFail) {
880 for (auto* ac : AccessChains()) {
881 std::ostringstream shaders;
882 shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt()
883 << TypesFloat() << R"(
884 %struct = OpTypeStruct %float %float %float
885 %var_ty = OpTypePointer Function %struct
886 %ptr_ty = OpTypePointer Function %float
887 %i = OpConstant %int -1
888 )" << MainPrefix() << R"(
889 %var = OpVariable %var_ty Function
890 ; CHECK: Member index -1 is out of bounds for struct type:
891 ; CHECK-SAME: %struct = OpTypeStruct %float %float %float
892 )"
893 << ACCheckFail(ac, "%i", "%i") << MainSuffix();
894 SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
895 }
896 }
897
TEST_F(GraphicsRobustAccessTest,ACRTArrayLeastInboundClamped)898 TEST_F(GraphicsRobustAccessTest, ACRTArrayLeastInboundClamped) {
899 for (auto* ac : AccessChains()) {
900 std::ostringstream shaders;
901 shaders << ShaderPreambleAC() << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 "
902 << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
903 %rtarr = OpTypeRuntimeArray %float
904 %ssbo_s = OpTypeStruct %uint %uint %rtarr
905 %var_ty = OpTypePointer Uniform %ssbo_s
906 %ptr_ty = OpTypePointer Uniform %float
907 %var = OpVariable %var_ty Uniform
908 %int_0 = OpConstant %int 0
909 %int_2 = OpConstant %int 2
910 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
911 ; CHECK: %int_1 = OpConstant %int 1
912 ; CHECK: OpLabel
913 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
914 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
915 ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %int_0 %int_0 %[[max]]
916 )"
917 << MainPrefix() << ACCheck(ac, "%int_2 %int_0", "%int_2 %[[clamp]]")
918 << MainSuffix();
919 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
920 }
921 }
922
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralShortIndexClamped)923 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralShortIndexClamped) {
924 for (auto* ac : AccessChains()) {
925 std::ostringstream shaders;
926 shaders << "OpCapability Int16\n"
927 << ShaderPreambleAC({"i"})
928 << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
929 << TypesVoid() << TypesShort() << TypesFloat() << R"(
930 %rtarr = OpTypeRuntimeArray %float
931 %ssbo_s = OpTypeStruct %short %short %rtarr
932 %var_ty = OpTypePointer Uniform %ssbo_s
933 %ptr_ty = OpTypePointer Uniform %float
934 %var = OpVariable %var_ty Uniform
935 %short_2 = OpConstant %short 2
936 %i = OpUndef %short
937 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
938 ; CHECK: %uint = OpTypeInt 32 0
939 ; CHECK-DAG: %uint_1 = OpConstant %uint 1
940 ; CHECK-DAG: %uint_0 = OpConstant %uint 0
941 ; CHECK: OpLabel
942 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
943 ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
944 ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
945 ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]]
946 )"
947 << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
948 << MainSuffix();
949 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
950 }
951 }
952
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralUShortIndexClamped)953 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUShortIndexClamped) {
954 for (auto* ac : AccessChains()) {
955 std::ostringstream shaders;
956 shaders << "OpCapability Int16\n"
957 << ShaderPreambleAC({"i"})
958 << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
959 << TypesVoid() << TypesShort() << TypesFloat() << R"(
960 %rtarr = OpTypeRuntimeArray %float
961 %ssbo_s = OpTypeStruct %short %short %rtarr
962 %var_ty = OpTypePointer Uniform %ssbo_s
963 %ptr_ty = OpTypePointer Uniform %float
964 %var = OpVariable %var_ty Uniform
965 %short_2 = OpConstant %short 2
966 %i = OpUndef %ushort
967 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
968 ; CHECK: %uint = OpTypeInt 32 0
969 ; CHECK-DAG: %uint_1 = OpConstant %uint 1
970 ; CHECK-DAG: %uint_0 = OpConstant %uint 0
971 ; CHECK: OpLabel
972 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
973 ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
974 ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
975 ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %[[i_ext]] %uint_0 %[[max]]
976 )"
977 << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
978 << MainSuffix();
979 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
980 }
981 }
982
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralIntIndexClamped)983 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralIntIndexClamped) {
984 for (auto* ac : AccessChains()) {
985 std::ostringstream shaders;
986 shaders << ShaderPreambleAC({"i"})
987 << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
988 << TypesVoid() << TypesInt() << TypesFloat() << R"(
989 %rtarr = OpTypeRuntimeArray %float
990 %ssbo_s = OpTypeStruct %int %int %rtarr
991 %var_ty = OpTypePointer Uniform %ssbo_s
992 %ptr_ty = OpTypePointer Uniform %float
993 %var = OpVariable %var_ty Uniform
994 %int_2 = OpConstant %int 2
995 %i = OpUndef %int
996 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
997 ; CHECK-DAG: %int_1 = OpConstant %int 1
998 ; CHECK-DAG: %int_0 = OpConstant %int 0
999 ; CHECK: OpLabel
1000 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1001 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1002 ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
1003 )" << MainPrefix()
1004 << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
1005 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1006 }
1007 }
1008
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralUIntIndexClamped)1009 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUIntIndexClamped) {
1010 for (auto* ac : AccessChains()) {
1011 std::ostringstream shaders;
1012 shaders << ShaderPreambleAC({"i"})
1013 << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
1014 << TypesVoid() << TypesInt() << TypesFloat() << R"(
1015 %rtarr = OpTypeRuntimeArray %float
1016 %ssbo_s = OpTypeStruct %int %int %rtarr
1017 %var_ty = OpTypePointer Uniform %ssbo_s
1018 %ptr_ty = OpTypePointer Uniform %float
1019 %var = OpVariable %var_ty Uniform
1020 %int_2 = OpConstant %int 2
1021 %i = OpUndef %uint
1022 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1023 ; CHECK-DAG: %uint_1 = OpConstant %uint 1
1024 ; CHECK-DAG: %uint_0 = OpConstant %uint 0
1025 ; CHECK: OpLabel
1026 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1027 ; CHECK: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
1028 ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UClamp %i %uint_0 %[[max]]
1029 )" << MainPrefix()
1030 << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
1031 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1032 }
1033 }
1034
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralLongIndexClamped)1035 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralLongIndexClamped) {
1036 for (auto* ac : AccessChains()) {
1037 std::ostringstream shaders;
1038 shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
1039 << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
1040 << TypesVoid() << TypesInt() << TypesLong() << TypesFloat() << R"(
1041 %rtarr = OpTypeRuntimeArray %float
1042 %ssbo_s = OpTypeStruct %int %int %rtarr
1043 %var_ty = OpTypePointer Uniform %ssbo_s
1044 %ptr_ty = OpTypePointer Uniform %float
1045 %var = OpVariable %var_ty Uniform
1046 %int_2 = OpConstant %int 2
1047 %i = OpUndef %long
1048 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1049 ; CHECK-DAG: %long_0 = OpConstant %long 0
1050 ; CHECK-DAG: %long_1 = OpConstant %long 1
1051 ; CHECK: OpLabel
1052 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1053 ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
1054 ; CHECK: %[[max:\w+]] = OpISub %long %[[arrlen_ext]] %long_1
1055 ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] UClamp %i %long_0 %[[max]]
1056 )"
1057 << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
1058 << MainSuffix();
1059 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1060 }
1061 }
1062
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralULongIndexClamped)1063 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralULongIndexClamped) {
1064 for (auto* ac : AccessChains()) {
1065 std::ostringstream shaders;
1066 shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
1067 << "OpMemberDecorate %ssbo_s 0 ArrayStride 4 " << DecoSSBO()
1068 << TypesVoid() << TypesInt() << TypesLong() << TypesFloat() << R"(
1069 %rtarr = OpTypeRuntimeArray %float
1070 %ssbo_s = OpTypeStruct %int %int %rtarr
1071 %var_ty = OpTypePointer Uniform %ssbo_s
1072 %ptr_ty = OpTypePointer Uniform %float
1073 %var = OpVariable %var_ty Uniform
1074 %int_2 = OpConstant %int 2
1075 %i = OpUndef %ulong
1076 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1077 ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
1078 ; CHECK-DAG: %ulong_1 = OpConstant %ulong 1
1079 ; CHECK: OpLabel
1080 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1081 ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
1082 ; CHECK: %[[max:\w+]] = OpISub %ulong %[[arrlen_ext]] %ulong_1
1083 ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UClamp %i %ulong_0 %[[max]]
1084 )"
1085 << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
1086 << MainSuffix();
1087 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1088 }
1089 }
1090
TEST_F(GraphicsRobustAccessTest,ACRTArrayStructVectorElem)1091 TEST_F(GraphicsRobustAccessTest, ACRTArrayStructVectorElem) {
1092 // The point of this test is that the access chain can have indices past the
1093 // index into the runtime array. For good measure, the index into the final
1094 // struct is out of bounds. We have to clamp that index too.
1095 for (auto* ac : AccessChains()) {
1096 std::ostringstream shaders;
1097 shaders << ShaderPreambleAC({"i", "j"})
1098 << "OpMemberDecorate %ssbo_s 0 ArrayStride 32\n"
1099 << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1100 << "OpMemberDecorate %rtelem 1 Offset 16\n"
1101 << TypesVoid() << TypesInt() << TypesFloat() << R"(
1102 %v4float = OpTypeVector %float 4
1103 %rtelem = OpTypeStruct %v4float %v4float
1104 %rtarr = OpTypeRuntimeArray %rtelem
1105 %ssbo_s = OpTypeStruct %int %int %rtarr
1106 %var_ty = OpTypePointer Uniform %ssbo_s
1107 %ptr_ty = OpTypePointer Uniform %float
1108 %var = OpVariable %var_ty Uniform
1109 %int_1 = OpConstant %int 1
1110 %int_2 = OpConstant %int 2
1111 %i = OpUndef %int
1112 %j = OpUndef %int
1113 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1114 ; CHECK-DAG: %int_0 = OpConstant %int 0
1115 ; CHECK-DAG: %int_3 = OpConstant %int 3
1116 ; CHECK: OpLabel
1117 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1118 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1119 ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
1120 ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %int_3
1121 )" << MainPrefix()
1122 << ACCheck(ac, "%int_2 %i %int_1 %j",
1123 "%int_2 %[[clamp_i]] %int_1 %[[clamp_j]]")
1124 << MainSuffix();
1125 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1126 }
1127 }
1128
TEST_F(GraphicsRobustAccessTest,ACArrayRTArrayStructVectorElem)1129 TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) {
1130 // Now add an additional level of arrays around the Block-decorated struct.
1131 for (auto* ac : AccessChains()) {
1132 std::ostringstream shaders;
1133 shaders << ShaderPreambleAC({"i", "ssbo_s"})
1134 << "OpMemberDecorate %ssbo_s 0 ArrayStride 32\n"
1135 << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1136 << "OpMemberDecorate %rtelem 1 Offset 16\n"
1137 << TypesVoid() << TypesInt() << TypesFloat() << R"(
1138 %v4float = OpTypeVector %float 4
1139 %rtelem = OpTypeStruct %v4float %v4float
1140 %rtarr = OpTypeRuntimeArray %rtelem
1141 %ssbo_s = OpTypeStruct %int %int %rtarr
1142 %arr_size = OpConstant %int 10
1143 %arr_ssbo = OpTypeArray %ssbo_s %arr_size
1144 %var_ty = OpTypePointer Uniform %arr_ssbo
1145 %ptr_ty = OpTypePointer Uniform %float
1146 %var = OpVariable %var_ty Uniform
1147 %int_1 = OpConstant %int 1
1148 %int_2 = OpConstant %int 2
1149 %int_17 = OpConstant %int 17
1150 %i = OpUndef %int
1151 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1152 ; CHECK-DAG: %[[ssbo_p:\w+]] = OpTypePointer Uniform %ssbo_s
1153 ; CHECK-DAG: %int_0 = OpConstant %int 0
1154 ; CHECK-DAG: %int_9 = OpConstant %int 9
1155 ; CHECK: OpLabel
1156 ; This access chain is manufatured only so we can compute the array length.
1157 ; Note that the %int_9 is already clamped
1158 ; CHECK: %[[ssbo_base:\w+]] = )" << ac
1159 << R"( %[[ssbo_p]] %var %int_9
1160 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %[[ssbo_base]] 2
1161 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1162 ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %[[max]]
1163 )" << MainPrefix()
1164 << ACCheck(ac, "%int_17 %int_2 %i %int_1 %int_2",
1165 "%int_9 %int_2 %[[clamp_i]] %int_1 %int_2")
1166 << MainSuffix();
1167 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1168 }
1169 }
1170
TEST_F(GraphicsRobustAccessTest,ACSplitACArrayRTArrayStructVectorElem)1171 TEST_F(GraphicsRobustAccessTest, ACSplitACArrayRTArrayStructVectorElem) {
1172 // Split the address calculation across two access chains. Force
1173 // the transform to walk up the access chains to find the base variable.
1174 for (auto* ac : AccessChains()) {
1175 std::ostringstream shaders;
1176 shaders << ShaderPreambleAC({"i", "j", "k", "ssbo_s", "ssbo_pty",
1177 "rtarr_pty", "ac_ssbo", "ac_rtarr"})
1178 << "OpMemberDecorate %ssbo_s 0 ArrayStride 32\n"
1179 << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1180 << "OpMemberDecorate %rtelem 1 Offset 16\n"
1181 << TypesVoid() << TypesInt() << TypesFloat() << R"(
1182 %v4float = OpTypeVector %float 4
1183 %rtelem = OpTypeStruct %v4float %v4float
1184 %rtarr = OpTypeRuntimeArray %rtelem
1185 %ssbo_s = OpTypeStruct %int %int %rtarr
1186 %arr_size = OpConstant %int 10
1187 %arr_ssbo = OpTypeArray %ssbo_s %arr_size
1188 %var_ty = OpTypePointer Uniform %arr_ssbo
1189 %ssbo_pty = OpTypePointer Uniform %ssbo_s
1190 %rtarr_pty = OpTypePointer Uniform %rtarr
1191 %ptr_ty = OpTypePointer Uniform %float
1192 %var = OpVariable %var_ty Uniform
1193 %int_1 = OpConstant %int 1
1194 %int_2 = OpConstant %int 2
1195 %i = OpUndef %int
1196 %j = OpUndef %int
1197 %k = OpUndef %int
1198 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1199 ; CHECK-DAG: %int_0 = OpConstant %int 0
1200 ; CHECK-DAG: %int_9 = OpConstant %int 9
1201 ; CHECK-DAG: %int_3 = OpConstant %int 3
1202 ; CHECK: OpLabel
1203 ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9
1204 ; CHECK: %ac_ssbo = )" << ac
1205 << R"( %ssbo_pty %var %[[clamp_i]]
1206 ; CHECK: %ac_rtarr = )"
1207 << ac << R"( %rtarr_pty %ac_ssbo %int_2
1208
1209 ; This is the interesting bit. This array length is needed for an OpAccessChain
1210 ; computing %ac, but the algorithm had to track back through %ac_rtarr's
1211 ; definition to find the base pointer %ac_ssbo.
1212 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
1213 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1214 ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]]
1215 ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3
1216 ; CHECK: %ac = )" << ac
1217 << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
1218 ; CHECK-NOT: AccessChain
1219 )" << MainPrefix()
1220 << "%ac_ssbo = " << ac << " %ssbo_pty %var %i\n"
1221 << "%ac_rtarr = " << ac << " %rtarr_pty %ac_ssbo %int_2\n"
1222 << "%ac = " << ac << " %ptr_ty %ac_rtarr %j %int_1 %k\n"
1223
1224 << MainSuffix();
1225 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1226 }
1227 }
1228
TEST_F(GraphicsRobustAccessTest,ACSplitACArrayRTArrayStructVectorElemAcrossBasicBlocks)1229 TEST_F(GraphicsRobustAccessTest,
1230 ACSplitACArrayRTArrayStructVectorElemAcrossBasicBlocks) {
1231 // Split the address calculation across two access chains. Force
1232 // the transform to walk up the access chains to find the base variable.
1233 // This time, put the different access chains in different basic blocks.
1234 // This sanity checks that we keep the instruction-to-block mapping
1235 // consistent.
1236 for (auto* ac : AccessChains()) {
1237 std::ostringstream shaders;
1238 shaders << ShaderPreambleAC({"i", "j", "k", "bb1", "bb2", "ssbo_s",
1239 "ssbo_pty", "rtarr_pty", "ac_ssbo",
1240 "ac_rtarr"})
1241 << "OpMemberDecorate %ssbo_s 0 ArrayStride 32\n"
1242 << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1243 << "OpMemberDecorate %rtelem 1 Offset 16\n"
1244 << TypesVoid() << TypesInt() << TypesFloat() << R"(
1245 %v4float = OpTypeVector %float 4
1246 %rtelem = OpTypeStruct %v4float %v4float
1247 %rtarr = OpTypeRuntimeArray %rtelem
1248 %ssbo_s = OpTypeStruct %int %int %rtarr
1249 %arr_size = OpConstant %int 10
1250 %arr_ssbo = OpTypeArray %ssbo_s %arr_size
1251 %var_ty = OpTypePointer Uniform %arr_ssbo
1252 %ssbo_pty = OpTypePointer Uniform %ssbo_s
1253 %rtarr_pty = OpTypePointer Uniform %rtarr
1254 %ptr_ty = OpTypePointer Uniform %float
1255 %var = OpVariable %var_ty Uniform
1256 %int_1 = OpConstant %int 1
1257 %int_2 = OpConstant %int 2
1258 %i = OpUndef %int
1259 %j = OpUndef %int
1260 %k = OpUndef %int
1261 ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1262 ; CHECK-DAG: %int_0 = OpConstant %int 0
1263 ; CHECK-DAG: %int_9 = OpConstant %int 9
1264 ; CHECK-DAG: %int_3 = OpConstant %int 3
1265 ; CHECK: OpLabel
1266 ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %i %int_0 %int_9
1267 ; CHECK: %ac_ssbo = )" << ac
1268 << R"( %ssbo_pty %var %[[clamp_i]]
1269 ; CHECK: %bb1 = OpLabel
1270 ; CHECK: %ac_rtarr = )"
1271 << ac << R"( %rtarr_pty %ac_ssbo %int_2
1272 ; CHECK: %bb2 = OpLabel
1273
1274 ; This is the interesting bit. This array length is needed for an OpAccessChain
1275 ; computing %ac, but the algorithm had to track back through %ac_rtarr's
1276 ; definition to find the base pointer %ac_ssbo.
1277 ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
1278 ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1279 ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %j %int_0 %[[max]]
1280 ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] UClamp %k %int_0 %int_3
1281 ; CHECK: %ac = )" << ac
1282 << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
1283 ; CHECK-NOT: AccessChain
1284 )" << MainPrefix()
1285 << "%ac_ssbo = " << ac << " %ssbo_pty %var %i\n"
1286 << "OpBranch %bb1\n%bb1 = OpLabel\n"
1287 << "%ac_rtarr = " << ac << " %rtarr_pty %ac_ssbo %int_2\n"
1288 << "OpBranch %bb2\n%bb2 = OpLabel\n"
1289 << "%ac = " << ac << " %ptr_ty %ac_rtarr %j %int_1 %k\n"
1290
1291 << MainSuffix();
1292 SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1293 }
1294 }
1295
1296 // TODO(dneto): Test access chain index wider than 64 bits?
1297 // TODO(dneto): Test struct access chain index wider than 64 bits?
1298 // TODO(dneto): OpImageTexelPointer
1299 // - all Dim types: 1D 2D Cube 3D Rect Buffer
1300 // - all Dim types that can be arrayed: 1D 2D 3D
1301 // - sample index: set to 0 if not multisampled
1302 // - Dim (2D, Cube Rect} with multisampling
1303 // -1 0 max excess
1304 // TODO(dneto): Test OpImageTexelPointer with coordinate component index other
1305 // than 32 bits.
1306
1307 } // namespace
1308