Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -2604,6 +2604,16 @@ /// that requires constrained FP intrinsics void setUsesFPIntrin(bool I) { FunctionDeclBits.UsesFPIntrin = I; } + /// Determine whether the function shadows an inline builtin definition. + bool shadowsGNUInlineIntrinsic() const { + return FunctionDeclBits.ShadowsGNUInlineIntrinsic; + } + + /// Set whether the function shadows an inline builtin definition. + void setShadowsGNUInlineIntrinsic(bool I) { + FunctionDeclBits.ShadowsGNUInlineIntrinsic = I; + } + /// Flag that this function is implicitly inline. void setImplicitlyInline(bool I = true) { FunctionDeclBits.IsInline = I; } Index: clang/include/clang/AST/DeclBase.h =================================================================== --- clang/include/clang/AST/DeclBase.h +++ clang/include/clang/AST/DeclBase.h @@ -1612,10 +1612,13 @@ /// Indicates if the function uses Floating Point Constrained Intrinsics uint64_t UsesFPIntrin : 1; + + /// Indicates that this function shadows a gnu_inline intrinsic redefinition + uint64_t ShadowsGNUInlineIntrinsic : 1; }; /// Number of non-inherited bits in FunctionDeclBitfields. - enum { NumFunctionDeclBits = 27 }; + enum { NumFunctionDeclBits = 28 }; /// Stores the bits used by CXXConstructorDecl. If modified /// NumCXXConstructorDeclBits and the accessor @@ -1632,7 +1635,7 @@ /// exactly 64 bits and thus the width of NumCtorInitializers /// will need to be shrunk if some bit is added to NumDeclContextBitfields, /// NumFunctionDeclBitfields or CXXConstructorDeclBitfields. - uint64_t NumCtorInitializers : 21; + uint64_t NumCtorInitializers : 20; uint64_t IsInheritingConstructor : 1; /// Whether this constructor has a trail-allocated explicit specifier. Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -2908,6 +2908,7 @@ FunctionDeclBits.IsMultiVersion = false; FunctionDeclBits.IsCopyDeductionCandidate = false; FunctionDeclBits.HasODRHash = false; + FunctionDeclBits.ShadowsGNUInlineIntrinsic = false; if (TrailingRequiresClause) setTrailingRequiresClause(TrailingRequiresClause); } 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) { - 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; + } + + // 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 (FD->shadowsGNUInlineIntrinsic()) { + std::string FDInlineName = (Fn->getName() + ".inline").str(); + llvm::Module *M = Fn->getParent(); + if (llvm::Function *Clone = M->getFunction(FDInlineName)) { + Clone->replaceAllUsesWith(Fn); + Clone->eraseFromParent(); + } } - Fn->setLinkage(llvm::GlobalValue::ExternalLinkage); - Fn = Clone; } // Check if we should generate debug info for this function. Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -10927,6 +10927,11 @@ } if (Redeclaration) { + FunctionDecl *OldFDecl = dyn_cast(OldDecl); + if (OldFDecl && OldFDecl->isInlineBuiltinDeclaration() && + !NewFD->isInlineBuiltinDeclaration()) + NewFD->setShadowsGNUInlineIntrinsic(true); + // NewFD and OldDecl represent declarations that need to be // merged. if (MergeFunctionDecl(NewFD, OldDecl, S, MergeTypeWithPrevious)) { Index: clang/test/CodeGen/gnu-inline-redecl.c =================================================================== --- /dev/null +++ clang/test/CodeGen/gnu-inline-redecl.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -O1 -o - %s | FileCheck %s +// +// Verifies that the gnu_inline version is ignored in favor of the redecl + +extern inline __attribute__((gnu_inline)) unsigned long some_size(int c) { + return 1; +} +unsigned long mycall(int s) { + // CHECK-LABEL: i64 @mycall + // CHECK: ret i64 2 + return some_size(s); +} +unsigned long some_size(int c) { + return 2; +} +unsigned long yourcall(int s) { + // CHECK-LABEL: i64 @yourcall + // CHECK: ret i64 2 + return some_size(s); +} Index: clang/test/CodeGen/strlen-inline-builtin-redecl.c =================================================================== --- /dev/null +++ clang/test/CodeGen/strlen-inline-builtin-redecl.c @@ -0,0 +1,20 @@ +// 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); + +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); +}