1 // Copyright (c) 2019 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/remove_block_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
21 namespace spvtools {
22 namespace reduce {
23 namespace {
24
TEST(RemoveBlockReductionPassTest,BasicCheck)25 TEST(RemoveBlockReductionPassTest, BasicCheck) {
26 std::string shader = R"(
27 OpCapability Shader
28 %1 = OpExtInstImport "GLSL.std.450"
29 OpMemoryModel Logical GLSL450
30 OpEntryPoint Fragment %4 "main"
31 OpExecutionMode %4 OriginUpperLeft
32 OpSource ESSL 310
33 OpName %4 "main"
34 OpName %8 "x"
35 %2 = OpTypeVoid
36 %3 = OpTypeFunction %2
37 %6 = OpTypeInt 32 1
38 %7 = OpTypePointer Function %6
39 %9 = OpConstant %6 1
40 %10 = OpConstant %6 2
41 %11 = OpConstant %6 3
42 %12 = OpConstant %6 4
43 %4 = OpFunction %2 None %3
44 %5 = OpLabel
45 %8 = OpVariable %7 Function
46 OpBranch %14
47 %13 = OpLabel ; unreachable
48 OpStore %8 %9
49 OpBranch %14
50 %14 = OpLabel
51 OpStore %8 %10
52 OpBranch %16
53 %15 = OpLabel ; unreachable
54 OpStore %8 %11
55 OpBranch %16
56 %16 = OpLabel
57 OpStore %8 %12
58 OpBranch %17
59 %17 = OpLabel
60 OpReturn
61 OpFunctionEnd
62 )";
63 const auto env = SPV_ENV_UNIVERSAL_1_3;
64 const auto consumer = nullptr;
65 const auto context =
66 BuildModule(env, consumer, shader, kReduceAssembleOption);
67 const auto ops =
68 RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
69 context.get(), 0);
70 ASSERT_EQ(2, ops.size());
71
72 ASSERT_TRUE(ops[0]->PreconditionHolds());
73 ops[0]->TryToApply();
74
75 std::string after_op_0 = R"(
76 OpCapability Shader
77 %1 = OpExtInstImport "GLSL.std.450"
78 OpMemoryModel Logical GLSL450
79 OpEntryPoint Fragment %4 "main"
80 OpExecutionMode %4 OriginUpperLeft
81 OpSource ESSL 310
82 OpName %4 "main"
83 OpName %8 "x"
84 %2 = OpTypeVoid
85 %3 = OpTypeFunction %2
86 %6 = OpTypeInt 32 1
87 %7 = OpTypePointer Function %6
88 %9 = OpConstant %6 1
89 %10 = OpConstant %6 2
90 %11 = OpConstant %6 3
91 %12 = OpConstant %6 4
92 %4 = OpFunction %2 None %3
93 %5 = OpLabel
94 %8 = OpVariable %7 Function
95 OpBranch %14
96 %14 = OpLabel
97 OpStore %8 %10
98 OpBranch %16
99 %15 = OpLabel
100 OpStore %8 %11
101 OpBranch %16
102 %16 = OpLabel
103 OpStore %8 %12
104 OpBranch %17
105 %17 = OpLabel
106 OpReturn
107 OpFunctionEnd
108 )";
109
110 CheckEqual(env, after_op_0, context.get());
111
112 ASSERT_TRUE(ops[1]->PreconditionHolds());
113 ops[1]->TryToApply();
114
115 std::string after_op_1 = R"(
116 OpCapability Shader
117 %1 = OpExtInstImport "GLSL.std.450"
118 OpMemoryModel Logical GLSL450
119 OpEntryPoint Fragment %4 "main"
120 OpExecutionMode %4 OriginUpperLeft
121 OpSource ESSL 310
122 OpName %4 "main"
123 OpName %8 "x"
124 %2 = OpTypeVoid
125 %3 = OpTypeFunction %2
126 %6 = OpTypeInt 32 1
127 %7 = OpTypePointer Function %6
128 %9 = OpConstant %6 1
129 %10 = OpConstant %6 2
130 %11 = OpConstant %6 3
131 %12 = OpConstant %6 4
132 %4 = OpFunction %2 None %3
133 %5 = OpLabel
134 %8 = OpVariable %7 Function
135 OpBranch %14
136 %14 = OpLabel
137 OpStore %8 %10
138 OpBranch %16
139 %16 = OpLabel
140 OpStore %8 %12
141 OpBranch %17
142 %17 = OpLabel
143 OpReturn
144 OpFunctionEnd
145 )";
146
147 CheckEqual(env, after_op_1, context.get());
148 }
149
TEST(RemoveBlockReductionPassTest,UnreachableContinueAndMerge)150 TEST(RemoveBlockReductionPassTest, UnreachableContinueAndMerge) {
151 // Loop with unreachable merge and continue target. There should be no
152 // opportunities.
153
154 std::string shader = R"(
155 OpCapability Shader
156 %1 = OpExtInstImport "GLSL.std.450"
157 OpMemoryModel Logical GLSL450
158 OpEntryPoint Fragment %4 "main"
159 OpExecutionMode %4 OriginUpperLeft
160 OpSource ESSL 310
161 OpName %4 "main"
162 %2 = OpTypeVoid
163 %3 = OpTypeFunction %2
164 %4 = OpFunction %2 None %3
165 %5 = OpLabel
166 OpBranch %13
167 %13 = OpLabel
168 OpLoopMerge %16 %15 None
169 OpBranch %14
170 %14 = OpLabel
171 OpReturn
172 %15 = OpLabel
173 OpBranch %13
174 %16 = OpLabel
175 OpReturn
176 OpFunctionEnd
177 )";
178 const auto env = SPV_ENV_UNIVERSAL_1_3;
179 const auto consumer = nullptr;
180 const auto context =
181 BuildModule(env, consumer, shader, kReduceAssembleOption);
182 const auto ops =
183 RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
184 context.get(), 0);
185 ASSERT_EQ(0, ops.size());
186 }
187
TEST(RemoveBlockReductionPassTest,OneBlock)188 TEST(RemoveBlockReductionPassTest, OneBlock) {
189 // Function with just one block. There should be no opportunities.
190
191 std::string shader = R"(
192 OpCapability Shader
193 %1 = OpExtInstImport "GLSL.std.450"
194 OpMemoryModel Logical GLSL450
195 OpEntryPoint Fragment %4 "main"
196 OpExecutionMode %4 OriginUpperLeft
197 OpSource ESSL 310
198 OpName %4 "main"
199 %2 = OpTypeVoid
200 %3 = OpTypeFunction %2
201 %4 = OpFunction %2 None %3
202 %5 = OpLabel
203 OpReturn
204 OpFunctionEnd
205 )";
206 const auto env = SPV_ENV_UNIVERSAL_1_3;
207 const auto consumer = nullptr;
208 const auto context =
209 BuildModule(env, consumer, shader, kReduceAssembleOption);
210 const auto ops =
211 RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
212 context.get(), 0);
213 ASSERT_EQ(0, ops.size());
214 }
215
TEST(RemoveBlockReductionPassTest,UnreachableBlocksWithOutsideIdUses)216 TEST(RemoveBlockReductionPassTest, UnreachableBlocksWithOutsideIdUses) {
217 // A function with two unreachable blocks A -> B. A defines ID %9 and B uses
218 // %9. There are no references to A, but removing A would be invalid because
219 // of B's use of %9, so there should be no opportunities.
220
221 std::string shader = R"(
222 OpCapability Shader
223 %1 = OpExtInstImport "GLSL.std.450"
224 OpMemoryModel Logical GLSL450
225 OpEntryPoint Fragment %2 "main"
226 OpExecutionMode %2 OriginUpperLeft
227 OpSource ESSL 310
228 OpName %2 "main"
229 %3 = OpTypeVoid
230 %4 = OpTypeInt 32 1
231 %5 = OpTypeFunction %3
232 %6 = OpConstant %4 1
233 %2 = OpFunction %3 None %5
234 %7 = OpLabel
235 OpReturn
236 %8 = OpLabel ; A
237 %9 = OpUndef %4
238 OpBranch %10
239 %10 = OpLabel ; B
240 %11 = OpIAdd %4 %6 %9 ; uses %9 from A, so A cannot be removed
241 OpReturn
242 OpFunctionEnd
243 )";
244 const auto env = SPV_ENV_UNIVERSAL_1_3;
245 const auto consumer = nullptr;
246 const auto context =
247 BuildModule(env, consumer, shader, kReduceAssembleOption);
248 const auto ops =
249 RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
250 context.get(), 0);
251 ASSERT_EQ(0, ops.size());
252 }
253
TEST(RemoveBlockReductionPassTest,UnreachableBlocksWithInsideIdUses)254 TEST(RemoveBlockReductionPassTest, UnreachableBlocksWithInsideIdUses) {
255 // Similar to the above test.
256
257 // A function with two unreachable blocks A -> B. Both blocks create and use
258 // IDs, but the uses are contained within each block, so A should be removed.
259
260 std::string shader = R"(
261 OpCapability Shader
262 %1 = OpExtInstImport "GLSL.std.450"
263 OpMemoryModel Logical GLSL450
264 OpEntryPoint Fragment %2 "main"
265 OpExecutionMode %2 OriginUpperLeft
266 OpSource ESSL 310
267 OpName %2 "main"
268 %3 = OpTypeVoid
269 %4 = OpTypeInt 32 1
270 %5 = OpTypeFunction %3
271 %6 = OpConstant %4 1
272 %2 = OpFunction %3 None %5
273 %7 = OpLabel
274 OpReturn
275 %8 = OpLabel ; A
276 %9 = OpUndef %4 ; define %9
277 %10 = OpIAdd %4 %6 %9 ; use %9
278 OpBranch %11
279 %11 = OpLabel ; B
280 %12 = OpUndef %4 ; define %12
281 %13 = OpIAdd %4 %6 %12 ; use %12
282 OpReturn
283 OpFunctionEnd
284 )";
285 const auto env = SPV_ENV_UNIVERSAL_1_3;
286 const auto consumer = nullptr;
287 const auto context =
288 BuildModule(env, consumer, shader, kReduceAssembleOption);
289 auto ops = RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
290 context.get(), 0);
291 ASSERT_EQ(1, ops.size());
292
293 ASSERT_TRUE(ops[0]->PreconditionHolds());
294
295 ops[0]->TryToApply();
296
297 // Same as above, but block A is removed.
298 std::string after_op_0 = R"(
299 OpCapability Shader
300 %1 = OpExtInstImport "GLSL.std.450"
301 OpMemoryModel Logical GLSL450
302 OpEntryPoint Fragment %2 "main"
303 OpExecutionMode %2 OriginUpperLeft
304 OpSource ESSL 310
305 OpName %2 "main"
306 %3 = OpTypeVoid
307 %4 = OpTypeInt 32 1
308 %5 = OpTypeFunction %3
309 %6 = OpConstant %4 1
310 %2 = OpFunction %3 None %5
311 %7 = OpLabel
312 OpReturn
313 %11 = OpLabel
314 %12 = OpUndef %4
315 %13 = OpIAdd %4 %6 %12
316 OpReturn
317 OpFunctionEnd
318 )";
319
320 CheckEqual(env, after_op_0, context.get());
321
322 // Find opportunities again. There are no reference to B. B should now be
323 // removed.
324
325 ops = RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities(
326 context.get(), 0);
327
328 ASSERT_EQ(1, ops.size());
329
330 ASSERT_TRUE(ops[0]->PreconditionHolds());
331
332 ops[0]->TryToApply();
333
334 // Same as above, but block B is removed.
335 std::string after_op_0_again = R"(
336 OpCapability Shader
337 %1 = OpExtInstImport "GLSL.std.450"
338 OpMemoryModel Logical GLSL450
339 OpEntryPoint Fragment %2 "main"
340 OpExecutionMode %2 OriginUpperLeft
341 OpSource ESSL 310
342 OpName %2 "main"
343 %3 = OpTypeVoid
344 %4 = OpTypeInt 32 1
345 %5 = OpTypeFunction %3
346 %6 = OpConstant %4 1
347 %2 = OpFunction %3 None %5
348 %7 = OpLabel
349 OpReturn
350 OpFunctionEnd
351 )";
352
353 CheckEqual(env, after_op_0_again, context.get());
354 }
355
356 } // namespace
357 } // namespace reduce
358 } // namespace spvtools
359