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