Index: llvm/trunk/include/llvm/Analysis/LazyCallGraph.h =================================================================== --- llvm/trunk/include/llvm/Analysis/LazyCallGraph.h +++ llvm/trunk/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,12 +1083,26 @@ 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) - Worklist.push_back(BA->getFunction()); + // If we've already visited the function referred to by the block + // address, we don't need to revisit it. + if (Visited.count(BA->getFunction())) + continue; + + // If all of the blockaddress' users are instructions within the + // referred to function, we don't need to insert a cycle. + if (llvm::all_of(BA->users(), [&](User *U) { + if (Instruction *I = dyn_cast(U)) + return I->getFunction() == BA->getFunction(); + return false; + })) + continue; + + // Otherwise we should go visit the referred to function. + Visited.insert(BA->getFunction()); + Worklist.push_back(BA->getFunction()); continue; } Index: llvm/trunk/lib/Analysis/InlineCost.cpp =================================================================== --- llvm/trunk/lib/Analysis/InlineCost.cpp +++ llvm/trunk/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); Index: llvm/trunk/test/Transforms/Inline/blockaddress.ll =================================================================== --- llvm/trunk/test/Transforms/Inline/blockaddress.ll +++ llvm/trunk/test/Transforms/Inline/blockaddress.ll @@ -49,3 +49,82 @@ } @run.bb = global [1 x i8*] zeroinitializer + +; Check that a function referenced by a global blockaddress wont be inlined, +; even if it contains a callbr. We might be able to relax this in the future +; as long as the global blockaddress is updated correctly. +@ba = internal global i8* blockaddress(@foo, %7), align 8 +define internal i32 @foo(i32) { + %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(@foo, %7), i8* blockaddress(@foo, %6)) #1 + to label %5 [label %7, label %6] + +;