1 // Copyright (c) 2018 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 "reduce_test_util.h"
16 
17 #include "source/reduce/reducer.h"
18 #include "source/reduce/reduction_pass.h"
19 #include "source/reduce/remove_instruction_reduction_opportunity.h"
20 
21 namespace spvtools {
22 namespace reduce {
23 namespace {
24 
25 // A dumb reduction pass that removes global values regardless of whether they
26 // are referenced. This is very likely to make the resulting module invalid.  We
27 // use this to test the reducer's behavior in the scenario where a bad reduction
28 // pass leads to an invalid module.
29 class BlindlyRemoveGlobalValuesPass : public ReductionPass {
30  public:
31   // Creates the reduction pass in the context of the given target environment
32   // |target_env|
BlindlyRemoveGlobalValuesPass(const spv_target_env target_env)33   explicit BlindlyRemoveGlobalValuesPass(const spv_target_env target_env)
34       : ReductionPass(target_env) {}
35 
36   ~BlindlyRemoveGlobalValuesPass() override = default;
37 
38   // The name of this pass.
GetName() const39   std::string GetName() const final { return "BlindlyRemoveGlobalValuesPass"; };
40 
41  protected:
42   // Adds opportunities to remove all global values.  Assuming they are all
43   // referenced (directly or indirectly) from elsewhere in the module, each such
44   // opportunity will make the module invalid.
GetAvailableOpportunities(opt::IRContext * context) const45   std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
46       opt::IRContext* context) const final {
47     std::vector<std::unique_ptr<ReductionOpportunity>> result;
48     for (auto& inst : context->module()->types_values()) {
49       if (inst.HasResultId()) {
50         result.push_back(
51             MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
52       }
53     }
54     return result;
55   }
56 };
57 
TEST(ValidationDuringReductionTest,CheckInvalidPassMakesNoProgress)58 TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) {
59   // A module whose global values are all referenced, so that any application of
60   // MakeModuleInvalidPass will make the module invalid.
61   std::string original = R"(
62                OpCapability Shader
63           %1 = OpExtInstImport "GLSL.std.450"
64                OpMemoryModel Logical GLSL450
65                OpEntryPoint Fragment %4 "main" %60
66                OpExecutionMode %4 OriginUpperLeft
67                OpSource ESSL 310
68                OpName %4 "main"
69                OpName %16 "buf2"
70                OpMemberName %16 0 "i"
71                OpName %18 ""
72                OpName %25 "buf1"
73                OpMemberName %25 0 "f"
74                OpName %27 ""
75                OpName %60 "_GLF_color"
76                OpMemberDecorate %16 0 Offset 0
77                OpDecorate %16 Block
78                OpDecorate %18 DescriptorSet 0
79                OpDecorate %18 Binding 2
80                OpMemberDecorate %25 0 Offset 0
81                OpDecorate %25 Block
82                OpDecorate %27 DescriptorSet 0
83                OpDecorate %27 Binding 1
84                OpDecorate %60 Location 0
85           %2 = OpTypeVoid
86           %3 = OpTypeFunction %2
87           %6 = OpTypeInt 32 1
88           %9 = OpConstant %6 0
89          %16 = OpTypeStruct %6
90          %17 = OpTypePointer Uniform %16
91          %18 = OpVariable %17 Uniform
92          %19 = OpTypePointer Uniform %6
93          %22 = OpTypeBool
94          %24 = OpTypeFloat 32
95          %25 = OpTypeStruct %24
96          %26 = OpTypePointer Uniform %25
97          %27 = OpVariable %26 Uniform
98          %28 = OpTypePointer Uniform %24
99          %31 = OpConstant %24 2
100          %56 = OpConstant %6 1
101          %58 = OpTypeVector %24 4
102          %59 = OpTypePointer Output %58
103          %60 = OpVariable %59 Output
104          %72 = OpUndef %24
105          %74 = OpUndef %6
106           %4 = OpFunction %2 None %3
107           %5 = OpLabel
108                OpBranch %10
109          %10 = OpLabel
110          %73 = OpPhi %6 %74 %5 %77 %34
111          %71 = OpPhi %24 %72 %5 %76 %34
112          %70 = OpPhi %6 %9 %5 %57 %34
113          %20 = OpAccessChain %19 %18 %9
114          %21 = OpLoad %6 %20
115          %23 = OpSLessThan %22 %70 %21
116                OpLoopMerge %12 %34 None
117                OpBranchConditional %23 %11 %12
118          %11 = OpLabel
119          %29 = OpAccessChain %28 %27 %9
120          %30 = OpLoad %24 %29
121          %32 = OpFOrdGreaterThan %22 %30 %31
122                OpSelectionMerge %34 None
123                OpBranchConditional %32 %33 %46
124          %33 = OpLabel
125          %40 = OpFAdd %24 %71 %30
126          %45 = OpISub %6 %73 %21
127                OpBranch %34
128          %46 = OpLabel
129          %50 = OpFMul %24 %71 %30
130          %54 = OpSDiv %6 %73 %21
131                OpBranch %34
132          %34 = OpLabel
133          %77 = OpPhi %6 %45 %33 %54 %46
134          %76 = OpPhi %24 %40 %33 %50 %46
135          %57 = OpIAdd %6 %70 %56
136                OpBranch %10
137          %12 = OpLabel
138          %61 = OpAccessChain %28 %27 %9
139          %62 = OpLoad %24 %61
140          %66 = OpConvertSToF %24 %21
141          %68 = OpConvertSToF %24 %73
142          %69 = OpCompositeConstruct %58 %62 %71 %66 %68
143                OpStore %60 %69
144                OpReturn
145                OpFunctionEnd
146   )";
147 
148   spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
149   Reducer reducer(env);
150   reducer.SetMessageConsumer(NopDiagnostic);
151 
152   // Say that every module is interesting.
153   reducer.SetInterestingnessFunction(
154       [](const std::vector<uint32_t>&, uint32_t) -> bool { return true; });
155 
156   reducer.AddReductionPass(MakeUnique<BlindlyRemoveGlobalValuesPass>(env));
157 
158   std::vector<uint32_t> binary_in;
159   SpirvTools t(env);
160 
161   ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
162   std::vector<uint32_t> binary_out;
163   spvtools::ReducerOptions reducer_options;
164   reducer_options.set_step_limit(500);
165 
166   reducer.Run(std::move(binary_in), &binary_out, reducer_options);
167 
168   // The reducer should have no impact.
169   CheckEqual(env, original, binary_out);
170 }
171 
TEST(ValidationDuringReductionTest,CheckNotAlwaysInvalidCanMakeProgress)172 TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) {
173   // A module with just one unreferenced global value.  All but one application
174   // of MakeModuleInvalidPass will make the module invalid.
175   std::string original = R"(
176                OpCapability Shader
177           %1 = OpExtInstImport "GLSL.std.450"
178                OpMemoryModel Logical GLSL450
179                OpEntryPoint Fragment %4 "main" %60
180                OpExecutionMode %4 OriginUpperLeft
181                OpSource ESSL 310
182                OpName %4 "main"
183                OpName %16 "buf2"
184                OpMemberName %16 0 "i"
185                OpName %18 ""
186                OpName %25 "buf1"
187                OpMemberName %25 0 "f"
188                OpName %27 ""
189                OpName %60 "_GLF_color"
190                OpMemberDecorate %16 0 Offset 0
191                OpDecorate %16 Block
192                OpDecorate %18 DescriptorSet 0
193                OpDecorate %18 Binding 2
194                OpMemberDecorate %25 0 Offset 0
195                OpDecorate %25 Block
196                OpDecorate %27 DescriptorSet 0
197                OpDecorate %27 Binding 1
198                OpDecorate %60 Location 0
199           %2 = OpTypeVoid
200           %3 = OpTypeFunction %2
201           %6 = OpTypeInt 32 1
202           %9 = OpConstant %6 0
203          %16 = OpTypeStruct %6
204          %17 = OpTypePointer Uniform %16
205          %18 = OpVariable %17 Uniform
206          %19 = OpTypePointer Uniform %6
207          %22 = OpTypeBool
208          %24 = OpTypeFloat 32
209          %25 = OpTypeStruct %24
210          %26 = OpTypePointer Uniform %25
211          %27 = OpVariable %26 Uniform
212          %28 = OpTypePointer Uniform %24
213          %31 = OpConstant %24 2
214          %56 = OpConstant %6 1
215        %1000 = OpConstant %6 1000 ; It should be possible to remove this instruction without making the module invalid.
216          %58 = OpTypeVector %24 4
217          %59 = OpTypePointer Output %58
218          %60 = OpVariable %59 Output
219          %72 = OpUndef %24
220          %74 = OpUndef %6
221           %4 = OpFunction %2 None %3
222           %5 = OpLabel
223                OpBranch %10
224          %10 = OpLabel
225          %73 = OpPhi %6 %74 %5 %77 %34
226          %71 = OpPhi %24 %72 %5 %76 %34
227          %70 = OpPhi %6 %9 %5 %57 %34
228          %20 = OpAccessChain %19 %18 %9
229          %21 = OpLoad %6 %20
230          %23 = OpSLessThan %22 %70 %21
231                OpLoopMerge %12 %34 None
232                OpBranchConditional %23 %11 %12
233          %11 = OpLabel
234          %29 = OpAccessChain %28 %27 %9
235          %30 = OpLoad %24 %29
236          %32 = OpFOrdGreaterThan %22 %30 %31
237                OpSelectionMerge %34 None
238                OpBranchConditional %32 %33 %46
239          %33 = OpLabel
240          %40 = OpFAdd %24 %71 %30
241          %45 = OpISub %6 %73 %21
242                OpBranch %34
243          %46 = OpLabel
244          %50 = OpFMul %24 %71 %30
245          %54 = OpSDiv %6 %73 %21
246                OpBranch %34
247          %34 = OpLabel
248          %77 = OpPhi %6 %45 %33 %54 %46
249          %76 = OpPhi %24 %40 %33 %50 %46
250          %57 = OpIAdd %6 %70 %56
251                OpBranch %10
252          %12 = OpLabel
253          %61 = OpAccessChain %28 %27 %9
254          %62 = OpLoad %24 %61
255          %66 = OpConvertSToF %24 %21
256          %68 = OpConvertSToF %24 %73
257          %69 = OpCompositeConstruct %58 %62 %71 %66 %68
258                OpStore %60 %69
259                OpReturn
260                OpFunctionEnd
261   )";
262 
263   // This is the same as the original, except that the constant declaration of
264   // 1000 is gone.
265   std::string expected = R"(
266                OpCapability Shader
267           %1 = OpExtInstImport "GLSL.std.450"
268                OpMemoryModel Logical GLSL450
269                OpEntryPoint Fragment %4 "main" %60
270                OpExecutionMode %4 OriginUpperLeft
271                OpSource ESSL 310
272                OpName %4 "main"
273                OpName %16 "buf2"
274                OpMemberName %16 0 "i"
275                OpName %18 ""
276                OpName %25 "buf1"
277                OpMemberName %25 0 "f"
278                OpName %27 ""
279                OpName %60 "_GLF_color"
280                OpMemberDecorate %16 0 Offset 0
281                OpDecorate %16 Block
282                OpDecorate %18 DescriptorSet 0
283                OpDecorate %18 Binding 2
284                OpMemberDecorate %25 0 Offset 0
285                OpDecorate %25 Block
286                OpDecorate %27 DescriptorSet 0
287                OpDecorate %27 Binding 1
288                OpDecorate %60 Location 0
289           %2 = OpTypeVoid
290           %3 = OpTypeFunction %2
291           %6 = OpTypeInt 32 1
292           %9 = OpConstant %6 0
293          %16 = OpTypeStruct %6
294          %17 = OpTypePointer Uniform %16
295          %18 = OpVariable %17 Uniform
296          %19 = OpTypePointer Uniform %6
297          %22 = OpTypeBool
298          %24 = OpTypeFloat 32
299          %25 = OpTypeStruct %24
300          %26 = OpTypePointer Uniform %25
301          %27 = OpVariable %26 Uniform
302          %28 = OpTypePointer Uniform %24
303          %31 = OpConstant %24 2
304          %56 = OpConstant %6 1
305          %58 = OpTypeVector %24 4
306          %59 = OpTypePointer Output %58
307          %60 = OpVariable %59 Output
308          %72 = OpUndef %24
309          %74 = OpUndef %6
310           %4 = OpFunction %2 None %3
311           %5 = OpLabel
312                OpBranch %10
313          %10 = OpLabel
314          %73 = OpPhi %6 %74 %5 %77 %34
315          %71 = OpPhi %24 %72 %5 %76 %34
316          %70 = OpPhi %6 %9 %5 %57 %34
317          %20 = OpAccessChain %19 %18 %9
318          %21 = OpLoad %6 %20
319          %23 = OpSLessThan %22 %70 %21
320                OpLoopMerge %12 %34 None
321                OpBranchConditional %23 %11 %12
322          %11 = OpLabel
323          %29 = OpAccessChain %28 %27 %9
324          %30 = OpLoad %24 %29
325          %32 = OpFOrdGreaterThan %22 %30 %31
326                OpSelectionMerge %34 None
327                OpBranchConditional %32 %33 %46
328          %33 = OpLabel
329          %40 = OpFAdd %24 %71 %30
330          %45 = OpISub %6 %73 %21
331                OpBranch %34
332          %46 = OpLabel
333          %50 = OpFMul %24 %71 %30
334          %54 = OpSDiv %6 %73 %21
335                OpBranch %34
336          %34 = OpLabel
337          %77 = OpPhi %6 %45 %33 %54 %46
338          %76 = OpPhi %24 %40 %33 %50 %46
339          %57 = OpIAdd %6 %70 %56
340                OpBranch %10
341          %12 = OpLabel
342          %61 = OpAccessChain %28 %27 %9
343          %62 = OpLoad %24 %61
344          %66 = OpConvertSToF %24 %21
345          %68 = OpConvertSToF %24 %73
346          %69 = OpCompositeConstruct %58 %62 %71 %66 %68
347                OpStore %60 %69
348                OpReturn
349                OpFunctionEnd
350   )";
351 
352   spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
353   Reducer reducer(env);
354   reducer.SetMessageConsumer(NopDiagnostic);
355 
356   // Say that every module is interesting.
357   reducer.SetInterestingnessFunction(
358       [](const std::vector<uint32_t>&, uint32_t) -> bool { return true; });
359 
360   reducer.AddReductionPass(MakeUnique<BlindlyRemoveGlobalValuesPass>(env));
361 
362   std::vector<uint32_t> binary_in;
363   SpirvTools t(env);
364 
365   ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
366   std::vector<uint32_t> binary_out;
367   spvtools::ReducerOptions reducer_options;
368   reducer_options.set_step_limit(500);
369 
370   reducer.Run(std::move(binary_in), &binary_out, reducer_options);
371   CheckEqual(env, expected, binary_out);
372 }
373 
374 }  // namespace
375 }  // namespace reduce
376 }  // namespace spvtools
377