1 // Copyright (c) 2017 Google Inc.
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 <string>
16 
17 #include "gmock/gmock.h"
18 #include "source/opt/build_module.h"
19 #include "source/opt/value_number_table.h"
20 #include "test/opt/assembly_builder.h"
21 #include "test/opt/pass_fixture.h"
22 #include "test/opt/pass_utils.h"
23 
24 namespace spvtools {
25 namespace opt {
26 namespace {
27 
28 using ::testing::HasSubstr;
29 using ::testing::MatchesRegex;
30 using PrivateToLocalTest = PassTest<::testing::Test>;
31 
TEST_F(PrivateToLocalTest,ChangeToLocal)32 TEST_F(PrivateToLocalTest, ChangeToLocal) {
33   // Change the private variable to a local, and change the types accordingly.
34   const std::string text = R"(
35                OpCapability Shader
36           %1 = OpExtInstImport "GLSL.std.450"
37                OpMemoryModel Logical GLSL450
38                OpEntryPoint Fragment %2 "main"
39                OpExecutionMode %2 OriginUpperLeft
40                OpSource GLSL 430
41           %3 = OpTypeVoid
42           %4 = OpTypeFunction %3
43 ; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32
44           %5 = OpTypeFloat 32
45 ; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]]
46           %6 = OpTypePointer Private %5
47 ; CHECK-NOT: OpVariable [[.+]] Private
48           %8 = OpVariable %6 Private
49 ; CHECK: OpFunction
50           %2 = OpFunction %3 None %4
51 ; CHECK: OpLabel
52           %7 = OpLabel
53 ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function
54 ; CHECK: OpLoad [[float]] [[newvar]]
55           %9 = OpLoad %5 %8
56                OpReturn
57                OpFunctionEnd
58   )";
59   SinglePassRunAndMatch<PrivateToLocalPass>(text, false);
60 }
61 
TEST_F(PrivateToLocalTest,ReuseExistingType)62 TEST_F(PrivateToLocalTest, ReuseExistingType) {
63   // Change the private variable to a local, and change the types accordingly.
64   const std::string text = R"(
65                OpCapability Shader
66           %1 = OpExtInstImport "GLSL.std.450"
67                OpMemoryModel Logical GLSL450
68                OpEntryPoint Fragment %2 "main"
69                OpExecutionMode %2 OriginUpperLeft
70                OpSource GLSL 430
71           %3 = OpTypeVoid
72           %4 = OpTypeFunction %3
73 ; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32
74           %5 = OpTypeFloat 32
75         %func_ptr = OpTypePointer Function %5
76 ; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]]
77 ; CHECK-NOT: [[%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]]
78           %6 = OpTypePointer Private %5
79 ; CHECK-NOT: OpVariable [[.+]] Private
80           %8 = OpVariable %6 Private
81 ; CHECK: OpFunction
82           %2 = OpFunction %3 None %4
83 ; CHECK: OpLabel
84           %7 = OpLabel
85 ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function
86 ; CHECK: OpLoad [[float]] [[newvar]]
87           %9 = OpLoad %5 %8
88                OpReturn
89                OpFunctionEnd
90   )";
91   SinglePassRunAndMatch<PrivateToLocalPass>(text, false);
92 }
93 
TEST_F(PrivateToLocalTest,UpdateAccessChain)94 TEST_F(PrivateToLocalTest, UpdateAccessChain) {
95   // Change the private variable to a local, and change the AccessChain.
96   const std::string text = R"(
97                OpCapability Shader
98           %1 = OpExtInstImport "GLSL.std.450"
99                OpMemoryModel Logical GLSL450
100                OpEntryPoint Fragment %2 "main"
101                OpExecutionMode %2 OriginUpperLeft
102                OpSource GLSL 430
103        %uint = OpTypeInt 32 0
104      %uint_0 = OpConstant %uint 0
105        %void = OpTypeVoid
106           %6 = OpTypeFunction %void
107 ; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat
108       %float = OpTypeFloat 32
109 ; CHECK: [[struct:%[a-zA-Z_\d]+]] = OpTypeStruct
110   %_struct_8 = OpTypeStruct %float
111 %_ptr_Private_float = OpTypePointer Private %float
112 ; CHECK: [[new_struct_type:%[a-zA-Z_\d]+]] = OpTypePointer Function [[struct]]
113 ; CHECK: [[new_float_type:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]]
114 %_ptr_Private__struct_8 = OpTypePointer Private %_struct_8
115 ; CHECK-NOT: OpVariable [[.+]] Private
116          %11 = OpVariable %_ptr_Private__struct_8 Private
117 ; CHECK: OpFunction
118           %2 = OpFunction %void None %6
119 ; CHECK: OpLabel
120          %12 = OpLabel
121 ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[new_struct_type]] Function
122 ; CHECK: [[member:%[a-zA-Z_\d]+]] = OpAccessChain [[new_float_type]] [[newvar]]
123          %13 = OpAccessChain %_ptr_Private_float %11 %uint_0
124 ; CHECK: OpLoad [[float]] [[member]]
125          %14 = OpLoad %float %13
126                OpReturn
127                OpFunctionEnd
128   )";
129   SinglePassRunAndMatch<PrivateToLocalPass>(text, false);
130 }
131 
TEST_F(PrivateToLocalTest,UseTexelPointer)132 TEST_F(PrivateToLocalTest, UseTexelPointer) {
133   // Change the private variable to a local, and change the OpImageTexelPointer.
134   const std::string text = R"(
135 OpCapability SampledBuffer
136                OpCapability StorageImageExtendedFormats
137                OpCapability ImageBuffer
138                OpCapability Shader
139           %1 = OpExtInstImport "GLSL.std.450"
140                OpMemoryModel Logical GLSL450
141                OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID
142                OpExecutionMode %2 LocalSize 64 1 1
143                OpSource HLSL 600
144                OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
145                OpDecorate %4 DescriptorSet 4
146                OpDecorate %4 Binding 70
147        %uint = OpTypeInt 32 0
148           %6 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
149 %_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
150 %_ptr_Private_6 = OpTypePointer Private %6
151        %void = OpTypeVoid
152          %10 = OpTypeFunction %void
153      %uint_0 = OpConstant %uint 0
154      %uint_1 = OpConstant %uint 1
155      %v3uint = OpTypeVector %uint 3
156 %_ptr_Input_v3uint = OpTypePointer Input %v3uint
157 %_ptr_Image_uint = OpTypePointer Image %uint
158           %4 = OpVariable %_ptr_UniformConstant_6 UniformConstant
159          %16 = OpVariable %_ptr_Private_6 Private
160 %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
161           %2 = OpFunction %void None %10
162          %17 = OpLabel
163 ; Make sure the variable was moved.
164 ; CHECK: OpFunction
165 ; CHECK-NEXT: OpLabel
166 ; CHECK-NEXT: OpVariable %_ptr_Function_6 Function
167          %18 = OpLoad %6 %4
168                OpStore %16 %18
169          %19 = OpImageTexelPointer %_ptr_Image_uint %16 %uint_0 %uint_0
170          %20 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %uint_1
171                OpReturn
172                OpFunctionEnd
173   )";
174   SinglePassRunAndMatch<PrivateToLocalPass>(text, false);
175 }
176 
TEST_F(PrivateToLocalTest,UsedInTwoFunctions)177 TEST_F(PrivateToLocalTest, UsedInTwoFunctions) {
178   // Should not change because it is used in multiple functions.
179   const std::string text = R"(
180                OpCapability Shader
181           %1 = OpExtInstImport "GLSL.std.450"
182                OpMemoryModel Logical GLSL450
183                OpEntryPoint Fragment %2 "main"
184                OpExecutionMode %2 OriginUpperLeft
185                OpSource GLSL 430
186           %3 = OpTypeVoid
187           %4 = OpTypeFunction %3
188           %5 = OpTypeFloat 32
189           %6 = OpTypePointer Private %5
190           %8 = OpVariable %6 Private
191           %2 = OpFunction %3 None %4
192           %7 = OpLabel
193           %9 = OpLoad %5 %8
194                OpReturn
195                OpFunctionEnd
196          %10 = OpFunction %3 None %4
197          %11 = OpLabel
198          %12 = OpLoad %5 %8
199                OpReturn
200                OpFunctionEnd
201   )";
202   auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
203       text, /* skip_nop = */ true, /* do_validation = */ false);
204   EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
205 }
206 
TEST_F(PrivateToLocalTest,UsedInFunctionCall)207 TEST_F(PrivateToLocalTest, UsedInFunctionCall) {
208   // Should not change because it is used in a function call.  Changing the
209   // signature of the function would require cloning the function, which is not
210   // worth it.
211   const std::string text = R"(
212                OpCapability Shader
213           %1 = OpExtInstImport "GLSL.std.450"
214                OpMemoryModel Logical GLSL450
215                OpEntryPoint Fragment %2 "main"
216                OpExecutionMode %2 OriginUpperLeft
217                OpSource GLSL 430
218        %void = OpTypeVoid
219           %4 = OpTypeFunction %void
220       %float = OpTypeFloat 32
221 %_ptr_Private_float = OpTypePointer Private %float
222           %7 = OpTypeFunction %void %_ptr_Private_float
223           %8 = OpVariable %_ptr_Private_float Private
224           %2 = OpFunction %void None %4
225           %9 = OpLabel
226          %10 = OpFunctionCall %void %11 %8
227                OpReturn
228                OpFunctionEnd
229          %11 = OpFunction %void None %7
230          %12 = OpFunctionParameter %_ptr_Private_float
231          %13 = OpLabel
232          %14 = OpLoad %float %12
233                OpReturn
234                OpFunctionEnd
235   )";
236   auto result = SinglePassRunAndDisassemble<StrengthReductionPass>(
237       text, /* skip_nop = */ true, /* do_validation = */ false);
238   EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
239 }
240 
TEST_F(PrivateToLocalTest,CreatePointerToAmbiguousStruct1)241 TEST_F(PrivateToLocalTest, CreatePointerToAmbiguousStruct1) {
242   // Test that the correct pointer type is picked up.
243   const std::string text = R"(
244 ; CHECK: [[struct1:%[a-zA-Z_\d]+]] = OpTypeStruct
245 ; CHECK: [[struct2:%[a-zA-Z_\d]+]] = OpTypeStruct
246 ; CHECK: [[priv_ptr:%[\w]+]] = OpTypePointer Private [[struct1]]
247 ; CHECK: [[fuct_ptr2:%[\w]+]] = OpTypePointer Function [[struct2]]
248 ; CHECK: [[fuct_ptr1:%[\w]+]] = OpTypePointer Function [[struct1]]
249 ; CHECK: OpFunction
250 ; CHECK: OpLabel
251 ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[fuct_ptr1]] Function
252 ; CHECK: OpLoad [[struct1]] [[newvar]]
253                OpCapability Shader
254           %1 = OpExtInstImport "GLSL.std.450"
255                OpMemoryModel Logical GLSL450
256                OpEntryPoint Fragment %2 "main"
257                OpExecutionMode %2 OriginUpperLeft
258                OpSource GLSL 430
259           %3 = OpTypeVoid
260           %4 = OpTypeFunction %3
261           %5 = OpTypeFloat 32
262     %struct1 = OpTypeStruct %5
263     %struct2 = OpTypeStruct %5
264           %6 = OpTypePointer Private %struct1
265   %func_ptr2 = OpTypePointer Function %struct2
266           %8 = OpVariable %6 Private
267           %2 = OpFunction %3 None %4
268           %7 = OpLabel
269           %9 = OpLoad %struct1 %8
270                OpReturn
271                OpFunctionEnd
272   )";
273   SinglePassRunAndMatch<PrivateToLocalPass>(text, false);
274 }
275 
TEST_F(PrivateToLocalTest,CreatePointerToAmbiguousStruct2)276 TEST_F(PrivateToLocalTest, CreatePointerToAmbiguousStruct2) {
277   // Test that the correct pointer type is picked up.
278   const std::string text = R"(
279 ; CHECK: [[struct1:%[a-zA-Z_\d]+]] = OpTypeStruct
280 ; CHECK: [[struct2:%[a-zA-Z_\d]+]] = OpTypeStruct
281 ; CHECK: [[priv_ptr:%[\w]+]] = OpTypePointer Private [[struct2]]
282 ; CHECK: [[fuct_ptr1:%[\w]+]] = OpTypePointer Function [[struct1]]
283 ; CHECK: [[fuct_ptr2:%[\w]+]] = OpTypePointer Function [[struct2]]
284 ; CHECK: OpFunction
285 ; CHECK: OpLabel
286 ; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[fuct_ptr2]] Function
287 ; CHECK: OpLoad [[struct2]] [[newvar]]
288                OpCapability Shader
289           %1 = OpExtInstImport "GLSL.std.450"
290                OpMemoryModel Logical GLSL450
291                OpEntryPoint Fragment %2 "main"
292                OpExecutionMode %2 OriginUpperLeft
293                OpSource GLSL 430
294           %3 = OpTypeVoid
295           %4 = OpTypeFunction %3
296           %5 = OpTypeFloat 32
297     %struct1 = OpTypeStruct %5
298     %struct2 = OpTypeStruct %5
299           %6 = OpTypePointer Private %struct2
300   %func_ptr2 = OpTypePointer Function %struct1
301           %8 = OpVariable %6 Private
302           %2 = OpFunction %3 None %4
303           %7 = OpLabel
304           %9 = OpLoad %struct2 %8
305                OpReturn
306                OpFunctionEnd
307   )";
308   SinglePassRunAndMatch<PrivateToLocalPass>(text, false);
309 }
310 
TEST_F(PrivateToLocalTest,SPV14RemoveFromInterface)311 TEST_F(PrivateToLocalTest, SPV14RemoveFromInterface) {
312   const std::string text = R"(
313 ; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv
314 ; CHECK: OpEntryPoint GLCompute %foo "foo" %in
315 ; CHECK: %priv = OpVariable {{%\w+}} Function
316 OpCapability Shader
317 OpMemoryModel Logical GLSL450
318 OpEntryPoint GLCompute %foo "foo" %in %priv
319 OpExecutionMode %foo LocalSize 1 1 1
320 OpName %foo "foo"
321 OpName %in "in"
322 OpName %priv "priv"
323 %void = OpTypeVoid
324 %int = OpTypeInt 32 0
325 %ptr_ssbo_int = OpTypePointer StorageBuffer %int
326 %ptr_private_int = OpTypePointer Private %int
327 %in = OpVariable %ptr_ssbo_int StorageBuffer
328 %priv = OpVariable %ptr_private_int Private
329 %void_fn = OpTypeFunction %void
330 %foo = OpFunction %void None %void_fn
331 %entry = OpLabel
332 %ld = OpLoad %int %in
333 OpStore %priv %ld
334 OpReturn
335 OpFunctionEnd
336 )";
337 
338   SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
339   SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
340 }
341 
TEST_F(PrivateToLocalTest,SPV14RemoveFromInterfaceMultipleEntryPoints)342 TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleEntryPoints) {
343   const std::string text = R"(
344 ; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv
345 ; CHECK-NOT: OpEntryPoint GLCompute %foo "bar" %in %priv
346 ; CHECK: OpEntryPoint GLCompute %foo "foo" %in
347 ; CHECK: OpEntryPoint GLCompute %foo "bar" %in
348 ; CHECK: %priv = OpVariable {{%\w+}} Function
349 OpCapability Shader
350 OpMemoryModel Logical GLSL450
351 OpEntryPoint GLCompute %foo "foo" %in %priv
352 OpEntryPoint GLCompute %foo "bar" %in %priv
353 OpExecutionMode %foo LocalSize 1 1 1
354 OpName %foo "foo"
355 OpName %in "in"
356 OpName %priv "priv"
357 %void = OpTypeVoid
358 %int = OpTypeInt 32 0
359 %ptr_ssbo_int = OpTypePointer StorageBuffer %int
360 %ptr_private_int = OpTypePointer Private %int
361 %in = OpVariable %ptr_ssbo_int StorageBuffer
362 %priv = OpVariable %ptr_private_int Private
363 %void_fn = OpTypeFunction %void
364 %foo = OpFunction %void None %void_fn
365 %entry = OpLabel
366 %ld = OpLoad %int %in
367 OpStore %priv %ld
368 OpReturn
369 OpFunctionEnd
370 )";
371 
372   SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
373   SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
374 }
375 
TEST_F(PrivateToLocalTest,SPV14RemoveFromInterfaceMultipleVariables)376 TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleVariables) {
377   const std::string text = R"(
378 ; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2
379 ; CHECK: OpEntryPoint GLCompute %foo "foo" %in
380 ; CHECK: %priv1 = OpVariable {{%\w+}} Function
381 ; CHECK: %priv2 = OpVariable {{%\w+}} Function
382 OpCapability Shader
383 OpMemoryModel Logical GLSL450
384 OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2
385 OpExecutionMode %foo LocalSize 1 1 1
386 OpName %foo "foo"
387 OpName %in "in"
388 OpName %priv1 "priv1"
389 OpName %priv2 "priv2"
390 %void = OpTypeVoid
391 %int = OpTypeInt 32 0
392 %ptr_ssbo_int = OpTypePointer StorageBuffer %int
393 %ptr_private_int = OpTypePointer Private %int
394 %in = OpVariable %ptr_ssbo_int StorageBuffer
395 %priv1 = OpVariable %ptr_private_int Private
396 %priv2 = OpVariable %ptr_private_int Private
397 %void_fn = OpTypeFunction %void
398 %foo = OpFunction %void None %void_fn
399 %entry = OpLabel
400 %1 = OpFunctionCall %void %bar1
401 %2 = OpFunctionCall %void %bar2
402 OpReturn
403 OpFunctionEnd
404 %bar1 = OpFunction %void None %void_fn
405 %3 = OpLabel
406 %ld1 = OpLoad %int %in
407 OpStore %priv1 %ld1
408 OpReturn
409 OpFunctionEnd
410 %bar2 = OpFunction %void None %void_fn
411 %4 = OpLabel
412 %ld2 = OpLoad %int %in
413 OpStore %priv2 %ld2
414 OpReturn
415 OpFunctionEnd
416 )";
417 
418   SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
419   SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
420 }
421 
TEST_F(PrivateToLocalTest,IdBoundOverflow1)422 TEST_F(PrivateToLocalTest, IdBoundOverflow1) {
423   const std::string text = R"(
424                OpCapability Shader
425                OpMemoryModel Logical GLSL450
426                OpEntryPoint Fragment %4 "main"
427                OpExecutionMode %4 OriginLowerLeft
428                OpSource HLSL 84
429           %2 = OpTypeVoid
430           %3 = OpTypeFunction %2
431           %6 = OpTypeFloat 32
432           %7 = OpTypeVector %6 4
433           %8 = OpTypeStruct %7
434     %4194302 = OpTypeStruct %8 %8
435           %9 = OpTypeStruct %8 %8
436          %11 = OpTypePointer Private %7
437          %18 = OpTypeStruct %6 %9
438          %12 = OpVariable %11 Private
439           %4 = OpFunction %2 None %3
440           %5 = OpLabel
441          %13 = OpLoad %7 %12
442                OpReturn
443                OpFunctionEnd
444   )";
445 
446   SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
447 
448   std::vector<Message> messages = {
449       {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
450   SetMessageConsumer(GetTestMessageConsumer(messages));
451   auto result = SinglePassRunToBinary<PrivateToLocalPass>(text, true);
452   EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
453 }
454 
455 }  // namespace
456 }  // namespace opt
457 }  // namespace spvtools
458