Skip to content

Commit 98f3151

Browse files
committed
[clang] Add no_builtin attribute
Summary: This is a follow up on https://reviews.llvm.org/D61634 This patch is simpler and only adds the no_builtin attribute. Reviewers: tejohnson, courbet, theraven, t.p.northover, jdoerfert Subscribers: mgrang, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D68028 This is a re-submit after it got reverted in https://reviews.llvm.org/rGbd8791610948 since the breakage doesn't seem to come from this patch.
1 parent 3011c7e commit 98f3151

File tree

10 files changed

+269
-4
lines changed

10 files changed

+269
-4
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2031,6 +2031,10 @@ class FunctionDecl : public DeclaratorDecl,
20312031
///
20322032
/// This does not determine whether the function has been defined (e.g., in a
20332033
/// previous definition); for that information, use isDefined.
2034+
///
2035+
/// Note: the function declaration does not become a definition until the
2036+
/// parser reaches the definition, if called before, this function will return
2037+
/// `false`.
20342038
bool isThisDeclarationADefinition() const {
20352039
return isDeletedAsWritten() || isDefaulted() || Body || hasSkippedBody() ||
20362040
isLateTemplateParsed() || willHaveBody() || hasDefiningAttr();

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3427,3 +3427,10 @@ def ObjCExternallyRetained : InheritableAttr {
34273427
let Subjects = SubjectList<[NonParmVar, Function, Block, ObjCMethod]>;
34283428
let Documentation = [ObjCExternallyRetainedDocs];
34293429
}
3430+
3431+
def NoBuiltin : Attr {
3432+
let Spellings = [Clang<"no_builtin">];
3433+
let Args = [VariadicStringArgument<"BuiltinNames">];
3434+
let Subjects = SubjectList<[Function]>;
3435+
let Documentation = [NoBuiltinDocs];
3436+
}

clang/include/clang/Basic/AttrDocs.td

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4413,3 +4413,40 @@ and is not a general mechanism for declaring arbitrary aliases for
44134413
clang builtin functions.
44144414
}];
44154415
}
4416+
4417+
def NoBuiltinDocs : Documentation {
4418+
let Category = DocCatFunction;
4419+
let Content = [{
4420+
.. Note:: This attribute is not yet fully implemented, it is validated but has
4421+
no effect on the generated code.
4422+
4423+
The ``__attribute__((no_builtin))`` is similar to the ``-fno-builtin`` flag
4424+
except it is specific to the body of a function. The attribute may also be
4425+
applied to a virtual function but has no effect on the behavior of overriding
4426+
functions in a derived class.
4427+
4428+
It accepts one or more strings corresponding to the specific names of the
4429+
builtins to disable (e.g. "memcpy", "memset").
4430+
If the attribute is used without parameters it will disable all buitins at
4431+
once.
4432+
4433+
.. code-block:: c++
4434+
4435+
// The compiler is not allowed to add any builtin to foo's body.
4436+
void foo(char* data, size_t count) __attribute__((no_builtin)) {
4437+
// The compiler is not allowed to convert the loop into
4438+
// `__builtin_memset(data, 0xFE, count);`.
4439+
for (size_t i = 0; i < count; ++i)
4440+
data[i] = 0xFE;
4441+
}
4442+
4443+
// The compiler is not allowed to add the `memcpy` builtin to bar's body.
4444+
void bar(char* data, size_t count) __attribute__((no_builtin("memcpy"))) {
4445+
// The compiler is allowed to convert the loop into
4446+
// `__builtin_memset(data, 0xFE, count);` but cannot generate any
4447+
// `__builtin_memcpy`
4448+
for (size_t i = 0; i < count; ++i)
4449+
data[i] = 0xFE;
4450+
}
4451+
}];
4452+
}

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3604,6 +3604,15 @@ def err_attribute_overloadable_no_prototype : Error<
36043604
def err_attribute_overloadable_multiple_unmarked_overloads : Error<
36053605
"at most one overload for a given name may lack the 'overloadable' "
36063606
"attribute">;
3607+
def warn_attribute_no_builtin_invalid_builtin_name : Warning<
3608+
"'%0' is not a valid builtin name for %1">,
3609+
InGroup<DiagGroup<"invalid-no-builtin-names">>;
3610+
def err_attribute_no_builtin_wildcard_or_builtin_name : Error<
3611+
"empty %0 cannot be composed with named ones">;
3612+
def err_attribute_no_builtin_on_non_definition : Error<
3613+
"%0 attribute is permitted on definitions only">;
3614+
def err_attribute_no_builtin_on_defaulted_deleted_function : Error<
3615+
"%0 attribute has no effect on defaulted or deleted functions">;
36073616
def warn_ns_attribute_wrong_return_type : Warning<
36083617
"%0 attribute only applies to %select{functions|methods|properties}1 that "
36093618
"return %select{an Objective-C object|a pointer|a non-retainable pointer}2">,

