• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "source/reduce/reducer.h"
16 
17 #include "source/opt/build_module.h"
18 #include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
19 #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
20 #include "test/reduce/reduce_test_util.h"
21 
22 namespace spvtools {
23 namespace reduce {
24 namespace {
25 
26 using opt::BasicBlock;
27 using opt::IRContext;
28 
29 const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
30 const MessageConsumer kMessageConsumer = CLIMessageConsumer;
31 
32 // This changes its mind each time IsInteresting is invoked as to whether the
33 // binary is interesting, until some limit is reached after which the binary is
34 // always deemed interesting.  This is useful to test that reduction passes
35 // interleave in interesting ways for a while, and then always succeed after
36 // some point; the latter is important to end up with a predictable final
37 // reduced binary for tests.
38 class PingPongInteresting {
39  public:
PingPongInteresting(uint32_t always_interesting_after)40   explicit PingPongInteresting(uint32_t always_interesting_after)
41       : is_interesting_(true),
42         always_interesting_after_(always_interesting_after),
43         count_(0) {}
44 
IsInteresting(const std::vector<uint32_t> &)45   bool IsInteresting(const std::vector<uint32_t>&) {
46     bool result;
47     if (count_ > always_interesting_after_) {
48       result = true;
49     } else {
50       result = is_interesting_;
51       is_interesting_ = !is_interesting_;
52     }
53     count_++;
54     return result;
55   }
56 
57  private:
58   bool is_interesting_;
59   const uint32_t always_interesting_after_;
60   uint32_t count_;
61 };
62 
TEST(ReducerTest,ExprToConstantAndRemoveUnreferenced)63 TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
64   // Check that ExprToConstant and RemoveUnreferenced work together; once some
65   // ID uses have been changed to constants, those IDs can be removed.
66   std::string original = R"(
67                OpCapability Shader
68           %1 = OpExtInstImport "GLSL.std.450"
69                OpMemoryModel Logical GLSL450
70                OpEntryPoint Fragment %4 "main" %60
71                OpExecutionMode %4 OriginUpperLeft
72                OpSource ESSL 310
73                OpName %4 "main"
74                OpName %16 "buf2"
75                OpMemberName %16 0 "i"
76                OpName %18 ""
77                OpName %25 "buf1"
78                OpMemberName %25 0 "f"
79                OpName %27 ""
80                OpName %60 "_GLF_color"
81                OpMemberDecorate %16 0 Offset 0
82                OpDecorate %16 Block
83                OpDecorate %18 DescriptorSet 0
84                OpDecorate %18 Binding 2
85                OpMemberDecorate %25 0 Offset 0
86                OpDecorate %25 Block
87                OpDecorate %27 DescriptorSet 0
88                OpDecorate %27 Binding 1
89                OpDecorate %60 Location 0
90           %2 = OpTypeVoid
91           %3 = OpTypeFunction %2
92           %6 = OpTypeInt 32 1
93           %9 = OpConstant %6 0
94          %16 = OpTypeStruct %6
95          %17 = OpTypePointer Uniform %16
96          %18 = OpVariable %17 Uniform
97          %19 = OpTypePointer Uniform %6
98          %22 = OpTypeBool
99         %100 = OpConstantTrue %22
100          %24 = OpTypeFloat 32
101          %25 = OpTypeStruct %24
102          %26 = OpTypePointer Uniform %25
103          %27 = OpVariable %26 Uniform
104          %28 = OpTypePointer Uniform %24
105          %31 = OpConstant %24 2
106          %56 = OpConstant %6 1
107          %58 = OpTypeVector %24 4
108          %59 = OpTypePointer Output %58
109          %60 = OpVariable %59 Output
110          %72 = OpUndef %24
111          %74 = OpUndef %6
112           %4 = OpFunction %2 None %3
113           %5 = OpLabel
114                OpBranch %10
115          %10 = OpLabel
116          %73 = OpPhi %6 %74 %5 %77 %34
117          %71 = OpPhi %24 %72 %5 %76 %34
118          %70 = OpPhi %6 %9 %5 %57 %34
119          %20 = OpAccessChain %19 %18 %9
120          %21 = OpLoad %6 %20
121          %23 = OpSLessThan %22 %70 %21
122                OpLoopMerge %12 %34 None
123                OpBranchConditional %23 %11 %12
124          %11 = OpLabel
125          %29 = OpAccessChain %28 %27 %9
126          %30 = OpLoad %24 %29
127          %32 = OpFOrdGreaterThan %22 %30 %31
128                OpSelectionMerge %90 None
129                OpBranchConditional %32 %33 %46
130          %33 = OpLabel
131          %40 = OpFAdd %24 %71 %30
132          %45 = OpISub %6 %73 %21
133                OpBranch %90
134          %46 = OpLabel
135          %50 = OpFMul %24 %71 %30
136          %54 = OpSDiv %6 %73 %21
137                OpBranch %90
138          %90 = OpLabel
139          %77 = OpPhi %6 %45 %33 %54 %46
140          %76 = OpPhi %24 %40 %33 %50 %46
141                OpBranch %34
142          %34 = OpLabel
143          %57 = OpIAdd %6 %70 %56
144                OpBranch %10
145          %12 = OpLabel
146          %61 = OpAccessChain %28 %27 %9
147          %62 = OpLoad %24 %61
148          %66 = OpConvertSToF %24 %21
149          %68 = OpConvertSToF %24 %73
150          %69 = OpCompositeConstruct %58 %62 %71 %66 %68
151                OpStore %60 %69
152                OpReturn
153                OpFunctionEnd
154   )";
155 
156   std::string expected = R"(
157                OpCapability Shader
158           %1 = OpExtInstImport "GLSL.std.450"
159                OpMemoryModel Logical GLSL450
160                OpEntryPoint Fragment %4 "main" %60
161                OpExecutionMode %4 OriginUpperLeft
162                OpMemberDecorate %16 0 Offset 0
163                OpDecorate %16 Block
164                OpDecorate %18 DescriptorSet 0
165                OpDecorate %18 Binding 2
166                OpMemberDecorate %25 0 Offset 0
167                OpDecorate %25 Block
168                OpDecorate %27 DescriptorSet 0
169                OpDecorate %27 Binding 1
170                OpDecorate %60 Location 0
171           %2 = OpTypeVoid
172           %3 = OpTypeFunction %2
173           %6 = OpTypeInt 32 1
174           %9 = OpConstant %6 0
175          %16 = OpTypeStruct %6
176          %17 = OpTypePointer Uniform %16
177          %18 = OpVariable %17 Uniform
178          %22 = OpTypeBool
179         %100 = OpConstantTrue %22
180          %24 = OpTypeFloat 32
181          %25 = OpTypeStruct %24
182          %26 = OpTypePointer Uniform %25
183          %27 = OpVariable %26 Uniform
184          %31 = OpConstant %24 2
185          %56 = OpConstant %6 1
186          %58 = OpTypeVector %24 4
187          %59 = OpTypePointer Output %58
188          %60 = OpVariable %59 Output
189          %72 = OpUndef %24
190          %74 = OpUndef %6
191           %4 = OpFunction %2 None %3
192           %5 = OpLabel
193                OpBranch %10
194          %10 = OpLabel
195                OpLoopMerge %12 %34 None
196                OpBranchConditional %100 %11 %12
197          %11 = OpLabel
198                OpSelectionMerge %90 None
199                OpBranchConditional %100 %33 %46
200          %33 = OpLabel
201                OpBranch %90
202          %46 = OpLabel
203                OpBranch %90
204          %90 = OpLabel
205                OpBranch %34
206          %34 = OpLabel
207                OpBranch %10
208          %12 = OpLabel
209                OpReturn
210                OpFunctionEnd
211   )";
212 
213   Reducer reducer(kEnv);
214   PingPongInteresting ping_pong_interesting(10);
215   reducer.SetMessageConsumer(NopDiagnostic);
216   reducer.SetInterestingnessFunction(
217       [&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
218         return ping_pong_interesting.IsInteresting(binary);
219       });
220   reducer.AddReductionPass(
221       MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>(
222           false));
223   reducer.AddReductionPass(
224       MakeUnique<OperandToConstReductionOpportunityFinder>());
225 
226   std::vector<uint32_t> binary_in;
227   SpirvTools t(kEnv);
228 
229   ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
230   std::vector<uint32_t> binary_out;
231   spvtools::ReducerOptions reducer_options;
232   reducer_options.set_step_limit(500);
233   reducer_options.set_fail_on_validation_error(true);
234   spvtools::ValidatorOptions validator_options;
235 
236   Reducer::ReductionResultStatus status = reducer.Run(
237       std::move(binary_in), &binary_out, reducer_options, validator_options);
238 
239   ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
240 
241   CheckEqual(kEnv, expected, binary_out);
242 }
243 
InterestingWhileOpcodeExists(const std::vector<uint32_t> & binary,uint32_t opcode,uint32_t count,bool dump)244 bool InterestingWhileOpcodeExists(const std::vector<uint32_t>& binary,
245                                   uint32_t opcode, uint32_t count, bool dump) {
246   if (dump) {
247     std::stringstream ss;
248     ss << "temp_" << count << ".spv";
249     DumpShader(binary, ss.str().c_str());
250   }
251 
252   std::unique_ptr<IRContext> context =
253       BuildModule(kEnv, kMessageConsumer, binary.data(), binary.size());
254   assert(context);
255   bool interesting = false;
256   for (auto& function : *context->module()) {
257     context->cfg()->ForEachBlockInPostOrder(
258         &*function.begin(), [opcode, &interesting](BasicBlock* block) -> void {
259           for (auto& inst : *block) {
260             if (inst.opcode() == opcode) {
261               interesting = true;
262               break;
263             }
264           }
265         });
266     if (interesting) {
267       break;
268     }
269   }
270   return interesting;
271 }
272 
InterestingWhileIMulReachable(const std::vector<uint32_t> & binary,uint32_t count)273 bool InterestingWhileIMulReachable(const std::vector<uint32_t>& binary,
274                                    uint32_t count) {
275   return InterestingWhileOpcodeExists(binary, SpvOpIMul, count, false);
276 }
277 
InterestingWhileSDivReachable(const std::vector<uint32_t> & binary,uint32_t count)278 bool InterestingWhileSDivReachable(const std::vector<uint32_t>& binary,
279                                    uint32_t count) {
280   return InterestingWhileOpcodeExists(binary, SpvOpSDiv, count, false);
281 }
282 
283 // The shader below was derived from the following GLSL, and optimized.
284 // #version 310 es
285 // precision highp float;
286 // layout(location = 0) out vec4 _GLF_color;
287 // int foo() {
288 //    int x = 1;
289 //    int y;
290 //    x = y / x;   // SDiv
291 //    return x;
292 // }
293 // void main() {
294 //    int c;
295 //    while (bool(c)) {
296 //        do {
297 //            if (bool(c)) {
298 //                if (bool(c)) {
299 //                    ++c;
300 //                } else {
301 //                    _GLF_color.x = float(c*c);  // IMul
302 //                }
303 //                return;
304 //            }
305 //        } while(bool(foo()));
306 //        return;
307 //    }
308 // }
309 const std::string kShaderWithLoopsDivAndMul = R"(
310                OpCapability Shader
311           %1 = OpExtInstImport "GLSL.std.450"
312                OpMemoryModel Logical GLSL450
313                OpEntryPoint Fragment %4 "main" %49
314                OpExecutionMode %4 OriginUpperLeft
315                OpSource ESSL 310
316                OpName %4 "main"
317                OpName %49 "_GLF_color"
318                OpDecorate %49 Location 0
319                OpDecorate %52 RelaxedPrecision
320                OpDecorate %77 RelaxedPrecision
321           %2 = OpTypeVoid
322           %3 = OpTypeFunction %2
323           %6 = OpTypeInt 32 1
324          %12 = OpConstant %6 1
325          %27 = OpTypeBool
326          %28 = OpTypeInt 32 0
327          %29 = OpConstant %28 0
328          %46 = OpTypeFloat 32
329          %47 = OpTypeVector %46 4
330          %48 = OpTypePointer Output %47
331          %49 = OpVariable %48 Output
332          %54 = OpTypePointer Output %46
333          %64 = OpConstantFalse %27
334          %67 = OpConstantTrue %27
335          %81 = OpUndef %6
336           %4 = OpFunction %2 None %3
337           %5 = OpLabel
338                OpBranch %61
339          %61 = OpLabel
340                OpLoopMerge %60 %63 None
341                OpBranch %20
342          %20 = OpLabel
343          %30 = OpINotEqual %27 %81 %29
344                OpLoopMerge %22 %23 None
345                OpBranchConditional %30 %21 %22
346          %21 = OpLabel
347                OpBranch %31
348          %31 = OpLabel
349                OpLoopMerge %33 %38 None
350                OpBranch %32
351          %32 = OpLabel
352                OpBranchConditional %30 %37 %38
353          %37 = OpLabel
354                OpSelectionMerge %42 None
355                OpBranchConditional %30 %41 %45
356          %41 = OpLabel
357                OpBranch %42
358          %45 = OpLabel
359          %52 = OpIMul %6 %81 %81
360          %53 = OpConvertSToF %46 %52
361          %55 = OpAccessChain %54 %49 %29
362                OpStore %55 %53
363                OpBranch %42
364          %42 = OpLabel
365                OpBranch %33
366          %38 = OpLabel
367          %77 = OpSDiv %6 %81 %12
368          %58 = OpINotEqual %27 %77 %29
369                OpBranchConditional %58 %31 %33
370          %33 = OpLabel
371          %86 = OpPhi %27 %67 %42 %64 %38
372                OpSelectionMerge %68 None
373                OpBranchConditional %86 %22 %68
374          %68 = OpLabel
375                OpBranch %22
376          %23 = OpLabel
377                OpBranch %20
378          %22 = OpLabel
379          %90 = OpPhi %27 %64 %20 %86 %33 %67 %68
380                OpSelectionMerge %70 None
381                OpBranchConditional %90 %60 %70
382          %70 = OpLabel
383                OpBranch %60
384          %63 = OpLabel
385                OpBranch %61
386          %60 = OpLabel
387                OpReturn
388                OpFunctionEnd
389   )";
390 
TEST(ReducerTest,ShaderReduceWhileMulReachable)391 TEST(ReducerTest, ShaderReduceWhileMulReachable) {
392   Reducer reducer(kEnv);
393 
394   reducer.SetInterestingnessFunction(InterestingWhileIMulReachable);
395   reducer.AddDefaultReductionPasses();
396   reducer.SetMessageConsumer(kMessageConsumer);
397 
398   std::vector<uint32_t> binary_in;
399   SpirvTools t(kEnv);
400 
401   ASSERT_TRUE(
402       t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption));
403   std::vector<uint32_t> binary_out;
404   spvtools::ReducerOptions reducer_options;
405   reducer_options.set_step_limit(500);
406   reducer_options.set_fail_on_validation_error(true);
407   spvtools::ValidatorOptions validator_options;
408 
409   Reducer::ReductionResultStatus status = reducer.Run(
410       std::move(binary_in), &binary_out, reducer_options, validator_options);
411 
412   ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
413 }
414 
TEST(ReducerTest,ShaderReduceWhileDivReachable)415 TEST(ReducerTest, ShaderReduceWhileDivReachable) {
416   Reducer reducer(kEnv);
417 
418   reducer.SetInterestingnessFunction(InterestingWhileSDivReachable);
419   reducer.AddDefaultReductionPasses();
420   reducer.SetMessageConsumer(kMessageConsumer);
421 
422   std::vector<uint32_t> binary_in;
423   SpirvTools t(kEnv);
424 
425   ASSERT_TRUE(
426       t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption));
427   std::vector<uint32_t> binary_out;
428   spvtools::ReducerOptions reducer_options;
429   reducer_options.set_step_limit(500);
430   reducer_options.set_fail_on_validation_error(true);
431   spvtools::ValidatorOptions validator_options;
432 
433   Reducer::ReductionResultStatus status = reducer.Run(
434       std::move(binary_in), &binary_out, reducer_options, validator_options);
435 
436   ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
437 }
438 
439 }  // namespace
440 }  // namespace reduce
441 }  // namespace spvtools
442