diff --git a/llvm/include/llvm/Analysis/LazyCallGraph.h b/llvm/include/llvm/Analysis/LazyCallGraph.h --- a/llvm/include/llvm/Analysis/LazyCallGraph.h +++ b/llvm/include/llvm/Analysis/LazyCallGraph.h @@ -38,6 +38,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" @@ -1082,11 +1083,16 @@ continue; } + // The blockaddress constant expression is a weird special case, we can't + // generically walk its operands the way we do for all other constants. if (BlockAddress *BA = dyn_cast(C)) { - // The blockaddress constant expression is a weird special case, we - // can't generically walk its operands the way we do for all other - // constants. - if (Visited.insert(BA->getFunction()).second) + // If any of the uses are not an instruction within the function + // referred to by the block address, add it to the worklist to go visit. + if (llvm::any_of(BA->users(), [&](User* U){ + if (Instruction *I = dyn_cast(U)) + return I->getFunction() != BA->getFunction(); + return false; + }) && Visited.insert(BA->getFunction()).second) Worklist.push_back(BA->getFunction()); continue; } 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 @@ -1830,14 +1830,18 @@ 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. + // FIXME: pr/39560: continue relaxing this overt restriction. 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. @@ -2081,13 +2085,16 @@ 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 which are used by non-callbr + // instructions. 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) { CallBase *Call = dyn_cast(&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] + +;