diff --git a/llvm/test/tools/llvm-reduce/Inputs/reduce-distinct-metadata.py b/llvm/test/tools/llvm-reduce/Inputs/reduce-distinct-metadata.py new file mode 100644 index 0000000000000..155031ab5d8aa --- /dev/null +++ b/llvm/test/tools/llvm-reduce/Inputs/reduce-distinct-metadata.py @@ -0,0 +1,19 @@ +# Helper script for distinct metadata reduction test + +import sys +import re + +input = open(sys.argv[1], "r").read().splitlines() + +depth_map = {"0": 1, "1": 3, "2": 3, "3": 2, "4": 1} + + +for i in range(len(depth_map)): + counter = 0 + for line in input: + if re.match(rf".*interesting_{i}.*", line) != None: + counter += 1 + if counter != depth_map[str(i)]: + sys.exit(1) + +sys.exit(0) diff --git a/llvm/test/tools/llvm-reduce/reduce-distinct-metadata.ll b/llvm/test/tools/llvm-reduce/reduce-distinct-metadata.ll new file mode 100644 index 0000000000000..4f51e78c63821 --- /dev/null +++ b/llvm/test/tools/llvm-reduce/reduce-distinct-metadata.ll @@ -0,0 +1,28 @@ +; Test that every boring node is removed and all interesting distinct nodes remain after aggressive distinct metadata reduction. + +; RUN: llvm-reduce --aggressive-named-md-reduction --test %python --test-arg %p/Inputs/reduce-distinct-metadata.py %s -o %t +; RUN: FileCheck %s < %t + +; CHECK-NOT: {{.*}}boring{{.*}} + +define void @main() { + ret void +} + +!named.metadata = !{!0, !2} +!llvm.test.other.metadata = !{} + +!0 = distinct !{!"interesting_0", !1, !3, !4, !10, !11} +!1 = distinct !{!"interesting_1", !5, !7, !"something"} +!2 = distinct !{!"boring_0", !3, !4, i32 5} +!3 = distinct !{!"interesting_1", !3, !4} +!4 = distinct !{!"interesting_1", !6, i2 1} +!5 = distinct !{!"interesting_2", !8} +!6 = distinct !{!"interesting_2", !10} +!7 = distinct !{!"interesting_2", !12} +!8 = distinct !{!"interesting_3", !10, !9} +!9 = distinct !{!"interesting_3", !11, !13} +!10 = distinct !{!"boring_1", i32 50} +!11 = distinct !{!"boring_1", i32 2} +!12 = distinct !{!"boring_3", i2 1} +!13 = distinct !{!"interesting_4"} diff --git a/llvm/test/tools/llvm-reduce/remove-metadata.ll b/llvm/test/tools/llvm-reduce/remove-metadata.ll index 51a50ca20a985..2401201c2947b 100644 --- a/llvm/test/tools/llvm-reduce/remove-metadata.ll +++ b/llvm/test/tools/llvm-reduce/remove-metadata.ll @@ -1,8 +1,11 @@ ; Test that llvm-reduce can remove uninteresting metadata from an IR file. ; The Metadata pass erases named & unnamed metadata nodes. ; +; RUN: llvm-reduce --aggressive-named-md-reduction --test %python --test-arg %p/Inputs/remove-metadata.py %s -o %t +; RUN: FileCheck --check-prefixes=AGGRESSIVE --implicit-check-not=! %s < %t + ; RUN: llvm-reduce --test %python --test-arg %p/Inputs/remove-metadata.py %s -o %t -; RUN: cat %t | FileCheck -implicit-check-not=! %s +; RUN: FileCheck --implicit-check-not=! %s < %t @global = global i32 0, !dbg !0 @@ -11,6 +14,7 @@ define void @main() !dbg !0 { } !uninteresting = !{!0} +; AGGRESSIVE: !interesting = !{} ; CHECK: !interesting = !{!0} !interesting = !{!1} diff --git a/llvm/tools/llvm-reduce/CMakeLists.txt b/llvm/tools/llvm-reduce/CMakeLists.txt index a4c605fcd2443..b8ad6f71b41e5 100644 --- a/llvm/tools/llvm-reduce/CMakeLists.txt +++ b/llvm/tools/llvm-reduce/CMakeLists.txt @@ -31,6 +31,7 @@ add_llvm_tool(llvm-reduce deltas/ReduceAttributes.cpp deltas/ReduceBasicBlocks.cpp deltas/ReduceDIMetadata.cpp + deltas/ReduceDistinctMetadata.cpp deltas/ReduceDbgRecords.cpp deltas/ReduceFunctionBodies.cpp deltas/ReduceFunctions.cpp diff --git a/llvm/tools/llvm-reduce/DeltaManager.cpp b/llvm/tools/llvm-reduce/DeltaManager.cpp index 67fbc2fdc7ad4..624b5306bc71b 100644 --- a/llvm/tools/llvm-reduce/DeltaManager.cpp +++ b/llvm/tools/llvm-reduce/DeltaManager.cpp @@ -21,6 +21,7 @@ #include "deltas/ReduceBasicBlocks.h" #include "deltas/ReduceDIMetadata.h" #include "deltas/ReduceDbgRecords.h" +#include "deltas/ReduceDistinctMetadata.h" #include "deltas/ReduceFunctionBodies.h" #include "deltas/ReduceFunctions.h" #include "deltas/ReduceGlobalObjects.h" @@ -93,6 +94,7 @@ static cl::list DELTA_PASS("global-variables", reduceGlobalsDeltaPass) \ DELTA_PASS("di-metadata", reduceDIMetadataDeltaPass) \ DELTA_PASS("dbg-records", reduceDbgRecordDeltaPass) \ + DELTA_PASS("distinct-metadata", reduceDistinctMetadataDeltaPass) \ DELTA_PASS("metadata", reduceMetadataDeltaPass) \ DELTA_PASS("named-metadata", reduceNamedMetadataDeltaPass) \ DELTA_PASS("arguments", reduceArgumentsDeltaPass) \ @@ -112,7 +114,7 @@ static cl::list DELTA_PASS("atomic-ordering", reduceAtomicOrderingDeltaPass) \ DELTA_PASS("syncscopes", reduceAtomicSyncScopesDeltaPass) \ DELTA_PASS("instruction-flags", reduceInstructionFlagsDeltaPass) \ -} while (false) + } while (false) #define DELTA_PASSES_MIR \ do { \ diff --git a/llvm/tools/llvm-reduce/deltas/ReduceDistinctMetadata.cpp b/llvm/tools/llvm-reduce/deltas/ReduceDistinctMetadata.cpp new file mode 100644 index 0000000000000..32fca80b5e5d6 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceDistinctMetadata.cpp @@ -0,0 +1,147 @@ +//===- ReduceDistinctMetadata.cpp - Specialized Delta Pass ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements two functions used by the Generic Delta Debugging +// Algorithm, which are used to reduce unnamed distinct metadata nodes. +// +//===----------------------------------------------------------------------===// + +#include "ReduceDistinctMetadata.h" +#include "Delta.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/InstIterator.h" +#include +#include + +using namespace llvm; + +// Traverse the graph breadth-first and try to remove unnamed metadata nodes +static void +reduceNodes(MDNode *Root, + SetVector> &NodesToDelete, + MDNode *TemporaryNode, Oracle &O, Module &Program) { + std::queue NodesToTraverse{}; + // Keep track of visited nodes not to get into loops + SetVector VisitedNodes{}; + NodesToTraverse.push(Root); + + while (!NodesToTraverse.empty()) { + MDNode *CurrentNode = NodesToTraverse.front(); + NodesToTraverse.pop(); + + // Mark the nodes for removal + for (unsigned int I = 0; I < CurrentNode->getNumOperands(); ++I) { + if (MDNode *Operand = + dyn_cast(CurrentNode->getOperand(I).get())) { + // Check whether node has been visited + if (!VisitedNodes.contains(Operand)) { + NodesToTraverse.push(Operand); + VisitedNodes.insert(Operand); + } + // Delete the node only if it is distinct + if (Operand->isDistinct()) { + // Add to removal list + NodesToDelete.insert(std::make_pair(I, CurrentNode)); + } + } + } + + // Remove the nodes + for (auto [PositionToReplace, Node] : NodesToDelete) { + if (!O.shouldKeep()) + Node->replaceOperandWith(PositionToReplace, TemporaryNode); + } + NodesToDelete.clear(); + } +} + +// After reducing metadata, we need to remove references to the temporary node, +// this is also done with BFS +static void cleanUpTemporaries(NamedMDNode &NamedNode, MDTuple *TemporaryTuple, + Module &Program) { + std::queue NodesToTraverse{}; + SetVector VisitedNodes{}; + + // Push all first level operands of the named node to the queue + for (auto I = NamedNode.op_begin(); I != NamedNode.op_end(); ++I) { + // If the node hasn't been traversed yet, add it to the queue of nodes to + // traverse. + if (MDTuple *TupleI = dyn_cast((*I))) { + if (!VisitedNodes.contains(TupleI)) { + NodesToTraverse.push(TupleI); + VisitedNodes.insert(TupleI); + } + } + } + + while (!NodesToTraverse.empty()) { + MDTuple *CurrentTuple = NodesToTraverse.front(); + NodesToTraverse.pop(); + + // Shift all of the interesting elements to the left, pop remaining + // afterwards + if (CurrentTuple->isDistinct()) { + // Do resizing and cleaning operations only if the node is distinct, + // as resizing is not supported for unique nodes and is redundant. + unsigned int NotToRemove = 0; + for (unsigned int I = 0; I < CurrentTuple->getNumOperands(); ++I) { + Metadata *Operand = CurrentTuple->getOperand(I).get(); + // If current operand is not the temporary node, move it to the front + // and increase notToRemove so that it will be saved + if (Operand != TemporaryTuple) { + Metadata *TemporaryMetadata = + CurrentTuple->getOperand(NotToRemove).get(); + CurrentTuple->replaceOperandWith(NotToRemove, Operand); + CurrentTuple->replaceOperandWith(I, TemporaryMetadata); + ++NotToRemove; + } + } + + // Remove all the uninteresting elements + unsigned int OriginalOperands = CurrentTuple->getNumOperands(); + for (unsigned int I = 0; I < OriginalOperands - NotToRemove; ++I) + CurrentTuple->pop_back(); + } + + // Push the remaining nodes into the queue + for (unsigned int I = 0; I < CurrentTuple->getNumOperands(); ++I) { + MDTuple *Operand = dyn_cast(CurrentTuple->getOperand(I).get()); + if (Operand && !VisitedNodes.contains(Operand)) { + NodesToTraverse.push(Operand); + // If the node hasn't been traversed yet, add it to the queue of nodes + // to traverse. + VisitedNodes.insert(Operand); + } + } + } +} + +static void extractDistinctMetadataFromModule(Oracle &O, + ReducerWorkItem &WorkItem) { + Module &Program = WorkItem.getModule(); + MDTuple *TemporaryTuple = + MDTuple::getDistinct(Program.getContext(), SmallVector{}); + SetVector> NodesToDelete{}; + for (NamedMDNode &NamedNode : + Program.named_metadata()) { // Iterate over the named nodes + for (unsigned int I = 0; I < NamedNode.getNumOperands(); + ++I) { // Iterate over first level unnamed nodes.. + if (MDTuple *Operand = dyn_cast(NamedNode.getOperand(I))) + reduceNodes(Operand, NodesToDelete, TemporaryTuple, O, Program); + } + } + for (NamedMDNode &NamedNode : Program.named_metadata()) + cleanUpTemporaries(NamedNode, TemporaryTuple, Program); +} + +void llvm::reduceDistinctMetadataDeltaPass(TestRunner &Test) { + runDeltaPass(Test, extractDistinctMetadataFromModule, + "Reducing Distinct Metadata"); +} diff --git a/llvm/tools/llvm-reduce/deltas/ReduceDistinctMetadata.h b/llvm/tools/llvm-reduce/deltas/ReduceDistinctMetadata.h new file mode 100644 index 0000000000000..d02e8e6107b75 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceDistinctMetadata.h @@ -0,0 +1,23 @@ +//===- ReduceDistinctMetadata.h - Specialized Delta Pass ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------------===// +// +// This file implements two functions used by the Generic Delta Debugging +// Algorithm, which are used to reduce Metadata nodes. +// +//===----------------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEDISTINCTMETADATA_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEDISTINCTMETADATA_H + +#include "TestRunner.h" + +namespace llvm { +void reduceDistinctMetadataDeltaPass(TestRunner &Test); +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-reduce/deltas/ReduceMetadata.cpp b/llvm/tools/llvm-reduce/deltas/ReduceMetadata.cpp index 30bf612fe9694..316c74876025a 100644 --- a/llvm/tools/llvm-reduce/deltas/ReduceMetadata.cpp +++ b/llvm/tools/llvm-reduce/deltas/ReduceMetadata.cpp @@ -20,6 +20,13 @@ using namespace llvm; +extern cl::OptionCategory LLVMReduceOptions; + +static cl::opt AggressiveMetadataReduction( + "aggressive-named-md-reduction", + cl::desc("Reduce named metadata without taking its type into account"), + cl::cat(LLVMReduceOptions)); + static bool shouldKeepDebugIntrinsicMetadata(Instruction &I, MDNode &MD) { return isa(MD) && isa(I); } @@ -44,24 +51,26 @@ static constexpr StringLiteral ListNamedMetadata[] = { static void reduceNamedMetadataOperands(Oracle &O, ReducerWorkItem &WorkItem) { Module &M = WorkItem.getModule(); - for (StringRef MDName : ListNamedMetadata) { - NamedMDNode *NamedNode = M.getNamedMetadata(MDName); - if (!NamedNode) + for (NamedMDNode &I : M.named_metadata()) { + // If we don't want to reduce mindlessly, check if our node is part of + // ListNamedMetadata before reducing it + if (!AggressiveMetadataReduction && + !is_contained(ListNamedMetadata, I.getName())) continue; bool MadeChange = false; - SmallVector KeptOperands; - for (auto I : seq(0, NamedNode->getNumOperands())) { + SmallVector KeptOperands; + for (auto J : seq(0, I.getNumOperands())) { if (O.shouldKeep()) - KeptOperands.push_back(NamedNode->getOperand(I)); + KeptOperands.push_back(I.getOperand(J)); else MadeChange = true; } if (MadeChange) { - NamedNode->clearOperands(); + I.clearOperands(); for (MDNode *KeptOperand : KeptOperands) - NamedNode->addOperand(KeptOperand); + I.addOperand(KeptOperand); } } }