diff --git a/llvm/test/BugPoint/metadata.ll b/llvm/test/BugPoint/metadata.ll --- a/llvm/test/BugPoint/metadata.ll +++ b/llvm/test/BugPoint/metadata.ll @@ -10,21 +10,24 @@ ; ; Bugpoint should keep the call's metadata attached to the call. -; CHECK: call void @foo(), !dbg ![[LOC:[0-9]+]], !attach ![[CALL:[0-9]+]] -; NODEBUG: call void @foo(), !attach ![[CALL:[0-9]+]] -; NOTYPE: call void @foo(), !dbg ![[LOC:[0-9]+]], !attach ![[CALL:[0-9]+]] -; NODEBUG-NOT: call void @foo(), !attach ![[CALL:[0-9]+]] +; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t-notype -bugpoint-crashmetadata -silence-passes > /dev/null +; RUN: llvm-dis %t-notype-reduced-simplified.bc -o - | FileCheck %s --check-prefix=RANGE +; + +; RANGE: %x = call i8 @bar(), !range ![[RANGE:[0-9]+]] +; CHECK: call void @foo() +; NODEBUG: call void @foo() +; NOTYPE: call void @foo() +; NODEBUG-NOT: call void @foo() ; NOTYPE-NOT: !DIBasicType ; NOTYPE: !DICompileUnit ; NOTYPE-NOT: !DIBasicType -; CHECK-DAG: ![[LOC]] = !DILocation(line: 104, column: 105, scope: ![[SCOPE:[0-9]+]]) -; CHECK-DAG: ![[SCOPE]] = distinct !DISubprogram(name: "test",{{.*}}file: ![[FILE:[0-9]+]] -; CHECK-DAG: ![[FILE]] = !DIFile(filename: "source.c", directory: "/dir") -; CHECK-DAG: ![[CALL]] = !{!"the call to foo"} +; RANGE: ![[RANGE]] = !{i8 0, i8 2} %rust_task = type {} define void @test(i32* %a, i8* %b) { - %s = mul i8 22, 9, !attach !0, !dbg !10 + %x = call i8 @bar(), !range !21 + %s = mul i8 22, %x, !attach !0, !dbg !10 store i8 %s, i8* %b, !attach !1, !dbg !11 call void @foo(), !attach !2, !dbg !12 store i32 7, i32* %a, !attach !3, !dbg !13 @@ -33,6 +36,7 @@ } declare void @foo() +declare i8 @bar() !llvm.module.flags = !{!17} !llvm.dbg.cu = !{!8} @@ -56,3 +60,4 @@ !18 = !DISubroutineType(types: !19) !19 = !{!20, !20} !20 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!21 = !{i8 0, i8 2} diff --git a/llvm/tools/bugpoint-passes/TestPasses.cpp b/llvm/tools/bugpoint-passes/TestPasses.cpp --- a/llvm/tools/bugpoint-passes/TestPasses.cpp +++ b/llvm/tools/bugpoint-passes/TestPasses.cpp @@ -13,6 +13,7 @@ #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constant.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/InstVisitor.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Type.h" @@ -147,3 +148,28 @@ static RegisterPass B("bugpoint-crashfuncattr", "BugPoint Test Pass - Intentionally crash on " "function attribute 'bugpoint-crash'"); + +namespace { +class CrashOnMetadata : public FunctionPass { +public: + static char ID; // Pass ID, replacement for typeid + CrashOnMetadata() : FunctionPass(ID) {} + +private: + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + bool runOnFunction(Function &F) override { + for (Instruction &I : instructions(F)) + if (I.hasMetadata("range")) + abort(); + return false; + } +}; +} // namespace + +char CrashOnMetadata::ID = 0; +static RegisterPass + C("bugpoint-crashmetadata", "BugPoint Test Pass - Intentionally crash on " + "instructions with tbaa metadata"); diff --git a/llvm/tools/bugpoint/CrashDebugger.cpp b/llvm/tools/bugpoint/CrashDebugger.cpp --- a/llvm/tools/bugpoint/CrashDebugger.cpp +++ b/llvm/tools/bugpoint/CrashDebugger.cpp @@ -818,6 +818,74 @@ return false; } +namespace { +/// ReduceCrashingMetadata reducer - This works by removing all metadata from +/// the specified instructions. +/// +class ReduceCrashingMetadata : public ListReducer { + BugDriver &BD; + BugTester TestFn; + +public: + ReduceCrashingMetadata(BugDriver &bd, BugTester testFn) + : BD(bd), TestFn(testFn) {} + + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { + if (!Kept.empty() && TestInsts(Kept)) + return KeepSuffix; + if (!Prefix.empty() && TestInsts(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestInsts(std::vector &Prefix); +}; +} // namespace + +bool ReduceCrashingMetadata::TestInsts(std::vector &Insts) { + // Clone the program to try hacking it apart... + ValueToValueMapTy VMap; + std::unique_ptr M = CloneModule(BD.getProgram(), VMap); + + // Convert list to set for fast lookup... + SmallPtrSet Instructions; + for (Instruction *I : Insts) + Instructions.insert(cast(VMap[I])); + + outs() << "Checking for crash with metadata removed from " + << Instructions.size(); + if (Instructions.size() == 1) + outs() << " instruction: "; + else + outs() << " instructions: "; + + for (Function &F : *M) + for (Instruction &Inst : instructions(F)) { + Inst.dropUnknownNonDebugMetadata(); + Inst.setDebugLoc({}); + } + + // Verify that this is still valid. + legacy::PassManager Passes; + Passes.add(createVerifierPass(/*FatalErrors=*/false)); + Passes.run(*M); + + // Try running on the hacked up program... + if (TestFn(BD, M.get())) { + BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... + + // Make sure to use instruction pointers that point into the now-current + // module, and that they don't include any deleted blocks. + Insts.clear(); + for (Instruction *I : Instructions) + Insts.push_back(I); + return true; + } + // It didn't crash, try something else. + return false; +} + namespace { // Reduce the list of Named Metadata nodes. We keep this as a list of // names to avoid having to convert back and forth every time. @@ -1094,6 +1162,21 @@ } } while (Simplification); + + // Attempt to drop metadata from instructions that does not contribute to the + // crash. + if (!BugpointIsInterrupted) { + std::vector Insts; + for (Function &F : BD.getProgram()) + for (Instruction &I : instructions(F)) + Insts.push_back(&I); + + Expected Result = + ReduceCrashingMetadata(BD, TestFn).reduceList(Insts); + if (Error E = Result.takeError()) + return E; + } + BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions"); return Error::success(); }