[clang] [clang] Add add attribute [[clang::builtin]] (PR #78071)
Nikolas Klauser via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 13 14:54:40 PST 2024
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/78071
None
>From f073a827ddbe728ced53a82431f517da58753776 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 11 Dec 2023 12:51:07 +0100
Subject: [PATCH] [clang] Add add attribute [[clang::builtin]]
---
clang/include/clang/Basic/Attr.td | 5 +-
clang/include/clang/Basic/DiagnosticGroups.td | 3 +
.../clang/Basic/DiagnosticSemaKinds.td | 11 +++
clang/lib/AST/Decl.cpp | 6 ++
clang/lib/AST/ExprConstant.cpp | 15 ++-
clang/lib/CodeGen/CGExpr.cpp | 6 +-
clang/lib/Headers/mmintrin.h | 9 +-
clang/lib/Sema/SemaDecl.cpp | 4 +-
clang/lib/Sema/SemaDeclAttr.cpp | 96 +++++++++++++++++++
clang/lib/Sema/SemaExpr.cpp | 3 +
clang/test/CodeGenCXX/attr-builtin.cpp | 71 ++++++++++++++
...a-attribute-supported-attributes-list.test | 1 +
clang/test/SemaCXX/attr-builtin-debug.cpp | 4 +
clang/test/SemaCXX/attr-builtin.cpp | 65 +++++++++++++
14 files changed, 285 insertions(+), 14 deletions(-)
create mode 100644 clang/test/CodeGenCXX/attr-builtin.cpp
create mode 100644 clang/test/SemaCXX/attr-builtin-debug.cpp
create mode 100644 clang/test/SemaCXX/attr-builtin.cpp
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b0a8ef10c500a7..fb435eac80bdd8 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4157,11 +4157,10 @@ def DiagnoseAsBuiltin : InheritableAttr {
}
def Builtin : InheritableAttr {
- let Spellings = [];
+ let Spellings = [Clang<"builtin">];
let Args = [UnsignedArgument<"ID">];
let Subjects = SubjectList<[Function]>;
- let SemaHandler = 0;
- let Documentation = [InternalOnly];
+ let Documentation = [Undocumented];
}
def EnforceTCB : InheritableAttr {
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index caee2dc6daadb6..2390a487b0bcb8 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -771,6 +771,8 @@ def NSobjectAttribute : DiagGroup<"NSObject-attribute">;
def NSConsumedMismatch : DiagGroup<"nsconsumed-mismatch">;
def NSReturnsMismatch : DiagGroup<"nsreturns-mismatch">;
+def UnsupportedBuiltin : DiagGroup<"unsupported-builtin">;
+
def IndependentClassAttribute : DiagGroup<"IndependentClass-attribute">;
def UnknownAttributes : DiagGroup<"unknown-attributes">;
def IgnoredAttributes : DiagGroup<"ignored-attributes">;
@@ -1063,6 +1065,7 @@ def Most : DiagGroup<"most", [
PrivateExtern,
SelTypeCast,
ExternCCompat,
+ UnsupportedBuiltin,
UserDefinedWarnings
]>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 28d95ca9b13893..3c096c07bd4708 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5759,6 +5759,17 @@ def warn_unavailable_def : Warning<
def warn_deprecated_builtin : Warning<
"builtin %0 is deprecated; use %1 instead">,
InGroup<DeprecatedBuiltins>;
+def err_unknown_builtin : Error<"unknown builtin">;
+def warn_unsupported_builtin : Warning<"builtin is not supported">,
+ InGroup<UnsupportedBuiltin>;
+def warn_unsupported_on_member_function : Warning<
+ "attribute 'builtin' is not supported on member functions">,
+ InGroup<UnsupportedBuiltin>;
+def err_unexpected_param_count : Error<
+ "expected %0 argument%select{s|}1 but got %2">;
+def err_signature_mismatch : Error<
+ "function signature does not match the signature of the builtin">;
+def note_expected_signature : Note<"expected signature is %0">;
def err_unavailable : Error<"%0 is unavailable">;
def err_property_method_unavailable :
Error<"property access is using %0 method which is unavailable">;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index c5c2edf1bfe3ab..63be2447cca308 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3575,6 +3575,12 @@ unsigned FunctionDecl::getBuiltinID(bool ConsiderWrapperFunctions) const {
BuiltinID = BAA->getBuiltinName()->getBuiltinID();
} else if (const auto *A = getAttr<BuiltinAttr>()) {
BuiltinID = A->getID();
+
+ // This is an explicit attribute, which means that this has been declared as
+ // a builtin by the user. In this case we can assume that the function is
+ // equivalent to the specified builtin.
+ if (!A->isImplicit())
+ return BuiltinID;
}
if (!BuiltinID)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f035c1419f4c98..52b45ba0385b88 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -7661,8 +7661,19 @@ class ExprEvaluatorBase
bool IsConstantEvaluatedBuiltinCall(const CallExpr *E) {
unsigned BuiltinOp = E->getBuiltinCallee();
- return BuiltinOp != 0 &&
- Info.Ctx.BuiltinInfo.isConstantEvaluated(BuiltinOp);
+
+ if (BuiltinOp == 0 || !Info.Ctx.BuiltinInfo.isConstantEvaluated(BuiltinOp))
+ return false;
+
+ if (const auto* CD = E->getCalleeDecl()) {
+ if (const auto* FD = CD->getAsFunction()) {
+ if (FD->isConstexpr())
+ return true;
+ if (const auto* BA = FD->getAttr<BuiltinAttr>())
+ return BA->isImplicit();
+ }
+ }
+ return true;
}
public:
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 69cf7f76be9a70..d514bf238a0096 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5183,11 +5183,12 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
// A CXXOperatorCallExpr is created even for explicit object methods, but
// these should be treated like static function call.
- if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+ if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E)) {
if (const auto *MD =
dyn_cast_if_present<CXXMethodDecl>(CE->getCalleeDecl());
MD && MD->isImplicitObjectMemberFunction())
return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue);
+ }
CGCallee callee = EmitCallee(E->getCallee());
@@ -5224,7 +5225,8 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) {
const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
if (auto builtinID = FD->getBuiltinID()) {
- std::string NoBuiltinFD = ("no-builtin-" + FD->getName()).str();
+ std::string NoBuiltinFD =
+ ("no-builtin-" + CGF.getContext().BuiltinInfo.getName(builtinID)).str();
std::string NoBuiltins = "no-builtins";
StringRef Ident = CGF.CGM.getMangledName(GD);
diff --git a/clang/lib/Headers/mmintrin.h b/clang/lib/Headers/mmintrin.h
index 08849f01071aea..6f0b595a320482 100644
--- a/clang/lib/Headers/mmintrin.h
+++ b/clang/lib/Headers/mmintrin.h
@@ -33,11 +33,9 @@ typedef char __v8qi __attribute__((__vector_size__(8)));
///
/// This intrinsic corresponds to the <c> EMMS </c> instruction.
///
-static __inline__ void __attribute__((__always_inline__, __nodebug__,
- __target__("mmx,no-evex512")))
-_mm_empty(void) {
- __builtin_ia32_emms();
-}
+static __inline__ void
+ __attribute__((__always_inline__, __nodebug__, __target__("mmx,no-evex512"),
+ __builtin__("__builtin_ia32_emms"))) _mm_empty(void);
/// Constructs a 64-bit integer vector, setting the lower 32 bits to the
/// value of the 32-bit integer parameter and setting the upper 32 bits to 0.
@@ -1561,4 +1559,3 @@ _mm_setr_pi8(char __b0, char __b1, char __b2, char __b3, char __b4, char __b5,
#define _m_pcmpgtd _mm_cmpgt_pi32
#endif /* __MMINTRIN_H */
-
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 19d972ed8ab2d8..ace3f664eae872 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15622,7 +15622,9 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
// Builtin functions cannot be defined.
if (unsigned BuiltinID = FD->getBuiltinID()) {
if (!Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID) &&
- !Context.BuiltinInfo.isPredefinedRuntimeFunction(BuiltinID)) {
+ !Context.BuiltinInfo.isPredefinedRuntimeFunction(BuiltinID) &&
+ FD->getAttr<BuiltinAttr>() &&
+ FD->getAttr<BuiltinAttr>()->isImplicit()) {
Diag(FD->getLocation(), diag::err_builtin_definition) << FD;
FD->setInvalidDecl();
}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 59e456fd9f7298..3df09d836513b5 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5921,6 +5921,98 @@ static void handleBuiltinAliasAttr(Sema &S, Decl *D,
D->addAttr(::new (S.Context) BuiltinAliasAttr(S.Context, AL, Ident));
}
+static void handleBuiltinAttr(Sema &S, Decl *D,
+ const ParsedAttr &AL) {
+ if (!AL.checkExactlyNumArgs(S, 1))
+ return;
+
+ StringRef BuiltinName;
+ if (!S.checkStringLiteralArgumentAttr(AL, 0, BuiltinName))
+ return;
+
+ bool IsInStdNamespace = BuiltinName.consume_front("std::");
+
+ unsigned ID =
+ S.getPreprocessor().getIdentifierTable().get(BuiltinName).getBuiltinID();
+
+ auto& BuiltinInfo = S.Context.BuiltinInfo;
+
+ if (ID == 0 || BuiltinInfo.isInStdNamespace(ID) != IsInStdNamespace) {
+ S.Diag(AL.getLoc(), diag::err_unknown_builtin);
+ return;
+ }
+
+ if (BuiltinInfo.isUnevaluated(ID) || BuiltinInfo.hasCustomTypechecking(ID)) {
+ S.Diag(AL.getLoc(), diag::warn_unsupported_builtin);
+ return;
+ }
+
+ ASTContext::GetBuiltinTypeError Error;
+ QualType Signature = S.Context.GetBuiltinType(ID, Error);
+ if (Error || Signature.isNull()) {
+ S.Diag(AL.getLoc(), diag::warn_unsupported_builtin);
+ return;
+ }
+
+ auto hasCompatibleSignature = [&] {
+ auto isTypeCompatible = [&](QualType LHS, QualType RHS) {
+ if (S.Context.hasSameType(LHS, RHS))
+ return true;
+ if (LHS->isPointerType() && RHS->isPointerType()) {
+ auto LHSPointee = LHS->getPointeeType();
+ auto RHSPointee = RHS->getPointeeType();
+ if (LHSPointee->isVoidType())
+ return RHSPointee.isAtLeastAsQualifiedAs(LHSPointee);
+ }
+ return false;
+ };
+
+ if (!isTypeCompatible(
+ cast<FunctionProtoType>(Signature)->getReturnType(),
+ cast<FunctionProtoType>(D->getFunctionType())->getReturnType()))
+ return false;
+
+ auto LHSParams = cast<FunctionProtoType>(Signature)->getParamTypes();
+ auto RHSParams =
+ cast<FunctionProtoType>(D->getFunctionType())->getParamTypes();
+
+ if (LHSParams.size() != RHSParams.size())
+ return false;
+
+ return llvm::all_of(llvm::zip(LHSParams, RHSParams), [&](auto tuple) {
+ return isTypeCompatible(std::get<0>(tuple), std::get<1>(tuple));
+ });
+ };
+
+ if (const auto* MD = dyn_cast<CXXMethodDecl>(D)) {
+ if (!MD->isStatic()) {
+ S.Diag(AL.getLoc(), diag::warn_unsupported_on_member_function);
+ return;
+ }
+ }
+
+ if (BuiltinInfo.allowTypeMismatch(ID)) {
+ auto ExpectedParamCount =
+ cast<FunctionProtoType>(Signature)->getNumParams();
+ auto ActualParamCount =
+ cast<FunctionProtoType>(D->getFunctionType())->getNumParams();
+ if (ExpectedParamCount != ActualParamCount) {
+ S.Diag(AL.getLoc(), diag::err_unexpected_param_count)
+ << ExpectedParamCount << (ExpectedParamCount == 1)
+ << ActualParamCount;
+ return;
+ }
+ } else if (QualType FuncSig = cast<FunctionDecl>(D)->getType();
+ !S.Context.hasSameFunctionTypeIgnoringExceptionSpec(Signature,
+ FuncSig) &&
+ !hasCompatibleSignature()) {
+ S.Diag(AL.getLoc(), diag::err_signature_mismatch);
+ S.Diag(AL.getLoc(), diag::note_expected_signature) << Signature;
+ }
+
+ D->addAttr(BuiltinAttr::Create(S.Context, ID));
+}
+
static void handlePreferredTypeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (!AL.hasParsedType()) {
S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1;
@@ -9717,6 +9809,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
handleBuiltinAliasAttr(S, D, AL);
break;
+ case ParsedAttr::AT_Builtin:
+ handleBuiltinAttr(S, D, AL);
+ break;
+
case ParsedAttr::AT_PreferredType:
handlePreferredTypeAttr(S, D, AL);
break;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d629be083d8c38..e3ba57cfff6a7d 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7147,6 +7147,9 @@ static void DiagnosedUnqualifiedCallsToStdFunctions(Sema &S,
if (BuiltinID != Builtin::BImove && BuiltinID != Builtin::BIforward)
return;
+ if (!FD->isInStdNamespace())
+ return;
+
S.Diag(DRE->getLocation(), diag::warn_unqualified_call_to_std_cast_function)
<< FD->getQualifiedNameAsString()
<< FixItHint::CreateInsertion(DRE->getLocation(), "std::");
diff --git a/clang/test/CodeGenCXX/attr-builtin.cpp b/clang/test/CodeGenCXX/attr-builtin.cpp
new file mode 100644
index 00000000000000..4da0354f43d3ff
--- /dev/null
+++ b/clang/test/CodeGenCXX/attr-builtin.cpp
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 %s -S -emit-llvm -triple x86_64-unknown-linux-gnu -o - -Wno-c++23-extensions | FileCheck %s
+
+[[clang::builtin("memcpy")]] void* my_memcpy(void*, const void*, unsigned long);
+
+void call_memcpy(int i) {
+ int j;
+ my_memcpy(&j, &i, sizeof(int));
+
+ // CHECK: define dso_local void @_Z11call_memcpyi(i32 noundef %i) #0 {
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %i.addr = alloca i32, align 4
+ // CHECK-NEXT: %j = alloca i32, align 4
+ // CHECK-NEXT: store i32 %i, ptr %i.addr, align 4
+ // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %j, ptr align 4 %i.addr, i64 4, i1 false)
+ // CHECK-NEXT: ret void
+ // CHECK-NEXT: }
+}
+
+[[clang::builtin("memmove")]] char* typed_memmove(char*, const char*, unsigned long);
+
+char* call_typed_memmove(char* dst, const char* src, unsigned long count) {
+ return typed_memmove(dst, src, count);
+
+ // CHECK: define dso_local noundef ptr @_Z18call_typed_memmovePcPKcm(ptr noundef %dst, ptr noundef %src, i64 noundef %count) #0 {
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %dst.addr = alloca ptr, align 8
+ // CHECK-NEXT: %src.addr = alloca ptr, align 8
+ // CHECK-NEXT: %count.addr = alloca i64, align 8
+ // CHECK-NEXT: store ptr %dst, ptr %dst.addr, align 8
+ // CHECK-NEXT: store ptr %src, ptr %src.addr, align 8
+ // CHECK-NEXT: store i64 %count, ptr %count.addr, align 8
+ // CHECK-NEXT: %0 = load ptr, ptr %dst.addr, align 8
+ // CHECK-NEXT: %1 = load ptr, ptr %src.addr, align 8
+ // CHECK-NEXT: %2 = load i64, ptr %count.addr, align 8
+ // CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 1 %0, ptr align 1 %1, i64 %2, i1 false)
+ // CHECK-NEXT: ret ptr %0
+ // CHECK-NEXT: }
+}
+
+template <class T>
+[[clang::builtin("std::move")]] __remove_reference_t(T)&& my_move(T&&);
+
+void call_move() {
+ int i = my_move(0);
+
+ // CHECK: define dso_local void @_Z9call_movev() #0 {
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %i = alloca i32, align 4
+ // CHECK-NEXT: %ref.tmp = alloca i32, align 4
+ // CHECK-NEXT: store i32 0, ptr %ref.tmp, align 4
+ // CHECK-NEXT: %0 = load i32, ptr %ref.tmp, align 4
+ // CHECK-NEXT: store i32 %0, ptr %i, align 4
+ // CHECK-NEXT: ret void
+ // CHECK-NEXT: }
+}
+
+struct identity {
+ template <class T>
+ [[clang::builtin("std::forward")]] static T&& operator()(T&&) noexcept;
+};
+
+void call_identity() {
+ (void)identity()(1);
+
+ // CHECK: define dso_local void @_Z13call_identityv() #0 {
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %ref.tmp = alloca i32, align 4
+ // CHECK-NEXT: store i32 1, ptr %ref.tmp, align 4
+ // CHECK-NEXT: ret void
+ // CHECK-NEXT: }
+}
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index bdfda430eea86c..96fb221e66eba3 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -25,6 +25,7 @@
// CHECK-NEXT: BPFPreserveAccessIndex (SubjectMatchRule_record)
// CHECK-NEXT: BPFPreserveStaticOffset (SubjectMatchRule_record)
// CHECK-NEXT: BTFDeclTag (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record, SubjectMatchRule_field, SubjectMatchRule_type_alias)
+// CHECK-NEXT: Builtin (SubjectMatchRule_function)
// CHECK-NEXT: BuiltinAlias (SubjectMatchRule_function)
// CHECK-NEXT: CFAuditedTransfer (SubjectMatchRule_function)
// CHECK-NEXT: CFConsumed (SubjectMatchRule_variable_is_parameter)
diff --git a/clang/test/SemaCXX/attr-builtin-debug.cpp b/clang/test/SemaCXX/attr-builtin-debug.cpp
new file mode 100644
index 00000000000000..c81e663137b0db
--- /dev/null
+++ b/clang/test/SemaCXX/attr-builtin-debug.cpp
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -ffreestanding -Wno-c++23-extensions
+
+[[clang::builtin("memcpy")]] void func(); // expected-error {{function signature does not match the signature of the builtin}} \
+ expected-note {{expected signature is 'void *(void *, const void *, unsigned long)'}}
diff --git a/clang/test/SemaCXX/attr-builtin.cpp b/clang/test/SemaCXX/attr-builtin.cpp
new file mode 100644
index 00000000000000..0fa72600737364
--- /dev/null
+++ b/clang/test/SemaCXX/attr-builtin.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-c++23-extensions
+// RUN: %clang_cc1 -fsyntax-only -verify %s -ffreestanding -Wno-c++23-extensions
+
+[[clang::builtin]] void func(); // expected-error {{'builtin' attribute takes one argument}}
+[[clang::builtin("unknown_builtin")]] void func(); // expected-warning {{builtin is not supported}}
+[[clang::builtin("memcpy")]] void func(); // expected-error {{function signature does not match the signature of the builtin}} \
+ expected-note {{expected signature is 'void *(void *, const void *, unsigned long)'}}
+[[clang::builtin("move")]] void func(); // expected-warning {{builtin is not supported}}
+
+// has unevaluated parameters
+[[clang::builtin("__builtin_constant_p")]] void constant_p(); // expected-warning {{builtin is not supported}}
+
+[[clang::builtin("memcpy")]] void* my_memcpy(void*, const void*, unsigned long);
+[[clang::builtin("memcpy")]] char* my_memcpy(char*, const char*, unsigned long);
+[[clang::builtin("memcpy")]] char* my_memcpy(char*, const char*); // expected-error {{function signature does not match the signature of the builtin}} \
+ expected-note {{expected signature is}}
+
+[[clang::builtin("memmove")]] char* typed_memmove(char*, const char*, unsigned long); // expected-note {{candidate function}}
+
+void call_memmove(void* ptr) {
+ typed_memmove(ptr, ptr, 1); // expected-error {{no matching function for call to 'typed_memmove'}}
+}
+
+[[clang::builtin("__builtin_memmove")]] void* non_constexpr_memmove(void*, const void*, unsigned long);
+
+constexpr void call_non_constexpr_memmove() { // expected-error {{constexpr function never produces a constant expression}}
+ int i = 0;
+ int j = 0;
+ non_constexpr_memmove(&j, &i, sizeof(int)); // expected-note {{subexpression not valid in a constant expression}}
+}
+
+[[clang::builtin("__builtin_memmove")]] constexpr void* constexpr_memmove(void*, const void*, unsigned long);
+
+constexpr void call_constexpr_memmove() {
+ int i = 0;
+ int j = 0;
+ constexpr_memmove(&j, &i, sizeof(int));
+}
+
+// allows type mismatches
+[[clang::builtin("std::move")]] void my_move(); // expected-error {{expected 1 argument but got 0}}
+[[clang::builtin("std::move")]] void my_move(int);
+
+void call_move() {
+ my_move(1); // expected-error {{unsupported signature for 'my_move'}}
+}
+
+// has custom type checking
+[[clang::builtin("__builtin_operator_new")]] void* my_operator_new(unsigned long); // expected-warning {{builtin is not supported}}
+
+// canonical types are compared
+using size_t = decltype(sizeof(int));
+[[clang::builtin("__builtin_memcmp")]] int my_memcmp(const char*, const char*, size_t);
+
+struct reject_on_member_functions {
+ template<class T>
+ [[clang::builtin("std::forward")]] T&& operator()(T&&) noexcept; // expected-warning {{attribute 'builtin' is not supported on member functions}}
+ [[clang::builtin("memchr")]] const void* memchr(const void*, int, unsigned long); // expected-warning {{attribute 'builtin' is not supported on member functions}}
+};
+
+struct accept_on_static_member_functions {
+ template <class T>
+ [[clang::builtin("std::forward")]] static T&& operator()(T&&) noexcept;
+ [[clang::builtin("memchr")]] static const void* memchr(const void*, int, unsigned long);
+};
More information about the cfe-commits
mailing list