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 <sstream>
16 #include <string>
17 #include <vector>
18 
19 #include "pass_fixture.h"
20 #include "pass_utils.h"
21 #include "source/opt/graphics_robust_access_pass.h"
22 
23 namespace {
24 
25 using namespace spvtools;
26 
27 using opt::GraphicsRobustAccessPass;
28 using GraphicsRobustAccessTest = opt::PassTest<::testing::Test>;
29 
30 // Test incompatible module, determined at module-level.
31 
TEST_F(GraphicsRobustAccessTest,FailNotShader)32 TEST_F(GraphicsRobustAccessTest, FailNotShader) {
33   const std::string text = R"(
34 ; CHECK: Can only process Shader modules
35 OpCapability Kernel
36 )";
37 
38   SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
39 }
40 
TEST_F(GraphicsRobustAccessTest,FailCantProcessVariablePointers)41 TEST_F(GraphicsRobustAccessTest, FailCantProcessVariablePointers) {
42   const std::string text = R"(
43 ; CHECK: Can't process modules with VariablePointers capability
44 OpCapability VariablePointers
45 )";
46 
47   SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
48 }
49 
TEST_F(GraphicsRobustAccessTest,FailCantProcessVariablePointersStorageBuffer)50 TEST_F(GraphicsRobustAccessTest, FailCantProcessVariablePointersStorageBuffer) {
51   const std::string text = R"(
52 ; CHECK: Can't process modules with VariablePointersStorageBuffer capability
53 OpCapability VariablePointersStorageBuffer
54 )";
55 
56   SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
57 }
58 
TEST_F(GraphicsRobustAccessTest,FailCantProcessRuntimeDescriptorArrayEXT)59 TEST_F(GraphicsRobustAccessTest, FailCantProcessRuntimeDescriptorArrayEXT) {
60   const std::string text = R"(
61 ; CHECK: Can't process modules with RuntimeDescriptorArrayEXT capability
62 OpCapability RuntimeDescriptorArrayEXT
63 )";
64 
65   SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
66 }
67 
TEST_F(GraphicsRobustAccessTest,FailCantProcessPhysical32AddressingModel)68 TEST_F(GraphicsRobustAccessTest, FailCantProcessPhysical32AddressingModel) {
69   const std::string text = R"(
70 ; CHECK: Addressing model must be Logical.  Found OpMemoryModel Physical32 OpenCL
71 OpCapability Shader
72 OpMemoryModel Physical32 OpenCL
73 )";
74 
75   SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
76 }
77 
TEST_F(GraphicsRobustAccessTest,FailCantProcessPhysical64AddressingModel)78 TEST_F(GraphicsRobustAccessTest, FailCantProcessPhysical64AddressingModel) {
79   const std::string text = R"(
80 ; CHECK: Addressing model must be Logical.  Found OpMemoryModel Physical64 OpenCL
81 OpCapability Shader
82 OpMemoryModel Physical64 OpenCL
83 )";
84 
85   SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
86 }
87 
TEST_F(GraphicsRobustAccessTest,FailCantProcessPhysicalStorageBuffer64EXTAddressingModel)88 TEST_F(GraphicsRobustAccessTest,
89        FailCantProcessPhysicalStorageBuffer64EXTAddressingModel) {
90   const std::string text = R"(
91 ; CHECK: Addressing model must be Logical.  Found OpMemoryModel PhysicalStorageBuffer64 GLSL450
92 OpCapability Shader
93 OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
94 )";
95 
96   SinglePassRunAndFail<GraphicsRobustAccessPass>(text);
97 }
98 
99 // Test access chains
100 
101 // Returns the names of access chain instructions handled by the pass.
102 // For the purposes of this pass, regular and in-bounds access chains are the
103 // same.)
AccessChains()104 std::vector<const char*> AccessChains() {
105   return {"OpAccessChain", "OpInBoundsAccessChain"};
106 }
107 
ShaderPreamble()108 std::string ShaderPreamble() {
109   return R"(
110   OpCapability Shader
111   OpMemoryModel Logical Simple
112   OpEntryPoint GLCompute %main "main"
113 )";
114 }
115 
ShaderPreamble(const std::vector<std::string> & names)116 std::string ShaderPreamble(const std::vector<std::string>& names) {
117   std::ostringstream os;
118   os << ShaderPreamble();
119   for (auto& name : names) {
120     os << "  OpName %" << name << " \"" << name << "\"\n";
121   }
122   return os.str();
123 }
124 
ShaderPreambleAC()125 std::string ShaderPreambleAC() {
126   return ShaderPreamble({"ac", "ptr_ty", "var"});
127 }
128 
ShaderPreambleAC(const std::vector<std::string> & names)129 std::string ShaderPreambleAC(const std::vector<std::string>& names) {
130   auto names2 = names;
131   names2.push_back("ac");
132   names2.push_back("ptr_ty");
133   names2.push_back("var");
134   return ShaderPreamble(names2);
135 }
136 
DecoSSBO()137 std::string DecoSSBO() {
138   return R"(
139   OpDecorate %ssbo_s BufferBlock
140   OpMemberDecorate %ssbo_s 0 Offset 0
141   OpMemberDecorate %ssbo_s 1 Offset 4
142   OpMemberDecorate %ssbo_s 2 Offset 16
143   OpDecorate %var DescriptorSet 0
144   OpDecorate %var Binding 0
145 )";
146 }
147 
TypesVoid()148 std::string TypesVoid() {
149   return R"(
150   %void = OpTypeVoid
151   %void_fn = OpTypeFunction %void
152 )";
153 }
154 
TypesInt()155 std::string TypesInt() {
156   return R"(
157   %uint = OpTypeInt 32 0
158   %int = OpTypeInt 32 1
159 )";
160 }
161 
TypesFloat()162 std::string TypesFloat() {
163   return R"(
164   %float = OpTypeFloat 32
165 )";
166 }
167 
TypesShort()168 std::string TypesShort() {
169   return R"(
170   %ushort = OpTypeInt 16 0
171   %short = OpTypeInt 16 1
172 )";
173 }
174 
TypesLong()175 std::string TypesLong() {
176   return R"(
177   %ulong = OpTypeInt 64 0
178   %long = OpTypeInt 64 1
179 )";
180 }
181 
MainPrefix()182 std::string MainPrefix() {
183   return R"(
184   %main = OpFunction %void None %void_fn
185   %entry = OpLabel
186 )";
187 }
188 
MainSuffix()189 std::string MainSuffix() {
190   return R"(
191   OpReturn
192   OpFunctionEnd
193 )";
194 }
195 
ACCheck(const std::string & access_chain_inst,const std::string & original,const std::string & transformed)196 std::string ACCheck(const std::string& access_chain_inst,
197                     const std::string& original,
198                     const std::string& transformed) {
199   return "\n ; CHECK: %ac = " + access_chain_inst + " %ptr_ty %var" +
200          (transformed.empty() ? "" : " ") + transformed +
201          "\n ; CHECK-NOT: " + access_chain_inst +
202          "\n ; CHECK-NEXT: OpReturn"
203          "\n %ac = " +
204          access_chain_inst + " %ptr_ty %var " + (original.empty() ? "" : " ") +
205          original + "\n";
206 }
207 
ACCheckFail(const std::string & access_chain_inst,const std::string & original,const std::string & transformed)208 std::string ACCheckFail(const std::string& access_chain_inst,
209                         const std::string& original,
210                         const std::string& transformed) {
211   return "\n ; CHECK: %ac = " + access_chain_inst + " %ptr_ty %var" +
212          (transformed.empty() ? "" : " ") + transformed +
213          "\n ; CHECK-NOT: " + access_chain_inst +
214          "\n ; CHECK-NOT: OpReturn"
215          "\n %ac = " +
216          access_chain_inst + " %ptr_ty %var " + (original.empty() ? "" : " ") +
217          original + "\n";
218 }
219 
220 // Access chain into:
221 //   Vector
222 //     Vector sizes 2, 3, 4
223 //   Matrix
224 //     Matrix columns 2, 4
225 //     Component is vector 2, 4
226 //   Array
227 //   Struct
228 //   TODO(dneto): RuntimeArray
229 
TEST_F(GraphicsRobustAccessTest,ACVectorLeastInboundConstantUntouched)230 TEST_F(GraphicsRobustAccessTest, ACVectorLeastInboundConstantUntouched) {
231   for (auto* ac : AccessChains()) {
232     std::ostringstream shaders;
233     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
234        %uvec2 = OpTypeVector %uint 2
235        %var_ty = OpTypePointer Function %uvec2
236        %ptr_ty = OpTypePointer Function %uint
237        %uint_0 = OpConstant %uint 0
238        )"
239             << MainPrefix() << R"(
240        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_0", "%uint_0")
241             << MainSuffix();
242     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
243   }
244 }
245 
TEST_F(GraphicsRobustAccessTest,ACVectorMostInboundConstantUntouched)246 TEST_F(GraphicsRobustAccessTest, ACVectorMostInboundConstantUntouched) {
247   for (auto* ac : AccessChains()) {
248     std::ostringstream shaders;
249     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
250        %v4uint = OpTypeVector %uint 4
251        %var_ty = OpTypePointer Function %v4uint
252        %ptr_ty = OpTypePointer Function %uint
253        %uint_3 = OpConstant %uint 3
254        )"
255             << MainPrefix() << R"(
256        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_3", "%uint_3")
257             << MainSuffix();
258     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
259   }
260 }
261 
TEST_F(GraphicsRobustAccessTest,ACVectorExcessConstantClamped)262 TEST_F(GraphicsRobustAccessTest, ACVectorExcessConstantClamped) {
263   for (auto* ac : AccessChains()) {
264     std::ostringstream shaders;
265     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
266        %v4uint = OpTypeVector %uint 4
267        %var_ty = OpTypePointer Function %v4uint
268        %ptr_ty = OpTypePointer Function %uint
269        %uint_4 = OpConstant %uint 4
270        )"
271             << MainPrefix() << R"(
272        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%int_3")
273             << MainSuffix();
274     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
275   }
276 }
277 
TEST_F(GraphicsRobustAccessTest,ACVectorNegativeConstantClamped)278 TEST_F(GraphicsRobustAccessTest, ACVectorNegativeConstantClamped) {
279   for (auto* ac : AccessChains()) {
280     std::ostringstream shaders;
281     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
282        %v4uint = OpTypeVector %uint 4
283        %var_ty = OpTypePointer Function %v4uint
284        %ptr_ty = OpTypePointer Function %uint
285        %int_n1 = OpConstant %int -1
286        )"
287             << MainPrefix() << R"(
288        ; CHECK: %int_0 = OpConstant %int 0
289        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%int_n1", "%int_0")
290             << MainSuffix();
291     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
292   }
293 }
294 
295 // Like the previous test, but ensures the pass knows how to modify an index
296 // which does not come first in the access chain.
TEST_F(GraphicsRobustAccessTest,ACVectorInArrayNegativeConstantClamped)297 TEST_F(GraphicsRobustAccessTest, ACVectorInArrayNegativeConstantClamped) {
298   for (auto* ac : AccessChains()) {
299     std::ostringstream shaders;
300     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
301        %v4uint = OpTypeVector %uint 4
302        %uint_1 = OpConstant %uint 1
303        %uint_2 = OpConstant %uint 2
304        %arr = OpTypeArray %v4uint %uint_2
305        %var_ty = OpTypePointer Function %arr
306        %ptr_ty = OpTypePointer Function %uint
307        %int_n1 = OpConstant %int -1
308        )"
309             << MainPrefix() << R"(
310        ; CHECK: %int_0 = OpConstant %int 0
311        %var = OpVariable %var_ty Function)"
312             << ACCheck(ac, "%uint_1 %int_n1", "%uint_1 %int_0") << MainSuffix();
313     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
314   }
315 }
316 
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralClamped)317 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralClamped) {
318   for (auto* ac : AccessChains()) {
319     std::ostringstream shaders;
320     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() << R"(
321        %v4uint = OpTypeVector %uint 4
322        %var_ty = OpTypePointer Function %v4uint
323        %ptr_ty = OpTypePointer Function %uint
324        %i = OpUndef %int)"
325             << MainPrefix() << R"(
326        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
327        ; CHECK-DAG: %int_0 = OpConstant %int 0
328        ; CHECK-DAG: %int_3 = OpConstant %int 3
329        ; CHECK: OpLabel
330        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3
331        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
332             << MainSuffix();
333     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
334   }
335 }
336 
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralShortClamped)337 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralShortClamped) {
338   // Show that signed 16 bit integers are clamped as well.
339   for (auto* ac : AccessChains()) {
340     std::ostringstream shaders;
341     shaders << "OpCapability Int16\n"
342             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort() <<
343         R"(
344        %v4short = OpTypeVector %short 4
345        %var_ty = OpTypePointer Function %v4short
346        %ptr_ty = OpTypePointer Function %short
347        %i = OpUndef %short)"
348             << MainPrefix() << R"(
349        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
350        ; CHECK-NOT: = OpTypeInt 32
351        ; CHECK-DAG: %short_0 = OpConstant %short 0
352        ; CHECK-DAG: %short_3 = OpConstant %short 3
353        ; CHECK-NOT: = OpTypeInt 32
354        ; CHECK: OpLabel
355        ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] SClamp %i %short_0 %short_3
356        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
357             << MainSuffix();
358     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
359   }
360 }
361 
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralUShortClamped)362 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralUShortClamped) {
363   // Show that unsigned 16 bit integers are clamped as well.
364   for (auto* ac : AccessChains()) {
365     std::ostringstream shaders;
366     shaders << "OpCapability Int16\n"
367             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort() <<
368         R"(
369        %v4ushort = OpTypeVector %ushort 4
370        %var_ty = OpTypePointer Function %v4ushort
371        %ptr_ty = OpTypePointer Function %ushort
372        %i = OpUndef %ushort)"
373             << MainPrefix() << R"(
374        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
375        ; CHECK-NOT: = OpTypeInt 32
376        ; CHECK-DAG: %short_0 = OpConstant %short 0
377        ; CHECK-DAG: %short_3 = OpConstant %short 3
378        ; CHECK-NOT: = OpTypeInt 32
379        ; CHECK: OpLabel
380        ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %short_3
381        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
382             << MainSuffix();
383     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
384   }
385 }
386 
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralLongClamped)387 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralLongClamped) {
388   // Show that signed 64 bit integers are clamped as well.
389   for (auto* ac : AccessChains()) {
390     std::ostringstream shaders;
391     shaders << "OpCapability Int64\n"
392             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesLong() <<
393         R"(
394        %v4long = OpTypeVector %long 4
395        %var_ty = OpTypePointer Function %v4long
396        %ptr_ty = OpTypePointer Function %long
397        %i = OpUndef %long)"
398             << MainPrefix() << R"(
399        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
400        ; CHECK-NOT: = OpTypeInt 32
401        ; CHECK-DAG: %long_0 = OpConstant %long 0
402        ; CHECK-DAG: %long_3 = OpConstant %long 3
403        ; CHECK-NOT: = OpTypeInt 32
404        ; CHECK: OpLabel
405        ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_3
406        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
407             << MainSuffix();
408     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
409   }
410 }
411 
TEST_F(GraphicsRobustAccessTest,ACVectorGeneralULongClamped)412 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralULongClamped) {
413   // Show that unsigned 64 bit integers are clamped as well.
414   for (auto* ac : AccessChains()) {
415     std::ostringstream shaders;
416     shaders << "OpCapability Int64\n"
417             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesLong() <<
418         R"(
419        %v4ulong = OpTypeVector %ulong 4
420        %var_ty = OpTypePointer Function %v4ulong
421        %ptr_ty = OpTypePointer Function %ulong
422        %i = OpUndef %ulong)"
423             << MainPrefix() << R"(
424        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
425        ; CHECK-NOT: = OpTypeInt 32
426        ; CHECK-DAG: %long_0 = OpConstant %long 0
427        ; CHECK-DAG: %long_3 = OpConstant %long 3
428        ; CHECK-NOT: = OpTypeInt 32
429        ; CHECK: OpLabel
430        ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_3
431        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
432             << MainSuffix();
433     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
434   }
435 }
436 
TEST_F(GraphicsRobustAccessTest,ACMatrixLeastInboundConstantUntouched)437 TEST_F(GraphicsRobustAccessTest, ACMatrixLeastInboundConstantUntouched) {
438   for (auto* ac : AccessChains()) {
439     std::ostringstream shaders;
440     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
441             << TypesFloat() << R"(
442        %v2float = OpTypeVector %float 2
443        %mat4v2float = OpTypeMatrix %v2float 4
444        %var_ty = OpTypePointer Function %mat4v2float
445        %ptr_ty = OpTypePointer Function %float
446        %uint_0 = OpConstant %uint 0
447        %uint_1 = OpConstant %uint 1
448        )" << MainPrefix() << R"(
449        %var = OpVariable %var_ty Function)"
450             << ACCheck(ac, "%uint_0 %uint_1", "%uint_0 %uint_1")
451             << MainSuffix();
452     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
453   }
454 }
455 
TEST_F(GraphicsRobustAccessTest,ACMatrixMostInboundConstantUntouched)456 TEST_F(GraphicsRobustAccessTest, ACMatrixMostInboundConstantUntouched) {
457   for (auto* ac : AccessChains()) {
458     std::ostringstream shaders;
459     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
460             << TypesFloat() << R"(
461        %v2float = OpTypeVector %float 2
462        %mat4v2float = OpTypeMatrix %v2float 4
463        %var_ty = OpTypePointer Function %mat4v2float
464        %ptr_ty = OpTypePointer Function %float
465        %uint_1 = OpConstant %uint 1
466        %uint_3 = OpConstant %uint 3
467        )" << MainPrefix() << R"(
468        %var = OpVariable %var_ty Function)"
469             << ACCheck(ac, "%uint_3 %uint_1", "%uint_3 %uint_1")
470             << MainSuffix();
471     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
472   }
473 }
474 
TEST_F(GraphicsRobustAccessTest,ACMatrixExcessConstantClamped)475 TEST_F(GraphicsRobustAccessTest, ACMatrixExcessConstantClamped) {
476   for (auto* ac : AccessChains()) {
477     std::ostringstream shaders;
478     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
479             << TypesFloat() << R"(
480        %v2float = OpTypeVector %float 2
481        %mat4v2float = OpTypeMatrix %v2float 4
482        %var_ty = OpTypePointer Function %mat4v2float
483        %ptr_ty = OpTypePointer Function %float
484        %uint_1 = OpConstant %uint 1
485        %uint_4 = OpConstant %uint 4
486        )" << MainPrefix() << R"(
487        ; CHECK: %int_3 = OpConstant %int 3
488        %var = OpVariable %var_ty Function)"
489             << ACCheck(ac, "%uint_4 %uint_1", "%int_3 %uint_1") << MainSuffix();
490     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
491   }
492 }
493 
TEST_F(GraphicsRobustAccessTest,ACMatrixNegativeConstantClamped)494 TEST_F(GraphicsRobustAccessTest, ACMatrixNegativeConstantClamped) {
495   for (auto* ac : AccessChains()) {
496     std::ostringstream shaders;
497     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
498             << TypesFloat() << R"(
499        %v2float = OpTypeVector %float 2
500        %mat4v2float = OpTypeMatrix %v2float 4
501        %var_ty = OpTypePointer Function %mat4v2float
502        %ptr_ty = OpTypePointer Function %float
503        %uint_1 = OpConstant %uint 1
504        %int_n1 = OpConstant %int -1
505        )" << MainPrefix() << R"(
506        ; CHECK: %int_0 = OpConstant %int 0
507        %var = OpVariable %var_ty Function)"
508             << ACCheck(ac, "%int_n1 %uint_1", "%int_0 %uint_1") << MainSuffix();
509     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
510   }
511 }
512 
TEST_F(GraphicsRobustAccessTest,ACMatrixGeneralClamped)513 TEST_F(GraphicsRobustAccessTest, ACMatrixGeneralClamped) {
514   for (auto* ac : AccessChains()) {
515     std::ostringstream shaders;
516     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
517             << TypesFloat() << R"(
518        %v2float = OpTypeVector %float 2
519        %mat4v2float = OpTypeMatrix %v2float 4
520        %var_ty = OpTypePointer Function %mat4v2float
521        %ptr_ty = OpTypePointer Function %float
522        %uint_1 = OpConstant %uint 1
523        %i = OpUndef %int
524        )" << MainPrefix() << R"(
525        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
526        ; CHECK-DAG: %int_0 = OpConstant %int 0
527        ; CHECK-DAG: %int_3 = OpConstant %int 3
528        ; CHECK: OpLabel
529        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3
530        %var = OpVariable %var_ty Function)"
531             << ACCheck(ac, "%i %uint_1", "%[[clamp]] %uint_1") << MainSuffix();
532     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
533   }
534 }
535 
TEST_F(GraphicsRobustAccessTest,ACArrayLeastInboundConstantUntouched)536 TEST_F(GraphicsRobustAccessTest, ACArrayLeastInboundConstantUntouched) {
537   for (auto* ac : AccessChains()) {
538     std::ostringstream shaders;
539     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
540             << TypesFloat() << R"(
541        %uint_200 = OpConstant %uint 200
542        %arr = OpTypeArray %float %uint_200
543        %var_ty = OpTypePointer Function %arr
544        %ptr_ty = OpTypePointer Function %float
545        %int_0 = OpConstant %int 0
546        )" << MainPrefix() << R"(
547        %var = OpVariable %var_ty Function)"
548             << ACCheck(ac, "%int_0", "%int_0") << MainSuffix();
549     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
550   }
551 }
552 
TEST_F(GraphicsRobustAccessTest,ACArrayMostInboundConstantUntouched)553 TEST_F(GraphicsRobustAccessTest, ACArrayMostInboundConstantUntouched) {
554   for (auto* ac : AccessChains()) {
555     std::ostringstream shaders;
556     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
557             << TypesFloat() << R"(
558        %uint_200 = OpConstant %uint 200
559        %arr = OpTypeArray %float %uint_200
560        %var_ty = OpTypePointer Function %arr
561        %ptr_ty = OpTypePointer Function %float
562        %int_199 = OpConstant %int 199
563        )" << MainPrefix() << R"(
564        %var = OpVariable %var_ty Function)"
565             << ACCheck(ac, "%int_199", "%int_199") << MainSuffix();
566     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
567   }
568 }
569 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralClamped)570 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralClamped) {
571   for (auto* ac : AccessChains()) {
572     std::ostringstream shaders;
573     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
574             << TypesFloat() << R"(
575        %uint_200 = OpConstant %uint 200
576        %arr = OpTypeArray %float %uint_200
577        %var_ty = OpTypePointer Function %arr
578        %ptr_ty = OpTypePointer Function %float
579        %i = OpUndef %int
580        )" << MainPrefix() << R"(
581        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
582        ; CHECK-DAG: %int_0 = OpConstant %int 0
583        ; CHECK-DAG: %int_199 = OpConstant %int 199
584        ; CHECK: OpLabel
585        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
586        %var = OpVariable %var_ty Function)"
587             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
588     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
589   }
590 }
591 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralShortIndexUIntBoundsClamped)592 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralShortIndexUIntBoundsClamped) {
593   // Index is signed short, array bounds overflows the index type.
594   for (auto* ac : AccessChains()) {
595     std::ostringstream shaders;
596     shaders << "OpCapability Int16\n"
597             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
598             << TypesShort() << TypesFloat() << R"(
599        %uint_70000 = OpConstant %uint 70000 ; overflows 16bits
600        %arr = OpTypeArray %float %uint_70000
601        %var_ty = OpTypePointer Function %arr
602        %ptr_ty = OpTypePointer Function %float
603        %i = OpUndef %short
604        )" << MainPrefix() << R"(
605        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
606        ; CHECK-DAG: %int_0 = OpConstant %int 0
607        ; CHECK-DAG: %int_69999 = OpConstant %int 69999
608        ; CHECK: OpLabel
609        ; CHECK: %[[i_ext:\w+]] = OpSConvert %uint %i
610        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
611        %var = OpVariable %var_ty Function)"
612             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
613     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
614   }
615 }
616 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralUShortIndexIntBoundsClamped)617 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUShortIndexIntBoundsClamped) {
618   // Index is unsigned short, array bounds overflows the index type.
619   for (auto* ac : AccessChains()) {
620     std::ostringstream shaders;
621     shaders << "OpCapability Int16\n"
622             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
623             << TypesShort() << TypesFloat() << R"(
624        %int_70000 = OpConstant %int 70000 ; overflows 16bits
625        %arr = OpTypeArray %float %int_70000
626        %var_ty = OpTypePointer Function %arr
627        %ptr_ty = OpTypePointer Function %float
628        %i = OpUndef %ushort
629        )" << MainPrefix() << R"(
630        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
631        ; CHECK-DAG: %int_0 = OpConstant %int 0
632        ; CHECK-DAG: %int_69999 = OpConstant %int 69999
633        ; CHECK: OpLabel
634        ; CHECK: %[[i_ext:\w+]] = OpUConvert %uint %i
635        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999
636        %var = OpVariable %var_ty Function)"
637             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
638     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
639   }
640 }
641 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralUIntIndexShortBoundsClamped)642 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUIntIndexShortBoundsClamped) {
643   // Signed int index i is wider than the array bounds type.
644   for (auto* ac : AccessChains()) {
645     std::ostringstream shaders;
646     shaders << "OpCapability Int16\n"
647             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
648             << TypesShort() << TypesFloat() << R"(
649        %short_200 = OpConstant %short 200
650        %arr = OpTypeArray %float %short_200
651        %var_ty = OpTypePointer Function %arr
652        %ptr_ty = OpTypePointer Function %float
653        %i = OpUndef %uint
654        )" << MainPrefix() << R"(
655        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
656        ; CHECK-DAG: %int_0 = OpConstant %int 0
657        ; CHECK-DAG: %int_199 = OpConstant %int 199
658        ; CHECK: OpLabel
659        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %int_199
660        %var = OpVariable %var_ty Function)"
661             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
662     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
663   }
664 }
665 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralIntIndexUShortBoundsClamped)666 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralIntIndexUShortBoundsClamped) {
667   // Unsigned int index i is wider than the array bounds type.
668   for (auto* ac : AccessChains()) {
669     std::ostringstream shaders;
670     shaders << "OpCapability Int16\n"
671             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
672             << TypesShort() << TypesFloat() << R"(
673        %ushort_200 = OpConstant %ushort 200
674        %arr = OpTypeArray %float %ushort_200
675        %var_ty = OpTypePointer Function %arr
676        %ptr_ty = OpTypePointer Function %float
677        %i = OpUndef %int
678        )" << MainPrefix() << R"(
679        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
680        ; CHECK-DAG: %int_0 = OpConstant %int 0
681        ; CHECK-DAG: %int_199 = OpConstant %int 199
682        ; CHECK: OpLabel
683        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199
684        %var = OpVariable %var_ty Function)"
685             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
686     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
687   }
688 }
689 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralLongIndexUIntBoundsClamped)690 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralLongIndexUIntBoundsClamped) {
691   // Signed long index i is wider than the array bounds type.
692   for (auto* ac : AccessChains()) {
693     std::ostringstream shaders;
694     shaders << "OpCapability Int64\n"
695             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
696             << TypesLong() << TypesFloat() << R"(
697        %uint_200 = OpConstant %uint 200
698        %arr = OpTypeArray %float %uint_200
699        %var_ty = OpTypePointer Function %arr
700        %ptr_ty = OpTypePointer Function %float
701        %i = OpUndef %long
702        )" << MainPrefix() << R"(
703        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
704        ; CHECK-DAG: %long_0 = OpConstant %long 0
705        ; CHECK-DAG: %long_199 = OpConstant %long 199
706        ; CHECK: OpLabel
707        ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_199
708        %var = OpVariable %var_ty Function)"
709             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
710     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
711   }
712 }
713 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralULongIndexIntBoundsClamped)714 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralULongIndexIntBoundsClamped) {
715   // Unsigned long index i is wider than the array bounds type.
716   for (auto* ac : AccessChains()) {
717     std::ostringstream shaders;
718     shaders << "OpCapability Int64\n"
719             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
720             << TypesLong() << TypesFloat() << R"(
721        %int_200 = OpConstant %int 200
722        %arr = OpTypeArray %float %int_200
723        %var_ty = OpTypePointer Function %arr
724        %ptr_ty = OpTypePointer Function %float
725        %i = OpUndef %ulong
726        )" << MainPrefix() << R"(
727        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
728        ; CHECK-DAG: %long_0 = OpConstant %long 0
729        ; CHECK-DAG: %long_199 = OpConstant %long 199
730        ; CHECK: OpLabel
731        ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_199
732        %var = OpVariable %var_ty Function)"
733             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
734     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
735   }
736 }
737 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax)738 TEST_F(GraphicsRobustAccessTest,
739        ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax) {
740   for (auto* ac : AccessChains()) {
741     std::ostringstream shaders;
742     shaders << "OpCapability Int16\n"
743             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort()
744             << TypesInt() << TypesFloat() << R"(
745        %uint_50000 = OpConstant %uint 50000
746        %arr = OpTypeArray %float %uint_50000
747        %var_ty = OpTypePointer Function %arr
748        %ptr_ty = OpTypePointer Function %float
749        %i = OpUndef %ushort
750        )" << MainPrefix() << R"(
751        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
752        ; CHECK-DAG: %short_0 = OpConstant %short 0
753        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %short 32767
754        ; CHECK: OpLabel
755        ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %[[intmax]]
756        %var = OpVariable %var_ty Function)"
757             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
758     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
759   }
760 }
761 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax)762 TEST_F(GraphicsRobustAccessTest,
763        ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax) {
764   for (auto* ac : AccessChains()) {
765     std::ostringstream shaders;
766     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
767             << TypesFloat() << R"(
768        %uint_3000000000 = OpConstant %uint 3000000000
769        %arr = OpTypeArray %float %uint_3000000000
770        %var_ty = OpTypePointer Function %arr
771        %ptr_ty = OpTypePointer Function %float
772        %i = OpUndef %uint
773        )" << MainPrefix() << R"(
774        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
775        ; CHECK-DAG: %int_0 = OpConstant %int 0
776        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
777        ; CHECK: OpLabel
778        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %[[intmax]]
779        %var = OpVariable %var_ty Function)"
780             << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix();
781     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
782   }
783 }
784 
TEST_F(GraphicsRobustAccessTest,ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax)785 TEST_F(GraphicsRobustAccessTest,
786        ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax) {
787   for (auto* ac : AccessChains()) {
788     std::ostringstream shaders;
789     shaders << "OpCapability Int64\n"
790             << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
791             << TypesLong()
792             << TypesFloat()
793             // 2^63 == 9,223,372,036,854,775,807
794             << R"(
795        %ulong_9223372036854775999 = OpConstant %ulong 9223372036854775999
796        %arr = OpTypeArray %float %ulong_9223372036854775999
797        %var_ty = OpTypePointer Function %arr
798        %ptr_ty = OpTypePointer Function %float
799        %i = OpUndef %ulong
800        )"
801             << MainPrefix() << R"(
802        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
803        ; CHECK-DAG: %long_0 = OpConstant %long 0
804        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %long 9223372036854775807
805        ; CHECK: OpLabel
806        ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %[[intmax]]
807        %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]")
808             << MainSuffix();
809     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
810   }
811 }
812 
TEST_F(GraphicsRobustAccessTest,ACArraySpecIdSizedAlwaysClamped)813 TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) {
814   for (auto* ac : AccessChains()) {
815     std::ostringstream shaders;
816     shaders << ShaderPreambleAC({"spec200"}) << R"(
817        OpDecorate %spec200 SpecId 0 )" << TypesVoid() << TypesInt()
818             << TypesFloat() << R"(
819        %spec200 = OpSpecConstant %int 200
820        %arr = OpTypeArray %float %spec200
821        %var_ty = OpTypePointer Function %arr
822        %ptr_ty = OpTypePointer Function %float
823        %uint_5 = OpConstant %uint 5
824        )" << MainPrefix() << R"(
825        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
826        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
827        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
828        ; CHECK-DAG: %[[uint_intmax:\w+]] = OpConstant %uint 2147483647
829        ; CHECK: OpLabel
830        ; CHECK: %[[max:\w+]] = OpISub %uint %spec200 %uint_1
831        ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[uint_intmax]]
832        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %uint_5 %uint_0 %[[smin]]
833        %var = OpVariable %var_ty Function)"
834             << ACCheck(ac, "%uint_5", "%[[clamp]]") << MainSuffix();
835     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
836   }
837 }
838 
TEST_F(GraphicsRobustAccessTest,ACStructLeastUntouched)839 TEST_F(GraphicsRobustAccessTest, ACStructLeastUntouched) {
840   for (auto* ac : AccessChains()) {
841     std::ostringstream shaders;
842     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
843             << TypesFloat() << R"(
844        %struct = OpTypeStruct %float %float %float
845        %var_ty = OpTypePointer Function %struct
846        %ptr_ty = OpTypePointer Function %float
847        %int_0 = OpConstant %int 0
848        )" << MainPrefix() << R"(
849        %var = OpVariable %var_ty Function)"
850             << ACCheck(ac, "%int_0", "%int_0") << MainSuffix();
851     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
852   }
853 }
854 
TEST_F(GraphicsRobustAccessTest,ACStructMostUntouched)855 TEST_F(GraphicsRobustAccessTest, ACStructMostUntouched) {
856   for (auto* ac : AccessChains()) {
857     std::ostringstream shaders;
858     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
859             << TypesFloat() << R"(
860        %struct = OpTypeStruct %float %float %float
861        %var_ty = OpTypePointer Function %struct
862        %ptr_ty = OpTypePointer Function %float
863        %int_2 = OpConstant %int 2
864        )" << MainPrefix() << R"(
865        %var = OpVariable %var_ty Function)"
866             << ACCheck(ac, "%int_2", "%int_2") << MainSuffix();
867     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
868   }
869 }
870 
TEST_F(GraphicsRobustAccessTest,ACStructSpecConstantFail)871 TEST_F(GraphicsRobustAccessTest, ACStructSpecConstantFail) {
872   for (auto* ac : AccessChains()) {
873     std::ostringstream shaders;
874     shaders << ShaderPreambleAC({"struct", "spec200"})
875             << "OpDecorate %spec200 SpecId 0\n"
876             <<
877 
878         TypesVoid() << TypesInt() << TypesFloat() << R"(
879        %spec200 = OpSpecConstant %int 200
880        %struct = OpTypeStruct %float %float %float
881        %var_ty = OpTypePointer Function %struct
882        %ptr_ty = OpTypePointer Function %float
883        )" << MainPrefix() << R"(
884        %var = OpVariable %var_ty Function
885        ; CHECK: Member index into struct is not a constant integer
886        ; CHECK-SAME: %spec200 = OpSpecConstant %int 200
887        )"
888             << ACCheckFail(ac, "%spec200", "%spec200") << MainSuffix();
889     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
890   }
891 }
892 
TEST_F(GraphicsRobustAccessTest,ACStructFloatConstantFail)893 TEST_F(GraphicsRobustAccessTest, ACStructFloatConstantFail) {
894   for (auto* ac : AccessChains()) {
895     std::ostringstream shaders;
896     shaders << ShaderPreambleAC({"struct"}) <<
897 
898         TypesVoid() << TypesInt() << TypesFloat() << R"(
899        %float_2 = OpConstant %float 2
900        %struct = OpTypeStruct %float %float %float
901        %var_ty = OpTypePointer Function %struct
902        %ptr_ty = OpTypePointer Function %float
903        )" << MainPrefix() << R"(
904        %var = OpVariable %var_ty Function
905        ; CHECK: Member index into struct is not a constant integer
906        ; CHECK-SAME: %float_2 = OpConstant %float 2
907        )"
908             << ACCheckFail(ac, "%float_2", "%float_2") << MainSuffix();
909     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
910   }
911 }
912 
TEST_F(GraphicsRobustAccessTest,ACStructNonConstantFail)913 TEST_F(GraphicsRobustAccessTest, ACStructNonConstantFail) {
914   for (auto* ac : AccessChains()) {
915     std::ostringstream shaders;
916     shaders << ShaderPreambleAC({"struct", "i"}) <<
917 
918         TypesVoid() << TypesInt() << TypesFloat() << R"(
919        %float_2 = OpConstant %float 2
920        %struct = OpTypeStruct %float %float %float
921        %var_ty = OpTypePointer Function %struct
922        %ptr_ty = OpTypePointer Function %float
923        %i = OpUndef %int
924        )" << MainPrefix() << R"(
925        %var = OpVariable %var_ty Function
926        ; CHECK: Member index into struct is not a constant integer
927        ; CHECK-SAME: %i = OpUndef %int
928        )"
929             << ACCheckFail(ac, "%i", "%i") << MainSuffix();
930     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
931   }
932 }
933 
TEST_F(GraphicsRobustAccessTest,ACStructExcessFail)934 TEST_F(GraphicsRobustAccessTest, ACStructExcessFail) {
935   for (auto* ac : AccessChains()) {
936     std::ostringstream shaders;
937     shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt()
938             << TypesFloat() << R"(
939        %struct = OpTypeStruct %float %float %float
940        %var_ty = OpTypePointer Function %struct
941        %ptr_ty = OpTypePointer Function %float
942        %i = OpConstant %int 4
943        )" << MainPrefix() << R"(
944        %var = OpVariable %var_ty Function
945        ; CHECK: Member index 4 is out of bounds for struct type:
946        ; CHECK-SAME: %struct = OpTypeStruct %float %float %float
947        )"
948             << ACCheckFail(ac, "%i", "%i") << MainSuffix();
949     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
950   }
951 }
952 
TEST_F(GraphicsRobustAccessTest,ACStructNegativeFail)953 TEST_F(GraphicsRobustAccessTest, ACStructNegativeFail) {
954   for (auto* ac : AccessChains()) {
955     std::ostringstream shaders;
956     shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt()
957             << TypesFloat() << R"(
958        %struct = OpTypeStruct %float %float %float
959        %var_ty = OpTypePointer Function %struct
960        %ptr_ty = OpTypePointer Function %float
961        %i = OpConstant %int -1
962        )" << MainPrefix() << R"(
963        %var = OpVariable %var_ty Function
964        ; CHECK: Member index -1 is out of bounds for struct type:
965        ; CHECK-SAME: %struct = OpTypeStruct %float %float %float
966        )"
967             << ACCheckFail(ac, "%i", "%i") << MainSuffix();
968     SinglePassRunAndFail<GraphicsRobustAccessPass>(shaders.str());
969   }
970 }
971 
TEST_F(GraphicsRobustAccessTest,ACRTArrayLeastInboundClamped)972 TEST_F(GraphicsRobustAccessTest, ACRTArrayLeastInboundClamped) {
973   for (auto* ac : AccessChains()) {
974     std::ostringstream shaders;
975     shaders << ShaderPreambleAC() << "OpDecorate %rtarr ArrayStride 4 "
976             << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
977        %rtarr = OpTypeRuntimeArray %float
978        %ssbo_s = OpTypeStruct %uint %uint %rtarr
979        %var_ty = OpTypePointer Uniform %ssbo_s
980        %ptr_ty = OpTypePointer Uniform %float
981        %var = OpVariable %var_ty Uniform
982        %int_0 = OpConstant %int 0
983        %int_2 = OpConstant %int 2
984        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
985        ; CHECK-DAG: %int_1 = OpConstant %int 1
986        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
987        ; CHECK: OpLabel
988        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
989        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
990        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
991        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %int_0 %int_0 %[[smin]]
992        )"
993             << MainPrefix() << ACCheck(ac, "%int_2 %int_0", "%int_2 %[[clamp]]")
994             << MainSuffix();
995     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
996   }
997 }
998 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralShortIndexClamped)999 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralShortIndexClamped) {
1000   for (auto* ac : AccessChains()) {
1001     std::ostringstream shaders;
1002     shaders << "OpCapability Int16\n"
1003             << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
1004             << DecoSSBO() << TypesVoid() << TypesShort() << TypesFloat() << R"(
1005        %rtarr = OpTypeRuntimeArray %float
1006        %ssbo_s = OpTypeStruct %short %short %rtarr
1007        %var_ty = OpTypePointer Uniform %ssbo_s
1008        %ptr_ty = OpTypePointer Uniform %float
1009        %var = OpVariable %var_ty Uniform
1010        %short_2 = OpConstant %short 2
1011        %i = OpUndef %short
1012        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1013        ; CHECK: %uint = OpTypeInt 32 0
1014        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
1015        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
1016        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
1017        ; CHECK: OpLabel
1018        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1019        ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
1020        ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
1021        ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1022        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
1023        )"
1024             << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
1025             << MainSuffix();
1026     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1027   }
1028 }
1029 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralUShortIndexClamped)1030 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUShortIndexClamped) {
1031   for (auto* ac : AccessChains()) {
1032     std::ostringstream shaders;
1033     shaders << "OpCapability Int16\n"
1034             << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
1035             << DecoSSBO() << TypesVoid() << TypesShort() << TypesFloat() << R"(
1036        %rtarr = OpTypeRuntimeArray %float
1037        %ssbo_s = OpTypeStruct %short %short %rtarr
1038        %var_ty = OpTypePointer Uniform %ssbo_s
1039        %ptr_ty = OpTypePointer Uniform %float
1040        %var = OpVariable %var_ty Uniform
1041        %short_2 = OpConstant %short 2
1042        %i = OpUndef %ushort
1043        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1044        ; CHECK: %uint = OpTypeInt 32 0
1045        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
1046        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
1047        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
1048        ; CHECK: OpLabel
1049        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1050        ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
1051        ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i
1052        ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1053        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]]
1054        )"
1055             << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]")
1056             << MainSuffix();
1057     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1058   }
1059 }
1060 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralIntIndexClamped)1061 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralIntIndexClamped) {
1062   for (auto* ac : AccessChains()) {
1063     std::ostringstream shaders;
1064     shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
1065             << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
1066        %rtarr = OpTypeRuntimeArray %float
1067        %ssbo_s = OpTypeStruct %int %int %rtarr
1068        %var_ty = OpTypePointer Uniform %ssbo_s
1069        %ptr_ty = OpTypePointer Uniform %float
1070        %var = OpVariable %var_ty Uniform
1071        %int_2 = OpConstant %int 2
1072        %i = OpUndef %int
1073        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1074        ; CHECK-DAG: %int_1 = OpConstant %int 1
1075        ; CHECK-DAG: %int_0 = OpConstant %int 0
1076        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1077        ; CHECK: OpLabel
1078        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1079        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1080        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1081        ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
1082        )"
1083             << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
1084             << MainSuffix();
1085     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1086   }
1087 }
1088 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralUIntIndexClamped)1089 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUIntIndexClamped) {
1090   for (auto* ac : AccessChains()) {
1091     std::ostringstream shaders;
1092     shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
1093             << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"(
1094        %rtarr = OpTypeRuntimeArray %float
1095        %ssbo_s = OpTypeStruct %int %int %rtarr
1096        %var_ty = OpTypePointer Uniform %ssbo_s
1097        %ptr_ty = OpTypePointer Uniform %float
1098        %var = OpVariable %var_ty Uniform
1099        %int_2 = OpConstant %int 2
1100        %i = OpUndef %uint
1101        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1102        ; CHECK-DAG: %uint_1 = OpConstant %uint 1
1103        ; CHECK-DAG: %uint_0 = OpConstant %uint 0
1104        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647
1105        ; CHECK: OpLabel
1106        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1107        ; CHECK: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1
1108        ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1109        ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %uint_0 %[[smin]]
1110        )"
1111             << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]")
1112             << MainSuffix();
1113     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1114   }
1115 }
1116 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralLongIndexClamped)1117 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralLongIndexClamped) {
1118   for (auto* ac : AccessChains()) {
1119     std::ostringstream shaders;
1120     shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
1121             << "OpDecorate %rtarr ArrayStride 4 " << DecoSSBO() << TypesVoid()
1122             << TypesInt() << TypesLong() << TypesFloat() << R"(
1123        %rtarr = OpTypeRuntimeArray %float
1124        %ssbo_s = OpTypeStruct %int %int %rtarr
1125        %var_ty = OpTypePointer Uniform %ssbo_s
1126        %ptr_ty = OpTypePointer Uniform %float
1127        %var = OpVariable %var_ty Uniform
1128        %int_2 = OpConstant %int 2
1129        %i = OpUndef %long
1130        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1131        ; CHECK-DAG: %long_0 = OpConstant %long 0
1132        ; CHECK-DAG: %long_1 = OpConstant %long 1
1133        ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %long 9223372036854775807
1134        ; CHECK: OpLabel
1135        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1136        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
1137        ; CHECK: %[[max:\w+]] = OpISub %long %[[arrlen_ext]] %long_1
1138        ; CHECK: %[[smin:\w+]] = OpExtInst %long %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
1139        ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %[[smin]]
1140        )" << MainPrefix()
1141             << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
1142     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1143   }
1144 }
1145 
TEST_F(GraphicsRobustAccessTest,ACRTArrayGeneralULongIndexClamped)1146 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralULongIndexClamped) {
1147   for (auto* ac : AccessChains()) {
1148     std::ostringstream shaders;
1149     shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
1150             << "OpDecorate %rtarr ArrayStride 4 " << DecoSSBO() << TypesVoid()
1151             << TypesInt() << TypesLong() << TypesFloat() << R"(
1152        %rtarr = OpTypeRuntimeArray %float
1153        %ssbo_s = OpTypeStruct %int %int %rtarr
1154        %var_ty = OpTypePointer Uniform %ssbo_s
1155        %ptr_ty = OpTypePointer Uniform %float
1156        %var = OpVariable %var_ty Uniform
1157        %int_2 = OpConstant %int 2
1158        %i = OpUndef %ulong
1159        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1160        ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0
1161        ; CHECK-DAG: %ulong_1 = OpConstant %ulong 1
1162        ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %ulong 9223372036854775807
1163        ; CHECK: OpLabel
1164        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1165        ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]]
1166        ; CHECK: %[[max:\w+]] = OpISub %ulong %[[arrlen_ext]] %ulong_1
1167        ; CHECK: %[[smin:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UMin %[[max]] %[[longmax]]
1168        ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %ulong_0 %[[smin]]
1169        )" << MainPrefix()
1170             << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix();
1171     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1172   }
1173 }
1174 
TEST_F(GraphicsRobustAccessTest,ACRTArrayStructVectorElem)1175 TEST_F(GraphicsRobustAccessTest, ACRTArrayStructVectorElem) {
1176   // The point of this test is that the access chain can have indices past the
1177   // index into the runtime array.  For good measure, the index into the final
1178   // struct is out of bounds.  We have to clamp that index too.
1179   for (auto* ac : AccessChains()) {
1180     std::ostringstream shaders;
1181     shaders << ShaderPreambleAC({"i", "j"})
1182             << "OpDecorate %rtarr ArrayStride 32\n"
1183             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1184             << "OpMemberDecorate %rtelem 1 Offset 16\n"
1185             << TypesVoid() << TypesInt() << TypesFloat() << R"(
1186        %v4float = OpTypeVector %float 4
1187        %rtelem = OpTypeStruct %v4float %v4float
1188        %rtarr = OpTypeRuntimeArray %rtelem
1189        %ssbo_s = OpTypeStruct %int %int %rtarr
1190        %var_ty = OpTypePointer Uniform %ssbo_s
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        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1198        ; CHECK-DAG: %int_0 = OpConstant %int 0
1199        ; CHECK-DAG: %int_3 = OpConstant %int 3
1200        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1201        ; CHECK: OpLabel
1202        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2
1203        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1204        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1205        ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
1206        ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %int_3
1207        )" << MainPrefix()
1208             << ACCheck(ac, "%int_2 %i %int_1 %j",
1209                        "%int_2 %[[clamp_i]] %int_1 %[[clamp_j]]")
1210             << MainSuffix();
1211     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1212   }
1213 }
1214 
TEST_F(GraphicsRobustAccessTest,ACArrayRTArrayStructVectorElem)1215 TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) {
1216   // Now add an additional level of arrays around the Block-decorated struct.
1217   for (auto* ac : AccessChains()) {
1218     std::ostringstream shaders;
1219     shaders << ShaderPreambleAC({"i", "ssbo_s"})
1220             << "OpDecorate %rtarr ArrayStride 32\n"
1221             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1222             << "OpMemberDecorate %rtelem 1 Offset 16\n"
1223             << TypesVoid() << TypesInt() << TypesFloat() << R"(
1224        %v4float = OpTypeVector %float 4
1225        %rtelem = OpTypeStruct %v4float %v4float
1226        %rtarr = OpTypeRuntimeArray %rtelem
1227        %ssbo_s = OpTypeStruct %int %int %rtarr
1228        %arr_size = OpConstant %int 10
1229        %arr_ssbo = OpTypeArray %ssbo_s %arr_size
1230        %var_ty = OpTypePointer Uniform %arr_ssbo
1231        %ptr_ty = OpTypePointer Uniform %float
1232        %var = OpVariable %var_ty Uniform
1233        %int_1 = OpConstant %int 1
1234        %int_2 = OpConstant %int 2
1235        %int_17 = OpConstant %int 17
1236        %i = OpUndef %int
1237        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1238        ; CHECK-DAG: %[[ssbo_p:\w+]] = OpTypePointer Uniform %ssbo_s
1239        ; CHECK-DAG: %int_0 = OpConstant %int 0
1240        ; CHECK-DAG: %int_9 = OpConstant %int 9
1241        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1242        ; CHECK: OpLabel
1243        ; This access chain is manufactured only so we can compute the array length.
1244        ; Note that the %int_9 is already clamped
1245        ; CHECK: %[[ssbo_base:\w+]] = )" << ac
1246             << R"( %[[ssbo_p]] %var %int_9
1247        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %[[ssbo_base]] 2
1248        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1249        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1250        ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]]
1251        )" << MainPrefix()
1252             << ACCheck(ac, "%int_17 %int_2 %i %int_1 %int_2",
1253                        "%int_9 %int_2 %[[clamp_i]] %int_1 %int_2")
1254             << MainSuffix();
1255     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1256   }
1257 }
1258 
TEST_F(GraphicsRobustAccessTest,ACSplitACArrayRTArrayStructVectorElem)1259 TEST_F(GraphicsRobustAccessTest, ACSplitACArrayRTArrayStructVectorElem) {
1260   // Split the address calculation across two access chains.  Force
1261   // the transform to walk up the access chains to find the base variable.
1262   for (auto* ac : AccessChains()) {
1263     std::ostringstream shaders;
1264     shaders << ShaderPreambleAC({"i", "j", "k", "ssbo_s", "ssbo_pty",
1265                                  "rtarr_pty", "ac_ssbo", "ac_rtarr"})
1266             << "OpDecorate %rtarr ArrayStride 32\n"
1267             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1268             << "OpMemberDecorate %rtelem 1 Offset 16\n"
1269             << TypesVoid() << TypesInt() << TypesFloat() << R"(
1270        %v4float = OpTypeVector %float 4
1271        %rtelem = OpTypeStruct %v4float %v4float
1272        %rtarr = OpTypeRuntimeArray %rtelem
1273        %ssbo_s = OpTypeStruct %int %int %rtarr
1274        %arr_size = OpConstant %int 10
1275        %arr_ssbo = OpTypeArray %ssbo_s %arr_size
1276        %var_ty = OpTypePointer Uniform %arr_ssbo
1277        %ssbo_pty = OpTypePointer Uniform %ssbo_s
1278        %rtarr_pty = OpTypePointer Uniform %rtarr
1279        %ptr_ty = OpTypePointer Uniform %float
1280        %var = OpVariable %var_ty Uniform
1281        %int_1 = OpConstant %int 1
1282        %int_2 = OpConstant %int 2
1283        %i = OpUndef %int
1284        %j = OpUndef %int
1285        %k = OpUndef %int
1286        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1287        ; CHECK-DAG: %int_0 = OpConstant %int 0
1288        ; CHECK-DAG: %int_9 = OpConstant %int 9
1289        ; CHECK-DAG: %int_3 = OpConstant %int 3
1290        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1291        ; CHECK: OpLabel
1292        ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
1293        ; CHECK: %ac_ssbo = )" << ac
1294             << R"( %ssbo_pty %var %[[clamp_i]]
1295        ; CHECK: %ac_rtarr = )"
1296             << ac << R"( %rtarr_pty %ac_ssbo %int_2
1297 
1298        ; This is the interesting bit.  This array length is needed for an OpAccessChain
1299        ; computing %ac, but the algorithm had to track back through %ac_rtarr's
1300        ; definition to find the base pointer %ac_ssbo.
1301        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
1302        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1303        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1304        ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
1305        ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
1306        ; CHECK: %ac = )" << ac
1307             << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
1308        ; CHECK-NOT: AccessChain
1309        )" << MainPrefix()
1310             << "%ac_ssbo = " << ac << " %ssbo_pty %var %i\n"
1311             << "%ac_rtarr = " << ac << " %rtarr_pty %ac_ssbo %int_2\n"
1312             << "%ac = " << ac << " %ptr_ty %ac_rtarr %j %int_1 %k\n"
1313 
1314             << MainSuffix();
1315     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1316   }
1317 }
1318 
TEST_F(GraphicsRobustAccessTest,ACSplitACArrayRTArrayStructVectorElemAcrossBasicBlocks)1319 TEST_F(GraphicsRobustAccessTest,
1320        ACSplitACArrayRTArrayStructVectorElemAcrossBasicBlocks) {
1321   // Split the address calculation across two access chains.  Force
1322   // the transform to walk up the access chains to find the base variable.
1323   // This time, put the different access chains in different basic blocks.
1324   // This is an integrity check to ensure that we keep the instruction-to-block
1325   // mapping consistent.
1326   for (auto* ac : AccessChains()) {
1327     std::ostringstream shaders;
1328     shaders << ShaderPreambleAC({"i", "j", "k", "bb1", "bb2", "ssbo_s",
1329                                  "ssbo_pty", "rtarr_pty", "ac_ssbo",
1330                                  "ac_rtarr"})
1331             << "OpDecorate %rtarr ArrayStride 32\n"
1332             << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n"
1333             << "OpMemberDecorate %rtelem 1 Offset 16\n"
1334             << TypesVoid() << TypesInt() << TypesFloat() << R"(
1335        %v4float = OpTypeVector %float 4
1336        %rtelem = OpTypeStruct %v4float %v4float
1337        %rtarr = OpTypeRuntimeArray %rtelem
1338        %ssbo_s = OpTypeStruct %int %int %rtarr
1339        %arr_size = OpConstant %int 10
1340        %arr_ssbo = OpTypeArray %ssbo_s %arr_size
1341        %var_ty = OpTypePointer Uniform %arr_ssbo
1342        %ssbo_pty = OpTypePointer Uniform %ssbo_s
1343        %rtarr_pty = OpTypePointer Uniform %rtarr
1344        %ptr_ty = OpTypePointer Uniform %float
1345        %var = OpVariable %var_ty Uniform
1346        %int_1 = OpConstant %int 1
1347        %int_2 = OpConstant %int 2
1348        %i = OpUndef %int
1349        %j = OpUndef %int
1350        %k = OpUndef %int
1351        ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450"
1352        ; CHECK-DAG: %int_0 = OpConstant %int 0
1353        ; CHECK-DAG: %int_9 = OpConstant %int 9
1354        ; CHECK-DAG: %int_3 = OpConstant %int 3
1355        ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647
1356        ; CHECK: OpLabel
1357        ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9
1358        ; CHECK: %ac_ssbo = )" << ac
1359             << R"( %ssbo_pty %var %[[clamp_i]]
1360        ; CHECK: %bb1 = OpLabel
1361        ; CHECK: %ac_rtarr = )"
1362             << ac << R"( %rtarr_pty %ac_ssbo %int_2
1363        ; CHECK: %bb2 = OpLabel
1364 
1365        ; This is the interesting bit.  This array length is needed for an OpAccessChain
1366        ; computing %ac, but the algorithm had to track back through %ac_rtarr's
1367        ; definition to find the base pointer %ac_ssbo.
1368        ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2
1369        ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1
1370        ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]]
1371        ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]]
1372        ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3
1373        ; CHECK: %ac = )" << ac
1374             << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]]
1375        ; CHECK-NOT: AccessChain
1376        )" << MainPrefix()
1377             << "%ac_ssbo = " << ac << " %ssbo_pty %var %i\n"
1378             << "OpBranch %bb1\n%bb1 = OpLabel\n"
1379             << "%ac_rtarr = " << ac << " %rtarr_pty %ac_ssbo %int_2\n"
1380             << "OpBranch %bb2\n%bb2 = OpLabel\n"
1381             << "%ac = " << ac << " %ptr_ty %ac_rtarr %j %int_1 %k\n"
1382 
1383             << MainSuffix();
1384     SinglePassRunAndMatch<GraphicsRobustAccessPass>(shaders.str(), true);
1385   }
1386 }
1387 
TEST_F(GraphicsRobustAccessTest,bug3813)1388 TEST_F(GraphicsRobustAccessTest, bug3813) {
1389   // This shader comes from Dawn's
1390   // TextureViewSamplingTest.TextureCubeMapOnWholeTexture, converted from GLSL
1391   // by glslang.
1392   // The pass was inserting a signed 32-bit int type, but not correctly marking
1393   // the shader as changed.
1394   std::string shader = R"(
1395 ; SPIR-V
1396 ; Version: 1.0
1397 ; Generator: Google Shaderc over Glslang; 10
1398 ; Bound: 46
1399 ; Schema: 0
1400        OpCapability Shader
1401   %1 = OpExtInstImport "GLSL.std.450"
1402        OpMemoryModel Logical GLSL450
1403        OpEntryPoint Fragment %4 "main" %12 %29
1404        OpExecutionMode %4 OriginUpperLeft
1405        OpSource GLSL 450
1406        OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
1407        OpSourceExtension "GL_GOOGLE_include_directive"
1408        OpName %4 "main"
1409        OpName %8 "sc"
1410        OpName %12 "texCoord"
1411        OpName %21 "tc"
1412        OpName %29 "fragColor"
1413        OpName %32 "texture0"
1414        OpName %36 "sampler0"
1415        OpDecorate %12 Location 0
1416        OpDecorate %29 Location 0
1417        OpDecorate %32 DescriptorSet 0
1418        OpDecorate %32 Binding 1
1419        OpDecorate %36 DescriptorSet 0
1420        OpDecorate %36 Binding 0
1421   %2 = OpTypeVoid
1422   %3 = OpTypeFunction %2
1423   %6 = OpTypeFloat 32
1424   %7 = OpTypePointer Function %6
1425   %9 = OpConstant %6 2
1426  %10 = OpTypeVector %6 2
1427  %11 = OpTypePointer Input %10
1428  %12 = OpVariable %11 Input
1429  %13 = OpTypeInt 32 0
1430  %14 = OpConstant %13 0
1431  %15 = OpTypePointer Input %6
1432  %19 = OpConstant %6 1
1433  %22 = OpConstant %13 1
1434  %27 = OpTypeVector %6 4
1435  %28 = OpTypePointer Output %27
1436  %29 = OpVariable %28 Output
1437  %30 = OpTypeImage %6 Cube 0 0 0 1 Unknown
1438  %31 = OpTypePointer UniformConstant %30
1439  %32 = OpVariable %31 UniformConstant
1440  %34 = OpTypeSampler
1441  %35 = OpTypePointer UniformConstant %34
1442  %36 = OpVariable %35 UniformConstant
1443  %38 = OpTypeSampledImage %30
1444  %43 = OpTypeVector %6 3
1445   %4 = OpFunction %2 None %3
1446   %5 = OpLabel
1447   %8 = OpVariable %7 Function
1448  %21 = OpVariable %7 Function
1449  %16 = OpAccessChain %15 %12 %14
1450  %17 = OpLoad %6 %16
1451  %18 = OpFMul %6 %9 %17
1452  %20 = OpFSub %6 %18 %19
1453        OpStore %8 %20
1454  %23 = OpAccessChain %15 %12 %22
1455  %24 = OpLoad %6 %23
1456  %25 = OpFMul %6 %9 %24
1457  %26 = OpFSub %6 %25 %19
1458        OpStore %21 %26
1459  %33 = OpLoad %30 %32
1460  %37 = OpLoad %34 %36
1461  %39 = OpSampledImage %38 %33 %37
1462  %40 = OpLoad %6 %21
1463  %41 = OpLoad %6 %8
1464  %42 = OpFNegate %6 %41
1465  %44 = OpCompositeConstruct %43 %19 %40 %42
1466  %45 = OpImageSampleImplicitLod %27 %39 %44
1467        OpStore %29 %45
1468        OpReturn
1469        OpFunctionEnd
1470 )";
1471 
1472   std::string expected = R"(OpCapability Shader
1473 %1 = OpExtInstImport "GLSL.std.450"
1474 OpMemoryModel Logical GLSL450
1475 OpEntryPoint Fragment %main "main" %texCoord %fragColor
1476 OpExecutionMode %main OriginUpperLeft
1477 OpSource GLSL 450
1478 OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
1479 OpSourceExtension "GL_GOOGLE_include_directive"
1480 OpName %main "main"
1481 OpName %sc "sc"
1482 OpName %texCoord "texCoord"
1483 OpName %tc "tc"
1484 OpName %fragColor "fragColor"
1485 OpName %texture0 "texture0"
1486 OpName %sampler0 "sampler0"
1487 OpDecorate %texCoord Location 0
1488 OpDecorate %fragColor Location 0
1489 OpDecorate %texture0 DescriptorSet 0
1490 OpDecorate %texture0 Binding 1
1491 OpDecorate %sampler0 DescriptorSet 0
1492 OpDecorate %sampler0 Binding 0
1493 %void = OpTypeVoid
1494 %10 = OpTypeFunction %void
1495 %float = OpTypeFloat 32
1496 %_ptr_Function_float = OpTypePointer Function %float
1497 %float_2 = OpConstant %float 2
1498 %v2float = OpTypeVector %float 2
1499 %_ptr_Input_v2float = OpTypePointer Input %v2float
1500 %texCoord = OpVariable %_ptr_Input_v2float Input
1501 %uint = OpTypeInt 32 0
1502 %uint_0 = OpConstant %uint 0
1503 %_ptr_Input_float = OpTypePointer Input %float
1504 %float_1 = OpConstant %float 1
1505 %uint_1 = OpConstant %uint 1
1506 %v4float = OpTypeVector %float 4
1507 %_ptr_Output_v4float = OpTypePointer Output %v4float
1508 %fragColor = OpVariable %_ptr_Output_v4float Output
1509 %23 = OpTypeImage %float Cube 0 0 0 1 Unknown
1510 %_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23
1511 %texture0 = OpVariable %_ptr_UniformConstant_23 UniformConstant
1512 %25 = OpTypeSampler
1513 %_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25
1514 %sampler0 = OpVariable %_ptr_UniformConstant_25 UniformConstant
1515 %27 = OpTypeSampledImage %23
1516 %v3float = OpTypeVector %float 3
1517 %int = OpTypeInt 32 1
1518 %main = OpFunction %void None %10
1519 %29 = OpLabel
1520 %sc = OpVariable %_ptr_Function_float Function
1521 %tc = OpVariable %_ptr_Function_float Function
1522 %30 = OpAccessChain %_ptr_Input_float %texCoord %uint_0
1523 %31 = OpLoad %float %30
1524 %32 = OpFMul %float %float_2 %31
1525 %33 = OpFSub %float %32 %float_1
1526 OpStore %sc %33
1527 %34 = OpAccessChain %_ptr_Input_float %texCoord %uint_1
1528 %35 = OpLoad %float %34
1529 %36 = OpFMul %float %float_2 %35
1530 %37 = OpFSub %float %36 %float_1
1531 OpStore %tc %37
1532 %38 = OpLoad %23 %texture0
1533 %39 = OpLoad %25 %sampler0
1534 %40 = OpSampledImage %27 %38 %39
1535 %41 = OpLoad %float %tc
1536 %42 = OpLoad %float %sc
1537 %43 = OpFNegate %float %42
1538 %44 = OpCompositeConstruct %v3float %float_1 %41 %43
1539 %45 = OpImageSampleImplicitLod %v4float %40 %44
1540 OpStore %fragColor %45
1541 OpReturn
1542 OpFunctionEnd
1543 )";
1544 
1545   SinglePassRunAndCheck<GraphicsRobustAccessPass>(shader, expected, false,
1546                                                   true);
1547 }
1548 
TEST_F(GraphicsRobustAccessTest,ReplaceIndexReportsChanged)1549 TEST_F(GraphicsRobustAccessTest, ReplaceIndexReportsChanged) {
1550   // A ClusterFuzz generated shader that triggered a
1551   // "Binary size unexpectedly changed despite the optimizer saying there was no
1552   // change" assertion.
1553   // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4166.
1554   std::string shader = R"(
1555 ; SPIR-V
1556 ; Version: 1.0
1557 ; Generator: Google Shaderc over Glslang; 245
1558 ; Bound: 41
1559 ; Schema: 0
1560                OpCapability Shader
1561           %1 = OpExtInstImport "GLSL.std.450"
1562                OpMemoryModel Logical GLSL450
1563                OpEntryPoint GLCompute %main "else" %gl_GlobalInvocationID
1564                OpExecutionMode %main LocalSize 1 1 3338665985
1565                OpSource GLSL 450
1566                OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
1567                OpSourceExtension "GL_GOOGLE_include_directive"
1568                OpName %main "main"
1569                OpName %index "index"
1570                OpName %gl_GlobalInvocationID "gl_GlobalInvocationID"
1571                OpName %S "S"
1572                OpMemberName %_struct_24 0 ""
1573                OpMemberName %_struct_24 1 ""
1574                OpName %Dst "Dst"
1575                OpMemberName %Dst 0 "s"
1576                OpName %dst "dst"
1577                OpName %Src "Src"
1578                OpMemberName %Src 0 "s"
1579                OpName %src "src"
1580                OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
1581                OpMemberDecorate %_struct_24 0 Offset 64
1582                OpMemberDecorate %_struct_24 1 Offset 8
1583                OpDecorate %_arr__struct_24_uint_1 ArrayStride 16
1584                OpMemberDecorate %Dst 0 Offset 0
1585                OpDecorate %Dst BufferBlock
1586                OpDecorate %dst DescriptorSet 0
1587                OpDecorate %dst Binding 1
1588                OpDecorate %_arr__struct_24_uint_1_0 ArrayStride 16
1589                OpMemberDecorate %Src 0 Offset 0
1590                OpDecorate %Src Block
1591                OpDecorate %src DescriptorSet 0
1592                OpDecorate %src Binding 0
1593        %void = OpTypeVoid
1594           %3 = OpTypeFunction %void
1595        %uint = OpTypeInt 32 0
1596 %_ptr_Function_uint = OpTypePointer Function %uint
1597      %v3uint = OpTypeVector %uint 3
1598 %_ptr_Input_v3uint = OpTypePointer Input %v3uint
1599 %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
1600   %uint_4864 = OpConstant %uint 4864
1601 %_ptr_Input_uint = OpTypePointer Input %uint
1602      %uint_1 = OpConstant %uint 1
1603        %bool = OpTypeBool
1604      %v2uint = OpTypeVector %uint 2
1605  %_struct_24 = OpTypeStruct %_ptr_Input_uint %v2uint
1606 %_arr__struct_24_uint_1 = OpTypeArray %_struct_24 %uint_1
1607         %Dst = OpTypeStruct %_arr__struct_24_uint_1
1608 %_ptr_Uniform_Dst = OpTypePointer Uniform %Dst
1609         %dst = OpVariable %_ptr_Uniform_Dst Uniform
1610         %int = OpTypeInt 32 1
1611       %int_0 = OpConstant %int 0
1612 %_arr__struct_24_uint_1_0 = OpTypeArray %_struct_24 %uint_1
1613         %Src = OpTypeStruct %_arr__struct_24_uint_1_0
1614 %_ptr_Uniform_Src = OpTypePointer Uniform %Src
1615         %src = OpVariable %_ptr_Uniform_Src Uniform
1616 %_ptr_Uniform__struct_24 = OpTypePointer Uniform %_struct_24
1617        %main = OpFunction %void None %3
1618           %5 = OpLabel
1619       %index = OpVariable %_ptr_Function_uint Function
1620          %14 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_4864
1621          %15 = OpLoad %uint %14
1622                OpStore %index %15
1623          %16 = OpLoad %uint %index
1624           %S = OpUGreaterThanEqual %bool %16 %uint_1
1625                OpSelectionMerge %21 None
1626                OpBranchConditional %S %20 %21
1627          %20 = OpLabel
1628                OpReturn
1629          %21 = OpLabel
1630          %31 = OpLoad %uint %index
1631          %36 = OpLoad %uint %index
1632          %38 = OpAccessChain %_ptr_Uniform__struct_24 %src %int_0 %36
1633          %39 = OpLoad %_struct_24 %38
1634          %40 = OpAccessChain %_ptr_Uniform__struct_24 %dst %int_0 %31
1635                OpStore %40 %39
1636                OpReturn
1637                OpFunctionEnd
1638 )";
1639 
1640   std::vector<uint32_t> optimized_bin;
1641   auto status = spvtools::opt::Pass::Status::Failure;
1642   std::tie(optimized_bin, status) =
1643       SinglePassRunToBinary<GraphicsRobustAccessPass>(shader, false);
1644   // Check whether the pass returns the correct modification indication.
1645   EXPECT_EQ(status, spvtools::opt::Pass::Status::SuccessWithChange);
1646 }
1647 
1648 // TODO(dneto): Test access chain index wider than 64 bits?
1649 // TODO(dneto): Test struct access chain index wider than 64 bits?
1650 // TODO(dneto): OpImageTexelPointer
1651 //   - all Dim types: 1D 2D Cube 3D Rect Buffer
1652 //   - all Dim types that can be arrayed: 1D 2D 3D
1653 //   - sample index: set to 0 if not multisampled
1654 //   - Dim (2D, Cube Rect} with multisampling
1655 //      -1 0 max excess
1656 // TODO(dneto): Test OpImageTexelPointer with coordinate component index other
1657 // than 32 bits.
1658 
1659 }  // namespace
1660