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", "%int_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]] SClamp %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]] SClamp %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: %short_0 = OpConstant %short 0
379        ; CHECK-DAG: %short_3 = OpConstant %short 3
380        ; CHECK-NOT: = OpTypeInt 32
381        ; CHECK: OpLabel
382        ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %short_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]] SClamp %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: %long_0 = OpConstant %long 0
429        ; CHECK-DAG: %long_3 = OpConstant %long 3
430        ; CHECK-NOT: = OpTypeInt 32
431        ; CHECK: OpLabel
432        ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_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: %int_3 = OpConstant %int 3
490        %var = OpVariable %var_ty Function)"
491             << ACCheck(ac, "%uint_4 %uint_1", "%int_3 %uint_1") << MainSuffix();
492     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
493   }
494 }
495 
TEST_F(GraphicsRobustAccessTest,ACMatrixNegativeConstantClamped)496 TEST_F(GraphicsRobustAccessTest, ACMatrixNegativeConstantClamped) {
497   for (auto* ac : AccessChains()) {
498     std::ostringstream shaders;
499     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
500             << TypesFloat() << R"(
501        %v2float = OpTypeVector %float 2
502        %mat4v2float = OpTypeMatrix %v2float 4
503        %var_ty = OpTypePointer Function %mat4v2float
504        %ptr_ty = OpTypePointer Function %float
505        %uint_1 = OpConstant %uint 1
506        %int_n1 = OpConstant %int -1
507        )" << MainPrefix() << R"(
508        ; CHECK: %int_0 = OpConstant %int 0
509        %var = OpVariable %var_ty Function)"
510             << ACCheck(ac, "%int_n1 %uint_1", "%int_0 %uint_1") << MainSuffix();
511     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
512   }
513 }
514 
TEST_F(GraphicsRobustAccessTest,ACMatrixGeneralClamped)515 TEST_F(GraphicsRobustAccessTest, ACMatrixGeneralClamped) {
516   for (auto* ac : AccessChains()) {
517     std::ostringstream shaders;
518     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
519             << TypesFloat() << R"(
520        %v2float = OpTypeVector %float 2
521        %mat4v2float = OpTypeMatrix %v2float 4
522        %var_ty = OpTypePointer Function %mat4v2float
523        %ptr_ty = OpTypePointer Function %float
524        %uint_1 = OpConstant %uint 1
525        %i = OpUndef %int
526        )" << MainPrefix() << R"(
527        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
528        ; CHECK-DAG: %int_0 = OpConstant %int 0
529        ; CHECK-DAG: %int_3 = OpConstant %int 3
530        ; CHECK: OpLabel
531        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3
532        %var = OpVariable %var_ty Function)"
533             << ACCheck(ac, "%i %uint_1", "%[[clamp]] %uint_1") << MainSuffix();
534     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
535   }
536 }
537 
TEST_F(GraphicsRobustAccessTest,ACArrayLeastInboundConstantUntouched)538 TEST_F(GraphicsRobustAccessTest, ACArrayLeastInboundConstantUntouched) {
539   for (auto* ac : AccessChains()) {
540     std::ostringstream shaders;
541     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
542             << TypesFloat() << R"(
543        %uint_200 = OpConstant %uint 200
544        %arr = OpTypeArray %float %uint_200
545        %var_ty = OpTypePointer Function %arr
546        %ptr_ty = OpTypePointer Function %float
547        %int_0 = OpConstant %int 0
548        )" << MainPrefix() << R"(
549        %var = OpVariable %var_ty Function)"
550             << ACCheck(ac, "%int_0", "%int_0") << MainSuffix();
551     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
552   }
553 }
554 
TEST_F(GraphicsRobustAccessTest,ACArrayMostInboundConstantUntouched)555 TEST_F(GraphicsRobustAccessTest, ACArrayMostInboundConstantUntouched) {
556   for (auto* ac : AccessChains()) {
557     std::ostringstream shaders;
558     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
559             << TypesFloat() << R"(
560        %uint_200 = OpConstant %uint 200
561        %arr = OpTypeArray %float %uint_200
562        %var_ty = OpTypePointer Function %arr
563        %ptr_ty = OpTypePointer Function %float
564        %int_199 = OpConstant %int 199
565        )" << MainPrefix() << R"(
566        %var = OpVariable %var_ty Function)"
567             << ACCheck(ac, "%int_199", "%int_199") << MainSuffix();
568     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
569   }
570 }
571 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralClamped)572 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralClamped) {
573   for (auto* ac : AccessChains()) {
574     std::ostringstream shaders;
575     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
576             << TypesFloat() << R"(
577        %uint_200 = OpConstant %uint 200
578        %arr = OpTypeArray %float %uint_200
579        %var_ty = OpTypePointer Function %arr
580        %ptr_ty = OpTypePointer Function %float
581        %i = OpUndef %int
582        )" << MainPrefix() << R"(
583        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
584        ; CHECK-DAG: %int_0 = OpConstant %int 0
585        ; CHECK-DAG: %int_199 = OpConstant %int 199
586        ; CHECK: OpLabel
587        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
588        %var = OpVariable %var_ty Function)"
589             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
590     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
591   }
592 }
593 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralShortIndexUIntBoundsClamped)594 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralShortIndexUIntBoundsClamped) {
595   // Index is signed short, array bounds overflows the index type.
596   for (auto* ac : AccessChains()) {
597     std::ostringstream shaders;
598     shaders << "OpCapability Int16\n"
599             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
600             << TypesShort() << TypesFloat() << R"(
601        %uint_70000 = OpConstant %uint 70000 ; overflows 16bits
602        %arr = OpTypeArray %float %uint_70000
603        %var_ty = OpTypePointer Function %arr
604        %ptr_ty = OpTypePointer Function %float
605        %i = OpUndef %short
606        )" << MainPrefix() << R"(
607        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
608        ; CHECK-DAG: %int_0 = OpConstant %int 0
609        ; CHECK-DAG: %int_69999 = OpConstant %int 69999
610        ; CHECK: OpLabel
611        ; CHECK: %[[i_ext:\w+]] = OpSConvert %uint %i
612        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
613        %var = OpVariable %var_ty Function)"
614             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
615     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
616   }
617 }
618 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralUShortIndexIntBoundsClamped)619 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUShortIndexIntBoundsClamped) {
620   // Index is unsigned short, array bounds overflows the index type.
621   for (auto* ac : AccessChains()) {
622     std::ostringstream shaders;
623     shaders << "OpCapability Int16\n"
624             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
625             << TypesShort() << TypesFloat() << R"(
626        %int_70000 = OpConstant %int 70000 ; overflows 16bits
627        %arr = OpTypeArray %float %int_70000
628        %var_ty = OpTypePointer Function %arr
629        %ptr_ty = OpTypePointer Function %float
630        %i = OpUndef %ushort
631        )" << MainPrefix() << R"(
632        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
633        ; CHECK-DAG: %int_0 = OpConstant %int 0
634        ; CHECK-DAG: %int_69999 = OpConstant %int 69999
635        ; CHECK: OpLabel
636        ; CHECK: %[[i_ext:\w+]] = OpUConvert %uint %i
637        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
638        %var = OpVariable %var_ty Function)"
639             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
640     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
641   }
642 }
643 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralUIntIndexShortBoundsClamped)644 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUIntIndexShortBoundsClamped) {
645   // Signed int index i is wider than the array bounds type.
646   for (auto* ac : AccessChains()) {
647     std::ostringstream shaders;
648     shaders << "OpCapability Int16\n"
649             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
650             << TypesShort() << TypesFloat() << R"(
651        %short_200 = OpConstant %short 200
652        %arr = OpTypeArray %float %short_200
653        %var_ty = OpTypePointer Function %arr
654        %ptr_ty = OpTypePointer Function %float
655        %i = OpUndef %uint
656        )" << MainPrefix() << R"(
657        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
658        ; CHECK-DAG: %int_0 = OpConstant %int 0
659        ; CHECK-DAG: %int_199 = OpConstant %int 199
660        ; CHECK: OpLabel
661        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %int_199
662        %var = OpVariable %var_ty Function)"
663             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
664     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
665   }
666 }
667 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralIntIndexUShortBoundsClamped)668 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralIntIndexUShortBoundsClamped) {
669   // Unsigned int index i is wider than the array bounds type.
670   for (auto* ac : AccessChains()) {
671     std::ostringstream shaders;
672     shaders << "OpCapability Int16\n"
673             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
674             << TypesShort() << TypesFloat() << R"(
675        %ushort_200 = OpConstant %ushort 200
676        %arr = OpTypeArray %float %ushort_200
677        %var_ty = OpTypePointer Function %arr
678        %ptr_ty = OpTypePointer Function %float
679        %i = OpUndef %int
680        )" << MainPrefix() << R"(
681        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
682        ; CHECK-DAG: %int_0 = OpConstant %int 0
683        ; CHECK-DAG: %int_199 = OpConstant %int 199
684        ; CHECK: OpLabel
685        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
686        %var = OpVariable %var_ty Function)"
687             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
688     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
689   }
690 }
691 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralLongIndexUIntBoundsClamped)692 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralLongIndexUIntBoundsClamped) {
693   // Signed long index i is wider than the array bounds type.
694   for (auto* ac : AccessChains()) {
695     std::ostringstream shaders;
696     shaders << "OpCapability Int64\n"
697             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
698             << TypesLong() << TypesFloat() << R"(
699        %uint_200 = OpConstant %uint 200
700        %arr = OpTypeArray %float %uint_200
701        %var_ty = OpTypePointer Function %arr
702        %ptr_ty = OpTypePointer Function %float
703        %i = OpUndef %long
704        )" << MainPrefix() << R"(
705        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
706        ; CHECK-DAG: %long_0 = OpConstant %long 0
707        ; CHECK-DAG: %long_199 = OpConstant %long 199
708        ; CHECK: OpLabel
709        ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_199
710        %var = OpVariable %var_ty Function)"
711             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
712     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
713   }
714 }
715 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralULongIndexIntBoundsClamped)716 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralULongIndexIntBoundsClamped) {
717   // Unsigned long index i is wider than the array bounds type.
718   for (auto* ac : AccessChains()) {
719     std::ostringstream shaders;
720     shaders << "OpCapability Int64\n"
721             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
722             << TypesLong() << TypesFloat() << R"(
723        %int_200 = OpConstant %int 200
724        %arr = OpTypeArray %float %int_200
725        %var_ty = OpTypePointer Function %arr
726        %ptr_ty = OpTypePointer Function %float
727        %i = OpUndef %ulong
728        )" << MainPrefix() << R"(
729        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
730        ; CHECK-DAG: %long_0 = OpConstant %long 0
731        ; CHECK-DAG: %long_199 = OpConstant %long 199
732        ; CHECK: OpLabel
733        ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_199
734        %var = OpVariable %var_ty Function)"
735             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
736     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
737   }
738 }
739 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax)740 TEST_F(GraphicsRobustAccessTest,
741        ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax) {
742   for (auto* ac : AccessChains()) {
743     std::ostringstream shaders;
744     shaders << "OpCapability Int16\n"
745             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort()
746             << TypesInt() << TypesFloat() << R"(
747        %uint_50000 = OpConstant %uint 50000
748        %arr = OpTypeArray %float %uint_50000
749        %var_ty = OpTypePointer Function %arr
750        %ptr_ty = OpTypePointer Function %float
751        %i = OpUndef %ushort
752        )" << MainPrefix() << R"(
753        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
754        ; CHECK-DAG: %short_0 = OpConstant %short 0
755        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %short 32767
756        ; CHECK: OpLabel
757        ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %[[intmax]]
758        %var = OpVariable %var_ty Function)"
759             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
760     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
761   }
762 }
763 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax)764 TEST_F(GraphicsRobustAccessTest,
765        ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax) {
766   for (auto* ac : AccessChains()) {
767     std::ostringstream shaders;
768     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
769             << TypesFloat() << R"(
770        %uint_3000000000 = OpConstant %uint 3000000000
771        %arr = OpTypeArray %float %uint_3000000000
772        %var_ty = OpTypePointer Function %arr
773        %ptr_ty = OpTypePointer Function %float
774        %i = OpUndef %uint
775        )" << MainPrefix() << R"(
776        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
777        ; CHECK-DAG: %int_0 = OpConstant %int 0
778        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
779        ; CHECK: OpLabel
780        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %[[intmax]]
781        %var = OpVariable %var_ty Function)"
782             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
783     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
784   }
785 }
786 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax)787 TEST_F(GraphicsRobustAccessTest,
788        ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax) {
789   for (auto* ac : AccessChains()) {
790     std::ostringstream shaders;
791     shaders << "OpCapability Int64\n"
792             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
793             << TypesLong()
794             << TypesFloat()
795             // 2^63 == 9,223,372,036,854,775,807
796             << R"(
797        %ulong_9223372036854775999 = OpConstant %ulong 9223372036854775999
798        %arr = OpTypeArray %float %ulong_9223372036854775999
799        %var_ty = OpTypePointer Function %arr
800        %ptr_ty = OpTypePointer Function %float
801        %i = OpUndef %ulong
802        )"
803             << MainPrefix() << R"(
804        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
805        ; CHECK-DAG: %long_0 = OpConstant %long 0
806        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %long 9223372036854775807
807        ; CHECK: OpLabel
808        ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %[[intmax]]
809        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
810             << MainSuffix();
811     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
812   }
813 }
814 
TEST_F(GraphicsRobustAccessTest,ACArraySpecIdSizedAlwaysClamped)815 TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) {
816   for (auto* ac : AccessChains()) {
817     std::ostringstream shaders;
818     shaders << ShaderPreambleAC({"spec200"}) << R"(
819        OpDecorate %spec200 SpecId 0 )" << TypesVoid() << TypesInt()
820             << TypesFloat() << R"(
821        %spec200 = OpSpecConstant %int 200
822        %arr = OpTypeArray %float %spec200
823        %var_ty = OpTypePointer Function %arr
824        %ptr_ty = OpTypePointer Function %float
825        %uint_5 = OpConstant %uint 5
826        )" << MainPrefix() << R"(
827        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
828        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
829        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
830        ; CHECK-DAG: %[[uint_intmax:\w+]] = OpConstant %uint 2147483647
831        ; CHECK: OpLabel
832        ; CHECK: %[[max:\w+]] = OpISub %uint %spec200 %uint_1
833        ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[uint_intmax]]
834        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %uint_5 %uint_0 %[[smin]]
835        %var = OpVariable %var_ty Function)"
836             << ACCheck(ac, "%uint_5", "%[[clamp]]") << MainSuffix();
837     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
838   }
839 }
840 
TEST_F(GraphicsRobustAccessTest,ACStructLeastUntouched)841 TEST_F(GraphicsRobustAccessTest, ACStructLeastUntouched) {
842   for (auto* ac : AccessChains()) {
843     std::ostringstream shaders;
844     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
845             << TypesFloat() << R"(
846        %struct = OpTypeStruct %float %float %float
847        %var_ty = OpTypePointer Function %struct
848        %ptr_ty = OpTypePointer Function %float
849        %int_0 = OpConstant %int 0
850        )" << MainPrefix() << R"(
851        %var = OpVariable %var_ty Function)"
852             << ACCheck(ac, "%int_0", "%int_0") << MainSuffix();
853     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
854   }
855 }
856 
TEST_F(GraphicsRobustAccessTest,ACStructMostUntouched)857 TEST_F(GraphicsRobustAccessTest, ACStructMostUntouched) {
858   for (auto* ac : AccessChains()) {
859     std::ostringstream shaders;
860     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
861             << TypesFloat() << R"(
862        %struct = OpTypeStruct %float %float %float
863        %var_ty = OpTypePointer Function %struct
864        %ptr_ty = OpTypePointer Function %float
865        %int_2 = OpConstant %int 2
866        )" << MainPrefix() << R"(
867        %var = OpVariable %var_ty Function)"
868             << ACCheck(ac, "%int_2", "%int_2") << MainSuffix();
869     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
870   }
871 }
872 
TEST_F(GraphicsRobustAccessTest,ACStructSpecConstantFail)873 TEST_F(GraphicsRobustAccessTest, ACStructSpecConstantFail) {
874   for (auto* ac : AccessChains()) {
875     std::ostringstream shaders;
876     shaders << ShaderPreambleAC({"struct", "spec200"})
877             << "OpDecorate %spec200 SpecId 0\n"
878             <<
879 
880         TypesVoid() << TypesInt() << TypesFloat() << R"(
881        %spec200 = OpSpecConstant %int 200
882        %struct = OpTypeStruct %float %float %float
883        %var_ty = OpTypePointer Function %struct
884        %ptr_ty = OpTypePointer Function %float
885        )" << MainPrefix() << R"(
886        %var = OpVariable %var_ty Function
887        ; CHECK: Member index into struct is not a constant integer
888        ; CHECK-SAME: %spec200 = OpSpecConstant %int 200
889        )"
890             << ACCheckFail(ac, "%spec200", "%spec200") << MainSuffix();
891     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
892   }
893 }
894 
TEST_F(GraphicsRobustAccessTest,ACStructFloatConstantFail)895 TEST_F(GraphicsRobustAccessTest, ACStructFloatConstantFail) {
896   for (auto* ac : AccessChains()) {
897     std::ostringstream shaders;
898     shaders << ShaderPreambleAC({"struct"}) <<
899 
900         TypesVoid() << TypesInt() << TypesFloat() << R"(
901        %float_2 = OpConstant %float 2
902        %struct = OpTypeStruct %float %float %float
903        %var_ty = OpTypePointer Function %struct
904        %ptr_ty = OpTypePointer Function %float
905        )" << MainPrefix() << R"(
906        %var = OpVariable %var_ty Function
907        ; CHECK: Member index into struct is not a constant integer
908        ; CHECK-SAME: %float_2 = OpConstant %float 2
909        )"
910             << ACCheckFail(ac, "%float_2", "%float_2") << MainSuffix();
911     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
912   }
913 }
914 
TEST_F(GraphicsRobustAccessTest,ACStructNonConstantFail)915 TEST_F(GraphicsRobustAccessTest, ACStructNonConstantFail) {
916   for (auto* ac : AccessChains()) {
917     std::ostringstream shaders;
918     shaders << ShaderPreambleAC({"struct", "i"}) <<
919 
920         TypesVoid() << TypesInt() << TypesFloat() << R"(
921        %float_2 = OpConstant %float 2
922        %struct = OpTypeStruct %float %float %float
923        %var_ty = OpTypePointer Function %struct
924        %ptr_ty = OpTypePointer Function %float
925        %i = OpUndef %int
926        )" << MainPrefix() << R"(
927        %var = OpVariable %var_ty Function
928        ; CHECK: Member index into struct is not a constant integer
929        ; CHECK-SAME: %i = OpUndef %int
930        )"
931             << ACCheckFail(ac, "%i", "%i") << MainSuffix();
932     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
933   }
934 }
935 
TEST_F(GraphicsRobustAccessTest,ACStructExcessFail)936 TEST_F(GraphicsRobustAccessTest, ACStructExcessFail) {
937   for (auto* ac : AccessChains()) {
938     std::ostringstream shaders;
939     shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt()
940             << TypesFloat() << R"(
941        %struct = OpTypeStruct %float %float %float
942        %var_ty = OpTypePointer Function %struct
943        %ptr_ty = OpTypePointer Function %float
944        %i = OpConstant %int 4
945        )" << MainPrefix() << R"(
946        %var = OpVariable %var_ty Function
947        ; CHECK: Member index 4 is out of bounds for struct type:
948        ; CHECK-SAME: %struct = OpTypeStruct %float %float %float
949        )"
950             << ACCheckFail(ac, "%i", "%i") << MainSuffix();
951     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
952   }
953 }
954 
TEST_F(GraphicsRobustAccessTest,ACStructNegativeFail)955 TEST_F(GraphicsRobustAccessTest, ACStructNegativeFail) {
956   for (auto* ac : AccessChains()) {
957     std::ostringstream shaders;
958     shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt()
959             << TypesFloat() << R"(
960        %struct = OpTypeStruct %float %float %float
961        %var_ty = OpTypePointer Function %struct
962        %ptr_ty = OpTypePointer Function %float
963        %i = OpConstant %int -1
964        )" << MainPrefix() << R"(
965        %var = OpVariable %var_ty Function
966        ; CHECK: Member index -1 is out of bounds for struct type:
967        ; CHECK-SAME: %struct = OpTypeStruct %float %float %float
968        )"
969             << ACCheckFail(ac, "%i", "%i") << MainSuffix();
970     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
971   }
972 }
973 
TEST_F(GraphicsRobustAccessTest,ACRTArrayLeastInboundClamped)974 TEST_F(GraphicsRobustAccessTest, ACRTArrayLeastInboundClamped) {
975   for (auto* ac : AccessChains()) {
976     std::ostringstream shaders;
977     shaders << ShaderPreambleAC() << "OpDecorate %rtarr ArrayStride 4 "
978             << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
979        %rtarr = OpTypeRuntimeArray %float
980        %ssbo_s = OpTypeStruct %uint %uint %rtarr
981        %var_ty = OpTypePointer Uniform %ssbo_s
982        %ptr_ty = OpTypePointer Uniform %float
983        %var = OpVariable %var_ty Uniform
984        %int_0 = OpConstant %int 0
985        %int_2 = OpConstant %int 2
986        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
987        ; CHECK-DAG: %int_1 = OpConstant %int 1
988        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
989        ; CHECK: OpLabel
990        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
991        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
992        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
993        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %int_0 %int_0 %[[smin]]
994        )"
995             << MainPrefix() << ACCheck(ac, "%int_2 %int_0", "%int_2 %[[clamp]]")
996             << MainSuffix();
997     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
998   }
999 }
1000 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralShortIndexClamped)1001 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralShortIndexClamped) {
1002   for (auto* ac : AccessChains()) {
1003     std::ostringstream shaders;
1004     shaders << "OpCapability Int16\n"
1005             << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
1006             << DecoSSBO() << TypesVoid() << TypesShort() << TypesFloat() << R"(
1007        %rtarr = OpTypeRuntimeArray %float
1008        %ssbo_s = OpTypeStruct %short %short %rtarr
1009        %var_ty = OpTypePointer Uniform %ssbo_s
1010        %ptr_ty = OpTypePointer Uniform %float
1011        %var = OpVariable %var_ty Uniform
1012        %short_2 = OpConstant %short 2
1013        %i = OpUndef %short
1014        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1015        ; CHECK: %uint = OpTypeInt 32 0
1016        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
1017        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
1018        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
1019        ; CHECK: OpLabel
1020        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1021        ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
1022        ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
1023        ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1024        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
1025        )"
1026             << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
1027             << MainSuffix();
1028     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1029   }
1030 }
1031 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralUShortIndexClamped)1032 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUShortIndexClamped) {
1033   for (auto* ac : AccessChains()) {
1034     std::ostringstream shaders;
1035     shaders << "OpCapability Int16\n"
1036             << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
1037             << DecoSSBO() << TypesVoid() << TypesShort() << TypesFloat() << R"(
1038        %rtarr = OpTypeRuntimeArray %float
1039        %ssbo_s = OpTypeStruct %short %short %rtarr
1040        %var_ty = OpTypePointer Uniform %ssbo_s
1041        %ptr_ty = OpTypePointer Uniform %float
1042        %var = OpVariable %var_ty Uniform
1043        %short_2 = OpConstant %short 2
1044        %i = OpUndef %ushort
1045        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1046        ; CHECK: %uint = OpTypeInt 32 0
1047        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
1048        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
1049        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
1050        ; CHECK: OpLabel
1051        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1052        ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
1053        ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
1054        ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1055        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
1056        )"
1057             << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
1058             << MainSuffix();
1059     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1060   }
1061 }
1062 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralIntIndexClamped)1063 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralIntIndexClamped) {
1064   for (auto* ac : AccessChains()) {
1065     std::ostringstream shaders;
1066     shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
1067             << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
1068        %rtarr = OpTypeRuntimeArray %float
1069        %ssbo_s = OpTypeStruct %int %int %rtarr
1070        %var_ty = OpTypePointer Uniform %ssbo_s
1071        %ptr_ty = OpTypePointer Uniform %float
1072        %var = OpVariable %var_ty Uniform
1073        %int_2 = OpConstant %int 2
1074        %i = OpUndef %int
1075        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1076        ; CHECK-DAG: %int_1 = OpConstant %int 1
1077        ; CHECK-DAG: %int_0 = OpConstant %int 0
1078        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1079        ; CHECK: OpLabel
1080        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1081        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1082        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1083        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
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,ACRTArrayGeneralUIntIndexClamped)1091 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUIntIndexClamped) {
1092   for (auto* ac : AccessChains()) {
1093     std::ostringstream shaders;
1094     shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
1095             << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
1096        %rtarr = OpTypeRuntimeArray %float
1097        %ssbo_s = OpTypeStruct %int %int %rtarr
1098        %var_ty = OpTypePointer Uniform %ssbo_s
1099        %ptr_ty = OpTypePointer Uniform %float
1100        %var = OpVariable %var_ty Uniform
1101        %int_2 = OpConstant %int 2
1102        %i = OpUndef %uint
1103        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1104        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
1105        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
1106        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
1107        ; CHECK: OpLabel
1108        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1109        ; CHECK: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
1110        ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1111        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %uint_0 %[[smin]]
1112        )"
1113             << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
1114             << MainSuffix();
1115     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1116   }
1117 }
1118 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralLongIndexClamped)1119 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralLongIndexClamped) {
1120   for (auto* ac : AccessChains()) {
1121     std::ostringstream shaders;
1122     shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
1123             << "OpDecorate %rtarr ArrayStride 4 " << DecoSSBO() << TypesVoid()
1124             << TypesInt() << TypesLong() << TypesFloat() << R"(
1125        %rtarr = OpTypeRuntimeArray %float
1126        %ssbo_s = OpTypeStruct %int %int %rtarr
1127        %var_ty = OpTypePointer Uniform %ssbo_s
1128        %ptr_ty = OpTypePointer Uniform %float
1129        %var = OpVariable %var_ty Uniform
1130        %int_2 = OpConstant %int 2
1131        %i = OpUndef %long
1132        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1133        ; CHECK-DAG: %long_0 = OpConstant %long 0
1134        ; CHECK-DAG: %long_1 = OpConstant %long 1
1135        ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %long 9223372036854775807
1136        ; CHECK: OpLabel
1137        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1138        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
1139        ; CHECK: %[[max:\w+]] = OpISub %long %[[arrlen_ext]] %long_1
1140        ; CHECK: %[[smin:\w+]] = OpExtInst %long %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
1141        ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %[[smin]]
1142        )" << MainPrefix()
1143             << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
1144     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1145   }
1146 }
1147 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralULongIndexClamped)1148 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralULongIndexClamped) {
1149   for (auto* ac : AccessChains()) {
1150     std::ostringstream shaders;
1151     shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
1152             << "OpDecorate %rtarr ArrayStride 4 " << DecoSSBO() << TypesVoid()
1153             << TypesInt() << TypesLong() << TypesFloat() << R"(
1154        %rtarr = OpTypeRuntimeArray %float
1155        %ssbo_s = OpTypeStruct %int %int %rtarr
1156        %var_ty = OpTypePointer Uniform %ssbo_s
1157        %ptr_ty = OpTypePointer Uniform %float
1158        %var = OpVariable %var_ty Uniform
1159        %int_2 = OpConstant %int 2
1160        %i = OpUndef %ulong
1161        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1162        ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
1163        ; CHECK-DAG: %ulong_1 = OpConstant %ulong 1
1164        ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %ulong 9223372036854775807
1165        ; CHECK: OpLabel
1166        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1167        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
1168        ; CHECK: %[[max:\w+]] = OpISub %ulong %[[arrlen_ext]] %ulong_1
1169        ; CHECK: %[[smin:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
1170        ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %ulong_0 %[[smin]]
1171        )" << MainPrefix()
1172             << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
1173     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1174   }
1175 }
1176 
TEST_F(GraphicsRobustAccessTest,ACRTArrayStructVectorElem)1177 TEST_F(GraphicsRobustAccessTest, ACRTArrayStructVectorElem) {
1178   // The point of this test is that the access chain can have indices past the
1179   // index into the runtime array.  For good measure, the index into the final
1180   // struct is out of bounds.  We have to clamp that index too.
1181   for (auto* ac : AccessChains()) {
1182     std::ostringstream shaders;
1183     shaders << ShaderPreambleAC({"i", "j"})
1184             << "OpDecorate %rtarr ArrayStride 32\n"
1185             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1186             << "OpMemberDecorate %rtelem 1 Offset 16\n"
1187             << TypesVoid() << TypesInt() << TypesFloat() << R"(
1188        %v4float = OpTypeVector %float 4
1189        %rtelem = OpTypeStruct %v4float %v4float
1190        %rtarr = OpTypeRuntimeArray %rtelem
1191        %ssbo_s = OpTypeStruct %int %int %rtarr
1192        %var_ty = OpTypePointer Uniform %ssbo_s
1193        %ptr_ty = OpTypePointer Uniform %float
1194        %var = OpVariable %var_ty Uniform
1195        %int_1 = OpConstant %int 1
1196        %int_2 = OpConstant %int 2
1197        %i = OpUndef %int
1198        %j = OpUndef %int
1199        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1200        ; CHECK-DAG: %int_0 = OpConstant %int 0
1201        ; CHECK-DAG: %int_3 = OpConstant %int 3
1202        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1203        ; CHECK: OpLabel
1204        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1205        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1206        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1207        ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
1208        ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %int_3
1209        )" << MainPrefix()
1210             << ACCheck(ac, "%int_2 %i %int_1 %j",
1211                        "%int_2 %[[clamp_i]] %int_1 %[[clamp_j]]")
1212             << MainSuffix();
1213     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1214   }
1215 }
1216 
TEST_F(GraphicsRobustAccessTest,ACArrayRTArrayStructVectorElem)1217 TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) {
1218   // Now add an additional level of arrays around the Block-decorated struct.
1219   for (auto* ac : AccessChains()) {
1220     std::ostringstream shaders;
1221     shaders << ShaderPreambleAC({"i", "ssbo_s"})
1222             << "OpDecorate %rtarr ArrayStride 32\n"
1223             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1224             << "OpMemberDecorate %rtelem 1 Offset 16\n"
1225             << TypesVoid() << TypesInt() << TypesFloat() << R"(
1226        %v4float = OpTypeVector %float 4
1227        %rtelem = OpTypeStruct %v4float %v4float
1228        %rtarr = OpTypeRuntimeArray %rtelem
1229        %ssbo_s = OpTypeStruct %int %int %rtarr
1230        %arr_size = OpConstant %int 10
1231        %arr_ssbo = OpTypeArray %ssbo_s %arr_size
1232        %var_ty = OpTypePointer Uniform %arr_ssbo
1233        %ptr_ty = OpTypePointer Uniform %float
1234        %var = OpVariable %var_ty Uniform
1235        %int_1 = OpConstant %int 1
1236        %int_2 = OpConstant %int 2
1237        %int_17 = OpConstant %int 17
1238        %i = OpUndef %int
1239        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1240        ; CHECK-DAG: %[[ssbo_p:\w+]] = OpTypePointer Uniform %ssbo_s
1241        ; CHECK-DAG: %int_0 = OpConstant %int 0
1242        ; CHECK-DAG: %int_9 = OpConstant %int 9
1243        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1244        ; CHECK: OpLabel
1245        ; This access chain is manufatured only so we can compute the array length.
1246        ; Note that the %int_9 is already clamped
1247        ; CHECK: %[[ssbo_base:\w+]] = )" << ac
1248             << R"( %[[ssbo_p]] %var %int_9
1249        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %[[ssbo_base]] 2
1250        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1251        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1252        ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
1253        )" << MainPrefix()
1254             << ACCheck(ac, "%int_17 %int_2 %i %int_1 %int_2",
1255                        "%int_9 %int_2 %[[clamp_i]] %int_1 %int_2")
1256             << MainSuffix();
1257     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1258   }
1259 }
1260 
TEST_F(GraphicsRobustAccessTest,ACSplitACArrayRTArrayStructVectorElem)1261 TEST_F(GraphicsRobustAccessTest, ACSplitACArrayRTArrayStructVectorElem) {
1262   // Split the address calculation across two access chains.  Force
1263   // the transform to walk up the access chains to find the base variable.
1264   for (auto* ac : AccessChains()) {
1265     std::ostringstream shaders;
1266     shaders << ShaderPreambleAC({"i", "j", "k", "ssbo_s", "ssbo_pty",
1267                                  "rtarr_pty", "ac_ssbo", "ac_rtarr"})
1268             << "OpDecorate %rtarr ArrayStride 32\n"
1269             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1270             << "OpMemberDecorate %rtelem 1 Offset 16\n"
1271             << TypesVoid() << TypesInt() << TypesFloat() << R"(
1272        %v4float = OpTypeVector %float 4
1273        %rtelem = OpTypeStruct %v4float %v4float
1274        %rtarr = OpTypeRuntimeArray %rtelem
1275        %ssbo_s = OpTypeStruct %int %int %rtarr
1276        %arr_size = OpConstant %int 10
1277        %arr_ssbo = OpTypeArray %ssbo_s %arr_size
1278        %var_ty = OpTypePointer Uniform %arr_ssbo
1279        %ssbo_pty = OpTypePointer Uniform %ssbo_s
1280        %rtarr_pty = OpTypePointer Uniform %rtarr
1281        %ptr_ty = OpTypePointer Uniform %float
1282        %var = OpVariable %var_ty Uniform
1283        %int_1 = OpConstant %int 1
1284        %int_2 = OpConstant %int 2
1285        %i = OpUndef %int
1286        %j = OpUndef %int
1287        %k = OpUndef %int
1288        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1289        ; CHECK-DAG: %int_0 = OpConstant %int 0
1290        ; CHECK-DAG: %int_9 = OpConstant %int 9
1291        ; CHECK-DAG: %int_3 = OpConstant %int 3
1292        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1293        ; CHECK: OpLabel
1294        ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
1295        ; CHECK: %ac_ssbo = )" << ac
1296             << R"( %ssbo_pty %var %[[clamp_i]]
1297        ; CHECK: %ac_rtarr = )"
1298             << ac << R"( %rtarr_pty %ac_ssbo %int_2
1299 
1300        ; This is the interesting bit.  This array length is needed for an OpAccessChain
1301        ; computing %ac, but the algorithm had to track back through %ac_rtarr's
1302        ; definition to find the base pointer %ac_ssbo.
1303        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
1304        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1305        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1306        ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
1307        ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
1308        ; CHECK: %ac = )" << ac
1309             << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
1310        ; CHECK-NOT: AccessChain
1311        )" << MainPrefix()
1312             << "%ac_ssbo = " << ac << " %ssbo_pty %var %i\n"
1313             << "%ac_rtarr = " << ac << " %rtarr_pty %ac_ssbo %int_2\n"
1314             << "%ac = " << ac << " %ptr_ty %ac_rtarr %j %int_1 %k\n"
1315 
1316             << MainSuffix();
1317     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1318   }
1319 }
1320 
TEST_F(GraphicsRobustAccessTest,ACSplitACArrayRTArrayStructVectorElemAcrossBasicBlocks)1321 TEST_F(GraphicsRobustAccessTest,
1322        ACSplitACArrayRTArrayStructVectorElemAcrossBasicBlocks) {
1323   // Split the address calculation across two access chains.  Force
1324   // the transform to walk up the access chains to find the base variable.
1325   // This time, put the different access chains in different basic blocks.
1326   // This is an integrity check to ensure that we keep the instruction-to-block
1327   // mapping consistent.
1328   for (auto* ac : AccessChains()) {
1329     std::ostringstream shaders;
1330     shaders << ShaderPreambleAC({"i", "j", "k", "bb1", "bb2", "ssbo_s",
1331                                  "ssbo_pty", "rtarr_pty", "ac_ssbo",
1332                                  "ac_rtarr"})
1333             << "OpDecorate %rtarr ArrayStride 32\n"
1334             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1335             << "OpMemberDecorate %rtelem 1 Offset 16\n"
1336             << TypesVoid() << TypesInt() << TypesFloat() << R"(
1337        %v4float = OpTypeVector %float 4
1338        %rtelem = OpTypeStruct %v4float %v4float
1339        %rtarr = OpTypeRuntimeArray %rtelem
1340        %ssbo_s = OpTypeStruct %int %int %rtarr
1341        %arr_size = OpConstant %int 10
1342        %arr_ssbo = OpTypeArray %ssbo_s %arr_size
1343        %var_ty = OpTypePointer Uniform %arr_ssbo
1344        %ssbo_pty = OpTypePointer Uniform %ssbo_s
1345        %rtarr_pty = OpTypePointer Uniform %rtarr
1346        %ptr_ty = OpTypePointer Uniform %float
1347        %var = OpVariable %var_ty Uniform
1348        %int_1 = OpConstant %int 1
1349        %int_2 = OpConstant %int 2
1350        %i = OpUndef %int
1351        %j = OpUndef %int
1352        %k = OpUndef %int
1353        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1354        ; CHECK-DAG: %int_0 = OpConstant %int 0
1355        ; CHECK-DAG: %int_9 = OpConstant %int 9
1356        ; CHECK-DAG: %int_3 = OpConstant %int 3
1357        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1358        ; CHECK: OpLabel
1359        ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
1360        ; CHECK: %ac_ssbo = )" << ac
1361             << R"( %ssbo_pty %var %[[clamp_i]]
1362        ; CHECK: %bb1 = OpLabel
1363        ; CHECK: %ac_rtarr = )"
1364             << ac << R"( %rtarr_pty %ac_ssbo %int_2
1365        ; CHECK: %bb2 = OpLabel
1366 
1367        ; This is the interesting bit.  This array length is needed for an OpAccessChain
1368        ; computing %ac, but the algorithm had to track back through %ac_rtarr's
1369        ; definition to find the base pointer %ac_ssbo.
1370        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
1371        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1372        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1373        ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
1374        ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
1375        ; CHECK: %ac = )" << ac
1376             << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
1377        ; CHECK-NOT: AccessChain
1378        )" << MainPrefix()
1379             << "%ac_ssbo = " << ac << " %ssbo_pty %var %i\n"
1380             << "OpBranch %bb1\n%bb1 = OpLabel\n"
1381             << "%ac_rtarr = " << ac << " %rtarr_pty %ac_ssbo %int_2\n"
1382             << "OpBranch %bb2\n%bb2 = OpLabel\n"
1383             << "%ac = " << ac << " %ptr_ty %ac_rtarr %j %int_1 %k\n"
1384 
1385             << MainSuffix();
1386     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1387   }
1388 }
1389 
TEST_F(GraphicsRobustAccessTest,bug3813)1390 TEST_F(GraphicsRobustAccessTest, bug3813) {
1391   // This shader comes from Dawn's
1392   // TextureViewSamplingTest.TextureCubeMapOnWholeTexture, converted from GLSL
1393   // by glslang.
1394   // The pass was inserting a signed 32-bit int type, but not correctly marking
1395   // the shader as changed.
1396   std::string shader = R"(
1397 ; SPIR-V
1398 ; Version: 1.0
1399 ; Generator: Google Shaderc over Glslang; 10
1400 ; Bound: 46
1401 ; Schema: 0
1402        OpCapability Shader
1403   %1 = OpExtInstImport "GLSL.std.450"
1404        OpMemoryModel Logical GLSL450
1405        OpEntryPoint Fragment %4 "main" %12 %29
1406        OpExecutionMode %4 OriginUpperLeft
1407        OpSource GLSL 450
1408        OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
1409        OpSourceExtension "GL_GOOGLE_include_directive"
1410        OpName %4 "main"
1411        OpName %8 "sc"
1412        OpName %12 "texCoord"
1413        OpName %21 "tc"
1414        OpName %29 "fragColor"
1415        OpName %32 "texture0"
1416        OpName %36 "sampler0"
1417        OpDecorate %12 Location 0
1418        OpDecorate %29 Location 0
1419        OpDecorate %32 DescriptorSet 0
1420        OpDecorate %32 Binding 1
1421        OpDecorate %36 DescriptorSet 0
1422        OpDecorate %36 Binding 0
1423   %2 = OpTypeVoid
1424   %3 = OpTypeFunction %2
1425   %6 = OpTypeFloat 32
1426   %7 = OpTypePointer Function %6
1427   %9 = OpConstant %6 2
1428  %10 = OpTypeVector %6 2
1429  %11 = OpTypePointer Input %10
1430  %12 = OpVariable %11 Input
1431  %13 = OpTypeInt 32 0
1432  %14 = OpConstant %13 0
1433  %15 = OpTypePointer Input %6
1434  %19 = OpConstant %6 1
1435  %22 = OpConstant %13 1
1436  %27 = OpTypeVector %6 4
1437  %28 = OpTypePointer Output %27
1438  %29 = OpVariable %28 Output
1439  %30 = OpTypeImage %6 Cube 0 0 0 1 Unknown
1440  %31 = OpTypePointer UniformConstant %30
1441  %32 = OpVariable %31 UniformConstant
1442  %34 = OpTypeSampler
1443  %35 = OpTypePointer UniformConstant %34
1444  %36 = OpVariable %35 UniformConstant
1445  %38 = OpTypeSampledImage %30
1446  %43 = OpTypeVector %6 3
1447   %4 = OpFunction %2 None %3
1448   %5 = OpLabel
1449   %8 = OpVariable %7 Function
1450  %21 = OpVariable %7 Function
1451  %16 = OpAccessChain %15 %12 %14
1452  %17 = OpLoad %6 %16
1453  %18 = OpFMul %6 %9 %17
1454  %20 = OpFSub %6 %18 %19
1455        OpStore %8 %20
1456  %23 = OpAccessChain %15 %12 %22
1457  %24 = OpLoad %6 %23
1458  %25 = OpFMul %6 %9 %24
1459  %26 = OpFSub %6 %25 %19
1460        OpStore %21 %26
1461  %33 = OpLoad %30 %32
1462  %37 = OpLoad %34 %36
1463  %39 = OpSampledImage %38 %33 %37
1464  %40 = OpLoad %6 %21
1465  %41 = OpLoad %6 %8
1466  %42 = OpFNegate %6 %41
1467  %44 = OpCompositeConstruct %43 %19 %40 %42
1468  %45 = OpImageSampleImplicitLod %27 %39 %44
1469        OpStore %29 %45
1470        OpReturn
1471        OpFunctionEnd
1472 )";
1473 
1474   std::string expected = R"(OpCapability Shader
1475 %1 = OpExtInstImport "GLSL.std.450"
1476 OpMemoryModel Logical GLSL450
1477 OpEntryPoint Fragment %main "main" %texCoord %fragColor
1478 OpExecutionMode %main OriginUpperLeft
1479 OpSource GLSL 450
1480 OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
1481 OpSourceExtension "GL_GOOGLE_include_directive"
1482 OpName %main "main"
1483 OpName %sc "sc"
1484 OpName %texCoord "texCoord"
1485 OpName %tc "tc"
1486 OpName %fragColor "fragColor"
1487 OpName %texture0 "texture0"
1488 OpName %sampler0 "sampler0"
1489 OpDecorate %texCoord Location 0
1490 OpDecorate %fragColor Location 0
1491 OpDecorate %texture0 DescriptorSet 0
1492 OpDecorate %texture0 Binding 1
1493 OpDecorate %sampler0 DescriptorSet 0
1494 OpDecorate %sampler0 Binding 0
1495 %void = OpTypeVoid
1496 %10 = OpTypeFunction %void
1497 %float = OpTypeFloat 32
1498 %_ptr_Function_float = OpTypePointer Function %float
1499 %float_2 = OpConstant %float 2
1500 %v2float = OpTypeVector %float 2
1501 %_ptr_Input_v2float = OpTypePointer Input %v2float
1502 %texCoord = OpVariable %_ptr_Input_v2float Input
1503 %uint = OpTypeInt 32 0
1504 %uint_0 = OpConstant %uint 0
1505 %_ptr_Input_float = OpTypePointer Input %float
1506 %float_1 = OpConstant %float 1
1507 %uint_1 = OpConstant %uint 1
1508 %v4float = OpTypeVector %float 4
1509 %_ptr_Output_v4float = OpTypePointer Output %v4float
1510 %fragColor = OpVariable %_ptr_Output_v4float Output
1511 %23 = OpTypeImage %float Cube 0 0 0 1 Unknown
1512 %_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23
1513 %texture0 = OpVariable %_ptr_UniformConstant_23 UniformConstant
1514 %25 = OpTypeSampler
1515 %_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25
1516 %sampler0 = OpVariable %_ptr_UniformConstant_25 UniformConstant
1517 %27 = OpTypeSampledImage %23
1518 %v3float = OpTypeVector %float 3
1519 %int = OpTypeInt 32 1
1520 %main = OpFunction %void None %10
1521 %29 = OpLabel
1522 %sc = OpVariable %_ptr_Function_float Function
1523 %tc = OpVariable %_ptr_Function_float Function
1524 %30 = OpAccessChain %_ptr_Input_float %texCoord %uint_0
1525 %31 = OpLoad %float %30
1526 %32 = OpFMul %float %float_2 %31
1527 %33 = OpFSub %float %32 %float_1
1528 OpStore %sc %33
1529 %34 = OpAccessChain %_ptr_Input_float %texCoord %uint_1
1530 %35 = OpLoad %float %34
1531 %36 = OpFMul %float %float_2 %35
1532 %37 = OpFSub %float %36 %float_1
1533 OpStore %tc %37
1534 %38 = OpLoad %23 %texture0
1535 %39 = OpLoad %25 %sampler0
1536 %40 = OpSampledImage %27 %38 %39
1537 %41 = OpLoad %float %tc
1538 %42 = OpLoad %float %sc
1539 %43 = OpFNegate %float %42
1540 %44 = OpCompositeConstruct %v3float %float_1 %41 %43
1541 %45 = OpImageSampleImplicitLod %v4float %40 %44
1542 OpStore %fragColor %45
1543 OpReturn
1544 OpFunctionEnd
1545 )";
1546 
1547   SinglePassRunAndCheck<GraphicsRobustAccessPass>(shader, expected, false,
1548                                                   true);
1549 }
1550 
1551 // TODO(dneto): Test access chain index wider than 64 bits?
1552 // TODO(dneto): Test struct access chain index wider than 64 bits?
1553 // TODO(dneto): OpImageTexelPointer
1554 //   - all Dim types: 1D 2D Cube 3D Rect Buffer
1555 //   - all Dim types that can be arrayed: 1D 2D 3D
1556 //   - sample index: set to 0 if not multisampled
1557 //   - Dim (2D, Cube Rect} with multisampling
1558 //      -1 0 max excess
1559 // TODO(dneto): Test OpImageTexelPointer with coordinate component index other
1560 // than 32 bits.
1561 
1562 }  // namespace
1563