Index: clang/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ clang/lib/CodeGen/CodeGenFunction.cpp @@ -1299,18 +1299,34 @@ // When generating code for a builtin with an inline declaration, use a // mangled name to hold the actual body, while keeping an external definition // in case the function pointer is referenced somewhere. - if (FD->isInlineBuiltinDeclaration() && Fn) { + if (Fn) { std::string FDInlineName = (Fn->getName() + ".inline").str(); llvm::Module *M = Fn->getParent(); llvm::Function *Clone = M->getFunction(FDInlineName); - if (!Clone) { - Clone = llvm::Function::Create(Fn->getFunctionType(), - llvm::GlobalValue::InternalLinkage, - Fn->getAddressSpace(), FDInlineName, M); - Clone->addFnAttr(llvm::Attribute::AlwaysInline); + if (FD->isInlineBuiltinDeclaration()) { + if (!Clone) { + Clone = llvm::Function::Create(Fn->getFunctionType(), + llvm::GlobalValue::InternalLinkage, + Fn->getAddressSpace(), FDInlineName, M); + Clone->addFnAttr(llvm::Attribute::AlwaysInline); + } + Fn->setLinkage(llvm::GlobalValue::ExternalLinkage); + Fn = Clone; + } + + // 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. Unfortunately, I cannot find a way + // to detect that situation before we reach codegen, so do some late + // replacement. + else if (Clone) { + for (auto const *Redecl : FD->redecls()) { + if (Redecl->isInlineBuiltinDeclaration()) { + Clone->replaceAllUsesWith(Fn); + Clone->eraseFromParent(); + } + } } - Fn->setLinkage(llvm::GlobalValue::ExternalLinkage); - Fn = Clone; } // Check if we should generate debug info for this function. Index: clang/test/CodeGen/strlen-inline-builtin-redecl.c =================================================================== --- /dev/null +++ clang/test/CodeGen/strlen-inline-builtin-redecl.c @@ -0,0 +1,21 @@ +// 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 + +// CHECK-NOT: strlen.inline + +unsigned long strnlen(const char *, unsigned long); +void fortify_panic(const char *); + +extern inline __attribute__((always_inline)) __attribute__((gnu_inline)) unsigned long strlen(const char *p) { + return 1; +} +unsigned long mystrlen(char const *s) { + return strlen(s); +} +unsigned long strlen(const char *s) { + return 2; +} +unsigned long yourstrlen(char const *s) { + return strlen(s); +}