1//===--------------- IRNormalizer.cpp - IR Normalizer ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9/// This file implements the IRNormalizer class which aims to transform LLVM
10/// Modules into a normal form by reordering and renaming instructions while
11/// preserving the same semantics. The normalizer makes it easier to spot
12/// semantic differences while diffing two modules which have undergone
13/// different passes.
14///
15//===----------------------------------------------------------------------===//
16
17#include "llvm/Transforms/Utils/IRNormalizer.h"
18#include "llvm/ADT/SetVector.h"
19#include "llvm/ADT/SmallPtrSet.h"
20#include "llvm/ADT/SmallString.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/IR/BasicBlock.h"
23#include "llvm/IR/Function.h"
24#include "llvm/IR/IRBuilder.h"
25#include "llvm/IR/InstIterator.h"
26#include "llvm/Pass.h"
27#include <stack>
28
29#define DEBUG_TYPE "normalize"
30
31using namespace llvm;
32
33namespace {
34/// IRNormalizer aims to transform LLVM IR into normal form.
35class IRNormalizer {
36public:
37 bool runOnFunction(Function &F);
38
39 IRNormalizer(IRNormalizerOptions Options) : Options(Options) {}
40
41private:
42 const IRNormalizerOptions Options;
43
44 // Random constant for hashing, so the state isn't zero.
45 const uint64_t MagicHashConstant = 0x6acaa36bef8325c5ULL;
46 DenseSet<const Instruction *> NamedInstructions;
47
48 SmallVector<Instruction *, 16> Outputs;
49
50 /// \name Naming.
51 /// @{
52 void nameFunctionArguments(Function &F) const;
53 void nameBasicBlocks(Function &F) const;
54 void nameInstruction(Instruction *I);
55 void nameAsInitialInstruction(Instruction *I) const;
56 void nameAsRegularInstruction(Instruction *I);
57 void foldInstructionName(Instruction *I) const;
58 /// @}
59
60 /// \name Reordering.
61 /// @{
62 void reorderInstructions(Function &F) const;
63 void reorderDefinition(Instruction *Definition,
64 std::stack<Instruction *> &TopologicalSort,
65 SmallPtrSet<const Instruction *, 32> &Visited) const;
66 void reorderInstructionOperandsByNames(Instruction *I) const;
67 void reorderPHIIncomingValues(PHINode *Phi) const;
68 /// @}
69
70 /// \name Utility methods.
71 /// @{
72 template <typename T>
73 void sortCommutativeOperands(Instruction *I, T &Operands) const;
74 SmallVector<Instruction *, 16> collectOutputInstructions(Function &F) const;
75 bool isOutput(const Instruction *I) const;
76 bool isInitialInstruction(const Instruction *I) const;
77 bool hasOnlyImmediateOperands(const Instruction *I) const;
78 SetVector<int>
79 getOutputFootprint(Instruction *I,
80 SmallPtrSet<const Instruction *, 32> &Visited) const;
81 /// @}
82};
83} // namespace
84
85/// Entry method to the IRNormalizer.
86///
87/// \param F Function to normalize.
88bool IRNormalizer::runOnFunction(Function &F) {
89 nameFunctionArguments(F);
90 nameBasicBlocks(F);
91
92 Outputs = collectOutputInstructions(F);
93
94 if (!Options.PreserveOrder)
95 reorderInstructions(F);
96
97 // TODO: Reorder basic blocks via a topological sort.
98
99 for (auto &I : Outputs)
100 nameInstruction(I);
101
102 for (auto &I : instructions(F)) {
103 if (!Options.PreserveOrder) {
104 if (Options.ReorderOperands)
105 reorderInstructionOperandsByNames(I: &I);
106
107 if (auto *Phi = dyn_cast<PHINode>(Val: &I))
108 reorderPHIIncomingValues(Phi);
109 }
110 foldInstructionName(I: &I);
111 }
112
113 return true;
114}
115
116/// Numbers arguments.
117///
118/// \param F Function whose arguments will be renamed.
119void IRNormalizer::nameFunctionArguments(Function &F) const {
120 int ArgumentCounter = 0;
121 for (auto &A : F.args()) {
122 if (Options.RenameAll || A.getName().empty()) {
123 A.setName("a" + Twine(ArgumentCounter));
124 ArgumentCounter += 1;
125 }
126 }
127}
128
129/// Names basic blocks using a generated hash for each basic block in
130/// a function considering the opcode and the order of output instructions.
131///
132/// \param F Function containing basic blocks to rename.
133void IRNormalizer::nameBasicBlocks(Function &F) const {
134 for (auto &B : F) {
135 // Initialize to a magic constant, so the state isn't zero.
136 uint64_t Hash = MagicHashConstant;
137
138 // Hash considering output instruction opcodes.
139 for (auto &I : B)
140 if (isOutput(I: &I))
141 Hash = hashing::detail::hash_16_bytes(low: Hash, high: I.getOpcode());
142
143 if (Options.RenameAll || B.getName().empty()) {
144 // Name basic block. Substring hash to make diffs more readable.
145 B.setName("bb" + std::to_string(val: Hash).substr(pos: 0, n: 5));
146 }
147 }
148}
149
150/// Names instructions graphically (recursive) in accordance with the
151/// def-use tree, starting from the initial instructions (defs), finishing at
152/// the output (top-most user) instructions (depth-first).
153///
154/// \param I Instruction to be renamed.
155void IRNormalizer::nameInstruction(Instruction *I) {
156 // Ensure instructions are not renamed. This is done
157 // to prevent situation where instructions are used
158 // before their definition (in phi nodes)
159 if (NamedInstructions.contains(V: I))
160 return;
161 NamedInstructions.insert(V: I);
162 if (isInitialInstruction(I)) {
163 nameAsInitialInstruction(I);
164 } else {
165 // This must be a regular instruction.
166 nameAsRegularInstruction(I);
167 }
168}
169
170template <typename T>
171void IRNormalizer::sortCommutativeOperands(Instruction *I, T &Operands) const {
172 if (!(I->isCommutative() && Operands.size() >= 2))
173 return;
174 auto CommutativeEnd = Operands.begin();
175 std::advance(CommutativeEnd, 2);
176 llvm::sort(Operands.begin(), CommutativeEnd);
177}
178
179/// Names instruction following the scheme:
180/// vl00000Callee(Operands)
181///
182/// Where 00000 is a hash calculated considering instruction's opcode and output
183/// footprint. Callee's name is only included when instruction's type is
184/// CallInst. In cases where instruction is commutative, operands list is also
185/// sorted.
186///
187/// Renames instruction only when RenameAll flag is raised or instruction is
188/// unnamed.
189///
190/// \see getOutputFootprint()
191/// \param I Instruction to be renamed.
192void IRNormalizer::nameAsInitialInstruction(Instruction *I) const {
193 if (I->getType()->isVoidTy())
194 return;
195 if (!(I->getName().empty() || Options.RenameAll))
196 return;
197 LLVM_DEBUG(dbgs() << "Naming initial instruction: " << *I << "\n");
198
199 // Instruction operands for further sorting.
200 SmallVector<SmallString<64>, 4> Operands;
201
202 // Collect operands.
203 for (auto &Op : I->operands()) {
204 if (!isa<Function>(Val: Op)) {
205 std::string TextRepresentation;
206 raw_string_ostream Stream(TextRepresentation);
207 Op->printAsOperand(O&: Stream, PrintType: false);
208 Operands.push_back(Elt: StringRef(Stream.str()));
209 }
210 }
211
212 sortCommutativeOperands(I, Operands);
213
214 // Initialize to a magic constant, so the state isn't zero.
215 uint64_t Hash = MagicHashConstant;
216
217 // Consider instruction's opcode in the hash.
218 Hash = hashing::detail::hash_16_bytes(low: Hash, high: I->getOpcode());
219
220 SmallPtrSet<const Instruction *, 32> Visited;
221 // Get output footprint for I.
222 SetVector<int> OutputFootprint = getOutputFootprint(I, Visited);
223
224 // Consider output footprint in the hash.
225 for (const int &Output : OutputFootprint)
226 Hash = hashing::detail::hash_16_bytes(low: Hash, high: Output);
227
228 // Base instruction name.
229 SmallString<256> Name;
230 Name.append(RHS: "vl" + std::to_string(val: Hash).substr(pos: 0, n: 5));
231
232 // In case of CallInst, consider callee in the instruction name.
233 if (const auto *CI = dyn_cast<CallInst>(Val: I)) {
234 Function *F = CI->getCalledFunction();
235
236 if (F != nullptr)
237 Name.append(RHS: F->getName());
238 }
239
240 Name.append(RHS: "(");
241 for (size_t i = 0; i < Operands.size(); ++i) {
242 Name.append(RHS: Operands[i]);
243
244 if (i < Operands.size() - 1)
245 Name.append(RHS: ", ");
246 }
247 Name.append(RHS: ")");
248
249 I->setName(Name);
250}
251
252/// Names instruction following the scheme:
253/// op00000Callee(Operands)
254///
255/// Where 00000 is a hash calculated considering instruction's opcode, its
256/// operands' opcodes and order. Callee's name is only included when
257/// instruction's type is CallInst. In cases where instruction is commutative,
258/// operand list is also sorted.
259///
260/// Names instructions recursively in accordance with the def-use tree,
261/// starting from the initial instructions (defs), finishing at
262/// the output (top-most user) instructions (depth-first).
263///
264/// Renames instruction only when RenameAll flag is raised or instruction is
265/// unnamed.
266///
267/// \see getOutputFootprint()
268/// \param I Instruction to be renamed.
269void IRNormalizer::nameAsRegularInstruction(Instruction *I) {
270 LLVM_DEBUG(dbgs() << "Naming regular instruction: " << *I << "\n");
271
272 // Instruction operands for further sorting.
273 SmallVector<SmallString<128>, 4> Operands;
274
275 // The name of a regular instruction depends
276 // on the names of its operands. Hence, all
277 // operands must be named first in the use-def
278 // walk.
279
280 // Collect operands.
281 for (auto &Op : I->operands()) {
282 if (auto *I = dyn_cast<Instruction>(Val&: Op)) {
283 // Walk down the use-def chain.
284 nameInstruction(I);
285 Operands.push_back(Elt: I->getName());
286 } else if (!isa<Function>(Val: Op)) {
287 // This must be an immediate value.
288 std::string TextRepresentation;
289 raw_string_ostream Stream(TextRepresentation);
290 Op->printAsOperand(O&: Stream, PrintType: false);
291 Operands.push_back(Elt: StringRef(Stream.str()));
292 }
293 }
294
295 sortCommutativeOperands(I, Operands);
296
297 // Initialize to a magic constant, so the state isn't zero.
298 uint64_t Hash = MagicHashConstant;
299
300 // Consider instruction opcode in the hash.
301 Hash = hashing::detail::hash_16_bytes(low: Hash, high: I->getOpcode());
302
303 // Operand opcodes for further sorting (commutative).
304 SmallVector<int, 4> OperandsOpcodes;
305
306 // Collect operand opcodes for hashing.
307 for (auto &Op : I->operands())
308 if (auto *I = dyn_cast<Instruction>(Val&: Op))
309 OperandsOpcodes.push_back(Elt: I->getOpcode());
310
311 sortCommutativeOperands(I, Operands&: OperandsOpcodes);
312
313 // Consider operand opcodes in the hash.
314 for (const int Code : OperandsOpcodes)
315 Hash = hashing::detail::hash_16_bytes(low: Hash, high: Code);
316
317 // Base instruction name.
318 SmallString<512> Name;
319 Name.append(RHS: "op" + std::to_string(val: Hash).substr(pos: 0, n: 5));
320
321 // In case of CallInst, consider callee in the instruction name.
322 if (const auto *CI = dyn_cast<CallInst>(Val: I))
323 if (const Function *F = CI->getCalledFunction())
324 Name.append(RHS: F->getName());
325
326 Name.append(RHS: "(");
327 for (size_t i = 0; i < Operands.size(); ++i) {
328 Name.append(RHS: Operands[i]);
329
330 if (i < Operands.size() - 1)
331 Name.append(RHS: ", ");
332 }
333 Name.append(RHS: ")");
334
335 if ((I->getName().empty() || Options.RenameAll) && !I->getType()->isVoidTy())
336 I->setName(Name);
337}
338
339/// Shortens instruction's name. This method removes called function name from
340/// the instruction name and substitutes the call chain with a corresponding
341/// list of operands.
342///
343/// Examples:
344/// op00000Callee(op00001Callee(...), vl00000Callee(1, 2), ...) ->
345/// op00000(op00001, vl00000, ...) vl00000Callee(1, 2) -> vl00000(1, 2)
346///
347/// This method omits output instructions and pre-output (instructions directly
348/// used by an output instruction) instructions (by default). By default it also
349/// does not affect user named instructions.
350///
351/// \param I Instruction whose name will be folded.
352void IRNormalizer::foldInstructionName(Instruction *I) const {
353 // If this flag is raised, fold all regular
354 // instructions (including pre-outputs).
355 if (!Options.FoldPreOutputs) {
356 // Don't fold if one of the users is an output instruction.
357 for (auto *U : I->users())
358 if (auto *IU = dyn_cast<Instruction>(Val: U))
359 if (isOutput(I: IU))
360 return;
361 }
362
363 // Don't fold if it is an output instruction or has no op prefix.
364 if (isOutput(I) || !I->getName().starts_with(Prefix: "op"))
365 return;
366
367 // Instruction operands.
368 SmallVector<SmallString<64>, 4> Operands;
369
370 for (auto &Op : I->operands()) {
371 if (const auto *I = dyn_cast<Instruction>(Val&: Op)) {
372 bool HasNormalName =
373 I->getName().starts_with(Prefix: "op") || I->getName().starts_with(Prefix: "vl");
374
375 Operands.push_back(Elt: HasNormalName ? I->getName().substr(Start: 0, N: 7)
376 : I->getName());
377 }
378 }
379
380 sortCommutativeOperands(I, Operands);
381
382 SmallString<256> Name;
383 Name.append(RHS: I->getName().substr(Start: 0, N: 7));
384
385 Name.append(RHS: "(");
386 for (size_t i = 0; i < Operands.size(); ++i) {
387 Name.append(RHS: Operands[i]);
388
389 if (i < Operands.size() - 1)
390 Name.append(RHS: ", ");
391 }
392 Name.append(RHS: ")");
393
394 I->setName(Name);
395}
396
397/// Reorders instructions by walking up the tree from each operand of an output
398/// instruction and reducing the def-use distance.
399/// This method assumes that output instructions were collected top-down,
400/// otherwise the def-use chain may be broken.
401/// This method is a wrapper for recursive reorderInstruction().
402///
403/// \see reorderInstruction()
404void IRNormalizer::reorderInstructions(Function &F) const {
405 for (auto &BB : F) {
406 LLVM_DEBUG(dbgs() << "Reordering instructions in basic block: "
407 << BB.getName() << "\n");
408 // Find the source nodes of the DAG of instructions in this basic block.
409 // Source nodes are instructions that have side effects, are terminators, or
410 // don't have a parent in the DAG of instructions.
411 //
412 // We must iterate from the first to the last instruction otherwise side
413 // effecting instructions could be reordered.
414
415 std::stack<Instruction *> TopologicalSort;
416 SmallPtrSet<const Instruction *, 32> Visited;
417 for (auto &I : BB) {
418 // First process side effecting and terminating instructions.
419 if (!(isOutput(I: &I) || I.isTerminator()))
420 continue;
421 LLVM_DEBUG(dbgs() << "\tReordering from source effecting instruction: ";
422 I.dump());
423 reorderDefinition(Definition: &I, TopologicalSort, Visited);
424 }
425
426 for (auto &I : BB) {
427 // Process the remaining instructions.
428 //
429 // TODO: Do more a intelligent sorting of these instructions. For example,
430 // seperate between dead instructinos and instructions used in another
431 // block. Use properties of the CFG the order instructions that are used
432 // in another block.
433 if (Visited.contains(Ptr: &I))
434 continue;
435 LLVM_DEBUG(dbgs() << "\tReordering from source instruction: "; I.dump());
436 reorderDefinition(Definition: &I, TopologicalSort, Visited);
437 }
438
439 LLVM_DEBUG(dbgs() << "Inserting instructions into: " << BB.getName()
440 << "\n");
441 // Reorder based on the topological sort.
442 while (!TopologicalSort.empty()) {
443 auto *Instruction = TopologicalSort.top();
444 auto FirstNonPHIOrDbgOrAlloca = BB.getFirstNonPHIOrDbgOrAlloca();
445 if (auto *Call = dyn_cast<CallInst>(Val: &*FirstNonPHIOrDbgOrAlloca)) {
446 if (Call->getIntrinsicID() ==
447 Intrinsic::experimental_convergence_entry ||
448 Call->getIntrinsicID() == Intrinsic::experimental_convergence_loop)
449 FirstNonPHIOrDbgOrAlloca++;
450 }
451 Instruction->moveBefore(InsertPos: FirstNonPHIOrDbgOrAlloca);
452 TopologicalSort.pop();
453 }
454 }
455}
456
457void IRNormalizer::reorderDefinition(
458 Instruction *Definition, std::stack<Instruction *> &TopologicalSort,
459 SmallPtrSet<const Instruction *, 32> &Visited) const {
460 if (Visited.contains(Ptr: Definition))
461 return;
462 Visited.insert(Ptr: Definition);
463
464 {
465 const auto *BasicBlock = Definition->getParent();
466 const auto FirstNonPHIOrDbgOrAlloca =
467 BasicBlock->getFirstNonPHIOrDbgOrAlloca();
468 if (FirstNonPHIOrDbgOrAlloca == BasicBlock->end())
469 return; // TODO: Is this necessary?
470 if (Definition->comesBefore(Other: &*FirstNonPHIOrDbgOrAlloca))
471 return; // TODO: Do some kind of ordering for these instructions.
472 }
473
474 for (auto &Operand : Definition->operands()) {
475 if (auto *Op = dyn_cast<Instruction>(Val&: Operand)) {
476 if (Op->getParent() != Definition->getParent())
477 continue; // Only reorder instruction within the same basic block
478 reorderDefinition(Definition: Op, TopologicalSort, Visited);
479 }
480 }
481
482 LLVM_DEBUG(dbgs() << "\t\tNext in topological sort: "; Definition->dump());
483 if (Definition->isTerminator())
484 return;
485 if (auto *Call = dyn_cast<CallInst>(Val: Definition)) {
486 if (Call->isMustTailCall())
487 return;
488 if (Call->getIntrinsicID() == Intrinsic::experimental_deoptimize)
489 return;
490 if (Call->getIntrinsicID() == Intrinsic::experimental_convergence_entry)
491 return;
492 if (Call->getIntrinsicID() == Intrinsic::experimental_convergence_loop)
493 return;
494 }
495 if (auto *BitCast = dyn_cast<BitCastInst>(Val: Definition)) {
496 if (auto *Call = dyn_cast<CallInst>(Val: BitCast->getOperand(i_nocapture: 0))) {
497 if (Call->isMustTailCall())
498 return;
499 }
500 }
501
502 TopologicalSort.emplace(args&: Definition);
503}
504
505/// Reorders instruction's operands alphabetically. This method assumes
506/// that passed instruction is commutative. Changing the operand order
507/// in other instructions may change the semantics.
508///
509/// \param I Instruction whose operands will be reordered.
510void IRNormalizer::reorderInstructionOperandsByNames(Instruction *I) const {
511 // This method assumes that passed I is commutative,
512 // changing the order of operands in other instructions
513 // may change the semantics.
514
515 // Instruction operands for further sorting.
516 SmallVector<std::pair<std::string, Value *>, 4> Operands;
517
518 // Collect operands.
519 for (auto &Op : I->operands()) {
520 if (auto *V = dyn_cast<Value>(Val&: Op)) {
521 if (isa<Instruction>(Val: V)) {
522 // This is an an instruction.
523 Operands.push_back(Elt: std::pair<std::string, Value *>(V->getName(), V));
524 } else {
525 std::string TextRepresentation;
526 raw_string_ostream Stream(TextRepresentation);
527 Op->printAsOperand(O&: Stream, PrintType: false);
528 Operands.push_back(Elt: std::pair<std::string, Value *>(Stream.str(), V));
529 }
530 }
531 }
532
533 // Sort operands.
534 sortCommutativeOperands(I, Operands);
535
536 // Reorder operands.
537 unsigned Position = 0;
538 for (auto &Op : I->operands()) {
539 Op.set(Operands[Position].second);
540 Position += 1;
541 }
542}
543
544/// Reorders PHI node's values according to the names of corresponding basic
545/// blocks.
546///
547/// \param Phi PHI node to normalize.
548void IRNormalizer::reorderPHIIncomingValues(PHINode *Phi) const {
549 // Values for further sorting.
550 SmallVector<std::pair<Value *, BasicBlock *>, 2> Values;
551
552 // Collect blocks and corresponding values.
553 for (auto &BB : Phi->blocks()) {
554 Value *V = Phi->getIncomingValueForBlock(BB);
555 Values.push_back(Elt: std::pair<Value *, BasicBlock *>(V, BB));
556 }
557
558 // Sort values according to the name of a basic block.
559 llvm::sort(C&: Values, Comp: [](const std::pair<Value *, BasicBlock *> &LHS,
560 const std::pair<Value *, BasicBlock *> &RHS) {
561 return LHS.second->getName() < RHS.second->getName();
562 });
563
564 // Swap.
565 for (unsigned i = 0; i < Values.size(); ++i) {
566 Phi->setIncomingBlock(i, BB: Values[i].second);
567 Phi->setIncomingValue(i, V: Values[i].first);
568 }
569}
570
571/// Returns a vector of output instructions. An output is an instruction which
572/// has side-effects or is ReturnInst. Uses isOutput().
573///
574/// \see isOutput()
575/// \param F Function to collect outputs from.
576SmallVector<Instruction *, 16>
577IRNormalizer::collectOutputInstructions(Function &F) const {
578 // Output instructions are collected top-down in each function,
579 // any change may break the def-use chain in reordering methods.
580 SmallVector<Instruction *, 16> Outputs;
581 for (auto &I : instructions(F))
582 if (isOutput(I: &I))
583 Outputs.push_back(Elt: &I);
584 return Outputs;
585}
586
587/// Helper method checking whether the instruction may have side effects or is
588/// ReturnInst.
589///
590/// \param I Considered instruction.
591bool IRNormalizer::isOutput(const Instruction *I) const {
592 // Outputs are such instructions which may have side effects or is ReturnInst.
593 return I->mayHaveSideEffects() || isa<ReturnInst>(Val: I);
594}
595
596/// Helper method checking whether the instruction has users and only
597/// immediate operands.
598///
599/// \param I Considered instruction.
600bool IRNormalizer::isInitialInstruction(const Instruction *I) const {
601 // Initial instructions are such instructions whose values are used by
602 // other instructions, yet they only depend on immediate values.
603 return !I->user_empty() && hasOnlyImmediateOperands(I);
604}
605
606/// Helper method checking whether the instruction has only immediate operands.
607///
608/// \param I Considered instruction.
609bool IRNormalizer::hasOnlyImmediateOperands(const Instruction *I) const {
610 for (const auto &Op : I->operands())
611 if (isa<Instruction>(Val: Op))
612 return false; // Found non-immediate operand (instruction).
613 return true;
614}
615
616/// Helper method returning indices (distance from the beginning of the basic
617/// block) of outputs using the \p I (eliminates repetitions). Walks down the
618/// def-use tree recursively.
619///
620/// \param I Considered instruction.
621/// \param Visited Set of visited instructions.
622SetVector<int> IRNormalizer::getOutputFootprint(
623 Instruction *I, SmallPtrSet<const Instruction *, 32> &Visited) const {
624
625 // Vector containing indexes of outputs (no repetitions),
626 // which use I in the order of walking down the def-use tree.
627 SetVector<int> Outputs;
628
629 if (!Visited.count(Ptr: I)) {
630 Visited.insert(Ptr: I);
631
632 if (isOutput(I)) {
633 // Gets output instruction's parent function.
634 Function *Func = I->getParent()->getParent();
635
636 // Finds and inserts the index of the output to the vector.
637 unsigned Count = 0;
638 for (const auto &B : *Func) {
639 for (const auto &E : B) {
640 if (&E == I)
641 Outputs.insert(X: Count);
642 Count += 1;
643 }
644 }
645
646 // Returns to the used instruction.
647 return Outputs;
648 }
649
650 for (auto *U : I->users()) {
651 if (auto *UI = dyn_cast<Instruction>(Val: U)) {
652 // Vector for outputs which use UI.
653 SetVector<int> OutputsUsingUI = getOutputFootprint(I: UI, Visited);
654 // Insert the indexes of outputs using UI.
655 Outputs.insert_range(R&: OutputsUsingUI);
656 }
657 }
658 }
659
660 // Return to the used instruction.
661 return Outputs;
662}
663
664PreservedAnalyses IRNormalizerPass::run(Function &F,
665 FunctionAnalysisManager &AM) const {
666 IRNormalizer(Options).runOnFunction(F);
667 PreservedAnalyses PA;
668 PA.preserveSet<CFGAnalyses>();
669 return PA;
670}
671