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