diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.h b/llvm/include/llvm/Analysis/TargetLibraryInfo.h --- a/llvm/include/llvm/Analysis/TargetLibraryInfo.h +++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.h @@ -192,6 +192,11 @@ /// vector functions. void getWidestVF(StringRef ScalarF, ElementCount &FixedVF, ElementCount &Scalable) const; + + /// Returns true if call site / callee has cdecl-compatible calling + /// conventions. + static bool isCallingConvCCompatible(CallBase *CI); + static bool isCallingConvCCompatible(Function *Callee); }; /// Provides information about what library functions are available for diff --git a/llvm/lib/Analysis/TargetLibraryInfo.cpp b/llvm/lib/Analysis/TargetLibraryInfo.cpp --- a/llvm/lib/Analysis/TargetLibraryInfo.cpp +++ b/llvm/lib/Analysis/TargetLibraryInfo.cpp @@ -65,6 +65,49 @@ return TT.isOSFreeBSD() || TT.isOSSolaris(); } +static bool isCallingConvCCompatible(CallingConv::ID CC, Triple TT, + FunctionType *FuncTy) { + switch (CC) { + default: + return false; + case llvm::CallingConv::C: + return true; + case llvm::CallingConv::ARM_APCS: + case llvm::CallingConv::ARM_AAPCS: + case llvm::CallingConv::ARM_AAPCS_VFP: { + + // The iOS ABI diverges from the standard in some cases, so for now don't + // try to simplify those calls. + if (TT.isiOS()) + return false; + + if (!FuncTy->getReturnType()->isPointerTy() && + !FuncTy->getReturnType()->isIntegerTy() && + !FuncTy->getReturnType()->isVoidTy()) + return false; + + for (auto *Param : FuncTy->params()) { + if (!Param->isPointerTy() && !Param->isIntegerTy()) + return false; + } + return true; + } + } + return false; +} + +bool TargetLibraryInfoImpl::isCallingConvCCompatible(CallBase *CI) { + return ::isCallingConvCCompatible(CI->getCallingConv(), + Triple(CI->getModule()->getTargetTriple()), + CI->getFunctionType()); +} + +bool TargetLibraryInfoImpl::isCallingConvCCompatible(Function *F) { + return ::isCallingConvCCompatible(F->getCallingConv(), + Triple(F->getParent()->getTargetTriple()), + F->getFunctionType()); +} + /// Initialize the set of available library functions based on the specified /// target triple. This should be carefully written so that a missing target /// triple gets a sane set of defaults. diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -2120,7 +2120,11 @@ // If the call and callee calling conventions don't match, this call must // be unreachable, as the call is undefined. - if (CalleeF->getCallingConv() != Call.getCallingConv() && + if ((CalleeF->getCallingConv() != Call.getCallingConv() && + !(CalleeF->getCallingConv() == llvm::CallingConv::C && + TargetLibraryInfoImpl::isCallingConvCCompatible(&Call)) && + !(Call.getCallingConv() == llvm::CallingConv::C && + TargetLibraryInfoImpl::isCallingConvCCompatible(CalleeF))) && // Only do this for calls to a function with a body. A prototype may // not actually end up matching the implementation's calling conv for a // variety of reasons (e.g. it may be written in assembly). diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp --- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -56,38 +56,6 @@ Func == LibFunc_llabs || Func == LibFunc_strlen; } -static bool isCallingConvCCompatible(CallInst *CI) { - switch(CI->getCallingConv()) { - default: - return false; - case llvm::CallingConv::C: - return true; - case llvm::CallingConv::ARM_APCS: - case llvm::CallingConv::ARM_AAPCS: - case llvm::CallingConv::ARM_AAPCS_VFP: { - - // The iOS ABI diverges from the standard in some cases, so for now don't - // try to simplify those calls. - if (Triple(CI->getModule()->getTargetTriple()).isiOS()) - return false; - - auto *FuncTy = CI->getFunctionType(); - - if (!FuncTy->getReturnType()->isPointerTy() && - !FuncTy->getReturnType()->isIntegerTy() && - !FuncTy->getReturnType()->isVoidTy()) - return false; - - for (auto Param : FuncTy->params()) { - if (!Param->isPointerTy() && !Param->isIntegerTy()) - return false; - } - return true; - } - } - return false; -} - /// Return true if it is only used in equality comparisons with With. static bool isOnlyUsedInEqualityComparison(Value *V, Value *With) { for (User *U : V->users()) { @@ -2862,9 +2830,10 @@ // Check for string/memory library functions. if (TLI->getLibFunc(*Callee, Func) && TLI->has(Func)) { // Make sure we never change the calling convention. - assert((ignoreCallingConv(Func) || - isCallingConvCCompatible(CI)) && - "Optimizing string/memory libcall would change the calling convention"); + assert( + (ignoreCallingConv(Func) || + TargetLibraryInfoImpl::isCallingConvCCompatible(CI)) && + "Optimizing string/memory libcall would change the calling convention"); switch (Func) { case LibFunc_strcat: return optimizeStrCat(CI, Builder); @@ -3048,7 +3017,7 @@ LibFunc Func; Function *Callee = CI->getCalledFunction(); - bool isCallingConvC = isCallingConvCCompatible(CI); + bool IsCallingConvC = TargetLibraryInfoImpl::isCallingConvCCompatible(CI); SmallVector OpBundles; CI->getOperandBundlesAsDefs(OpBundles); @@ -3066,7 +3035,7 @@ // First, check for intrinsics. if (IntrinsicInst *II = dyn_cast(CI)) { - if (!isCallingConvC) + if (!IsCallingConvC) return nullptr; // The FP intrinsics have corresponding constrained versions so we don't // need to check for the StrictFP attribute here. @@ -3119,7 +3088,7 @@ // Then check for known library functions. if (TLI->getLibFunc(*Callee, Func) && TLI->has(Func)) { // We never change the calling convention. - if (!ignoreCallingConv(Func) && !isCallingConvC) + if (!ignoreCallingConv(Func) && !IsCallingConvC) return nullptr; if (Value *V = optimizeStringMemoryLibCall(CI, Builder)) return V; @@ -3503,7 +3472,7 @@ LibFunc Func; Function *Callee = CI->getCalledFunction(); - bool isCallingConvC = isCallingConvCCompatible(CI); + bool IsCallingConvC = TargetLibraryInfoImpl::isCallingConvCCompatible(CI); SmallVector OpBundles; CI->getOperandBundlesAsDefs(OpBundles); @@ -3517,7 +3486,7 @@ return nullptr; // We never change the calling convention. - if (!ignoreCallingConv(Func) && !isCallingConvC) + if (!ignoreCallingConv(Func) && !IsCallingConvC) return nullptr; switch (Func) { diff --git a/llvm/test/Transforms/InstCombine/call-callconv-mismatch.ll b/llvm/test/Transforms/InstCombine/call-callconv-mismatch.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/call-callconv-mismatch.ll @@ -0,0 +1,22 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s +; Verify that a cdecl-compatible calling convention does not trigger emitting +; unreachable idom `store i1 true, i1* undef`. + +define arm_aapcs_vfpcc i8 @bar(i8* %0) { +; CHECK-LABEL: @bar( +; CHECK-NEXT: [[TMP2:%.*]] = load i8, i8* [[TMP0:%.*]], align 1 +; CHECK-NEXT: ret i8 [[TMP2]] +; + %2 = load i8, i8* %0, align 1 + ret i8 %2 +} + +define dso_local arm_aapcs_vfpcc i8 @foo(i8* %0) { +; CHECK-LABEL: @foo( +; CHECK-NEXT: [[TMP2:%.*]] = call i8 @bar(i8* [[TMP0:%.*]]) +; CHECK-NEXT: ret i8 [[TMP2]] +; + %2 = call i8 @bar(i8* %0) + ret i8 %2 +}