clang/lib/CodeGen/CGCall.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,11 +1853,27 @@ void CodeGenModule::ConstructAttributeList(
18531853
if (const FunctionDecl *Fn = dyn_cast<FunctionDecl>(TargetDecl)) {
18541854
AddAttributesFromFunctionProtoType(
18551855
getContext(), FuncAttrs, Fn->getType()->getAs<FunctionProtoType>());
1856-
// Don't use [[noreturn]] or _Noreturn for a call to a virtual function.
1857-
// These attributes are not inherited by overloads.
18581856
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Fn);
1859-
if (Fn->isNoReturn() && !(AttrOnCallSite && MD && MD->isVirtual()))
1860-
FuncAttrs.addAttribute(llvm::Attribute::NoReturn);
1857+
const bool IsVirtualCall = MD && MD->isVirtual();
1858+
// Don't use [[noreturn]], _Noreturn or [[no_builtin]] for a call to a
1859+
// virtual function. These attributes are not inherited by overloads.
1860+
if (!(AttrOnCallSite && IsVirtualCall)) {
1861+
if (Fn->isNoReturn())
1862+
FuncAttrs.addAttribute(llvm::Attribute::NoReturn);
1863+
1864+
if (const auto *NBA = TargetDecl->getAttr<NoBuiltinAttr>()) {
1865+
bool HasWildcard = llvm::is_contained(NBA->builtinNames(), "*");
1866+
if (HasWildcard)
1867+
FuncAttrs.addAttribute("no-builtins");
1868+
else
1869+
for (StringRef BuiltinName : NBA->builtinNames()) {
1870+
SmallString<32> AttributeName;
1871+
AttributeName += "no-builtin-";
1872+
AttributeName += BuiltinName;
1873+
FuncAttrs.addAttribute(AttributeName);
1874+
}
1875+
}
1876+
}
18611877
}
18621878

18631879
// 'const', 'pure' and 'noalias' attributed functions are also nounwind.

clang/lib/Sema/SemaDecl.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9529,6 +9529,29 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
95299529
}
95309530
}
95319531

9532+
// Diagnose no_builtin attribute on function declaration that are not a
9533+
// definition.
9534+
// FIXME: We should really be doing this in
9535+
// SemaDeclAttr.cpp::handleNoBuiltinAttr, unfortunately we only have access to
9536+
// the FunctionDecl and at this point of the code
9537+
// FunctionDecl::isThisDeclarationADefinition() which always returns `false`
9538+
// because Sema::ActOnStartOfFunctionDef has not been called yet.
9539+
if (const auto *NBA = NewFD->getAttr<NoBuiltinAttr>())
9540+
switch (D.getFunctionDefinitionKind()) {
9541+
case FDK_Defaulted:
9542+
case FDK_Deleted:
9543+
Diag(NBA->getLocation(),
9544+
diag::err_attribute_no_builtin_on_defaulted_deleted_function)
9545+
<< NBA->getSpelling();
9546+
break;
9547+
case FDK_Declaration:
9548+
Diag(NBA->getLocation(), diag::err_attribute_no_builtin_on_non_definition)
9549+
<< NBA->getSpelling();
9550+
break;
9551+
case FDK_Definition:
9552+
break;
9553+
}
9554+
95329555
return NewFD;
95339556
}
95349557

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,56 @@ static void handleDiagnoseIfAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
10691069
S.Context, AL, Cond, Msg, DiagType, ArgDependent, cast<NamedDecl>(D)));
10701070
}
10711071

