diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1299,18 +1299,35 @@ // 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) { - 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 (Fn) { + if (FD->isInlineBuiltinDeclaration()) { + 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); + } + Fn->setLinkage(llvm::GlobalValue::ExternalLinkage); + Fn = Clone; + } + // If the inline version is shadowed by a non-inline version, pick the + // external one. That's GCC behavior too. + else { + for (auto const *Redecl : FD->redecls()) { + if (Redecl->isInlineBuiltinDeclaration()) { + std::string FDInlineName = (Fn->getName() + ".inline").str(); + llvm::Module *M = Fn->getParent(); + llvm::Function *Clone = M->getFunction(FDInlineName); + if (Clone) { + Clone->replaceAllUsesWith(Fn); + Clone->eraseFromParent(); + } + } + } } - Fn->setLinkage(llvm::GlobalValue::ExternalLinkage); - Fn = Clone; } // Check if we should generate debug info for this function. diff --git a/clang/test/CodeGen/strlen-inline-builtin-redecl.c b/clang/test/CodeGen/strlen-inline-builtin-redecl.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/strlen-inline-builtin-redecl.c @@ -0,0 +1,42 @@ +// 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) { + unsigned long ret; + unsigned long p_size = __builtin_object_size(p, 0); + + if (p_size == (unsigned long)-1 || + (__builtin_constant_p(p[p_size - 1]) && p[p_size - 1] == '\0')) + return __builtin_strlen(p); + ret = strnlen(p, p_size); + if (p_size <= ret) + fortify_panic(__func__); + return ret; +} +char *strim(char *s) { + unsigned long size; + char *end; + + size = strlen(s); + if (!size) + return s; + + end = s + size - 1; + return end; +} +unsigned long strlen(const char *s) { + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + ; + return sc - s; +} +extern typeof(strlen) strlen;