diff --git a/llvm/lib/Analysis/CGSCCPassManager.cpp b/llvm/lib/Analysis/CGSCCPassManager.cpp --- a/llvm/lib/Analysis/CGSCCPassManager.cpp +++ b/llvm/lib/Analysis/CGSCCPassManager.cpp @@ -464,10 +464,13 @@ // Now walk all references. for (Instruction &I : instructions(F)) - for (Value *Op : I.operand_values()) + for (Value *Op : I.operand_values()) { + if (isa(Op)) + continue; if (auto *C = dyn_cast(Op)) if (Visited.insert(C).second) Worklist.push_back(C); + } auto VisitRef = [&](Function &Referee) { Node &RefereeN = *G.lookup(Referee); diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp --- a/llvm/lib/Analysis/InlineCost.cpp +++ b/llvm/lib/Analysis/InlineCost.cpp @@ -1826,14 +1826,17 @@ if (BB->empty()) continue; - // Disallow inlining a blockaddress. A blockaddress only has defined - // behavior for an indirect branch in the same function, and we do not - // currently support inlining indirect branches. But, the inliner may not - // see an indirect branch that ends up being dead code at a particular call - // site. If the blockaddress escapes the function, e.g., via a global - // variable, inlining may lead to an invalid cross-function reference. + // Disallow inlining a blockaddress with uses other than strictly callbr. + // A blockaddress only has defined behavior for an indirect branch in the + // same function, and we do not currently support inlining indirect + // branches. But, the inliner may not see an indirect branch that ends up + // being dead code at a particular call site. If the blockaddress escapes + // the function, e.g., via a global variable, inlining may lead to an + // invalid cross-function reference. if (BB->hasAddressTaken()) - return "blockaddress"; + for (User *U : BlockAddress::get(&*BB)->users()) + if (!isa(*U)) + return "blockaddress used outside of callbr"; // Analyze the cost of this block. If we blow through the threshold, this // returns false, and we can bail on out. @@ -2077,13 +2080,15 @@ InlineResult llvm::isInlineViable(Function &F) { bool ReturnsTwice = F.hasFnAttribute(Attribute::ReturnsTwice); for (Function::iterator BI = F.begin(), BE = F.end(); BI != BE; ++BI) { - // Disallow inlining of functions which contain indirect branches or - // blockaddresses. + // Disallow inlining of functions which contain indirect branches. if (isa(BI->getTerminator())) return "contains indirect branches"; + // Disallow inlining of blockaddresses whose sole users are not callbr's. if (BI->hasAddressTaken()) - return "uses block address"; + for (User *U : BlockAddress::get(&*BI)->users()) + if (!isa(*U)) + return "blockaddress used outside of callbr"; for (auto &II : *BI) { CallSite CS(&II); diff --git a/llvm/test/Transforms/Inline/callbr.ll b/llvm/test/Transforms/Inline/callbr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Inline/callbr.ll @@ -0,0 +1,54 @@ +; RUN: opt -inline -S < %s | FileCheck %s +; RUN: opt -passes='cgscc(inline)' -S < %s | FileCheck %s + +define dso_local i32 @main() #0 { + %1 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %2 = call i32 @t32(i32 0) + ret i32 %2 +} + +define internal i32 @t32(i32) #0 { + %2 = alloca i32, align 4 + %3 = alloca i32, align 4 + store i32 %0, i32* %3, align 4 + %4 = load i32, i32* %3, align 4 + callbr void asm sideeffect "testl $0, $0; jne ${1:l};", "r,X,X,~{dirflag},~{fpsr},~{flags}"(i32 %4, i8* blockaddress(@t32, %7), i8* blockaddress(@t32, %6)) #1 + to label %5 [label %7, label %6] + +;