1072+
static void handleNoBuiltinAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
1073+
static constexpr const StringRef kWildcard = "*";
1074+
1075+
llvm::SmallVector<StringRef, 16> Names;
1076+
bool HasWildcard = false;
1077+
1078+
const auto AddBuiltinName = [&Names, &HasWildcard](StringRef Name) {
1079+
if (Name == kWildcard)
1080+
HasWildcard = true;
1081+
Names.push_back(Name);
1082+
};
1083+
1084+
// Add previously defined attributes.
1085+
if (const auto *NBA = D->getAttr<NoBuiltinAttr>())
1086+
for (StringRef BuiltinName : NBA->builtinNames())
1087+
AddBuiltinName(BuiltinName);
1088+
1089+
// Add current attributes.
1090+
if (AL.getNumArgs() == 0)
1091+
AddBuiltinName(kWildcard);
1092+
else
1093+
for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
1094+
StringRef BuiltinName;
1095+
SourceLocation LiteralLoc;
1096+
if (!S.checkStringLiteralArgumentAttr(AL, I, BuiltinName, &LiteralLoc))
1097+
return;
1098+
1099+
if (Builtin::Context::isBuiltinFunc(BuiltinName.data()))
1100+
AddBuiltinName(BuiltinName);
1101+
else
1102+
S.Diag(LiteralLoc, diag::warn_attribute_no_builtin_invalid_builtin_name)
1103+
<< BuiltinName << AL.getAttrName()->getName();
1104+
}
1105+
1106+
// Repeating the same attribute is fine.
1107+
llvm::sort(Names);
1108+
Names.erase(std::unique(Names.begin(), Names.end()), Names.end());
1109+
1110+
// Empty no_builtin must be on its own.
1111+
if (HasWildcard && Names.size() > 1)
1112+
S.Diag(D->getLocation(),
1113+
diag::err_attribute_no_builtin_wildcard_or_builtin_name)
1114+
<< AL.getAttrName()->getName();
1115+
1116+
if (D->hasAttr<NoBuiltinAttr>())
1117+
D->dropAttr<NoBuiltinAttr>();
1118+
D->addAttr(::new (S.Context)
1119+
NoBuiltinAttr(S.Context, AL, Names.data(), Names.size()));
1120+
}
1121+
10721122
static void handlePassObjectSizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
10731123
if (D->hasAttr<PassObjectSizeAttr>()) {
10741124
S.Diag(D->getBeginLoc(), diag::err_attribute_only_once_per_parameter) << AL;
@@ -6608,6 +6658,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
66086658
case ParsedAttr::AT_DiagnoseIf:
66096659
handleDiagnoseIfAttr(S, D, AL);
66106660
break;
6661+
case ParsedAttr::AT_NoBuiltin:
6662+
handleNoBuiltinAttr(S, D, AL);
6663+
break;
66116664
case ParsedAttr::AT_ExtVectorType:
66126665
handleExtVectorTypeAttr(S, D, AL);
66136666
break;

clang/test/CodeGen/no-builtin.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -S -emit-llvm -o - %s | FileCheck %s
2+
3+
// CHECK-LABEL: define void @foo_no_mempcy() #0
4+
extern "C" void foo_no_mempcy() __attribute__((no_builtin("memcpy"))) {}
5+
6+
// CHECK-LABEL: define void @foo_no_mempcy_twice() #0
7+
extern "C" void foo_no_mempcy_twice() __attribute__((no_builtin("memcpy"))) __attribute__((no_builtin("memcpy"))) {}
8+
9+
// CHECK-LABEL: define void @foo_no_builtins() #1
10+
extern "C" void foo_no_builtins() __attribute__((no_builtin)) {}
11+
12+
// CHECK-LABEL: define void @foo_no_mempcy_memset() #2
13+
extern "C" void foo_no_mempcy_memset() __attribute__((no_builtin("memset", "memcpy"))) {}
14+
15+
// CHECK-LABEL: define void @separate_attrs() #2
16+
extern "C" void separate_attrs() __attribute__((no_builtin("memset"))) __attribute__((no_builtin("memcpy"))) {}
17+
18+
// CHECK-LABEL: define void @separate_attrs_ordering() #2
19+
extern "C" void separate_attrs_ordering() __attribute__((no_builtin("memcpy"))) __attribute__((no_builtin("memset"))) {}
20+
21+
struct A {
22+
virtual int foo() const __attribute__((no_builtin("memcpy"))) { return 1; }
23+
virtual ~A();
24+
};
25+
26+
struct B : public A {
27+
int foo() const override __attribute__((no_builtin("memmove"))) { return 2; }
28+
virtual ~B();
29+
};
30+
31+
// CHECK-LABEL: define void @call_a_foo(%struct.A* %a) #3
32+
extern "C" void call_a_foo(A *a) {
33+
// CHECK: %call = call i32 %2(%struct.A* %0)
34+
a->foo(); // virtual call is not annotated
35+
}
36+
37+
// CHECK-LABEL: define void @call_b_foo(%struct.B* %b) #3
38+
extern "C" void call_b_foo(B *b) {
39+
// CHECK: %call = call i32 %2(%struct.B* %0)
40+
b->foo(); // virtual call is not annotated
41+
}
42+
43+
// CHECK-LABEL: define void @call_foo_no_mempcy() #3
44+
extern "C" void call_foo_no_mempcy() {
45+
// CHECK: call void @foo_no_mempcy() #6
46+
foo_no_mempcy(); // call gets annotated with "no-builtin-memcpy"
47+
}
48+
49+
A::~A() {} // Anchoring A so A::foo() gets generated
50+
B::~B() {} // Anchoring B so B::foo() gets generated
51+
52+
// CHECK-LABEL: define linkonce_odr i32 @_ZNK1A3fooEv(%struct.A* %this) unnamed_addr #0 comdat align 2
53+
// CHECK-LABEL: define linkonce_odr i32 @_ZNK1B3fooEv(%struct.B* %this) unnamed_addr #5 comdat align 2
54+
55+
// CHECK: attributes #0 = {{{.*}}"no-builtin-memcpy"{{.*}}}
56+
// CHECK-NOT: attributes #0 = {{{.*}}"no-builtin-memmove"{{.*}}}
57+
// CHECK-NOT: attributes #0 = {{{.*}}"no-builtin-memset"{{.*}}}
58+
// CHECK: attributes #1 = {{{.*}}"no-builtins"{{.*}}}
59+
// CHECK: attributes #2 = {{{.*}}"no-builtin-memcpy"{{.*}}"no-builtin-memset"{{.*}}}
60+
// CHECK-NOT: attributes #2 = {{{.*}}"no-builtin-memmove"{{.*}}}
61+
// CHECK: attributes #5 = {{{.*}}"no-builtin-memmove"{{.*}}}
62+
// CHECK-NOT: attributes #5 = {{{.*}}"no-builtin-memcpy"{{.*}}}
63+
// CHECK-NOT: attributes #5 = {{{.*}}"no-builtin-memset"{{.*}}}
64+
// CHECK: attributes #6 = { "no-builtin-memcpy" }

clang/test/Misc/pragma-attribute-supported-attributes-list.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
// CHECK-NEXT: NSConsumed (SubjectMatchRule_variable_is_parameter)
7676
// CHECK-NEXT: NSConsumesSelf (SubjectMatchRule_objc_method)
7777
// CHECK-NEXT: Naked (SubjectMatchRule_function)
78+
// CHECK-NEXT: NoBuiltin (SubjectMatchRule_function)
7879
// CHECK-NEXT: NoCommon (SubjectMatchRule_variable)
7980
// CHECK-NEXT: NoDebug (SubjectMatchRule_type_alias, SubjectMatchRule_hasType_functionType, SubjectMatchRule_objc_method, SubjectMatchRule_variable_not_is_parameter)
8081
// CHECK-NEXT: NoDestroy (SubjectMatchRule_variable)

clang/test/Sema/no-builtin.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s
2+
3+
/// Prevent use of all builtins.
4+
void valid_attribute_all_1() __attribute__((no_builtin)) {}
5+
void valid_attribute_all_2() __attribute__((no_builtin())) {}
6+
7+
/// Prevent use of specific builtins.
8+
void valid_attribute_function() __attribute__((no_builtin("memcpy"))) {}
9+
void valid_attribute_functions() __attribute__((no_builtin("memcpy"))) __attribute__((no_builtin("memcmp"))) {}
10+
11+
/// Many times the same builtin is fine.
12+
void many_attribute_function_1() __attribute__((no_builtin)) __attribute__((no_builtin)) {}
13+
void many_attribute_function_2() __attribute__((no_builtin("memcpy"))) __attribute__((no_builtin("memcpy"))) {}
14+
void many_attribute_function_3() __attribute__((no_builtin("memcpy", "memcpy"))) {}
15+
void many_attribute_function_4() __attribute__((no_builtin("memcpy", "memcpy"))) __attribute__((no_builtin("memcpy"))) {}
16+
17+
/// Invalid builtin name.
18+
void invalid_builtin() __attribute__((no_builtin("not_a_builtin"))) {}
19+
// expected-warning@-1 {{'not_a_builtin' is not a valid builtin name for no_builtin}}
20+
21+
/// Can't use bare no_builtin with a named one.
22+
void wildcard_and_functionname() __attribute__((no_builtin)) __attribute__((no_builtin("memcpy"))) {}
23+
// expected-error@-1 {{empty no_builtin cannot be composed with named ones}}
24+
25+
/// Can't attach attribute to a variable.
26+
int __attribute__((no_builtin)) variable;
27+
// expected-warning@-1 {{'no_builtin' attribute only applies to functions}}
28+
29+
/// Can't attach attribute to a declaration.
30+
void nobuiltin_on_declaration() __attribute__((no_builtin));
31+
// expected-error@-1 {{no_builtin attribute is permitted on definitions only}}
32+
33+
struct S {
34+
/// Can't attach attribute to a defaulted function,
35+
S()
36+
__attribute__((no_builtin)) = default;
37+
// expected-error@-1 {{no_builtin attribute has no effect on defaulted or deleted functions}}
38+
39+
/// Can't attach attribute to a deleted function,
40+
S(const S &)
41+
__attribute__((no_builtin)) = delete;
42+
// expected-error@-1 {{no_builtin attribute has no effect on defaulted or deleted functions}}
43+
44+
void whatever() __attribute__((no_builtin("memcpy")));
45+
// expected-error@-1 {{no_builtin attribute is permitted on definitions only}}
46+
};
47+
48+
/// Can't attach attribute to an aliased function.
49+
void alised_function() {}
50+
void aliasing_function() __attribute__((no_builtin)) __attribute__((alias("alised_function")));
51+
// expected-error@-1 {{no_builtin attribute is permitted on definitions only}}

0 commit comments

Comments
 (0)