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