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<SpvOp> GetUseOpcodes(const std::unique_ptr<IRContext>& context,
54 uint32_t id) {
55 std::vector<SpvOp> 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() != SpvOpConstant) {
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() != SpvOpConstant) {
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_CASE_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_CASE_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_CASE_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<SpvOp> opcodes = GetUseOpcodes(context, 6u);
1302 EXPECT_THAT(opcodes, UnorderedElementsAre(SpvOpSwitch, SpvOpReturnValue));
1303 }
1304 {
1305 EXPECT_EQ(6u, NumUses(context, 7));
1306 std::vector<SpvOp> opcodes = GetUseOpcodes(context, 7u);
1307 // OpSwitch is now a user of %7.
1308 EXPECT_THAT(opcodes, UnorderedElementsAre(SpvOpSelectionMerge, SpvOpBranch,
1309 SpvOpBranch, SpvOpBranch,
1310 SpvOpBranch, SpvOpSwitch));
1311 }
1312 // Check all ids only used by OpSwitch after replacement.
1313 for (const auto id : {8u, 10u, 11u}) {
1314 EXPECT_EQ(1u, NumUses(context, id));
1315 EXPECT_EQ(SpvOpSwitch, GetUseOpcodes(context, id).back());
1316 }
1317 }
1318
1319 // Test case for analyzing individual instructions.
1320 struct AnalyzeInstDefUseTestCase {
1321 const char* module_text;
1322 InstDefUse expected_define_use;
1323 };
1324
1325 using AnalyzeInstDefUseTest =
1326 ::testing::TestWithParam<AnalyzeInstDefUseTestCase>;
1327
1328 // Test the analyzing result for individual instructions.
TEST_P(AnalyzeInstDefUseTest,Case)1329 TEST_P(AnalyzeInstDefUseTest, Case) {
1330 auto tc = GetParam();
1331
1332 // Build module.
1333 std::unique_ptr<IRContext> context =
1334 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.module_text);
1335 ASSERT_NE(nullptr, context);
1336
1337 // Analyze the instructions.
1338 DefUseManager manager(context->module());
1339
1340 CheckDef(tc.expected_define_use, manager.id_to_defs());
1341 CheckUse(tc.expected_define_use, &manager, context->module()->IdBound());
1342 // CheckUse(tc.expected_define_use, manager.id_to_uses());
1343 }
1344
1345 // clang-format off
1346 INSTANTIATE_TEST_CASE_P(
1347 TestCase, AnalyzeInstDefUseTest,
1348 ::testing::ValuesIn(std::vector<AnalyzeInstDefUseTestCase>{
1349 { // A type declaring instruction.
1350 "%1 = OpTypeInt 32 1",
1351 {
1352 // defs
1353 {{1, "%1 = OpTypeInt 32 1"}},
1354 {}, // no uses
1355 },
1356 },
1357 { // A type declaring instruction and a constant value.
1358 "%1 = OpTypeBool "
1359 "%2 = OpConstantTrue %1",
1360 {
1361 { // defs
1362 {1, "%1 = OpTypeBool"},
1363 {2, "%2 = OpConstantTrue %1"},
1364 },
1365 { // uses
1366 {1, {"%2 = OpConstantTrue %1"}},
1367 },
1368 },
1369 },
1370 }));
1371 // clang-format on
1372
1373 using AnalyzeInstDefUse = ::testing::Test;
1374
TEST(AnalyzeInstDefUse,UseWithNoResultId)1375 TEST(AnalyzeInstDefUse, UseWithNoResultId) {
1376 IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr);
1377
1378 // Analyze the instructions.
1379 DefUseManager manager(context.module());
1380
1381 Instruction label(&context, SpvOpLabel, 0, 2, {});
1382 manager.AnalyzeInstDefUse(&label);
1383
1384 Instruction branch(&context, SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {2}}});
1385 manager.AnalyzeInstDefUse(&branch);
1386 context.module()->SetIdBound(3);
1387
1388 InstDefUse expected = {
1389 // defs
1390 {
1391 {2, "%2 = OpLabel"},
1392 },
1393 // uses
1394 {{2, {"OpBranch %2"}}},
1395 };
1396
1397 CheckDef(expected, manager.id_to_defs());
1398 CheckUse(expected, &manager, context.module()->IdBound());
1399 }
1400
TEST(AnalyzeInstDefUse,AddNewInstruction)1401 TEST(AnalyzeInstDefUse, AddNewInstruction) {
1402 const std::string input = "%1 = OpTypeBool";
1403
1404 // Build module.
1405 std::unique_ptr<IRContext> context =
1406 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input);
1407 ASSERT_NE(nullptr, context);
1408
1409 // Analyze the instructions.
1410 DefUseManager manager(context->module());
1411
1412 Instruction newInst(context.get(), SpvOpConstantTrue, 1, 2, {});
1413 manager.AnalyzeInstDefUse(&newInst);
1414
1415 InstDefUse expected = {
1416 {
1417 // defs
1418 {1, "%1 = OpTypeBool"},
1419 {2, "%2 = OpConstantTrue %1"},
1420 },
1421 {
1422 // uses
1423 {1, {"%2 = OpConstantTrue %1"}},
1424 },
1425 };
1426
1427 CheckDef(expected, manager.id_to_defs());
1428 CheckUse(expected, &manager, context->module()->IdBound());
1429 }
1430
1431 struct KillInstTestCase {
1432 const char* before;
1433 std::unordered_set<uint32_t> indices_for_inst_to_kill;
1434 const char* after;
1435 InstDefUse expected_define_use;
1436 };
1437
1438 using KillInstTest = ::testing::TestWithParam<KillInstTestCase>;
1439
TEST_P(KillInstTest,Case)1440 TEST_P(KillInstTest, Case) {
1441 auto tc = GetParam();
1442
1443 // Build module.
1444 std::unique_ptr<IRContext> context =
1445 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.before,
1446 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1447 ASSERT_NE(nullptr, context);
1448
1449 // Force a re-build of the def-use manager.
1450 context->InvalidateAnalyses(IRContext::Analysis::kAnalysisDefUse);
1451 (void)context->get_def_use_mgr();
1452
1453 // KillInst
1454 context->module()->ForEachInst([&tc, &context](Instruction* inst) {
1455 if (tc.indices_for_inst_to_kill.count(inst->result_id())) {
1456 context->KillInst(inst);
1457 }
1458 });
1459
1460 EXPECT_EQ(tc.after, DisassembleModule(context->module()));
1461 CheckDef(tc.expected_define_use, context->get_def_use_mgr()->id_to_defs());
1462 CheckUse(tc.expected_define_use, context->get_def_use_mgr(),
1463 context->module()->IdBound());
1464 }
1465
1466 // clang-format off
1467 INSTANTIATE_TEST_CASE_P(
1468 TestCase, KillInstTest,
1469 ::testing::ValuesIn(std::vector<KillInstTestCase>{
1470 // Kill id defining instructions.
1471 {
1472 "%3 = OpTypeVoid "
1473 "%1 = OpTypeFunction %3 "
1474 "%2 = OpFunction %1 None %3 "
1475 "%4 = OpLabel "
1476 " OpBranch %5 "
1477 "%5 = OpLabel "
1478 " OpBranch %6 "
1479 "%6 = OpLabel "
1480 " OpBranch %4 "
1481 "%7 = OpLabel "
1482 " OpReturn "
1483 " OpFunctionEnd",
1484 {3, 5, 7},
1485 "%1 = OpTypeFunction %3\n"
1486 "%2 = OpFunction %1 None %3\n"
1487 "%4 = OpLabel\n"
1488 "OpBranch %5\n"
1489 "OpNop\n"
1490 "OpBranch %6\n"
1491 "%6 = OpLabel\n"
1492 "OpBranch %4\n"
1493 "OpNop\n"
1494 "OpReturn\n"
1495 "OpFunctionEnd",
1496 {
1497 // defs
1498 {
1499 {1, "%1 = OpTypeFunction %3"},
1500 {2, "%2 = OpFunction %1 None %3"},
1501 {4, "%4 = OpLabel"},
1502 {6, "%6 = OpLabel"},
1503 },
1504 // uses
1505 {
1506 {1, {"%2 = OpFunction %1 None %3"}},
1507 {4, {"OpBranch %4"}},
1508 {6, {"OpBranch %6"}},
1509 }
1510 }
1511 },
1512 // Kill instructions that do not have result ids.
1513 {
1514 "%3 = OpTypeVoid "
1515 "%1 = OpTypeFunction %3 "
1516 "%2 = OpFunction %1 None %3 "
1517 "%4 = OpLabel "
1518 " OpBranch %5 "
1519 "%5 = OpLabel "
1520 " OpBranch %6 "
1521 "%6 = OpLabel "
1522 " OpBranch %4 "
1523 "%7 = OpLabel "
1524 " OpReturn "
1525 " OpFunctionEnd",
1526 {2, 4},
1527 "%3 = OpTypeVoid\n"
1528 "%1 = OpTypeFunction %3\n"
1529 "OpNop\n"
1530 "OpNop\n"
1531 "OpBranch %5\n"
1532 "%5 = OpLabel\n"
1533 "OpBranch %6\n"
1534 "%6 = OpLabel\n"
1535 "OpBranch %4\n"
1536 "%7 = OpLabel\n"
1537 "OpReturn\n"
1538 "OpFunctionEnd",
1539 {
1540 // defs
1541 {
1542 {1, "%1 = OpTypeFunction %3"},
1543 {3, "%3 = OpTypeVoid"},
1544 {5, "%5 = OpLabel"},
1545 {6, "%6 = OpLabel"},
1546 {7, "%7 = OpLabel"},
1547 },
1548 // uses
1549 {
1550 {3, {"%1 = OpTypeFunction %3"}},
1551 {5, {"OpBranch %5"}},
1552 {6, {"OpBranch %6"}},
1553 }
1554 }
1555 },
1556 }));
1557 // clang-format on
1558
1559 struct GetAnnotationsTestCase {
1560 const char* code;
1561 uint32_t id;
1562 std::vector<std::string> annotations;
1563 };
1564
1565 using GetAnnotationsTest = ::testing::TestWithParam<GetAnnotationsTestCase>;
1566
TEST_P(GetAnnotationsTest,Case)1567 TEST_P(GetAnnotationsTest, Case) {
1568 const GetAnnotationsTestCase& tc = GetParam();
1569
1570 // Build module.
1571 std::unique_ptr<IRContext> context =
1572 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.code);
1573 ASSERT_NE(nullptr, context);
1574
1575 // Get annotations
1576 DefUseManager manager(context->module());
1577 auto insts = manager.GetAnnotations(tc.id);
1578
1579 // Check
1580 ASSERT_EQ(tc.annotations.size(), insts.size())
1581 << "wrong number of annotation instructions";
1582 auto inst_iter = insts.begin();
1583 for (const std::string& expected_anno_inst : tc.annotations) {
1584 EXPECT_EQ(expected_anno_inst, DisassembleInst(*inst_iter))
1585 << "annotation instruction mismatch";
1586 inst_iter++;
1587 }
1588 }
1589
1590 // clang-format off
1591 INSTANTIATE_TEST_CASE_P(
1592 TestCase, GetAnnotationsTest,
1593 ::testing::ValuesIn(std::vector<GetAnnotationsTestCase>{
1594 // empty
1595 {"", 0, {}},
1596 // basic
1597 {
1598 // code
1599 "OpDecorate %1 Block "
1600 "OpDecorate %1 RelaxedPrecision "
1601 "%3 = OpTypeInt 32 0 "
1602 "%1 = OpTypeStruct %3",
1603 // id
1604 1,
1605 // annotations
1606 {
1607 "OpDecorate %1 Block",
1608 "OpDecorate %1 RelaxedPrecision",
1609 },
1610 },
1611 // with debug instructions
1612 {
1613 // code
1614 "OpName %1 \"struct_type\" "
1615 "OpName %3 \"int_type\" "
1616 "OpDecorate %1 Block "
1617 "OpDecorate %1 RelaxedPrecision "
1618 "%3 = OpTypeInt 32 0 "
1619 "%1 = OpTypeStruct %3",
1620 // id
1621 1,
1622 // annotations
1623 {
1624 "OpDecorate %1 Block",
1625 "OpDecorate %1 RelaxedPrecision",
1626 },
1627 },
1628 // no annotations
1629 {
1630 // code
1631 "OpName %1 \"struct_type\" "
1632 "OpName %3 \"int_type\" "
1633 "OpDecorate %1 Block "
1634 "OpDecorate %1 RelaxedPrecision "
1635 "%3 = OpTypeInt 32 0 "
1636 "%1 = OpTypeStruct %3",
1637 // id
1638 3,
1639 // annotations
1640 {},
1641 },
1642 // decoration group
1643 {
1644 // code
1645 "OpDecorate %1 Block "
1646 "OpDecorate %1 RelaxedPrecision "
1647 "%1 = OpDecorationGroup "
1648 "OpGroupDecorate %1 %2 %3 "
1649 "%4 = OpTypeInt 32 0 "
1650 "%2 = OpTypeStruct %4 "
1651 "%3 = OpTypeStruct %4 %4",
1652 // id
1653 3,
1654 // annotations
1655 {
1656 "OpGroupDecorate %1 %2 %3",
1657 },
1658 },
1659 // memeber decorate
1660 {
1661 // code
1662 "OpMemberDecorate %1 0 RelaxedPrecision "
1663 "%2 = OpTypeInt 32 0 "
1664 "%1 = OpTypeStruct %2 %2",
1665 // id
1666 1,
1667 // annotations
1668 {
1669 "OpMemberDecorate %1 0 RelaxedPrecision",
1670 },
1671 },
1672 }));
1673
1674 using UpdateUsesTest = PassTest<::testing::Test>;
1675
TEST_F(UpdateUsesTest,KeepOldUses)1676 TEST_F(UpdateUsesTest, KeepOldUses) {
1677 const std::vector<const char*> text = {
1678 // clang-format off
1679 "OpCapability Shader",
1680 "%1 = OpExtInstImport \"GLSL.std.450\"",
1681 "OpMemoryModel Logical GLSL450",
1682 "OpEntryPoint Vertex %main \"main\"",
1683 "OpName %main \"main\"",
1684 "%void = OpTypeVoid",
1685 "%4 = OpTypeFunction %void",
1686 "%uint = OpTypeInt 32 0",
1687 "%uint_5 = OpConstant %uint 5",
1688 "%25 = OpConstant %uint 25",
1689 "%main = OpFunction %void None %4",
1690 "%8 = OpLabel",
1691 "%9 = OpIMul %uint %uint_5 %uint_5",
1692 "%10 = OpIMul %uint %9 %uint_5",
1693 "OpReturn",
1694 "OpFunctionEnd"
1695 // clang-format on
1696 };
1697
1698 std::unique_ptr<IRContext> context =
1699 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text),
1700 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1701 ASSERT_NE(nullptr, context);
1702
1703 DefUseManager* def_use_mgr = context->get_def_use_mgr();
1704 Instruction* def = def_use_mgr->GetDef(9);
1705 Instruction* use = def_use_mgr->GetDef(10);
1706 def->SetOpcode(SpvOpCopyObject);
1707 def->SetInOperands({{SPV_OPERAND_TYPE_ID, {25}}});
1708 context->UpdateDefUse(def);
1709
1710 auto users = def_use_mgr->id_to_users();
1711 UserEntry entry = {def, use};
1712 EXPECT_THAT(users, Contains(entry));
1713 }
1714 // clang-format on
1715
1716 } // namespace
1717 } // namespace analysis
1718 } // namespace opt
1719 } // namespace spvtools
1720