diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4948,6 +4948,16 @@ return EmitCall(E->getCallee()->getType(), Callee, E, ReturnValue); } +// Detect the unusual situation where an inline version is shadowed by a +// non-inline version. In that case we should pick the external one +// everywhere. That's GCC behavior too. +static bool OnlyHasInlineBuiltinDeclaration(const FunctionDecl *FD) { + for (const FunctionDecl *PD = FD; PD; PD = PD->getPreviousDecl()) + if (!PD->isInlineBuiltinDeclaration()) + return false; + return true; +} + static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) { const FunctionDecl *FD = cast(GD.getDecl()); @@ -4955,8 +4965,8 @@ std::string FDInlineName = (FD->getName() + ".inline").str(); // When directing calling an inline builtin, call it through it's mangled // name to make it clear it's not the actual builtin. - if (FD->isInlineBuiltinDeclaration() && - CGF.CurFn->getName() != FDInlineName) { + if (CGF.CurFn->getName() != FDInlineName && + OnlyHasInlineBuiltinDeclaration(FD)) { llvm::Constant *CalleePtr = EmitFunctionDeclPointer(CGF.CGM, GD); llvm::Function *Fn = llvm::cast(CalleePtr); llvm::Module *M = Fn->getParent(); diff --git a/clang/test/CodeGen/fread-inline-builtin-late-redecl.c b/clang/test/CodeGen/fread-inline-builtin-late-redecl.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/fread-inline-builtin-late-redecl.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s +// +// Verifies that clang-generated *.inline are removed when shadowed by an +// external definition, even when that definition appears at the end of the +// file. + +// CHECK-NOT: strlen.inline + +extern unsigned long strlen(char const *s); + +extern __inline __attribute__((__always_inline__)) __attribute__((__gnu_inline__)) unsigned long strlen(char const *s) { + return 1; +} + +static unsigned long chesterfield(char const *s) { + return strlen(s); +} +static unsigned long (*_strlen)(char const *ptr); + +unsigned long blutch(char const *s) { + return chesterfield(s); +} + +unsigned long strlen(char const *s) { + return _strlen(s); +}