Index: llvm/trunk/test/BugPoint/func-attrs-keyval.ll =================================================================== --- llvm/trunk/test/BugPoint/func-attrs-keyval.ll +++ llvm/trunk/test/BugPoint/func-attrs-keyval.ll @@ -0,0 +1,11 @@ +; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t -bugpoint-crashfuncattr -silence-passes +; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s +; REQUIRES: loadable_module + +; CHECK: f() #[[ATTRS:[0-9]+]] +define void @f() #0 { + ret void +} + +; CHECK: attributes #[[ATTRS]] = { "bugpoint-crash"="sure" } +attributes #0 = { "bugpoint-crash"="sure" noreturn "no-frame-pointer-elim-non-leaf" } Index: llvm/trunk/test/BugPoint/func-attrs.ll =================================================================== --- llvm/trunk/test/BugPoint/func-attrs.ll +++ llvm/trunk/test/BugPoint/func-attrs.ll @@ -0,0 +1,11 @@ +; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t -bugpoint-crashfuncattr -silence-passes +; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s +; REQUIRES: loadable_module + +; CHECK: f() #[[ATTRS:[0-9]+]] +define void @f() #0 { + ret void +} + +; CHECK: attributes #[[ATTRS]] = { "bugpoint-crash" } +attributes #0 = { noinline "bugpoint-crash" "no-frame-pointer-elim-non-leaf" } Index: llvm/trunk/tools/bugpoint-passes/TestPasses.cpp =================================================================== --- llvm/trunk/tools/bugpoint-passes/TestPasses.cpp +++ llvm/trunk/tools/bugpoint-passes/TestPasses.cpp @@ -123,3 +123,28 @@ static RegisterPass A("bugpoint-crash-too-many-cus", "BugPoint Test Pass - Intentionally crash on too many CUs"); + +namespace { +class CrashOnFunctionAttribute : public FunctionPass { +public: + static char ID; // Pass ID, replacement for typeid + CrashOnFunctionAttribute() : FunctionPass(ID) {} + +private: + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + bool runOnFunction(Function &F) override { + AttributeSet A = F.getAttributes().getFnAttributes(); + if (A.hasAttribute("bugpoint-crash")) + abort(); + return false; + } +}; +} // namespace + +char CrashOnFunctionAttribute::ID = 0; +static RegisterPass + B("bugpoint-crashfuncattr", "BugPoint Test Pass - Intentionally crash on " + "function attribute 'bugpoint-crash'"); Index: llvm/trunk/tools/bugpoint/CrashDebugger.cpp =================================================================== --- llvm/trunk/tools/bugpoint/CrashDebugger.cpp +++ llvm/trunk/tools/bugpoint/CrashDebugger.cpp @@ -315,6 +315,66 @@ } namespace { +/// ReduceCrashingFunctionAttributes reducer - This works by removing +/// attributes on a particular function and seeing if the program still crashes. +/// If it does, then keep the newer, smaller program. +/// +class ReduceCrashingFunctionAttributes : public ListReducer { + BugDriver &BD; + std::string FnName; + BugTester TestFn; + +public: + ReduceCrashingFunctionAttributes(BugDriver &bd, const std::string &FnName, + BugTester testFn) + : BD(bd), FnName(FnName), TestFn(testFn) {} + + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { + if (!Kept.empty() && TestFuncAttrs(Kept)) + return KeepSuffix; + if (!Prefix.empty() && TestFuncAttrs(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestFuncAttrs(std::vector &Attrs); +}; +} + +bool ReduceCrashingFunctionAttributes::TestFuncAttrs( + std::vector &Attrs) { + // Clone the program to try hacking it apart... + std::unique_ptr M = CloneModule(BD.getProgram()); + Function *F = M->getFunction(FnName); + + // Build up an AttributeList from the attributes we've been given by the + // reducer. + AttrBuilder AB; + for (auto A : Attrs) + AB.addAttribute(A); + AttributeList NewAttrs; + NewAttrs = + NewAttrs.addAttributes(BD.getContext(), AttributeList::FunctionIndex, AB); + + // Set this new list of attributes on the function. + F->setAttributes(NewAttrs); + + // Try running on the hacked up program... + if (TestFn(BD, M.get())) { + BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... + + // Pass along the set of attributes that caused the crash. + Attrs.clear(); + for (Attribute A : NewAttrs.getFnAttributes()) { + Attrs.push_back(A); + } + return true; + } + return false; +} + +namespace { /// Simplify the CFG without completely destroying it. /// This is not well defined, but basically comes down to "try to eliminate /// unreachable blocks and constant fold terminators without deciding that @@ -1056,6 +1116,38 @@ BD.EmitProgressBitcode(BD.getProgram(), "reduced-function"); } + // For each remaining function, try to reduce that function's attributes. + std::vector FunctionNames; + for (Function &F : BD.getProgram()) + FunctionNames.push_back(F.getName()); + + if (!FunctionNames.empty() && !BugpointIsInterrupted) { + outs() << "\n*** Attempting to reduce the number of function attributes in " + "the testcase\n"; + + unsigned OldSize = 0; + unsigned NewSize = 0; + for (std::string &Name : FunctionNames) { + Function *Fn = BD.getProgram().getFunction(Name); + assert(Fn && "Could not find funcion?"); + + std::vector Attrs; + for (Attribute A : Fn->getAttributes().getFnAttributes()) + Attrs.push_back(A); + + OldSize += Attrs.size(); + Expected Result = + ReduceCrashingFunctionAttributes(BD, Name, TestFn).reduceList(Attrs); + if (Error E = Result.takeError()) + return E; + + NewSize += Attrs.size(); + } + + if (OldSize < NewSize) + BD.EmitProgressBitcode(BD.getProgram(), "reduced-function-attributes"); + } + // Attempt to change conditional branches into unconditional branches to // eliminate blocks. if (!DisableSimplifyCFG && !BugpointIsInterrupted) {