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/remove_unused_instruction_reduction_opportunity_finder.h"
16
17 #include "source/opt/build_module.h"
18 #include "source/reduce/reduction_opportunity.h"
19 #include "source/util/make_unique.h"
20 #include "test/reduce/reduce_test_util.h"
21
22 namespace spvtools {
23 namespace reduce {
24 namespace {
25
26 const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
27
TEST(RemoveUnusedInstructionReductionPassTest,RemoveStores)28 TEST(RemoveUnusedInstructionReductionPassTest, RemoveStores) {
29 // A module with some unused instructions, including some unused OpStore
30 // instructions.
31
32 RemoveUnusedInstructionReductionOpportunityFinder finder(true);
33
34 const std::string original = R"(
35 OpCapability Shader
36 %1 = OpExtInstImport "GLSL.std.450"
37 OpMemoryModel Logical GLSL450
38 OpEntryPoint Fragment %4 "main"
39 OpExecutionMode %4 OriginUpperLeft
40 OpSource ESSL 310 ; 0
41 OpName %4 "main" ; 1
42 OpName %8 "a" ; 2
43 OpName %10 "b" ; 3
44 OpName %12 "c" ; 4
45 OpName %14 "d" ; 5
46 %2 = OpTypeVoid
47 %3 = OpTypeFunction %2
48 %6 = OpTypeInt 32 1
49 %7 = OpTypePointer Function %6
50 %9 = OpConstant %6 10
51 %11 = OpConstant %6 20
52 %13 = OpConstant %6 30
53 %4 = OpFunction %2 None %3
54 %5 = OpLabel
55 %8 = OpVariable %7 Function
56 %10 = OpVariable %7 Function
57 %12 = OpVariable %7 Function
58 %14 = OpVariable %7 Function
59 OpStore %8 %9 ; 6
60 OpStore %10 %11 ; 7
61 OpStore %12 %13 ; 8
62 %15 = OpLoad %6 %8
63 OpStore %14 %15 ; 9
64 OpReturn
65 OpFunctionEnd
66
67 )";
68
69 const MessageConsumer consumer = nullptr;
70 const auto context =
71 BuildModule(kEnv, consumer, original, kReduceAssembleOption);
72
73 CheckValid(kEnv, context.get());
74
75 auto ops = finder.GetAvailableOpportunities(context.get(), 0);
76
77 ASSERT_EQ(10, ops.size());
78
79 for (auto& op : ops) {
80 ASSERT_TRUE(op->PreconditionHolds());
81 op->TryToApply();
82 CheckValid(kEnv, context.get());
83 }
84
85 const std::string step_2 = R"(
86 OpCapability Shader
87 %1 = OpExtInstImport "GLSL.std.450"
88 OpMemoryModel Logical GLSL450
89 OpEntryPoint Fragment %4 "main"
90 OpExecutionMode %4 OriginUpperLeft
91 %2 = OpTypeVoid
92 %3 = OpTypeFunction %2
93 %6 = OpTypeInt 32 1
94 %7 = OpTypePointer Function %6
95 %9 = OpConstant %6 10 ; 0
96 %11 = OpConstant %6 20 ; 1
97 %13 = OpConstant %6 30 ; 2
98 %4 = OpFunction %2 None %3
99 %5 = OpLabel
100 %8 = OpVariable %7 Function
101 %10 = OpVariable %7 Function ; 3
102 %12 = OpVariable %7 Function ; 4
103 %14 = OpVariable %7 Function ; 5
104 %15 = OpLoad %6 %8 ; 6
105 OpReturn
106 OpFunctionEnd
107 )";
108
109 CheckEqual(kEnv, step_2, context.get());
110
111 ops = finder.GetAvailableOpportunities(context.get(), 0);
112
113 ASSERT_EQ(7, ops.size());
114
115 for (auto& op : ops) {
116 ASSERT_TRUE(op->PreconditionHolds());
117 op->TryToApply();
118 CheckValid(kEnv, context.get());
119 }
120
121 const std::string step_3 = R"(
122 OpCapability Shader
123 %1 = OpExtInstImport "GLSL.std.450"
124 OpMemoryModel Logical GLSL450
125 OpEntryPoint Fragment %4 "main"
126 OpExecutionMode %4 OriginUpperLeft
127 %2 = OpTypeVoid
128 %3 = OpTypeFunction %2
129 %6 = OpTypeInt 32 1
130 %7 = OpTypePointer Function %6
131 %4 = OpFunction %2 None %3
132 %5 = OpLabel
133 %8 = OpVariable %7 Function ; 0
134 OpReturn
135 OpFunctionEnd
136 )";
137
138 CheckEqual(kEnv, step_3, context.get());
139
140 ops = finder.GetAvailableOpportunities(context.get(), 0);
141
142 ASSERT_EQ(1, ops.size());
143
144 for (auto& op : ops) {
145 ASSERT_TRUE(op->PreconditionHolds());
146 op->TryToApply();
147 CheckValid(kEnv, context.get());
148 }
149
150 const std::string step_4 = R"(
151 OpCapability Shader
152 %1 = OpExtInstImport "GLSL.std.450"
153 OpMemoryModel Logical GLSL450
154 OpEntryPoint Fragment %4 "main"
155 OpExecutionMode %4 OriginUpperLeft
156 %2 = OpTypeVoid
157 %3 = OpTypeFunction %2
158 %6 = OpTypeInt 32 1
159 %7 = OpTypePointer Function %6 ; 0
160 %4 = OpFunction %2 None %3
161 %5 = OpLabel
162 OpReturn
163 OpFunctionEnd
164 )";
165
166 CheckEqual(kEnv, step_4, context.get());
167
168 ops = finder.GetAvailableOpportunities(context.get(), 0);
169
170 ASSERT_EQ(1, ops.size());
171
172 for (auto& op : ops) {
173 ASSERT_TRUE(op->PreconditionHolds());
174 op->TryToApply();
175 CheckValid(kEnv, context.get());
176 }
177
178 const std::string step_5 = R"(
179 OpCapability Shader
180 %1 = OpExtInstImport "GLSL.std.450"
181 OpMemoryModel Logical GLSL450
182 OpEntryPoint Fragment %4 "main"
183 OpExecutionMode %4 OriginUpperLeft
184 %2 = OpTypeVoid
185 %3 = OpTypeFunction %2
186 %6 = OpTypeInt 32 1 ; 0
187 %4 = OpFunction %2 None %3
188 %5 = OpLabel
189 OpReturn
190 OpFunctionEnd
191 )";
192
193 CheckEqual(kEnv, step_5, context.get());
194
195 ops = finder.GetAvailableOpportunities(context.get(), 0);
196
197 ASSERT_EQ(1, ops.size());
198
199 for (auto& op : ops) {
200 ASSERT_TRUE(op->PreconditionHolds());
201 op->TryToApply();
202 CheckValid(kEnv, context.get());
203 }
204
205 const std::string step_6 = R"(
206 OpCapability Shader
207 %1 = OpExtInstImport "GLSL.std.450"
208 OpMemoryModel Logical GLSL450
209 OpEntryPoint Fragment %4 "main"
210 OpExecutionMode %4 OriginUpperLeft
211 %2 = OpTypeVoid
212 %3 = OpTypeFunction %2
213 %4 = OpFunction %2 None %3
214 %5 = OpLabel
215 OpReturn
216 OpFunctionEnd
217 )";
218
219 CheckEqual(kEnv, step_6, context.get());
220
221 ops = finder.GetAvailableOpportunities(context.get(), 0);
222
223 ASSERT_EQ(0, ops.size());
224 }
225
TEST(RemoveUnusedInstructionReductionPassTest,Referenced)226 TEST(RemoveUnusedInstructionReductionPassTest, Referenced) {
227 // A module with some unused global variables, constants, and types. Some will
228 // not be removed initially because of the OpDecorate instructions.
229
230 RemoveUnusedInstructionReductionOpportunityFinder finder(true);
231
232 const std::string shader = R"(
233 OpCapability Shader
234 %1 = OpExtInstImport "GLSL.std.450"
235 OpMemoryModel Logical GLSL450
236 OpEntryPoint Fragment %4 "main"
237 OpExecutionMode %4 OriginUpperLeft
238 OpSource ESSL 310 ; 1
239 OpName %4 "main" ; 2
240 OpName %12 "a" ; 3
241 OpDecorate %12 RelaxedPrecision ; 4
242 OpDecorate %13 RelaxedPrecision ; 5
243 %2 = OpTypeVoid
244 %3 = OpTypeFunction %2
245 %6 = OpTypeBool
246 %7 = OpConstantTrue %6 ; 6
247 %10 = OpTypeInt 32 1
248 %11 = OpTypePointer Private %10
249 %12 = OpVariable %11 Private
250 %13 = OpConstant %10 1
251 %4 = OpFunction %2 None %3
252 %5 = OpLabel
253 OpReturn
254 OpFunctionEnd
255 )";
256
257 auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
258
259 CheckValid(kEnv, context.get());
260
261 auto ops = finder.GetAvailableOpportunities(context.get(), 0);
262
263 ASSERT_EQ(6, ops.size());
264
265 for (auto& op : ops) {
266 ASSERT_TRUE(op->PreconditionHolds());
267 op->TryToApply();
268 CheckValid(kEnv, context.get());
269 }
270
271 std::string after = R"(
272 OpCapability Shader
273 %1 = OpExtInstImport "GLSL.std.450"
274 OpMemoryModel Logical GLSL450
275 OpEntryPoint Fragment %4 "main"
276 OpExecutionMode %4 OriginUpperLeft
277 %2 = OpTypeVoid
278 %3 = OpTypeFunction %2
279 %6 = OpTypeBool ; 1
280 %10 = OpTypeInt 32 1
281 %11 = OpTypePointer Private %10
282 %12 = OpVariable %11 Private ; 2
283 %13 = OpConstant %10 1 ; 3
284 %4 = OpFunction %2 None %3
285 %5 = OpLabel
286 OpReturn
287 OpFunctionEnd
288 )";
289
290 CheckEqual(kEnv, after, context.get());
291
292 ops = finder.GetAvailableOpportunities(context.get(), 0);
293
294 ASSERT_EQ(3, ops.size());
295
296 for (auto& op : ops) {
297 ASSERT_TRUE(op->PreconditionHolds());
298 op->TryToApply();
299 CheckValid(kEnv, context.get());
300 }
301
302 std::string after_2 = R"(
303 OpCapability Shader
304 %1 = OpExtInstImport "GLSL.std.450"
305 OpMemoryModel Logical GLSL450
306 OpEntryPoint Fragment %4 "main"
307 OpExecutionMode %4 OriginUpperLeft
308 %2 = OpTypeVoid
309 %3 = OpTypeFunction %2
310 %10 = OpTypeInt 32 1
311 %11 = OpTypePointer Private %10 ; 1
312 %4 = OpFunction %2 None %3
313 %5 = OpLabel
314 OpReturn
315 OpFunctionEnd
316 )";
317
318 CheckEqual(kEnv, after_2, context.get());
319
320 ops = finder.GetAvailableOpportunities(context.get(), 0);
321
322 ASSERT_EQ(1, ops.size());
323
324 for (auto& op : ops) {
325 ASSERT_TRUE(op->PreconditionHolds());
326 op->TryToApply();
327 CheckValid(kEnv, context.get());
328 }
329
330 std::string after_3 = R"(
331 OpCapability Shader
332 %1 = OpExtInstImport "GLSL.std.450"
333 OpMemoryModel Logical GLSL450
334 OpEntryPoint Fragment %4 "main"
335 OpExecutionMode %4 OriginUpperLeft
336 %2 = OpTypeVoid
337 %3 = OpTypeFunction %2
338 %10 = OpTypeInt 32 1 ; 1
339 %4 = OpFunction %2 None %3
340 %5 = OpLabel
341 OpReturn
342 OpFunctionEnd
343 )";
344
345 CheckEqual(kEnv, after_3, context.get());
346
347 ops = finder.GetAvailableOpportunities(context.get(), 0);
348
349 ASSERT_EQ(1, ops.size());
350
351 for (auto& op : ops) {
352 ASSERT_TRUE(op->PreconditionHolds());
353 op->TryToApply();
354 CheckValid(kEnv, context.get());
355 }
356
357 std::string after_4 = R"(
358 OpCapability Shader
359 %1 = OpExtInstImport "GLSL.std.450"
360 OpMemoryModel Logical GLSL450
361 OpEntryPoint Fragment %4 "main"
362 OpExecutionMode %4 OriginUpperLeft
363 %2 = OpTypeVoid
364 %3 = OpTypeFunction %2
365 %4 = OpFunction %2 None %3
366 %5 = OpLabel
367 OpReturn
368 OpFunctionEnd
369 )";
370
371 CheckEqual(kEnv, after_4, context.get());
372
373 ops = finder.GetAvailableOpportunities(context.get(), 0);
374
375 ASSERT_EQ(0, ops.size());
376 }
377
TEST(RemoveUnusedResourceVariableTest,RemoveUnusedResourceVariables)378 TEST(RemoveUnusedResourceVariableTest, RemoveUnusedResourceVariables) {
379 std::string shader = R"(
380 OpCapability Shader
381 %1 = OpExtInstImport "GLSL.std.450"
382 OpMemoryModel Logical GLSL450
383 OpEntryPoint GLCompute %4 "main"
384 OpExecutionMode %4 LocalSize 1 1 1
385 OpMemberDecorate %9 0 Offset 0
386 OpDecorate %9 Block
387 OpDecorate %11 DescriptorSet 0
388 OpDecorate %11 Binding 1
389 OpMemberDecorate %16 0 Offset 0
390 OpMemberDecorate %16 1 Offset 4
391 OpDecorate %16 Block
392 OpDecorate %18 DescriptorSet 0
393 OpDecorate %18 Binding 0
394 OpMemberDecorate %19 0 Offset 0
395 OpDecorate %19 BufferBlock
396 OpDecorate %21 DescriptorSet 1
397 OpDecorate %21 Binding 0
398 OpMemberDecorate %22 0 Offset 0
399 OpDecorate %22 Block
400 OpDecorate %29 DescriptorSet 1
401 OpDecorate %29 Binding 1
402 OpDecorate %32 DescriptorSet 1
403 OpDecorate %32 Binding 2
404 OpDecorate %32 NonReadable
405 %2 = OpTypeVoid
406 %3 = OpTypeFunction %2
407 %6 = OpTypeInt 32 1
408 %9 = OpTypeStruct %6
409 %10 = OpTypePointer Uniform %9
410 %11 = OpVariable %10 Uniform
411 %13 = OpTypePointer Uniform %6
412 %16 = OpTypeStruct %6 %6
413 %17 = OpTypePointer Uniform %16
414 %18 = OpVariable %17 Uniform
415 %19 = OpTypeStruct %6
416 %20 = OpTypePointer Uniform %19
417 %21 = OpVariable %20 Uniform
418 %22 = OpTypeStruct %6
419 %23 = OpTypePointer PushConstant %22
420 %24 = OpVariable %23 PushConstant
421 %25 = OpTypeFloat 32
422 %26 = OpTypeImage %25 2D 0 0 0 1 Unknown
423 %27 = OpTypeSampledImage %26
424 %28 = OpTypePointer UniformConstant %27
425 %29 = OpVariable %28 UniformConstant
426 %30 = OpTypeImage %25 2D 0 0 0 2 Unknown
427 %31 = OpTypePointer UniformConstant %30
428 %32 = OpVariable %31 UniformConstant
429 %4 = OpFunction %2 None %3
430 %5 = OpLabel
431 OpReturn
432 OpFunctionEnd
433 )";
434
435 const auto env = SPV_ENV_UNIVERSAL_1_3;
436 const auto consumer = nullptr;
437 const auto context =
438 BuildModule(env, consumer, shader, kReduceAssembleOption);
439
440 auto ops = RemoveUnusedInstructionReductionOpportunityFinder(true)
441 .GetAvailableOpportunities(context.get(), 0);
442 ASSERT_EQ(7, ops.size());
443
444 for (auto& op : ops) {
445 ASSERT_TRUE(op->PreconditionHolds());
446 op->TryToApply();
447 }
448
449 std::string expected_1 = R"(
450 OpCapability Shader
451 %1 = OpExtInstImport "GLSL.std.450"
452 OpMemoryModel Logical GLSL450
453 OpEntryPoint GLCompute %4 "main"
454 OpExecutionMode %4 LocalSize 1 1 1
455 OpMemberDecorate %9 0 Offset 0
456 OpDecorate %9 Block
457 OpMemberDecorate %16 0 Offset 0
458 OpMemberDecorate %16 1 Offset 4
459 OpDecorate %16 Block
460 OpMemberDecorate %19 0 Offset 0
461 OpDecorate %19 BufferBlock
462 OpMemberDecorate %22 0 Offset 0
463 OpDecorate %22 Block
464 %2 = OpTypeVoid
465 %3 = OpTypeFunction %2
466 %6 = OpTypeInt 32 1
467 %9 = OpTypeStruct %6
468 %10 = OpTypePointer Uniform %9
469 %16 = OpTypeStruct %6 %6
470 %17 = OpTypePointer Uniform %16
471 %19 = OpTypeStruct %6
472 %20 = OpTypePointer Uniform %19
473 %22 = OpTypeStruct %6
474 %23 = OpTypePointer PushConstant %22
475 %25 = OpTypeFloat 32
476 %26 = OpTypeImage %25 2D 0 0 0 1 Unknown
477 %27 = OpTypeSampledImage %26
478 %28 = OpTypePointer UniformConstant %27
479 %30 = OpTypeImage %25 2D 0 0 0 2 Unknown
480 %31 = OpTypePointer UniformConstant %30
481 %4 = OpFunction %2 None %3
482 %5 = OpLabel
483 OpReturn
484 OpFunctionEnd
485 )";
486
487 CheckEqual(env, expected_1, context.get());
488
489 ops = RemoveUnusedInstructionReductionOpportunityFinder(true)
490 .GetAvailableOpportunities(context.get(), 0);
491 ASSERT_EQ(6, ops.size());
492
493 for (auto& op : ops) {
494 ASSERT_TRUE(op->PreconditionHolds());
495 op->TryToApply();
496 }
497
498 std::string expected_2 = R"(
499 OpCapability Shader
500 %1 = OpExtInstImport "GLSL.std.450"
501 OpMemoryModel Logical GLSL450
502 OpEntryPoint GLCompute %4 "main"
503 OpExecutionMode %4 LocalSize 1 1 1
504 OpMemberDecorate %9 0 Offset 0
505 OpDecorate %9 Block
506 OpMemberDecorate %16 0 Offset 0
507 OpMemberDecorate %16 1 Offset 4
508 OpDecorate %16 Block
509 OpMemberDecorate %19 0 Offset 0
510 OpDecorate %19 BufferBlock
511 OpMemberDecorate %22 0 Offset 0
512 OpDecorate %22 Block
513 %2 = OpTypeVoid
514 %3 = OpTypeFunction %2
515 %6 = OpTypeInt 32 1
516 %9 = OpTypeStruct %6
517 %16 = OpTypeStruct %6 %6
518 %19 = OpTypeStruct %6
519 %22 = OpTypeStruct %6
520 %25 = OpTypeFloat 32
521 %26 = OpTypeImage %25 2D 0 0 0 1 Unknown
522 %27 = OpTypeSampledImage %26
523 %30 = OpTypeImage %25 2D 0 0 0 2 Unknown
524 %4 = OpFunction %2 None %3
525 %5 = OpLabel
526 OpReturn
527 OpFunctionEnd
528 )";
529
530 CheckEqual(env, expected_2, context.get());
531
532 ops = RemoveUnusedInstructionReductionOpportunityFinder(true)
533 .GetAvailableOpportunities(context.get(), 0);
534 ASSERT_EQ(6, ops.size());
535
536 for (auto& op : ops) {
537 ASSERT_TRUE(op->PreconditionHolds());
538 op->TryToApply();
539 }
540
541 std::string expected_3 = R"(
542 OpCapability Shader
543 %1 = OpExtInstImport "GLSL.std.450"
544 OpMemoryModel Logical GLSL450
545 OpEntryPoint GLCompute %4 "main"
546 OpExecutionMode %4 LocalSize 1 1 1
547 %2 = OpTypeVoid
548 %3 = OpTypeFunction %2
549 %6 = OpTypeInt 32 1
550 %25 = OpTypeFloat 32
551 %26 = OpTypeImage %25 2D 0 0 0 1 Unknown
552 %4 = OpFunction %2 None %3
553 %5 = OpLabel
554 OpReturn
555 OpFunctionEnd
556 )";
557
558 CheckEqual(env, expected_3, context.get());
559 }
560
561 } // namespace
562 } // namespace reduce
563 } // namespace spvtools
564