• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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