diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake --- a/llvm/cmake/modules/HandleLLVMOptions.cmake +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake @@ -229,10 +229,10 @@ if( LLVM_USE_LINKER ) set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fuse-ld=${LLVM_USE_LINKER}") - check_cxx_source_compiles("int main() { return 0; }" CXX_SUPPORTS_CUSTOM_LINKER) - if ( NOT CXX_SUPPORTS_CUSTOM_LINKER ) - message(FATAL_ERROR "Host compiler does not support '-fuse-ld=${LLVM_USE_LINKER}'") - endif() + #check_cxx_source_compiles("int main() { return 0; }" CXX_SUPPORTS_CUSTOM_LINKER) + #if ( NOT CXX_SUPPORTS_CUSTOM_LINKER ) + #message(FATAL_ERROR "Host compiler does not support '-fuse-ld=${LLVM_USE_LINKER}'") + #endif() set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) append("-fuse-ld=${LLVM_USE_LINKER}" CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) 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 @@ -1082,12 +1082,21 @@ 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. + bool AllUsersAreSameFn = true; + for (User *U : BA->users()) + if (Instruction *I = dyn_cast(U)) + if (I->getFunction() != BA->getFunction()) { + AllUsersAreSameFn = false; + break; + } if (Visited.insert(BA->getFunction()).second) - Worklist.push_back(BA->getFunction()); + // If the blockaddress' function it refers to is a function it's not + // defined within, visit the other function. + if (!AllUsersAreSameFn) + 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 @@ -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] + +;