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 @@ -5193,6 +5193,7 @@ const FunctionDecl *FD = cast(GD.getDecl()); if (auto builtinID = FD->getBuiltinID()) { + std::string AttributeNoBuiltin = "no-builtin-" + FD->getName().str(); 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. @@ -5213,8 +5214,9 @@ // Replaceable builtins provide their own implementation of a builtin. If we // are in an inline builtin implementation, avoid trivial infinite - // recursion. - else + // recursion. Honor __attribute__((no_builtin("foo"))) on the current + // function. + else if (!CGF.CurFn->getAttributes().hasFnAttr(AttributeNoBuiltin)) return CGCallee::forBuiltin(builtinID, FD); } diff --git a/clang/test/CodeGen/no-builtin-2.c b/clang/test/CodeGen/no-builtin-2.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/no-builtin-2.c @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s + +#include + +void bar(char *s); +void *memset(void *s, int c, size_t n); +void *memcpy(void *d, const void *s, size_t n); + +// CHECK: define{{.*}} void @foo1({{.*}}) #[[NO_NOBUILTIN:[0-9]+]] +// CHECK: call void @bar +// CHECK: call void @llvm.memset +// CHECK: call void @llvm.memcpy +void foo1(char *s, char *d, size_t n) +{ + bar(s); + memset(s, 0, n); + memcpy(d, s, n); +} + +// CHECK: define{{.*}} void @foo2({{.*}}) #[[NOBUILTIN_MEMSET:[0-9]+]] +// CHECK: call void @bar +// CHECK: {{.*}}call i8* @memset +// CHECK: call void @llvm.memcpy +void foo2(char *s, char *d, size_t n) __attribute__((no_builtin("memset"))) +{ + bar(s); + memset(s, 1, n); + memcpy(d, s, n); +} + +// CHECK: define{{.*}} void @foo3({{.*}}) #[[NOBUILTIN_MEMSET_MEMCPY:[0-9]+]] +// CHECK: call void @bar +// CHECK: {{.*}}call i8* @memset +// CHECK: {{.*}}call i8* @memcpy +void foo3(char *s, char *d, size_t n) __attribute__((no_builtin("memset", "memcpy"))) +{ + bar(s); + memset(s, 2, n); + memcpy(d, s, n); +} + +// CHECK-NOT: attributes #[[NO_NOBUILTIN]] = {{{.*}}"no-builtin-memset"{{.*}}} +// CHECK-NOT: attributes #[[NO_NOBUILTIN]] = {{{.*}}"no-builtin-memcpy"{{.*}}"no-builtin-memset"{{.*}}} +// CHECK: attributes #[[NOBUILTIN_MEMSET]] = {{{.*}}"no-builtin-memset"{{.*}}} +// CHECK: attributes #[[NOBUILTIN_MEMSET_MEMCPY]] = {{{.*}}"no-builtin-memcpy"{{.*}}"no-builtin-memset"{{.*}}}