|
| 1 | +//===- ReduceDistinctMetadata.cpp - Specialized Delta Pass ----------------===// |
| 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 | +// |
| 9 | +// This file implements two functions used by the Generic Delta Debugging |
| 10 | +// Algorithm, which are used to reduce unnamed distinct metadata nodes. |
| 11 | +// |
| 12 | +//===----------------------------------------------------------------------===// |
| 13 | + |
| 14 | +#include "ReduceDistinctMetadata.h" |
| 15 | +#include "Delta.h" |
| 16 | +#include "llvm/ADT/Sequence.h" |
| 17 | +#include "llvm/ADT/SetVector.h" |
| 18 | +#include "llvm/ADT/SmallVector.h" |
| 19 | +#include "llvm/IR/InstIterator.h" |
| 20 | +#include <algorithm> |
| 21 | +#include <queue> |
| 22 | + |
| 23 | +using namespace llvm; |
| 24 | + |
| 25 | +// Traverse the graph breadth-first and try to remove unnamed metadata nodes |
| 26 | +static void |
| 27 | +reduceNodes(MDNode *Root, |
| 28 | + SetVector<std::pair<unsigned int, MDNode *>> &NodesToDelete, |
| 29 | + MDNode *TemporaryNode, Oracle &O, Module &Program) { |
| 30 | + std::queue<MDNode *> NodesToTraverse{}; |
| 31 | + // Keep track of visited nodes not to get into loops |
| 32 | + SetVector<MDNode *> VisitedNodes{}; |
| 33 | + NodesToTraverse.push(Root); |
| 34 | + |
| 35 | + while (!NodesToTraverse.empty()) { |
| 36 | + MDNode *CurrentNode = NodesToTraverse.front(); |
| 37 | + NodesToTraverse.pop(); |
| 38 | + |
| 39 | + // Mark the nodes for removal |
| 40 | + for (unsigned int I = 0; I < CurrentNode->getNumOperands(); ++I) { |
| 41 | + if (MDNode *Operand = |
| 42 | + dyn_cast<MDNode>(CurrentNode->getOperand(I).get())) { |
| 43 | + // Check whether node has been visited |
| 44 | + if (!VisitedNodes.contains(Operand)) { |
| 45 | + NodesToTraverse.push(Operand); |
| 46 | + VisitedNodes.insert(Operand); |
| 47 | + } |
| 48 | + // Delete the node only if it is distinct |
| 49 | + if (Operand->isDistinct()) { |
| 50 | + // Add to removal list |
| 51 | + NodesToDelete.insert(std::make_pair(I, CurrentNode)); |
| 52 | + } |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + // Remove the nodes |
| 57 | + for (auto [PositionToReplace, Node] : NodesToDelete) { |
| 58 | + if (!O.shouldKeep()) |
| 59 | + Node->replaceOperandWith(PositionToReplace, TemporaryNode); |
| 60 | + } |
| 61 | + NodesToDelete.clear(); |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +// After reducing metadata, we need to remove references to the temporary node, |
| 66 | +// this is also done with BFS |
| 67 | +static void cleanUpTemporaries(NamedMDNode &NamedNode, MDTuple *TemporaryTuple, |
| 68 | + Module &Program) { |
| 69 | + std::queue<MDTuple *> NodesToTraverse{}; |
| 70 | + SetVector<MDTuple *> VisitedNodes{}; |
| 71 | + |
| 72 | + // Push all first level operands of the named node to the queue |
| 73 | + for (auto I = NamedNode.op_begin(); I != NamedNode.op_end(); ++I) { |
| 74 | + // If the node hasn't been traversed yet, add it to the queue of nodes to |
| 75 | + // traverse. |
| 76 | + if (MDTuple *TupleI = dyn_cast<MDTuple>((*I))) { |
| 77 | + if (!VisitedNodes.contains(TupleI)) { |
| 78 | + NodesToTraverse.push(TupleI); |
| 79 | + VisitedNodes.insert(TupleI); |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + while (!NodesToTraverse.empty()) { |
| 85 | + MDTuple *CurrentTuple = NodesToTraverse.front(); |
| 86 | + NodesToTraverse.pop(); |
| 87 | + |
| 88 | + // Shift all of the interesting elements to the left, pop remaining |
| 89 | + // afterwards |
| 90 | + if (CurrentTuple->isDistinct()) { |
| 91 | + // Do resizing and cleaning operations only if the node is distinct, |
| 92 | + // as resizing is not supported for unique nodes and is redundant. |
| 93 | + unsigned int NotToRemove = 0; |
| 94 | + for (unsigned int I = 0; I < CurrentTuple->getNumOperands(); ++I) { |
| 95 | + Metadata *Operand = CurrentTuple->getOperand(I).get(); |
| 96 | + // If current operand is not the temporary node, move it to the front |
| 97 | + // and increase notToRemove so that it will be saved |
| 98 | + if (Operand != TemporaryTuple) { |
| 99 | + Metadata *TemporaryMetadata = |
| 100 | + CurrentTuple->getOperand(NotToRemove).get(); |
| 101 | + CurrentTuple->replaceOperandWith(NotToRemove, Operand); |
| 102 | + CurrentTuple->replaceOperandWith(I, TemporaryMetadata); |
| 103 | + ++NotToRemove; |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + // Remove all the uninteresting elements |
| 108 | + unsigned int OriginalOperands = CurrentTuple->getNumOperands(); |
| 109 | + for (unsigned int I = 0; I < OriginalOperands - NotToRemove; ++I) |
| 110 | + CurrentTuple->pop_back(); |
| 111 | + } |
| 112 | + |
| 113 | + // Push the remaining nodes into the queue |
| 114 | + for (unsigned int I = 0; I < CurrentTuple->getNumOperands(); ++I) { |
| 115 | + MDTuple *Operand = dyn_cast<MDTuple>(CurrentTuple->getOperand(I).get()); |
| 116 | + if (Operand && !VisitedNodes.contains(Operand)) { |
| 117 | + NodesToTraverse.push(Operand); |
| 118 | + // If the node hasn't been traversed yet, add it to the queue of nodes |
| 119 | + // to traverse. |
| 120 | + VisitedNodes.insert(Operand); |
| 121 | + } |
| 122 | + } |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +static void extractDistinctMetadataFromModule(Oracle &O, |
| 127 | + ReducerWorkItem &WorkItem) { |
| 128 | + Module &Program = WorkItem.getModule(); |
| 129 | + MDTuple *TemporaryTuple = |
| 130 | + MDTuple::getDistinct(Program.getContext(), SmallVector<Metadata *, 1>{}); |
| 131 | + SetVector<std::pair<unsigned int, MDNode *>> NodesToDelete{}; |
| 132 | + for (NamedMDNode &NamedNode : |
| 133 | + Program.named_metadata()) { // Iterate over the named nodes |
| 134 | + for (unsigned int I = 0; I < NamedNode.getNumOperands(); |
| 135 | + ++I) { // Iterate over first level unnamed nodes.. |
| 136 | + if (MDTuple *Operand = dyn_cast<MDTuple>(NamedNode.getOperand(I))) |
| 137 | + reduceNodes(Operand, NodesToDelete, TemporaryTuple, O, Program); |
| 138 | + } |
| 139 | + } |
| 140 | + for (NamedMDNode &NamedNode : Program.named_metadata()) |
| 141 | + cleanUpTemporaries(NamedNode, TemporaryTuple, Program); |
| 142 | +} |
| 143 | + |
| 144 | +void llvm::reduceDistinctMetadataDeltaPass(TestRunner &Test) { |
| 145 | + runDeltaPass(Test, extractDistinctMetadataFromModule, |
| 146 | + "Reducing Distinct Metadata"); |
| 147 | +} |
0 commit comments