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/operand_to_const_reduction_opportunity_finder.h"
16
17#include "source/opt/build_module.h"
18#include "source/reduce/reduction_opportunity.h"
19#include "test/reduce/reduce_test_util.h"
20
21namespace spvtools {
22namespace reduce {
23namespace {
24
25TEST(OperandToConstantReductionPassTest, BasicCheck) {
26  std::string prologue = R"(
27               OpCapability Shader
28          %1 = OpExtInstImport "GLSL.std.450"
29               OpMemoryModel Logical GLSL450
30               OpEntryPoint Fragment %4 "main" %37
31               OpExecutionMode %4 OriginUpperLeft
32               OpSource ESSL 310
33               OpName %4 "main"
34               OpName %9 "buf1"
35               OpMemberName %9 0 "f"
36               OpName %11 ""
37               OpName %24 "buf2"
38               OpMemberName %24 0 "i"
39               OpName %26 ""
40               OpName %37 "_GLF_color"
41               OpMemberDecorate %9 0 Offset 0
42               OpDecorate %9 Block
43               OpDecorate %11 DescriptorSet 0
44               OpDecorate %11 Binding 1
45               OpMemberDecorate %24 0 Offset 0
46               OpDecorate %24 Block
47               OpDecorate %26 DescriptorSet 0
48               OpDecorate %26 Binding 2
49               OpDecorate %37 Location 0
50          %2 = OpTypeVoid
51          %3 = OpTypeFunction %2
52          %6 = OpTypeFloat 32
53          %9 = OpTypeStruct %6
54         %10 = OpTypePointer Uniform %9
55         %11 = OpVariable %10 Uniform
56         %12 = OpTypeInt 32 1
57         %13 = OpConstant %12 0
58         %14 = OpTypePointer Uniform %6
59         %20 = OpConstant %6 2
60         %24 = OpTypeStruct %12
61         %25 = OpTypePointer Uniform %24
62         %26 = OpVariable %25 Uniform
63         %27 = OpTypePointer Uniform %12
64         %33 = OpConstant %12 3
65         %35 = OpTypeVector %6 4
66         %36 = OpTypePointer Output %35
67         %37 = OpVariable %36 Output
68          %4 = OpFunction %2 None %3
69          %5 = OpLabel
70         %15 = OpAccessChain %14 %11 %13
71         %16 = OpLoad %6 %15
72         %19 = OpFAdd %6 %16 %16
73         %21 = OpFAdd %6 %19 %20
74         %28 = OpAccessChain %27 %26 %13
75         %29 = OpLoad %12 %28
76  )";
77
78  std::string epilogue = R"(
79         %45 = OpConvertSToF %6 %34
80         %46 = OpCompositeConstruct %35 %16 %21 %43 %45
81               OpStore %37 %46
82               OpReturn
83               OpFunctionEnd
84  )";
85
86  std::string original = prologue + R"(
87         %32 = OpIAdd %12 %29 %29
88         %34 = OpIAdd %12 %32 %33
89         %43 = OpConvertSToF %6 %29
90  )" + epilogue;
91
92  std::string expected = prologue + R"(
93         %32 = OpIAdd %12 %13 %13 ; %29 -> %13 x 2
94         %34 = OpIAdd %12 %13 %33 ; %32 -> %13
95         %43 = OpConvertSToF %6 %13 ; %29 -> %13
96  )" + epilogue;
97
98  const auto env = SPV_ENV_UNIVERSAL_1_3;
99  const auto consumer = nullptr;
100  const auto context =
101      BuildModule(env, consumer, original, kReduceAssembleOption);
102  const auto ops =
103      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
104          context.get(), 0);
105  ASSERT_EQ(17, ops.size());
106  ASSERT_TRUE(ops[0]->PreconditionHolds());
107  ops[0]->TryToApply();
108  ASSERT_TRUE(ops[1]->PreconditionHolds());
109  ops[1]->TryToApply();
110  ASSERT_TRUE(ops[2]->PreconditionHolds());
111  ops[2]->TryToApply();
112  ASSERT_TRUE(ops[3]->PreconditionHolds());
113  ops[3]->TryToApply();
114
115  CheckEqual(env, expected, context.get());
116}
117
118TEST(OperandToConstantReductionPassTest, WithCalledFunction) {
119  std::string shader = R"(
120               OpCapability Shader
121          %1 = OpExtInstImport "GLSL.std.450"
122               OpMemoryModel Logical GLSL450
123               OpEntryPoint Fragment %4 "main" %10 %12
124               OpExecutionMode %4 OriginUpperLeft
125               OpSource ESSL 310
126          %2 = OpTypeVoid
127          %3 = OpTypeFunction %2
128          %6 = OpTypeFloat 32
129          %7 = OpTypeVector %6 4
130          %8 = OpTypeFunction %7
131          %9 = OpTypePointer Output %7
132         %10 = OpVariable %9 Output
133         %11 = OpTypePointer Input %7
134         %12 = OpVariable %11 Input
135         %13 = OpConstant %6 0
136         %14 = OpConstantComposite %7 %13 %13 %13 %13
137          %4 = OpFunction %2 None %3
138          %5 = OpLabel
139         %15 = OpFunctionCall %7 %16
140               OpReturn
141               OpFunctionEnd
142         %16 = OpFunction %7 None %8
143         %17 = OpLabel
144               OpReturnValue %14
145               OpFunctionEnd
146  )";
147
148  const auto env = SPV_ENV_UNIVERSAL_1_3;
149  const auto consumer = nullptr;
150  const auto context =
151      BuildModule(env, consumer, shader, kReduceAssembleOption);
152  const auto ops =
153      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
154          context.get(), 0);
155  ASSERT_EQ(0, ops.size());
156}
157
158TEST(OperandToConstantReductionPassTest, TargetSpecificFunction) {
159  std::string shader = R"(
160               OpCapability Shader
161          %1 = OpExtInstImport "GLSL.std.450"
162               OpMemoryModel Logical GLSL450
163               OpEntryPoint Fragment %4 "main"
164               OpExecutionMode %4 OriginUpperLeft
165               OpSource ESSL 320
166          %2 = OpTypeVoid
167          %3 = OpTypeFunction %2
168          %6 = OpTypeInt 32 1
169          %7 = OpTypePointer Function %6
170          %8 = OpTypeFunction %6 %7
171         %17 = OpConstant %6 1
172         %20 = OpConstant %6 2
173         %23 = OpConstant %6 0
174         %24 = OpTypeBool
175         %35 = OpConstant %6 3
176         %53 = OpConstant %6 10
177          %4 = OpFunction %2 None %3
178          %5 = OpLabel
179         %65 = OpVariable %7 Function
180         %68 = OpVariable %7 Function
181         %73 = OpVariable %7 Function
182               OpStore %65 %35
183         %66 = OpLoad %6 %65
184         %67 = OpIAdd %6 %66 %17
185               OpStore %65 %67
186         %69 = OpLoad %6 %65
187               OpStore %68 %69
188         %70 = OpFunctionCall %6 %13 %68
189         %71 = OpLoad %6 %65
190         %72 = OpIAdd %6 %71 %70
191               OpStore %65 %72
192         %74 = OpLoad %6 %65
193               OpStore %73 %74
194         %75 = OpFunctionCall %6 %10 %73
195         %76 = OpLoad %6 %65
196         %77 = OpIAdd %6 %76 %75
197               OpStore %65 %77
198               OpReturn
199               OpFunctionEnd
200         %10 = OpFunction %6 None %8
201          %9 = OpFunctionParameter %7
202         %11 = OpLabel
203         %15 = OpVariable %7 Function
204         %16 = OpLoad %6 %9
205         %18 = OpIAdd %6 %16 %17
206               OpStore %15 %18
207         %19 = OpLoad %6 %15
208         %21 = OpIAdd %6 %19 %20
209               OpStore %15 %21
210         %22 = OpLoad %6 %15
211         %25 = OpSGreaterThan %24 %22 %23
212               OpSelectionMerge %27 None
213               OpBranchConditional %25 %26 %27
214         %26 = OpLabel
215         %28 = OpLoad %6 %9
216               OpReturnValue %28
217         %27 = OpLabel
218         %30 = OpLoad %6 %9
219         %31 = OpIAdd %6 %30 %17
220               OpReturnValue %31
221               OpFunctionEnd
222         %13 = OpFunction %6 None %8
223         %12 = OpFunctionParameter %7
224         %14 = OpLabel
225         %41 = OpVariable %7 Function
226         %46 = OpVariable %7 Function
227         %55 = OpVariable %7 Function
228         %34 = OpLoad %6 %12
229         %36 = OpIEqual %24 %34 %35
230               OpSelectionMerge %38 None
231               OpBranchConditional %36 %37 %38
232         %37 = OpLabel
233         %39 = OpLoad %6 %12
234         %40 = OpIMul %6 %20 %39
235               OpStore %41 %40
236         %42 = OpFunctionCall %6 %10 %41
237               OpReturnValue %42
238         %38 = OpLabel
239         %44 = OpLoad %6 %12
240         %45 = OpIAdd %6 %44 %17
241               OpStore %12 %45
242               OpStore %46 %23
243               OpBranch %47
244         %47 = OpLabel
245               OpLoopMerge %49 %50 None
246               OpBranch %51
247         %51 = OpLabel
248         %52 = OpLoad %6 %46
249         %54 = OpSLessThan %24 %52 %53
250               OpBranchConditional %54 %48 %49
251         %48 = OpLabel
252         %56 = OpLoad %6 %12
253               OpStore %55 %56
254         %57 = OpFunctionCall %6 %10 %55
255         %58 = OpLoad %6 %12
256         %59 = OpIAdd %6 %58 %57
257               OpStore %12 %59
258               OpBranch %50
259         %50 = OpLabel
260         %60 = OpLoad %6 %46
261         %61 = OpIAdd %6 %60 %17
262               OpStore %46 %61
263               OpBranch %47
264         %49 = OpLabel
265         %62 = OpLoad %6 %12
266               OpReturnValue %62
267               OpFunctionEnd
268  )";
269
270  const auto env = SPV_ENV_UNIVERSAL_1_3;
271  const auto consumer = nullptr;
272  const auto context =
273      BuildModule(env, consumer, shader, kReduceAssembleOption);
274
275  // Targeting all functions, there are quite a few opportunities.  To avoid
276  // making the test too sensitive, we check that there are more than a number
277  // somewhat lower than the real number.
278  const auto all_ops =
279      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
280          context.get(), 0);
281  ASSERT_TRUE(all_ops.size() > 100);
282
283  // Targeting individual functions, there are fewer opportunities.  Again, we
284  // avoid checking against an exact number so that the test is not too
285  // sensitive.
286  const auto ops_for_function_4 =
287      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
288          context.get(), 4);
289  const auto ops_for_function_10 =
290      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
291          context.get(), 10);
292  const auto ops_for_function_13 =
293      OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
294          context.get(), 13);
295  ASSERT_TRUE(ops_for_function_4.size() < 60);
296  ASSERT_TRUE(ops_for_function_10.size() < 50);
297  ASSERT_TRUE(ops_for_function_13.size() < 80);
298
299  // The total number of opportunities should be the sum of the per-function
300  // opportunities.
301  ASSERT_EQ(all_ops.size(), ops_for_function_4.size() +
302                                ops_for_function_10.size() +
303                                ops_for_function_13.size());
304}
305
306}  // namespace
307}  // namespace reduce
308}  // namespace spvtools
309