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