1 // Copyright (c) 2016 Google Inc.
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 <memory>
16 #include <unordered_set>
17
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20
21 #include "opt/build_module.h"
22 #include "opt/def_use_manager.h"
23 #include "pass_utils.h"
24 #include "spirv-tools/libspirv.hpp"
25
26 namespace {
27
28 using ::testing::ElementsAre;
29
30 using namespace spvtools;
31 using spvtools::opt::analysis::DefUseManager;
32
33 // Disassembles the given |inst| and returns the disassembly.
DisassembleInst(ir::Instruction * inst)34 std::string DisassembleInst(ir::Instruction* inst) {
35 SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
36
37 std::vector<uint32_t> binary;
38 // We need this to generate the necessary header in the binary.
39 tools.Assemble("", &binary);
40 inst->ToBinaryWithoutAttachedDebugInsts(&binary);
41
42 std::string text;
43 // We'll need to check the underlying id numbers.
44 // So turn off friendly names for ids.
45 tools.Disassemble(binary, &text, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
46 while (!text.empty() && text.back() == '\n') text.pop_back();
47 return text;
48 }
49
50 // A struct for holding expected id defs and uses.
51 struct InstDefUse {
52 using IdInstPair = std::pair<uint32_t, const char*>;
53 using IdInstsPair = std::pair<uint32_t, std::vector<const char*>>;
54
55 // Ids and their corresponding def instructions.
56 std::vector<IdInstPair> defs;
57 // Ids and their corresponding use instructions.
58 std::vector<IdInstsPair> uses;
59 };
60
61 // Checks that the |actual_defs| and |actual_uses| are in accord with
62 // |expected_defs_uses|.
CheckDef(const InstDefUse & expected_defs_uses,const DefUseManager::IdToDefMap & actual_defs)63 void CheckDef(const InstDefUse& expected_defs_uses,
64 const DefUseManager::IdToDefMap& actual_defs) {
65 // Check defs.
66 ASSERT_EQ(expected_defs_uses.defs.size(), actual_defs.size());
67 for (uint32_t i = 0; i < expected_defs_uses.defs.size(); ++i) {
68 const auto id = expected_defs_uses.defs[i].first;
69 const auto expected_def = expected_defs_uses.defs[i].second;
70 ASSERT_EQ(1u, actual_defs.count(id)) << "expected to def id [" << id << "]";
71 EXPECT_EQ(expected_def, DisassembleInst(actual_defs.at(id)));
72 }
73 }
74
CheckUse(const InstDefUse & expected_defs_uses,const DefUseManager::IdToUsesMap & actual_uses)75 void CheckUse(const InstDefUse& expected_defs_uses,
76 const DefUseManager::IdToUsesMap& actual_uses) {
77 // Check uses.
78 ASSERT_EQ(expected_defs_uses.uses.size(), actual_uses.size());
79 for (uint32_t i = 0; i < expected_defs_uses.uses.size(); ++i) {
80 const auto id = expected_defs_uses.uses[i].first;
81 const auto& expected_uses = expected_defs_uses.uses[i].second;
82
83 ASSERT_EQ(1u, actual_uses.count(id)) << "expected to use id [" << id << "]";
84 const auto& uses = actual_uses.at(id);
85
86 ASSERT_EQ(expected_uses.size(), uses.size())
87 << "id [" << id << "] # uses: expected: " << expected_uses.size()
88 << " actual: " << uses.size();
89 auto it = uses.cbegin();
90 for (const auto expected_use : expected_uses) {
91 EXPECT_EQ(expected_use, DisassembleInst(it->inst))
92 << "id [" << id << "] use instruction mismatch";
93 ++it;
94 }
95 }
96 }
97
98 // The following test case mimics how LLVM handles induction variables.
99 // But, yeah, it's not very readable. However, we only care about the id
100 // defs and uses. So, no need to make sure this is valid OpPhi construct.
101 const char kOpPhiTestFunction[] =
102 " %2 = OpFunction %1 None %3 "
103 " %4 = OpLabel "
104 " OpBranch %5 "
105
106 " %5 = OpLabel "
107 " %7 = OpPhi %6 %8 %4 %9 %5 "
108 "%11 = OpPhi %10 %12 %4 %13 %5 "
109 " %9 = OpIAdd %6 %7 %14 "
110 "%13 = OpFAdd %10 %11 %15 "
111 "%17 = OpSLessThan %16 %7 %18 "
112 " OpLoopMerge %19 %5 None "
113 " OpBranchConditional %17 %5 %19 "
114
115 "%19 = OpLabel "
116 " OpReturn "
117 " OpFunctionEnd";
118
119 struct ParseDefUseCase {
120 const char* text;
121 InstDefUse du;
122 };
123
124 using ParseDefUseTest = ::testing::TestWithParam<ParseDefUseCase>;
125
TEST_P(ParseDefUseTest,Case)126 TEST_P(ParseDefUseTest, Case) {
127 const auto& tc = GetParam();
128
129 // Build module.
130 const std::vector<const char*> text = {tc.text};
131 std::unique_ptr<ir::Module> module =
132 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text));
133 ASSERT_NE(nullptr, module);
134
135 // Analyze def and use.
136 opt::analysis::DefUseManager manager(nullptr, module.get());
137
138 CheckDef(tc.du, manager.id_to_defs());
139 CheckUse(tc.du, manager.id_to_uses());
140 }
141
142 // clang-format off
143 INSTANTIATE_TEST_CASE_P(
144 TestCase, ParseDefUseTest,
145 ::testing::ValuesIn(std::vector<ParseDefUseCase>{
146 {"", {{}, {}}}, // no instruction
147 {"OpMemoryModel Logical GLSL450", {{}, {}}}, // no def and use
148 { // single def, no use
149 "%1 = OpString \"wow\"",
150 {
151 {{1, "%1 = OpString \"wow\""}}, // defs
152 {} // uses
153 }
154 },
155 { // multiple def, no use
156 "%1 = OpString \"hello\" "
157 "%2 = OpString \"world\" "
158 "%3 = OpTypeVoid",
159 {
160 { // defs
161 {1, "%1 = OpString \"hello\""},
162 {2, "%2 = OpString \"world\""},
163 {3, "%3 = OpTypeVoid"},
164 },
165 {} // uses
166 }
167 },
168 { // single use, no def
169 "OpTypeForwardPointer %1 Input",
170 {
171 {}, // defs
172 { // uses
173 {1, {"OpTypeForwardPointer %1 Input"}},
174 }
175 }
176 },
177 { // multiple use, no def
178 "OpEntryPoint Fragment %1 \"main\" "
179 "OpTypeForwardPointer %2 Input "
180 "OpTypeForwardPointer %3 Output",
181 {
182 {}, // defs
183 { // uses
184 {1, {"OpEntryPoint Fragment %1 \"main\""}},
185 {2, {"OpTypeForwardPointer %2 Input"}},
186 {3, {"OpTypeForwardPointer %3 Output"}},
187 }
188 }
189 },
190 { // multiple def, multiple use
191 "%1 = OpTypeBool "
192 "%2 = OpTypeVector %1 3 "
193 "%3 = OpTypeMatrix %2 3",
194 {
195 { // defs
196 {1, "%1 = OpTypeBool"},
197 {2, "%2 = OpTypeVector %1 3"},
198 {3, "%3 = OpTypeMatrix %2 3"},
199 },
200 { // uses
201 {1, {"%2 = OpTypeVector %1 3"}},
202 {2, {"%3 = OpTypeMatrix %2 3"}},
203 }
204 }
205 },
206 { // multiple use of the same id
207 "%1 = OpTypeBool "
208 "%2 = OpTypeVector %1 2 "
209 "%3 = OpTypeVector %1 3 "
210 "%4 = OpTypeVector %1 4",
211 {
212 { // defs
213 {1, "%1 = OpTypeBool"},
214 {2, "%2 = OpTypeVector %1 2"},
215 {3, "%3 = OpTypeVector %1 3"},
216 {4, "%4 = OpTypeVector %1 4"},
217 },
218 { // uses
219 {1,
220 {
221 "%2 = OpTypeVector %1 2",
222 "%3 = OpTypeVector %1 3",
223 "%4 = OpTypeVector %1 4",
224 }
225 },
226 }
227 }
228 },
229 { // labels
230 "%2 = OpFunction %1 None %3 "
231
232 "%4 = OpLabel "
233 "OpBranchConditional %5 %6 %7 "
234
235 "%6 = OpLabel "
236 "OpBranch %7 "
237
238 "%7 = OpLabel "
239 "OpReturn "
240
241 "OpFunctionEnd",
242 {
243 { // defs
244 {2, "%2 = OpFunction %1 None %3"},
245 {4, "%4 = OpLabel"},
246 {6, "%6 = OpLabel"},
247 {7, "%7 = OpLabel"},
248 },
249 { // uses
250 {1, {"%2 = OpFunction %1 None %3"}},
251 {3, {"%2 = OpFunction %1 None %3"}},
252 {5, {"OpBranchConditional %5 %6 %7"}},
253 {6, {"OpBranchConditional %5 %6 %7"}},
254 {7,
255 {
256 "OpBranchConditional %5 %6 %7",
257 "OpBranch %7",
258 }
259 },
260 }
261 }
262 },
263 { // cross function
264 "%1 = OpTypeBool "
265
266 "%2 = OpFunction %1 None %3 "
267
268 "%4 = OpLabel "
269 "%5 = OpVariable %1 Function "
270 "%6 = OpFunctionCall %1 %2 %5 "
271 "OpReturnValue %6 "
272
273 "OpFunctionEnd",
274 {
275 { // defs
276 {1, "%1 = OpTypeBool"},
277 {2, "%2 = OpFunction %1 None %3"},
278 {4, "%4 = OpLabel"},
279 {5, "%5 = OpVariable %1 Function"},
280 {6, "%6 = OpFunctionCall %1 %2 %5"},
281 },
282 { // uses
283 {1,
284 {
285 "%2 = OpFunction %1 None %3",
286 "%5 = OpVariable %1 Function",
287 "%6 = OpFunctionCall %1 %2 %5",
288 }
289 },
290 {2, {"%6 = OpFunctionCall %1 %2 %5"}},
291 {5, {"%6 = OpFunctionCall %1 %2 %5"}},
292 {3, {"%2 = OpFunction %1 None %3"}},
293 {6, {"OpReturnValue %6"}},
294 }
295 }
296 },
297 { // selection merge and loop merge
298 "%2 = OpFunction %1 None %3 "
299
300 "%4 = OpLabel "
301 "OpLoopMerge %5 %4 None "
302 "OpBranch %6 "
303
304 "%5 = OpLabel "
305 "OpReturn "
306
307 "%6 = OpLabel "
308 "OpSelectionMerge %7 None "
309 "OpBranchConditional %8 %9 %7 "
310
311 "%7 = OpLabel "
312 "OpReturn "
313
314 "%9 = OpLabel "
315 "OpReturn "
316
317 "OpFunctionEnd",
318 {
319 { // defs
320 {2, "%2 = OpFunction %1 None %3"},
321 {4, "%4 = OpLabel"},
322 {5, "%5 = OpLabel"},
323 {6, "%6 = OpLabel"},
324 {7, "%7 = OpLabel"},
325 {9, "%9 = OpLabel"},
326 },
327 { // uses
328 {1, {"%2 = OpFunction %1 None %3"}},
329 {3, {"%2 = OpFunction %1 None %3"}},
330 {4, {"OpLoopMerge %5 %4 None"}},
331 {5, {"OpLoopMerge %5 %4 None"}},
332 {6, {"OpBranch %6"}},
333 {7,
334 {
335 "OpSelectionMerge %7 None",
336 "OpBranchConditional %8 %9 %7",
337 }
338 },
339 {8, {"OpBranchConditional %8 %9 %7"}},
340 {9, {"OpBranchConditional %8 %9 %7"}},
341 }
342 }
343 },
344 { // Forward reference
345 "OpDecorate %1 Block "
346 "OpTypeForwardPointer %2 Input "
347 "%3 = OpTypeInt 32 0 "
348 "%1 = OpTypeStruct %3 "
349 "%2 = OpTypePointer Input %3",
350 {
351 { // defs
352 {1, "%1 = OpTypeStruct %3"},
353 {2, "%2 = OpTypePointer Input %3"},
354 {3, "%3 = OpTypeInt 32 0"},
355 },
356 { // uses
357 {1, {"OpDecorate %1 Block"}},
358 {2, {"OpTypeForwardPointer %2 Input"}},
359 {3,
360 {
361 "%1 = OpTypeStruct %3",
362 "%2 = OpTypePointer Input %3",
363 }
364 }
365 },
366 },
367 },
368 { // OpPhi
369 kOpPhiTestFunction,
370 {
371 { // defs
372 {2, "%2 = OpFunction %1 None %3"},
373 {4, "%4 = OpLabel"},
374 {5, "%5 = OpLabel"},
375 {7, "%7 = OpPhi %6 %8 %4 %9 %5"},
376 {9, "%9 = OpIAdd %6 %7 %14"},
377 {11, "%11 = OpPhi %10 %12 %4 %13 %5"},
378 {13, "%13 = OpFAdd %10 %11 %15"},
379 {17, "%17 = OpSLessThan %16 %7 %18"},
380 {19, "%19 = OpLabel"},
381 },
382 { // uses
383 {1, {"%2 = OpFunction %1 None %3"}},
384 {3, {"%2 = OpFunction %1 None %3"}},
385 {4,
386 {
387 "%7 = OpPhi %6 %8 %4 %9 %5",
388 "%11 = OpPhi %10 %12 %4 %13 %5",
389 }
390 },
391 {5,
392 {
393 "OpBranch %5",
394 "%7 = OpPhi %6 %8 %4 %9 %5",
395 "%11 = OpPhi %10 %12 %4 %13 %5",
396 "OpLoopMerge %19 %5 None",
397 "OpBranchConditional %17 %5 %19",
398 }
399 },
400 {6,
401 {
402 "%7 = OpPhi %6 %8 %4 %9 %5",
403 "%9 = OpIAdd %6 %7 %14",
404 }
405 },
406 {7,
407 {
408 "%9 = OpIAdd %6 %7 %14",
409 "%17 = OpSLessThan %16 %7 %18",
410 }
411 },
412 {8, {"%7 = OpPhi %6 %8 %4 %9 %5"}},
413 {9, {"%7 = OpPhi %6 %8 %4 %9 %5"}},
414 {10,
415 {
416 "%11 = OpPhi %10 %12 %4 %13 %5",
417 "%13 = OpFAdd %10 %11 %15",
418 }
419 },
420 {11, {"%13 = OpFAdd %10 %11 %15"}},
421 {12, {"%11 = OpPhi %10 %12 %4 %13 %5"}},
422 {13, {"%11 = OpPhi %10 %12 %4 %13 %5"}},
423 {14, {"%9 = OpIAdd %6 %7 %14"}},
424 {15, {"%13 = OpFAdd %10 %11 %15"}},
425 {16, {"%17 = OpSLessThan %16 %7 %18"}},
426 {17, {"OpBranchConditional %17 %5 %19"}},
427 {18, {"%17 = OpSLessThan %16 %7 %18"}},
428 {19,
429 {
430 "OpLoopMerge %19 %5 None",
431 "OpBranchConditional %17 %5 %19",
432 }
433 },
434 },
435 },
436 },
437 { // OpPhi defining and referencing the same id.
438 "%1 = OpTypeBool "
439 "%2 = OpConstantTrue %1 "
440
441 "%4 = OpFunction %3 None %5 "
442 "%6 = OpLabel "
443 " OpBranch %7 "
444 "%7 = OpLabel "
445 "%8 = OpPhi %1 %8 %7 %2 %6 " // both defines and uses %8
446 " OpBranch %7 "
447 " OpFunctionEnd",
448 {
449 { // defs
450 {1, "%1 = OpTypeBool"},
451 {2, "%2 = OpConstantTrue %1"},
452 {4, "%4 = OpFunction %3 None %5"},
453 {6, "%6 = OpLabel"},
454 {7, "%7 = OpLabel"},
455 {8, "%8 = OpPhi %1 %8 %7 %2 %6"},
456 },
457 { // uses
458 {1,
459 {
460 "%2 = OpConstantTrue %1",
461 "%8 = OpPhi %1 %8 %7 %2 %6",
462 }
463 },
464 {2, {"%8 = OpPhi %1 %8 %7 %2 %6"}},
465 {3, {"%4 = OpFunction %3 None %5"}},
466 {5, {"%4 = OpFunction %3 None %5"}},
467 {6, {"%8 = OpPhi %1 %8 %7 %2 %6"}},
468 {7,
469 {
470 "OpBranch %7",
471 "%8 = OpPhi %1 %8 %7 %2 %6",
472 "OpBranch %7",
473 }
474 },
475 {8, {"%8 = OpPhi %1 %8 %7 %2 %6"}},
476 },
477 },
478 },
479 })
480 );
481 // clang-format on
482
483 struct ReplaceUseCase {
484 const char* before;
485 std::vector<std::pair<uint32_t, uint32_t>> candidates;
486 const char* after;
487 InstDefUse du;
488 };
489
490 using ReplaceUseTest = ::testing::TestWithParam<ReplaceUseCase>;
491
492 // Disassembles the given |module| and returns the disassembly.
DisassembleModule(ir::Module * module)493 std::string DisassembleModule(ir::Module* module) {
494 SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
495
496 std::vector<uint32_t> binary;
497 module->ToBinary(&binary, /* skip_nop = */ false);
498
499 std::string text;
500 // We'll need to check the underlying id numbers.
501 // So turn off friendly names for ids.
502 tools.Disassemble(binary, &text, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
503 while (!text.empty() && text.back() == '\n') text.pop_back();
504 return text;
505 }
506
TEST_P(ReplaceUseTest,Case)507 TEST_P(ReplaceUseTest, Case) {
508 const auto& tc = GetParam();
509
510 // Build module.
511 const std::vector<const char*> text = {tc.before};
512 std::unique_ptr<ir::Module> module =
513 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text));
514 ASSERT_NE(nullptr, module);
515
516 // Analyze def and use.
517 opt::analysis::DefUseManager manager(nullptr, module.get());
518
519 // Do the substitution.
520 for (const auto& candiate : tc.candidates) {
521 manager.ReplaceAllUsesWith(candiate.first, candiate.second);
522 }
523
524 EXPECT_EQ(tc.after, DisassembleModule(module.get()));
525 CheckDef(tc.du, manager.id_to_defs());
526 CheckUse(tc.du, manager.id_to_uses());
527 }
528
529 // clang-format off
530 INSTANTIATE_TEST_CASE_P(
531 TestCase, ReplaceUseTest,
532 ::testing::ValuesIn(std::vector<ReplaceUseCase>{
533 { // no use, no replace request
534 "", {}, "", {},
535 },
536 { // no use, some replace requests
537 "OpMemoryModel Logical GLSL450",
538 {{1, 2}, {3, 4}, {7, 8}, {7, 9}, {7, 10}, {2, 10}, {3, 10}},
539 "OpMemoryModel Logical GLSL450",
540 {},
541 },
542 { // replace one use
543 "%1 = OpTypeBool "
544 "%2 = OpTypeVector %1 3",
545 {{1, 3}},
546 "%1 = OpTypeBool\n"
547 "%2 = OpTypeVector %3 3",
548 {
549 { // defs
550 {1, "%1 = OpTypeBool"},
551 {2, "%2 = OpTypeVector %3 3"},
552 },
553 { // uses
554 {3, {"%2 = OpTypeVector %3 3"}},
555 },
556 },
557 },
558 { // replace and then replace back
559 "%1 = OpTypeBool "
560 "%2 = OpTypeVector %1 3",
561 {{1, 3}, {3, 1}},
562 "%1 = OpTypeBool\n"
563 "%2 = OpTypeVector %1 3",
564 {
565 { // defs
566 {1, "%1 = OpTypeBool"},
567 {2, "%2 = OpTypeVector %1 3"},
568 },
569 { // uses
570 {1, {"%2 = OpTypeVector %1 3"}},
571 },
572 },
573 },
574 { // replace with the same id
575 "%1 = OpTypeBool "
576 "%2 = OpTypeVector %1 3",
577 {{1, 1}, {2, 2}, {3, 3}},
578 "%1 = OpTypeBool\n"
579 "%2 = OpTypeVector %1 3",
580 {
581 { // defs
582 {1, "%1 = OpTypeBool"},
583 {2, "%2 = OpTypeVector %1 3"},
584 },
585 { // uses
586 {1, {"%2 = OpTypeVector %1 3"}},
587 },
588 },
589 },
590 { // replace in sequence
591 "%1 = OpTypeBool "
592 "%2 = OpTypeVector %1 3",
593 {{1, 3}, {3, 4}, {4, 5}, {5, 100}},
594 "%1 = OpTypeBool\n"
595 "%2 = OpTypeVector %100 3",
596 {
597 { // defs
598 {1, "%1 = OpTypeBool"},
599 {2, "%2 = OpTypeVector %100 3"},
600 },
601 { // uses
602 {100, {"%2 = OpTypeVector %100 3"}},
603 },
604 },
605 },
606 { // replace multiple uses
607 "%1 = OpTypeBool "
608 "%2 = OpTypeVector %1 2 "
609 "%3 = OpTypeVector %1 3 "
610 "%4 = OpTypeVector %1 4 "
611 "%5 = OpTypeMatrix %2 2 "
612 "%6 = OpTypeMatrix %3 3 "
613 "%7 = OpTypeMatrix %4 4",
614 {{1, 10}, {2, 20}, {4, 40}},
615 "%1 = OpTypeBool\n"
616 "%2 = OpTypeVector %10 2\n"
617 "%3 = OpTypeVector %10 3\n"
618 "%4 = OpTypeVector %10 4\n"
619 "%5 = OpTypeMatrix %20 2\n"
620 "%6 = OpTypeMatrix %3 3\n"
621 "%7 = OpTypeMatrix %40 4",
622 {
623 { // defs
624 {1, "%1 = OpTypeBool"},
625 {2, "%2 = OpTypeVector %10 2"},
626 {3, "%3 = OpTypeVector %10 3"},
627 {4, "%4 = OpTypeVector %10 4"},
628 {5, "%5 = OpTypeMatrix %20 2"},
629 {6, "%6 = OpTypeMatrix %3 3"},
630 {7, "%7 = OpTypeMatrix %40 4"},
631 },
632 { // uses
633 {10,
634 {
635 "%2 = OpTypeVector %10 2",
636 "%3 = OpTypeVector %10 3",
637 "%4 = OpTypeVector %10 4",
638 }
639 },
640 {20, {"%5 = OpTypeMatrix %20 2"}},
641 {3, {"%6 = OpTypeMatrix %3 3"}},
642 {40, {"%7 = OpTypeMatrix %40 4"}},
643 },
644 },
645 },
646 { // OpPhi.
647 kOpPhiTestFunction,
648 // replace one id used by OpPhi, replace one id generated by OpPhi
649 {{9, 9000}, {11, 9}},
650 "%2 = OpFunction %1 None %3\n"
651 "%4 = OpLabel\n"
652 "OpBranch %5\n"
653
654 "%5 = OpLabel\n"
655 "%7 = OpPhi %6 %8 %4 %9000 %5\n" // %9 -> %9000
656 "%11 = OpPhi %10 %12 %4 %13 %5\n"
657 "%9 = OpIAdd %6 %7 %14\n"
658 "%13 = OpFAdd %10 %9 %15\n" // %11 -> %9
659 "%17 = OpSLessThan %16 %7 %18\n"
660 "OpLoopMerge %19 %5 None\n"
661 "OpBranchConditional %17 %5 %19\n"
662
663 "%19 = OpLabel\n"
664 "OpReturn\n"
665 "OpFunctionEnd",
666 {
667 { // defs.
668 {2, "%2 = OpFunction %1 None %3"},
669 {4, "%4 = OpLabel"},
670 {5, "%5 = OpLabel"},
671 {7, "%7 = OpPhi %6 %8 %4 %9000 %5"},
672 {9, "%9 = OpIAdd %6 %7 %14"},
673 {11, "%11 = OpPhi %10 %12 %4 %13 %5"},
674 {13, "%13 = OpFAdd %10 %9 %15"},
675 {17, "%17 = OpSLessThan %16 %7 %18"},
676 {19, "%19 = OpLabel"},
677 },
678 { // uses
679 {1, {"%2 = OpFunction %1 None %3"}},
680 {3, {"%2 = OpFunction %1 None %3"}},
681 {4,
682 {
683 "%7 = OpPhi %6 %8 %4 %9000 %5",
684 "%11 = OpPhi %10 %12 %4 %13 %5",
685 }
686 },
687 {5,
688 {
689 "OpBranch %5",
690 "%7 = OpPhi %6 %8 %4 %9000 %5",
691 "%11 = OpPhi %10 %12 %4 %13 %5",
692 "OpLoopMerge %19 %5 None",
693 "OpBranchConditional %17 %5 %19",
694 }
695 },
696 {6,
697 {
698 "%7 = OpPhi %6 %8 %4 %9000 %5",
699 "%9 = OpIAdd %6 %7 %14",
700 }
701 },
702 {7,
703 {
704 "%9 = OpIAdd %6 %7 %14",
705 "%17 = OpSLessThan %16 %7 %18",
706 }
707 },
708 {8, {"%7 = OpPhi %6 %8 %4 %9000 %5"}},
709 {9, {"%13 = OpFAdd %10 %9 %15"}}, // uses of %9 changed from %7 to %13
710 {10,
711 {
712 "%11 = OpPhi %10 %12 %4 %13 %5",
713 "%13 = OpFAdd %10 %9 %15",
714 }
715 },
716 // no more uses of %11
717 {12, {"%11 = OpPhi %10 %12 %4 %13 %5"}},
718 {13, {"%11 = OpPhi %10 %12 %4 %13 %5"}},
719 {14, {"%9 = OpIAdd %6 %7 %14"}},
720 {15, {"%13 = OpFAdd %10 %9 %15"}},
721 {16, {"%17 = OpSLessThan %16 %7 %18"}},
722 {17, {"OpBranchConditional %17 %5 %19"}},
723 {18, {"%17 = OpSLessThan %16 %7 %18"}},
724 {19,
725 {
726 "OpLoopMerge %19 %5 None",
727 "OpBranchConditional %17 %5 %19",
728 }
729 },
730 // new uses of %9000
731 {9000, {"%7 = OpPhi %6 %8 %4 %9000 %5"}},
732 },
733 },
734 },
735 { // OpPhi defining and referencing the same id.
736 "%1 = OpTypeBool "
737 "%2 = OpConstantTrue %1 "
738
739 "%4 = OpFunction %3 None %5 "
740 "%6 = OpLabel "
741 " OpBranch %7 "
742 "%7 = OpLabel "
743 "%8 = OpPhi %1 %8 %7 %2 %6 " // both defines and uses %8
744 " OpBranch %7 "
745 " OpFunctionEnd",
746 {{8, 2}},
747 "%1 = OpTypeBool\n"
748 "%2 = OpConstantTrue %1\n"
749
750 "%4 = OpFunction %3 None %5\n"
751 "%6 = OpLabel\n"
752 "OpBranch %7\n"
753 "%7 = OpLabel\n"
754 "%8 = OpPhi %1 %2 %7 %2 %6\n" // use of %8 changed to %2
755 "OpBranch %7\n"
756 "OpFunctionEnd",
757 {
758 { // defs
759 {1, "%1 = OpTypeBool"},
760 {2, "%2 = OpConstantTrue %1"},
761 {4, "%4 = OpFunction %3 None %5"},
762 {6, "%6 = OpLabel"},
763 {7, "%7 = OpLabel"},
764 {8, "%8 = OpPhi %1 %2 %7 %2 %6"},
765 },
766 { // uses
767 {1,
768 {
769 "%2 = OpConstantTrue %1",
770 "%8 = OpPhi %1 %2 %7 %2 %6",
771 }
772 },
773 {2,
774 {
775 // TODO(antiagainst): address this.
776 // We have duplication here because we didn't check existence
777 // before inserting uses.
778 "%8 = OpPhi %1 %2 %7 %2 %6",
779 "%8 = OpPhi %1 %2 %7 %2 %6",
780 }
781 },
782 {3, {"%4 = OpFunction %3 None %5"}},
783 {5, {"%4 = OpFunction %3 None %5"}},
784 {6, {"%8 = OpPhi %1 %2 %7 %2 %6"}},
785 {7,
786 {
787 "OpBranch %7",
788 "%8 = OpPhi %1 %2 %7 %2 %6",
789 "OpBranch %7",
790 }
791 },
792 // {8, {"%8 = OpPhi %1 %8 %7 %2 %6"}},
793 },
794 },
795 },
796 })
797 );
798 // clang-format on
799
800 struct KillDefCase {
801 const char* before;
802 std::vector<uint32_t> ids_to_kill;
803 const char* after;
804 InstDefUse du;
805 };
806
807 using KillDefTest = ::testing::TestWithParam<KillDefCase>;
808
TEST_P(KillDefTest,Case)809 TEST_P(KillDefTest, Case) {
810 const auto& tc = GetParam();
811
812 // Build module.
813 const std::vector<const char*> text = {tc.before};
814 std::unique_ptr<ir::Module> module =
815 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text));
816 ASSERT_NE(nullptr, module);
817
818 // Analyze def and use.
819 opt::analysis::DefUseManager manager(nullptr, module.get());
820
821 // Do the substitution.
822 for (const auto id : tc.ids_to_kill) manager.KillDef(id);
823
824 EXPECT_EQ(tc.after, DisassembleModule(module.get()));
825 CheckDef(tc.du, manager.id_to_defs());
826 CheckUse(tc.du, manager.id_to_uses());
827 }
828
829 // clang-format off
830 INSTANTIATE_TEST_CASE_P(
831 TestCase, KillDefTest,
832 ::testing::ValuesIn(std::vector<KillDefCase>{
833 { // no def, no use, no kill
834 "", {}, "", {}
835 },
836 { // kill nothing
837 "%1 = OpTypeBool "
838 "%2 = OpTypeVector %1 2 "
839 "%3 = OpTypeVector %1 3 ",
840 {},
841 "%1 = OpTypeBool\n"
842 "%2 = OpTypeVector %1 2\n"
843 "%3 = OpTypeVector %1 3",
844 {
845 { // defs
846 {1, "%1 = OpTypeBool"},
847 {2, "%2 = OpTypeVector %1 2"},
848 {3, "%3 = OpTypeVector %1 3"},
849 },
850 { // uses
851 {1,
852 {
853 "%2 = OpTypeVector %1 2",
854 "%3 = OpTypeVector %1 3",
855 }
856 },
857 },
858 },
859 },
860 { // kill id used, kill id not used, kill id not defined
861 "%1 = OpTypeBool "
862 "%2 = OpTypeVector %1 2 "
863 "%3 = OpTypeVector %1 3 "
864 "%4 = OpTypeVector %1 4 "
865 "%5 = OpTypeMatrix %3 3 "
866 "%6 = OpTypeMatrix %2 3",
867 {1, 3, 5, 10}, // ids to kill
868 "OpNop\n"
869 "%2 = OpTypeVector %1 2\n"
870 "OpNop\n"
871 "%4 = OpTypeVector %1 4\n"
872 "OpNop\n"
873 "%6 = OpTypeMatrix %2 3",
874 {
875 { // defs
876 {2, "%2 = OpTypeVector %1 2"},
877 {4, "%4 = OpTypeVector %1 4"},
878 {6, "%6 = OpTypeMatrix %2 3"},
879 },
880 { // uses. %1 and %3 are both killed, so no uses
881 // recorded for them anymore.
882 {2, {"%6 = OpTypeMatrix %2 3"}},
883 }
884 },
885 },
886 { // OpPhi.
887 kOpPhiTestFunction,
888 {9, 11}, // kill one id used by OpPhi, kill one id generated by OpPhi
889 "%2 = OpFunction %1 None %3\n"
890 "%4 = OpLabel\n"
891 "OpBranch %5\n"
892
893 "%5 = OpLabel\n"
894 "%7 = OpPhi %6 %8 %4 %9 %5\n"
895 "OpNop\n"
896 "OpNop\n"
897 "%13 = OpFAdd %10 %11 %15\n"
898 "%17 = OpSLessThan %16 %7 %18\n"
899 "OpLoopMerge %19 %5 None\n"
900 "OpBranchConditional %17 %5 %19\n"
901
902 "%19 = OpLabel\n"
903 "OpReturn\n"
904 "OpFunctionEnd",
905 {
906 { // defs. %9 & %11 are killed.
907 {2, "%2 = OpFunction %1 None %3"},
908 {4, "%4 = OpLabel"},
909 {5, "%5 = OpLabel"},
910 {7, "%7 = OpPhi %6 %8 %4 %9 %5"},
911 {13, "%13 = OpFAdd %10 %11 %15"},
912 {17, "%17 = OpSLessThan %16 %7 %18"},
913 {19, "%19 = OpLabel"},
914 },
915 { // uses
916 {1, {"%2 = OpFunction %1 None %3"}},
917 {3, {"%2 = OpFunction %1 None %3"}},
918 {4,
919 {
920 "%7 = OpPhi %6 %8 %4 %9 %5",
921 // "%11 = OpPhi %10 %12 %4 %13 %5",
922 }
923 },
924 {5,
925 {
926 "OpBranch %5",
927 "%7 = OpPhi %6 %8 %4 %9 %5",
928 // "%11 = OpPhi %10 %12 %4 %13 %5",
929 "OpLoopMerge %19 %5 None",
930 "OpBranchConditional %17 %5 %19",
931 }
932 },
933 {6,
934 {
935 "%7 = OpPhi %6 %8 %4 %9 %5",
936 // "%9 = OpIAdd %6 %7 %14",
937 }
938 },
939 {7,
940 {
941 // "%9 = OpIAdd %6 %7 %14",
942 "%17 = OpSLessThan %16 %7 %18",
943 }
944 },
945 {8, {"%7 = OpPhi %6 %8 %4 %9 %5"}},
946 // {9, {"%7 = OpPhi %6 %8 %4 %9 %5"}},
947 {10,
948 {
949 // "%11 = OpPhi %10 %12 %4 %13 %5",
950 "%13 = OpFAdd %10 %11 %15",
951 }
952 },
953 // {11, {"%13 = OpFAdd %10 %11 %15"}},
954 // {12, {"%11 = OpPhi %10 %12 %4 %13 %5"}},
955 // {13, {"%11 = OpPhi %10 %12 %4 %13 %5"}},
956 // {14, {"%9 = OpIAdd %6 %7 %14"}},
957 {15, {"%13 = OpFAdd %10 %11 %15"}},
958 {16, {"%17 = OpSLessThan %16 %7 %18"}},
959 {17, {"OpBranchConditional %17 %5 %19"}},
960 {18, {"%17 = OpSLessThan %16 %7 %18"}},
961 {19,
962 {
963 "OpLoopMerge %19 %5 None",
964 "OpBranchConditional %17 %5 %19",
965 }
966 },
967 },
968 },
969 },
970 { // OpPhi defining and referencing the same id.
971 "%1 = OpTypeBool "
972 "%2 = OpConstantTrue %1 "
973
974 "%4 = OpFunction %3 None %5 "
975 "%6 = OpLabel "
976 " OpBranch %7 "
977 "%7 = OpLabel "
978 "%8 = OpPhi %1 %8 %7 %2 %6 " // both defines and uses %8
979 " OpBranch %7 "
980 " OpFunctionEnd",
981 {8},
982 "%1 = OpTypeBool\n"
983 "%2 = OpConstantTrue %1\n"
984
985 "%4 = OpFunction %3 None %5\n"
986 "%6 = OpLabel\n"
987 "OpBranch %7\n"
988 "%7 = OpLabel\n"
989 "OpNop\n"
990 "OpBranch %7\n"
991 "OpFunctionEnd",
992 {
993 { // defs
994 {1, "%1 = OpTypeBool"},
995 {2, "%2 = OpConstantTrue %1"},
996 {4, "%4 = OpFunction %3 None %5"},
997 {6, "%6 = OpLabel"},
998 {7, "%7 = OpLabel"},
999 // {8, "%8 = OpPhi %1 %8 %7 %2 %6"},
1000 },
1001 { // uses
1002 {1,
1003 {
1004 "%2 = OpConstantTrue %1",
1005 // "%8 = OpPhi %1 %8 %7 %2 %6",
1006 }
1007 },
1008 // {2, {"%8 = OpPhi %1 %8 %7 %2 %6"}},
1009 {3, {"%4 = OpFunction %3 None %5"}},
1010 {5, {"%4 = OpFunction %3 None %5"}},
1011 // {6, {"%8 = OpPhi %1 %8 %7 %2 %6"}},
1012 {7,
1013 {
1014 "OpBranch %7",
1015 // "%8 = OpPhi %1 %8 %7 %2 %6",
1016 "OpBranch %7",
1017 }
1018 },
1019 // {8, {"%8 = OpPhi %1 %8 %7 %2 %6"}},
1020 },
1021 },
1022 },
1023 })
1024 );
1025 // clang-format on
1026 //
TEST(DefUseTest,OpSwitch)1027 TEST(DefUseTest, OpSwitch) {
1028 // Because disassembler has basic type check for OpSwitch's selector, we
1029 // cannot use the DisassembleInst() in the above. Thus, this special spotcheck
1030 // test case.
1031
1032 const char original_text[] =
1033 // int64 f(int64 v) {
1034 // switch (v) {
1035 // case 1: break;
1036 // case -4294967296: break;
1037 // case 9223372036854775807: break;
1038 // default: break;
1039 // }
1040 // return v;
1041 // }
1042 " %1 = OpTypeInt 64 1 "
1043 " %2 = OpFunction %1 None %3 " // %3 is int64(int64)*
1044 " %4 = OpFunctionParameter %1 "
1045 " %5 = OpLabel "
1046 " %6 = OpLoad %1 %4 " // selector value
1047 " OpSelectionMerge %7 None "
1048 " OpSwitch %6 %8 "
1049 " 1 %9 " // 1
1050 " -4294967296 %10 " // -2^32
1051 " 9223372036854775807 %11 " // 2^63-1
1052 " %8 = OpLabel " // default
1053 " OpBranch %7 "
1054 " %9 = OpLabel "
1055 " OpBranch %7 "
1056 "%10 = OpLabel "
1057 " OpBranch %7 "
1058 "%11 = OpLabel "
1059 " OpBranch %7 "
1060 " %7 = OpLabel "
1061 " OpReturnValue %6 "
1062 " OpFunctionEnd";
1063
1064 std::unique_ptr<ir::Module> module =
1065 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, original_text);
1066 ASSERT_NE(nullptr, module);
1067
1068 // Analyze def and use.
1069 opt::analysis::DefUseManager manager(nullptr, module.get());
1070
1071 // Do a bunch replacements.
1072 manager.ReplaceAllUsesWith(9, 900); // to unused id
1073 manager.ReplaceAllUsesWith(10, 1000); // to unused id
1074 manager.ReplaceAllUsesWith(11, 7); // to existing id
1075
1076 // clang-format off
1077 const char modified_text[] =
1078 "%1 = OpTypeInt 64 1\n"
1079 "%2 = OpFunction %1 None %3\n" // %3 is int64(int64)*
1080 "%4 = OpFunctionParameter %1\n"
1081 "%5 = OpLabel\n"
1082 "%6 = OpLoad %1 %4\n" // selector value
1083 "OpSelectionMerge %7 None\n"
1084 "OpSwitch %6 %8 1 %900 -4294967296 %1000 9223372036854775807 %7\n" // changed!
1085 "%8 = OpLabel\n" // default
1086 "OpBranch %7\n"
1087 "%9 = OpLabel\n"
1088 "OpBranch %7\n"
1089 "%10 = OpLabel\n"
1090 "OpBranch %7\n"
1091 "%11 = OpLabel\n"
1092 "OpBranch %7\n"
1093 "%7 = OpLabel\n"
1094 "OpReturnValue %6\n"
1095 "OpFunctionEnd";
1096 // clang-format on
1097
1098 EXPECT_EQ(modified_text, DisassembleModule(module.get()));
1099
1100 InstDefUse def_uses = {};
1101 def_uses.defs = {
1102 {1, "%1 = OpTypeInt 64 1"},
1103 {2, "%2 = OpFunction %1 None %3"},
1104 {4, "%4 = OpFunctionParameter %1"},
1105 {5, "%5 = OpLabel"},
1106 {6, "%6 = OpLoad %1 %4"},
1107 {7, "%7 = OpLabel"},
1108 {8, "%8 = OpLabel"},
1109 {9, "%9 = OpLabel"},
1110 {10, "%10 = OpLabel"},
1111 {11, "%11 = OpLabel"},
1112 };
1113 CheckDef(def_uses, manager.id_to_defs());
1114
1115 {
1116 auto* use_list = manager.GetUses(6);
1117 ASSERT_NE(nullptr, use_list);
1118 EXPECT_EQ(2u, use_list->size());
1119 EXPECT_EQ(SpvOpSwitch, use_list->front().inst->opcode());
1120 EXPECT_EQ(SpvOpReturnValue, use_list->back().inst->opcode());
1121 }
1122 {
1123 auto* use_list = manager.GetUses(7);
1124 ASSERT_NE(nullptr, use_list);
1125 EXPECT_EQ(6u, use_list->size());
1126 std::vector<SpvOp> opcodes;
1127 for (const auto& use : *use_list) {
1128 opcodes.push_back(use.inst->opcode());
1129 }
1130 // OpSwitch is now a user of %7.
1131 EXPECT_THAT(opcodes,
1132 ElementsAre(SpvOpSelectionMerge, SpvOpBranch, SpvOpBranch,
1133 SpvOpBranch, SpvOpBranch, SpvOpSwitch));
1134 }
1135 // Check all ids only used by OpSwitch after replacement.
1136 for (const auto id : {8, 900, 1000}) {
1137 auto* use_list = manager.GetUses(id);
1138 ASSERT_NE(nullptr, use_list);
1139 EXPECT_EQ(1u, use_list->size());
1140 EXPECT_EQ(SpvOpSwitch, use_list->front().inst->opcode());
1141 }
1142 }
1143
1144 // Creates an |result_id| = OpTypeInt 32 1 instruction.
Int32TypeInstruction(uint32_t result_id)1145 ir::Instruction Int32TypeInstruction(uint32_t result_id) {
1146 return ir::Instruction(SpvOp::SpvOpTypeInt, 0, result_id,
1147 {ir::Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {32}),
1148 ir::Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {1})});
1149 }
1150
1151 // Creates an |result_id| = OpConstantTrue/Flase |type_id| instruction.
ConstantBoolInstruction(bool value,uint32_t type_id,uint32_t result_id)1152 ir::Instruction ConstantBoolInstruction(bool value, uint32_t type_id,
1153 uint32_t result_id) {
1154 return ir::Instruction(
1155 value ? SpvOp::SpvOpConstantTrue : SpvOp::SpvOpConstantFalse, type_id,
1156 result_id, {});
1157 }
1158
1159 // Creates an |result_id| = OpLabel instruction.
LabelInstruction(uint32_t result_id)1160 ir::Instruction LabelInstruction(uint32_t result_id) {
1161 return ir::Instruction(SpvOp::SpvOpLabel, 0, result_id, {});
1162 }
1163
1164 // Creates an OpBranch |target_id| instruction.
BranchInstruction(uint32_t target_id)1165 ir::Instruction BranchInstruction(uint32_t target_id) {
1166 return ir::Instruction(SpvOp::SpvOpBranch, 0, 0,
1167 {
1168 ir::Operand(SPV_OPERAND_TYPE_ID, {target_id}),
1169 });
1170 }
1171
1172 // Test case for analyzing individual instructions.
1173 struct AnalyzeInstDefUseTestCase {
1174 std::vector<ir::Instruction> insts; // instrutions to be analyzed in order.
1175 const char* module_text;
1176 InstDefUse expected_define_use;
1177 };
1178
1179 using AnalyzeInstDefUseTest =
1180 ::testing::TestWithParam<AnalyzeInstDefUseTestCase>;
1181
1182 // Test the analyzing result for individual instructions.
TEST_P(AnalyzeInstDefUseTest,Case)1183 TEST_P(AnalyzeInstDefUseTest, Case) {
1184 auto tc = GetParam();
1185
1186 // Build module.
1187 std::unique_ptr<ir::Module> module =
1188 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.module_text);
1189 ASSERT_NE(nullptr, module);
1190
1191 // Analyze the instructions.
1192 opt::analysis::DefUseManager manager(nullptr, module.get());
1193 for (ir::Instruction& inst : tc.insts) {
1194 manager.AnalyzeInstDefUse(&inst);
1195 }
1196
1197 CheckDef(tc.expected_define_use, manager.id_to_defs());
1198 CheckUse(tc.expected_define_use, manager.id_to_uses());
1199 }
1200
1201 // clang-format off
1202 INSTANTIATE_TEST_CASE_P(
1203 TestCase, AnalyzeInstDefUseTest,
1204 ::testing::ValuesIn(std::vector<AnalyzeInstDefUseTestCase>{
1205 { // A type declaring instruction.
1206 {Int32TypeInstruction(1)},
1207 "",
1208 {
1209 // defs
1210 {{1, "%1 = OpTypeInt 32 1"}},
1211 {}, // no uses
1212 },
1213 },
1214 { // A type declaring instruction and a constant value.
1215 {
1216 Int32TypeInstruction(1),
1217 ConstantBoolInstruction(true, 1, 2),
1218 },
1219 "",
1220 {
1221 { // defs
1222 {1, "%1 = OpTypeInt 32 1"},
1223 {2, "%2 = OpConstantTrue %1"}, // It is fine the SPIR-V code here is invalid.
1224 },
1225 { // uses
1226 {1, {"%2 = OpConstantTrue %1"}},
1227 },
1228 },
1229 },
1230 { // Analyze two instrutions that have same result id. The def use info
1231 // of the result id from the first instruction should be overwritten by
1232 // the second instruction.
1233 {
1234 ConstantBoolInstruction(true, 1, 2),
1235 // The def-use info of the following instruction should overwrite the
1236 // records of the above one.
1237 ConstantBoolInstruction(false, 3, 2),
1238 },
1239 "",
1240 {
1241 // defs
1242 {{2, "%2 = OpConstantFalse %3"}},
1243 // uses
1244 {{3, {"%2 = OpConstantFalse %3"}}}
1245 }
1246 },
1247 { // Analyze forward reference instruction, also instruction that does
1248 // not have result id.
1249 {
1250 BranchInstruction(2),
1251 LabelInstruction(2),
1252 },
1253 "",
1254 {
1255 // defs
1256 {{2, "%2 = OpLabel"}},
1257 // uses
1258 {{2, {"OpBranch %2"}}},
1259 }
1260 },
1261 { // Analyzing an additional instruction with new result id to an
1262 // existing module.
1263 {
1264 ConstantBoolInstruction(true, 1, 2),
1265 },
1266 "%1 = OpTypeInt 32 1 ",
1267 {
1268 { // defs
1269 {1, "%1 = OpTypeInt 32 1"},
1270 {2, "%2 = OpConstantTrue %1"},
1271 },
1272 { // uses
1273 {1, {"%2 = OpConstantTrue %1"}},
1274 },
1275 }
1276 },
1277 { // Analyzing an additional instruction with existing result id to an
1278 // existing module.
1279 {
1280 ConstantBoolInstruction(true, 1, 2),
1281 },
1282 "%1 = OpTypeInt 32 1 "
1283 "%2 = OpTypeBool ",
1284 {
1285 { // defs
1286 {1, "%1 = OpTypeInt 32 1"},
1287 {2, "%2 = OpConstantTrue %1"},
1288 },
1289 { // uses
1290 {1, {"%2 = OpConstantTrue %1"}},
1291 },
1292 }
1293 },
1294 }));
1295 // clang-format on
1296
1297 struct KillInstTestCase {
1298 const char* before;
1299 std::unordered_set<uint32_t> indices_for_inst_to_kill;
1300 const char* after;
1301 InstDefUse expected_define_use;
1302 };
1303
1304 using KillInstTest = ::testing::TestWithParam<KillInstTestCase>;
1305
TEST_P(KillInstTest,Case)1306 TEST_P(KillInstTest, Case) {
1307 auto tc = GetParam();
1308
1309 // Build module.
1310 std::unique_ptr<ir::Module> module =
1311 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.before);
1312 ASSERT_NE(nullptr, module);
1313
1314 // KillInst
1315 uint32_t index = 0;
1316 opt::analysis::DefUseManager manager(nullptr, module.get());
1317 module->ForEachInst([&index, &tc, &manager](ir::Instruction* inst) {
1318 if (tc.indices_for_inst_to_kill.count(index) != 0) {
1319 manager.KillInst(inst);
1320 }
1321 index++;
1322 });
1323
1324 EXPECT_EQ(tc.after, DisassembleModule(module.get()));
1325 CheckDef(tc.expected_define_use, manager.id_to_defs());
1326 CheckUse(tc.expected_define_use, manager.id_to_uses());
1327 }
1328
1329 // clang-format off
1330 INSTANTIATE_TEST_CASE_P(
1331 TestCase, KillInstTest,
1332 ::testing::ValuesIn(std::vector<KillInstTestCase>{
1333 // Kill id defining instructions.
1334 {
1335 "%2 = OpFunction %1 None %3 "
1336 "%4 = OpLabel "
1337 " OpBranch %5 "
1338 "%5 = OpLabel "
1339 " OpBranch %6 "
1340 "%6 = OpLabel "
1341 " OpBranch %4 "
1342 "%7 = OpLabel "
1343 " OpReturn "
1344 " OpFunctionEnd",
1345 {0, 3, 5, 7},
1346 "OpNop\n"
1347 "%4 = OpLabel\n"
1348 "OpBranch %5\n"
1349 "OpNop\n"
1350 "OpBranch %6\n"
1351 "OpNop\n"
1352 "OpBranch %4\n"
1353 "OpNop\n"
1354 "OpReturn\n"
1355 "OpFunctionEnd",
1356 {
1357 // defs
1358 {{4, "%4 = OpLabel"}},
1359 // uses
1360 {{4, {"OpBranch %4"}}}
1361 }
1362 },
1363 // Kill instructions that do not have result ids.
1364 {
1365 "%2 = OpFunction %1 None %3 "
1366 "%4 = OpLabel "
1367 " OpBranch %5 "
1368 "%5 = OpLabel "
1369 " OpBranch %6 "
1370 "%6 = OpLabel "
1371 " OpBranch %4 "
1372 "%7 = OpLabel "
1373 " OpReturn "
1374 " OpFunctionEnd",
1375 {2, 4},
1376 "%2 = OpFunction %1 None %3\n"
1377 "%4 = OpLabel\n"
1378 "OpNop\n"
1379 "%5 = OpLabel\n"
1380 "OpNop\n"
1381 "%6 = OpLabel\n"
1382 "OpBranch %4\n"
1383 "%7 = OpLabel\n"
1384 "OpReturn\n"
1385 "OpFunctionEnd",
1386 {
1387 // defs
1388 {
1389 {2, "%2 = OpFunction %1 None %3"},
1390 {4, "%4 = OpLabel"},
1391 {5, "%5 = OpLabel"},
1392 {6, "%6 = OpLabel"},
1393 {7, "%7 = OpLabel"},
1394 },
1395 // uses
1396 {
1397 {1, {"%2 = OpFunction %1 None %3"}},
1398 {3, {"%2 = OpFunction %1 None %3"}},
1399 {4, {"OpBranch %4"}},
1400 }
1401 }
1402 },
1403 }));
1404 // clang-format on
1405
1406 struct GetAnnotationsTestCase {
1407 const char* code;
1408 uint32_t id;
1409 std::vector<std::string> annotations;
1410 };
1411
1412 using GetAnnotationsTest = ::testing::TestWithParam<GetAnnotationsTestCase>;
1413
TEST_P(GetAnnotationsTest,Case)1414 TEST_P(GetAnnotationsTest, Case) {
1415 const GetAnnotationsTestCase& tc = GetParam();
1416
1417 // Build module.
1418 std::unique_ptr<ir::Module> module =
1419 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.code);
1420 ASSERT_NE(nullptr, module);
1421
1422 // Get annotations
1423 opt::analysis::DefUseManager manager(nullptr, module.get());
1424 auto insts = manager.GetAnnotations(tc.id);
1425
1426 // Check
1427 ASSERT_EQ(tc.annotations.size(), insts.size())
1428 << "wrong number of annotation instructions";
1429 auto inst_iter = insts.begin();
1430 for (const std::string& expected_anno_inst : tc.annotations) {
1431 EXPECT_EQ(expected_anno_inst, DisassembleInst(*inst_iter))
1432 << "annotation instruction mismatch";
1433 inst_iter++;
1434 }
1435 }
1436
1437 // clang-format off
1438 INSTANTIATE_TEST_CASE_P(
1439 TestCase, GetAnnotationsTest,
1440 ::testing::ValuesIn(std::vector<GetAnnotationsTestCase>{
1441 // empty
1442 {"", 0, {}},
1443 // basic
1444 {
1445 // code
1446 "OpDecorate %1 Block "
1447 "OpDecorate %1 RelaxedPrecision "
1448 "%3 = OpTypeInt 32 0 "
1449 "%1 = OpTypeStruct %3",
1450 // id
1451 1,
1452 // annotations
1453 {
1454 "OpDecorate %1 Block",
1455 "OpDecorate %1 RelaxedPrecision",
1456 },
1457 },
1458 // with debug instructions
1459 {
1460 // code
1461 "OpName %1 \"struct_type\" "
1462 "OpName %3 \"int_type\" "
1463 "OpDecorate %1 Block "
1464 "OpDecorate %1 RelaxedPrecision "
1465 "%3 = OpTypeInt 32 0 "
1466 "%1 = OpTypeStruct %3",
1467 // id
1468 1,
1469 // annotations
1470 {
1471 "OpDecorate %1 Block",
1472 "OpDecorate %1 RelaxedPrecision",
1473 },
1474 },
1475 // no annotations
1476 {
1477 // code
1478 "OpName %1 \"struct_type\" "
1479 "OpName %3 \"int_type\" "
1480 "OpDecorate %1 Block "
1481 "OpDecorate %1 RelaxedPrecision "
1482 "%3 = OpTypeInt 32 0 "
1483 "%1 = OpTypeStruct %3",
1484 // id
1485 3,
1486 // annotations
1487 {},
1488 },
1489 // decoration group
1490 {
1491 // code
1492 "OpDecorate %1 Block "
1493 "OpDecorate %1 RelaxedPrecision "
1494 "%1 = OpDecorationGroup "
1495 "OpGroupDecorate %1 %2 %3 "
1496 "%4 = OpTypeInt 32 0 "
1497 "%2 = OpTypeStruct %4 "
1498 "%3 = OpTypeStruct %4 %4",
1499 // id
1500 3,
1501 // annotations
1502 {
1503 "OpGroupDecorate %1 %2 %3",
1504 },
1505 },
1506 // memeber decorate
1507 {
1508 // code
1509 "OpMemberDecorate %1 0 RelaxedPrecision "
1510 "%2 = OpTypeInt 32 0 "
1511 "%1 = OpTypeStruct %2 %2",
1512 // id
1513 1,
1514 // annotations
1515 {
1516 "OpMemberDecorate %1 0 RelaxedPrecision",
1517 },
1518 },
1519 }));
1520 // clang-format on
1521 } // anonymous namespace
1522