diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2272,6 +2272,9 @@ /// true through IsAligned. bool isReplaceableGlobalAllocationFunction(bool *IsAligned = nullptr) const; + /// Determine if this function provides the implementation of a system Builtin + bool isReplaceableSystemFunction() const; + /// Determine whether this is a destroying operator delete. bool isDestroyingOperatorDelete() const; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3003,6 +3003,15 @@ return Params == FPT->getNumParams(); } +bool FunctionDecl::isReplaceableSystemFunction() const { + FunctionDecl const *Definition; + if (hasBody(Definition)) { + const SourceManager &SM = getASTContext().getSourceManager(); + return SM.isInSystemHeader(Definition->getLocation()); + } + return false; +} + bool FunctionDecl::isDestroyingOperatorDelete() const { // C++ P0722: // Within a class C, a single object deallocation function with signature @@ -3165,6 +3174,12 @@ // function. Determine whether it actually refers to the C library // function or whether it just has the same name. + // If a system-level body was provided, use it instead of the intrinsic. Some + // C library do that to implement fortified version. + if (isReplaceableSystemFunction()) { + return 0; + } + // If this is a static function, it's not a builtin. if (!ConsiderWrapperFunctions && getStorageClass() == SC_Static) return 0; diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1831,6 +1831,11 @@ else if (const auto *SA = FD->getAttr()) F->setSection(SA->getName()); + if (FD->isReplaceableSystemFunction()) { + F->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoBuiltin); + } + if (FD->isReplaceableGlobalAllocationFunction()) { // A replaceable global allocation function does not act like a builtin by // default, only if it is invoked by a new-expression or delete-expression. diff --git a/clang/test/CodeGen/memcpy-nobuitin.c b/clang/test/CodeGen/memcpy-nobuitin.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/memcpy-nobuitin.c @@ -0,0 +1,8 @@ +// RUN: clang -S -emit-llvm -o- %s -isystem . -DWITH_DECL | FileCheck --check-prefix=CHECK-WITH-DECL %s +// RUN: clang -S -emit-llvm -o- %s -isystem . -UWITH_DECL | FileCheck --check-prefix=CHECK-NO-DECL %s +// CHECK-WITH-DECL-NOT: @llvm.memcpy +// CHECK-NO-DECL: @llvm.memcpy +#include +void test(void *dest, void const *from, size_t n) { + memcpy(dest, from, n); +} diff --git a/clang/test/CodeGen/memcpy-nobuitin.inc b/clang/test/CodeGen/memcpy-nobuitin.inc new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/memcpy-nobuitin.inc @@ -0,0 +1,12 @@ +#include +extern void *memcpy(void *dest, void const *from, size_t n); + +#ifdef WITH_DECL +inline void *memcpy(void *dest, void const *from, size_t n) { + char const *ifrom = from; + char *idest = dest; + while (n--) + *idest++ = *ifrom++; + return dest; +} +#endif