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