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