diff --git a/llvm/test/tools/llvm-reduce/Inputs/remove-dimetadata.py b/llvm/test/tools/llvm-reduce/Inputs/remove-dimetadata.py new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-reduce/Inputs/remove-dimetadata.py @@ -0,0 +1,8 @@ +import sys + +input = open(sys.argv[1], "r") +for line in input: + if "interesting" in line: + sys.exit(0) + +sys.exit(1) diff --git a/llvm/test/tools/llvm-reduce/remove-debug-info-nodes.ll b/llvm/test/tools/llvm-reduce/remove-debug-info-nodes.ll --- a/llvm/test/tools/llvm-reduce/remove-debug-info-nodes.ll +++ b/llvm/test/tools/llvm-reduce/remove-debug-info-nodes.ll @@ -1,7 +1,7 @@ ; Test that llvm-reduce can drop unneeded debug metadata nodes referenced by ; DICompileUnit and DISuprogram. ; -; RUN: llvm-reduce --delta-passes=metadata --test FileCheck --test-arg --check-prefixes=CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t +; RUN: llvm-reduce --delta-passes=di-metadata --abort-on-invalid-reduction --test FileCheck --test-arg --check-prefixes=CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t ; RUN: cat %t | FileCheck %s ; CHECK-INTERESTINGNESS: define void @test() !dbg @@ -21,13 +21,12 @@ ; CHECK: !llvm.dbg.cu = !{[[CU:.+]]} ; CHECK-DAG: [[CU]] = distinct !DICompileUnit(language: DW_LANG_C99,{{.*}}, retainedTypes: [[TYPES:![0-9]+]], globals: [[GLOBALS:![0-9]+]] +; CHECK-DAG: [[EMPTY:![0-9]+]] = !{} ; CHECK-DAG: [[TYPES]] = !{[[T0:![0-9]+]] ; CHECK-DAG: [[T0]] = !DIBasicType(name: "unsigned int", -; CHECK-DAG: [[GLOBALS]] = !{!{{.+}} +; CHECK-DAG: [[GLOBALS]] = !{{{![0-9]+}} - -; CHECK-DAG: [[SUBPROG]] = distinct !DISubprogram(name: "test", {{.*}}retainedNodes: [[RETAINED_NODES:.+]]) -; CHECK-DAG: [[RETAINED_NODES]] = !{!{{.+}}, +; CHECK-DAG: [[SUBPROG]] = distinct !DISubprogram(name: "test", {{.*}}retainedNodes: [[EMPTY]]) define void @test() !dbg !17 { ret void diff --git a/llvm/test/tools/llvm-reduce/remove-dimetadata.ll b/llvm/test/tools/llvm-reduce/remove-dimetadata.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-reduce/remove-dimetadata.ll @@ -0,0 +1,34 @@ +; Test that llvm-reduce can remove uninteresting DI metadata from an IR file. +; +; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=di-metadata --test %python --test-arg %p/Inputs/remove-dimetadata.py %s -o %t +; RUN: FileCheck <%t %s + +@global = global i32 0 + +define void @main() !dbg !5 { + ret void +} + +!llvm.module.flags = !{!0} +!llvm.dbg.cu = !{!4} + +!0 = !{i32 2, !"Debug Info Version", i32 3} +!1 = !DIFile(filename: "test.c", directory: "test") +!2 = !{!10} +; CHECK: [[EMPTY:![0-9]+]] = !{} +!4 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !9) +!5 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 498, type: !6, scopeLine: 0, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !4, retainedNodes: !2) +!6 = !DISubroutineType(types: !7) +; CHECK: !DISubroutineType(types: [[EMPTY]]) +!7 = !{!13} +!8 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !9) +!9 = !{} +!10 = !DILocalVariable(name: "one", scope: !14, file: !1, line: 0, type: !15) +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = distinct !DILexicalBlock(scope: !5, file: !1, line: 3) +!15 = !DICompositeType(tag: DW_TAG_structure_type, name: "test", file: !1, size: 64, align: 32, flags: DIFlagPublic, elements: !16) +!16 = !{!17, !18} +; CHECK: elements: [[EL:![0-9]+]]) +; CHECK: [[EL]] = !{!{{[0-9]+}}} +!17 = !DIDerivedType(tag: DW_TAG_member, name: "interesting", scope: !14, file: !1, baseType: !13, size: 32, align: 32, flags: DIFlagPublic) +!18 = !DIDerivedType(tag: DW_TAG_member, name: "uninteresting", scope: !14, file: !1, baseType: !13, size: 32, align: 32, offset: 32, flags: DIFlagPublic) diff --git a/llvm/tools/llvm-reduce/CMakeLists.txt b/llvm/tools/llvm-reduce/CMakeLists.txt --- a/llvm/tools/llvm-reduce/CMakeLists.txt +++ b/llvm/tools/llvm-reduce/CMakeLists.txt @@ -28,6 +28,7 @@ deltas/ReduceArguments.cpp deltas/ReduceAttributes.cpp deltas/ReduceBasicBlocks.cpp + deltas/ReduceDIMetadata.cpp deltas/ReduceFunctionBodies.cpp deltas/ReduceFunctions.cpp deltas/ReduceGlobalObjects.cpp diff --git a/llvm/tools/llvm-reduce/DeltaManager.cpp b/llvm/tools/llvm-reduce/DeltaManager.cpp --- a/llvm/tools/llvm-reduce/DeltaManager.cpp +++ b/llvm/tools/llvm-reduce/DeltaManager.cpp @@ -19,6 +19,7 @@ #include "deltas/ReduceArguments.h" #include "deltas/ReduceAttributes.h" #include "deltas/ReduceBasicBlocks.h" +#include "deltas/ReduceDIMetadata.h" #include "deltas/ReduceFunctionBodies.h" #include "deltas/ReduceFunctions.h" #include "deltas/ReduceGlobalObjects.h" @@ -65,6 +66,7 @@ DELTA_PASS("global-objects", reduceGlobalObjectsDeltaPass) \ DELTA_PASS("global-initializers", reduceGlobalsInitializersDeltaPass) \ DELTA_PASS("global-variables", reduceGlobalsDeltaPass) \ + DELTA_PASS("di-metadata", reduceDIMetadataDeltaPass) \ DELTA_PASS("metadata", reduceMetadataDeltaPass) \ DELTA_PASS("arguments", reduceArgumentsDeltaPass) \ DELTA_PASS("instructions", reduceInstructionsDeltaPass) \ diff --git a/llvm/tools/llvm-reduce/deltas/ReduceDIMetadata.h b/llvm/tools/llvm-reduce/deltas/ReduceDIMetadata.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceDIMetadata.h @@ -0,0 +1,23 @@ +//===- ReduceMetadata.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_REDUCEDIMETADATA_H +#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEDIMETADATA_H + +#include "TestRunner.h" + +namespace llvm { +void reduceDIMetadataDeltaPass(TestRunner &Test); +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-reduce/deltas/ReduceDIMetadata.cpp b/llvm/tools/llvm-reduce/deltas/ReduceDIMetadata.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-reduce/deltas/ReduceDIMetadata.cpp @@ -0,0 +1,102 @@ +//===- ReduceDIMetadata.cpp - Specialized Delta pass for DebugInfo --------===// +// +// 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 DebugInfo metadata nodes. +// +//===----------------------------------------------------------------------===// + +#include "ReduceDIMetadata.h" +#include "Delta.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/InstIterator.h" +#include +#include +#include +#include + +using namespace llvm; + +using MDNodeList = SmallVector; + +void identifyUninterestingMDNodes(Oracle &O, MDNodeList &MDs) { + DenseSet> Tuples; + std::vector ToLook; + DenseSet Visited; + + // Start by looking at the attachments we collected + for (const auto &NMD : MDs) + if (NMD && !Visited.count(NMD)) + ToLook.push_back(NMD); + + while (!ToLook.empty()) { + MDNode *MD = ToLook.back(); + ToLook.pop_back(); + + if (Visited.count(MD)) + continue; + + // Determine if the current MDNode is DebugInfo + if (DINode *DIM = dyn_cast_or_null(MD)) { + // Scan operands and record attached tuples + for (size_t I = 0; I < DIM->getNumOperands(); ++I) + if (MDTuple *MDT = dyn_cast_or_null(DIM->getOperand(I))) + if (!Visited.count(MDT) && MDT->getNumOperands()) + Tuples.insert({DIM, I, MDT}); + } + + // Add all of the operands of the current node to the loop's todo list. + for (Metadata *Op : MD->operands()) + if (MDNode *OMD = dyn_cast_or_null(Op)) + ToLook.push_back(OMD); + + Visited.insert(MD); + } + + for (auto &T : Tuples) { + auto [DbgNode, OpIdx, Tup] = T; + // Remove the operands of the tuple that are not in the desired chunks. + SmallVector TN; + for (size_t I = 0; I < Tup->getNumOperands(); ++I) { + // Ignore any operands that are not DebugInfo metadata nodes. + if (MDNode *OMD = dyn_cast_or_null(Tup->getOperand(I))) + // Don't add uninteresting operands to the tuple. + if (!O.shouldKeep()) + continue; + + TN.push_back(Tup->getOperand(I)); + } + if (TN.size() != Tup->getNumOperands()) + DbgNode->replaceOperandWith(OpIdx, DbgNode->get(DbgNode->getContext(), TN)); + } +} + +static void extractDIMetadataFromModule(Oracle &O, Module &Program) { + MDNodeList MDs; + // Collect all !dbg metadata attachments. + for (const auto &DC : Program.debug_compile_units()) + if (DC) + MDs.push_back(DC); + for (GlobalVariable &GV : Program.globals()) + GV.getMetadata(llvm::LLVMContext::MD_dbg, MDs); + for (Function &F : Program.functions()) { + F.getMetadata(llvm::LLVMContext::MD_dbg, MDs); + for (Instruction &I : instructions(F)) + if (auto *DI = I.getMetadata(llvm::LLVMContext::MD_dbg)) + MDs.push_back(DI); + } + identifyUninterestingMDNodes(O, MDs); +} + +void llvm::reduceDIMetadataDeltaPass(TestRunner &Test) { + outs() << "*** Reducing DIMetadata...\n"; + runDeltaPass(Test, extractDIMetadataFromModule); + outs() << "----------------------------\n"; +}