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/operand_to_const_reduction_pass.h"
18 #include "source/reduce/reducer.h"
19 #include "source/reduce/remove_opname_instruction_reduction_pass.h"
20 #include "source/reduce/remove_unreferenced_instruction_reduction_pass.h"
21 
22 namespace spvtools {
23 namespace reduce {
24 namespace {
25 
26 // This changes its mind each time IsInteresting is invoked as to whether the
27 // binary is interesting, until some limit is reached after which the binary is
28 // always deemed interesting.  This is useful to test that reduction passes
29 // interleave in interesting ways for a while, and then always succeed after
30 // some point; the latter is important to end up with a predictable final
31 // reduced binary for tests.
32 class PingPongInteresting {
33  public:
PingPongInteresting(uint32_t always_interesting_after)34   explicit PingPongInteresting(uint32_t always_interesting_after)
35       : is_interesting_(true),
36         always_interesting_after_(always_interesting_after),
37         count_(0) {}
38 
IsInteresting(const std::vector<uint32_t> &)39   bool IsInteresting(const std::vector<uint32_t>&) {
40     bool result;
41     if (count_ > always_interesting_after_) {
42       result = true;
43     } else {
44       result = is_interesting_;
45       is_interesting_ = !is_interesting_;
46     }
47     count_++;
48     return result;
49   }
50 
51  private:
52   bool is_interesting_;
53   const uint32_t always_interesting_after_;
54   uint32_t count_;
55 };
56 
TEST(ReducerTest,ExprToConstantAndRemoveUnreferenced)57 TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
58   std::string original = R"(
59                OpCapability Shader
60           %1 = OpExtInstImport "GLSL.std.450"
61                OpMemoryModel Logical GLSL450
62                OpEntryPoint Fragment %4 "main" %60
63                OpExecutionMode %4 OriginUpperLeft
64                OpSource ESSL 310
65                OpName %4 "main"
66                OpName %16 "buf2"
67                OpMemberName %16 0 "i"
68                OpName %18 ""
69                OpName %25 "buf1"
70                OpMemberName %25 0 "f"
71                OpName %27 ""
72                OpName %60 "_GLF_color"
73                OpMemberDecorate %16 0 Offset 0
74                OpDecorate %16 Block
75                OpDecorate %18 DescriptorSet 0
76                OpDecorate %18 Binding 2
77                OpMemberDecorate %25 0 Offset 0
78                OpDecorate %25 Block
79                OpDecorate %27 DescriptorSet 0
80                OpDecorate %27 Binding 1
81                OpDecorate %60 Location 0
82           %2 = OpTypeVoid
83           %3 = OpTypeFunction %2
84           %6 = OpTypeInt 32 1
85           %9 = OpConstant %6 0
86          %16 = OpTypeStruct %6
87          %17 = OpTypePointer Uniform %16
88          %18 = OpVariable %17 Uniform
89          %19 = OpTypePointer Uniform %6
90          %22 = OpTypeBool
91         %100 = OpConstantTrue %22
92          %24 = OpTypeFloat 32
93          %25 = OpTypeStruct %24
94          %26 = OpTypePointer Uniform %25
95          %27 = OpVariable %26 Uniform
96          %28 = OpTypePointer Uniform %24
97          %31 = OpConstant %24 2
98          %56 = OpConstant %6 1
99          %58 = OpTypeVector %24 4
100          %59 = OpTypePointer Output %58
101          %60 = OpVariable %59 Output
102          %72 = OpUndef %24
103          %74 = OpUndef %6
104           %4 = OpFunction %2 None %3
105           %5 = OpLabel
106                OpBranch %10
107          %10 = OpLabel
108          %73 = OpPhi %6 %74 %5 %77 %34
109          %71 = OpPhi %24 %72 %5 %76 %34
110          %70 = OpPhi %6 %9 %5 %57 %34
111          %20 = OpAccessChain %19 %18 %9
112          %21 = OpLoad %6 %20
113          %23 = OpSLessThan %22 %70 %21
114                OpLoopMerge %12 %34 None
115                OpBranchConditional %23 %11 %12
116          %11 = OpLabel
117          %29 = OpAccessChain %28 %27 %9
118          %30 = OpLoad %24 %29
119          %32 = OpFOrdGreaterThan %22 %30 %31
120                OpSelectionMerge %34 None
121                OpBranchConditional %32 %33 %46
122          %33 = OpLabel
123          %40 = OpFAdd %24 %71 %30
124          %45 = OpISub %6 %73 %21
125                OpBranch %34
126          %46 = OpLabel
127          %50 = OpFMul %24 %71 %30
128          %54 = OpSDiv %6 %73 %21
129                OpBranch %34
130          %34 = OpLabel
131          %77 = OpPhi %6 %45 %33 %54 %46
132          %76 = OpPhi %24 %40 %33 %50 %46
133          %57 = OpIAdd %6 %70 %56
134                OpBranch %10
135          %12 = OpLabel
136          %61 = OpAccessChain %28 %27 %9
137          %62 = OpLoad %24 %61
138          %66 = OpConvertSToF %24 %21
139          %68 = OpConvertSToF %24 %73
140          %69 = OpCompositeConstruct %58 %62 %71 %66 %68
141                OpStore %60 %69
142                OpReturn
143                OpFunctionEnd
144   )";
145 
146   std::string expected = R"(
147                OpCapability Shader
148           %1 = OpExtInstImport "GLSL.std.450"
149                OpMemoryModel Logical GLSL450
150                OpEntryPoint Fragment %4 "main" %60
151                OpExecutionMode %4 OriginUpperLeft
152                OpSource ESSL 310
153                OpName %4 "main"
154                OpName %16 "buf2"
155                OpMemberName %16 0 "i"
156                OpName %18 ""
157                OpName %25 "buf1"
158                OpMemberName %25 0 "f"
159                OpName %27 ""
160                OpName %60 "_GLF_color"
161                OpMemberDecorate %16 0 Offset 0
162                OpDecorate %16 Block
163                OpDecorate %18 DescriptorSet 0
164                OpDecorate %18 Binding 2
165                OpMemberDecorate %25 0 Offset 0
166                OpDecorate %25 Block
167                OpDecorate %27 DescriptorSet 0
168                OpDecorate %27 Binding 1
169                OpDecorate %60 Location 0
170           %2 = OpTypeVoid
171           %3 = OpTypeFunction %2
172           %6 = OpTypeInt 32 1
173           %9 = OpConstant %6 0
174          %16 = OpTypeStruct %6
175          %17 = OpTypePointer Uniform %16
176          %18 = OpVariable %17 Uniform
177          %19 = OpTypePointer Uniform %6
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          %28 = OpTypePointer Uniform %24
185          %31 = OpConstant %24 2
186          %56 = OpConstant %6 1
187          %58 = OpTypeVector %24 4
188          %59 = OpTypePointer Output %58
189          %60 = OpVariable %59 Output
190          %72 = OpUndef %24
191          %74 = OpUndef %6
192           %4 = OpFunction %2 None %3
193           %5 = OpLabel
194                OpBranch %10
195          %10 = OpLabel
196                OpLoopMerge %12 %34 None
197                OpBranchConditional %100 %11 %12
198          %11 = OpLabel
199                OpSelectionMerge %34 None
200                OpBranchConditional %100 %33 %46
201          %33 = OpLabel
202                OpBranch %34
203          %46 = OpLabel
204                OpBranch %34
205          %34 = OpLabel
206                OpBranch %10
207          %12 = OpLabel
208                OpReturn
209                OpFunctionEnd
210   )";
211 
212   spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
213   Reducer reducer(env);
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(MakeUnique<OperandToConstReductionPass>(env));
221   reducer.AddReductionPass(
222       MakeUnique<RemoveUnreferencedInstructionReductionPass>(env));
223 
224   std::vector<uint32_t> binary_in;
225   SpirvTools t(env);
226 
227   ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
228   std::vector<uint32_t> binary_out;
229   spvtools::ReducerOptions reducer_options;
230   reducer_options.set_step_limit(500);
231 
232   reducer.Run(std::move(binary_in), &binary_out, reducer_options);
233 
234   CheckEqual(env, expected, binary_out);
235 }
236 
TEST(ReducerTest,RemoveOpnameAndRemoveUnreferenced)237 TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) {
238   const std::string original = R"(
239                OpCapability Shader
240           %1 = OpExtInstImport "GLSL.std.450"
241                OpMemoryModel Logical GLSL450
242                OpEntryPoint Fragment %2 "main"
243                OpExecutionMode %2 OriginUpperLeft
244                OpSource ESSL 310
245                OpName %2 "main"
246                OpName %3 "a"
247                OpName %4 "this-name-counts-as-usage-for-load-instruction"
248           %5 = OpTypeVoid
249           %6 = OpTypeFunction %5
250           %7 = OpTypeFloat 32
251           %8 = OpTypePointer Function %7
252           %9 = OpConstant %7 1
253           %2 = OpFunction %5 None %6
254          %10 = OpLabel
255           %3 = OpVariable %8 Function
256           %4 = OpLoad %7 %3
257                OpStore %3 %7
258                OpReturn
259                OpFunctionEnd
260   )";
261 
262   const std::string expected = R"(
263                OpCapability Shader
264           %1 = OpExtInstImport "GLSL.std.450"
265                OpMemoryModel Logical GLSL450
266                OpEntryPoint Fragment %2 "main"
267                OpExecutionMode %2 OriginUpperLeft
268                OpSource ESSL 310
269           %5 = OpTypeVoid
270           %6 = OpTypeFunction %5
271           %7 = OpTypeFloat 32
272           %8 = OpTypePointer Function %7
273           %9 = OpConstant %7 1
274           %2 = OpFunction %5 None %6
275          %10 = OpLabel
276                OpReturn
277                OpFunctionEnd
278   )";
279 
280   spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
281   Reducer reducer(env);
282   // Make ping-pong interesting very quickly, as there are not much
283   // opportunities.
284   PingPongInteresting ping_pong_interesting(1);
285   reducer.SetMessageConsumer(NopDiagnostic);
286   reducer.SetInterestingnessFunction(
287       [&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
288         return ping_pong_interesting.IsInteresting(binary);
289       });
290   reducer.AddReductionPass(
291       MakeUnique<RemoveOpNameInstructionReductionPass>(env));
292   reducer.AddReductionPass(
293       MakeUnique<RemoveUnreferencedInstructionReductionPass>(env));
294 
295   std::vector<uint32_t> binary_in;
296   SpirvTools t(env);
297 
298   ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
299   std::vector<uint32_t> binary_out;
300   spvtools::ReducerOptions reducer_options;
301   reducer_options.set_step_limit(500);
302 
303   reducer.Run(std::move(binary_in), &binary_out, reducer_options);
304 
305   CheckEqual(env, expected, binary_out);
306 }
307 
308 }  // namespace
309 }  // namespace reduce
310 }  // namespace spvtools