diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -307,6 +307,10 @@ - The ``__declspec(naked)`` attribute can no longer be written on a member function in Microsoft compatibility mode, matching the behavior of cl.exe. +- Attribute ``no_builtin`` should now affect the generated code. It now disables + builtins (corresponding to the specific names listed in the attribute) in the + body of the function the attribute is on. + Windows Support --------------- diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -6000,9 +6000,6 @@ def NoBuiltinDocs : Documentation { let Category = DocCatFunction; let Content = [{ -.. Note:: This attribute is not yet fully implemented, it is validated but has - no effect on the generated code. - The ``__attribute__((no_builtin))`` is similar to the ``-fno-builtin`` flag except it is specific to the body of a function. The attribute may also be applied to a virtual function but has no effect on the behavior of overriding 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 @@ -5034,7 +5034,16 @@ const FunctionDecl *FD = cast(GD.getDecl()); if (auto builtinID = FD->getBuiltinID()) { + std::string NoBuiltinFD = ("no-builtin-" + FD->getName()).str(); + std::string NoBuiltins = "no-builtins"; std::string FDInlineName = (FD->getName() + ".inline").str(); + + bool IsPredefinedLibFunction = + CGF.getContext().BuiltinInfo.isPredefinedLibFunction(builtinID); + bool HasAttributeNoBuiltin = + CGF.CurFn->getAttributes().hasFnAttr(NoBuiltinFD) || + CGF.CurFn->getAttributes().hasFnAttr(NoBuiltins); + // When directing calling an inline builtin, call it through it's mangled // name to make it clear it's not the actual builtin. if (CGF.CurFn->getName() != FDInlineName && @@ -5054,8 +5063,11 @@ // 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"))) or + // __attribute((no_builtin("*"))) on the current function unless foo is + // not a predefined library function which means we must generate the + // builtin no matter what. + else if (!IsPredefinedLibFunction || !HasAttributeNoBuiltin) return CGCallee::forBuiltin(builtinID, FD); } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/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) 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,63 @@ +// 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); +void *memmove(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 +// CHECK: call void @llvm.memmove +void foo1(char *s, char *d, size_t n) { + bar(s); + memset(s, 0, n); + memcpy(d, s, n); + memmove(d, s, n); +} + +// CHECK: define{{.*}} void @foo2({{.*}}) #[[NOBUILTIN_MEMSET:[0-9]+]] +// CHECK: call void @bar +// CHECK: {{.*}}call {{.*}} @memset +// CHECK: call void @llvm.memcpy +// CHECK: call void @llvm.memmove +void foo2(char *s, char *d, size_t n) __attribute__((no_builtin("memset"))) { + bar(s); + memset(s, 1, n); + memcpy(d, s, n); + memmove(d, s, n); +} + +// CHECK: define{{.*}} void @foo3({{.*}}) #[[NOBUILTIN_MEMSET_MEMCPY:[0-9]+]] +// CHECK: call void @bar +// CHECK: {{.*}}call {{.*}} @memset +// CHECK: {{.*}}call {{.*}} @memcpy +// CHECK: call void @llvm.memmove +void foo3(char *s, char *d, size_t n) __attribute__((no_builtin("memset", "memcpy"))) { + bar(s); + memset(s, 2, n); + memcpy(d, s, n); + memmove(d, s, n); +} + +// CHECK: define{{.*}} void @foo4({{.*}}) #[[NOBUILTINS:[0-9]+]] +// CHECK: call void @bar +// CHECK: {{.*}}call {{.*}} @memset +// CHECK: {{.*}}call {{.*}} @memcpy +// CHECK: {{.*}}call {{.*}} @memmove +void foo4(char *s, char *d, size_t n) __attribute__((no_builtin("*"))) { + bar(s); + memset(s, 2, n); + memcpy(d, s, n); + memmove(s, d, n); +} + +// CHECK-NOT: attributes #[[NO_NOBUILTIN]] = {{{.*}}"no-builtin-memset"{{.*}}} +// CHECK-NOT: attributes #[[NO_NOBUILTIN]] = {{{.*}}"no-builtin-memcpy"{{.*}}"no-builtin-memset"{{.*}}} +// CHECK-NOT: attributes #[[NO_NOBUILTIN]] = {{{.*}}"no-builtins"{{.*}}} +// CHECK: attributes #[[NOBUILTIN_MEMSET]] = {{{.*}}"no-builtin-memset"{{.*}}} +// CHECK: attributes #[[NOBUILTIN_MEMSET_MEMCPY]] = {{{.*}}"no-builtin-memcpy"{{.*}}"no-builtin-memset"{{.*}}} +// CHECK: attributes #[[NOBUILTINS]] = {{{.*}}"no-builtins"{{.*}}}