Index: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -5034,6 +5034,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. @@ -5054,8 +5055,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); } Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -1142,7 +1142,8 @@ if (!S.checkStringLiteralArgumentAttr(AL, I, BuiltinName, &LiteralLoc)) return; - if (Builtin::Context::isBuiltinFunc(BuiltinName)) + bool ParsedAttrIsWildCard = BuiltinName == kWildcard; + if (Builtin::Context::isBuiltinFunc(BuiltinName) || ParsedAttrIsWildCard) AddBuiltinName(BuiltinName); else S.Diag(LiteralLoc, diag::warn_attribute_no_builtin_invalid_builtin_name) Index: clang/test/CodeGen/no-builtin-2.c =================================================================== --- /dev/null +++ clang/test/CodeGen/no-builtin-2.c @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s + +typedef typeof(sizeof(0)) size_t; + +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 {{.*}} @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 {{.*}} @memset +// CHECK: {{.*}}call {{.*}} @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"{{.*}}}