Index: include/clang/Basic/BuiltinsAArch64.def =================================================================== --- include/clang/Basic/BuiltinsAArch64.def +++ include/clang/Basic/BuiltinsAArch64.def @@ -52,6 +52,14 @@ BUILTIN(__builtin_arm_crc32d, "UiUiWUi", "nc") BUILTIN(__builtin_arm_crc32cd, "UiUiWUi", "nc") +// Memory Tagging Extensions (MTE) +BUILTIN(__builtin_arm_irg, "v*v*Ui", "t") +BUILTIN(__builtin_arm_addg, "v*v*Ui", "t") +BUILTIN(__builtin_arm_gmi, "Uiv*Ui", "t") +BUILTIN(__builtin_arm_ldg, "v*v*", "t") +BUILTIN(__builtin_arm_stg, "vv*", "t") +BUILTIN(__builtin_arm_subp, "Uiv*v*", "t") + // Memory barrier BUILTIN(__builtin_arm_dmb, "vUi", "nc") BUILTIN(__builtin_arm_dsb, "vUi", "nc") Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -9621,6 +9621,18 @@ "the type is not trivially copyable|" "the type does not have the expected form}1">; +// Memory Tagging Extensions (MTE) diagnostics +def err_memtag_arg_null_or_pointer : Error< + "%0 argument of MTE builtin function must be a null or a pointer (%1 invalid)">; +def err_memtag_any2arg_pointer : Error< + "at least one argument of MTE builtin function must be a pointer (%0, %1 invalid)">; +def err_memtag_arg_must_be_pointer : Error< + "%0 argument of MTE builtin function must be a pointer (%1 invalid)">; +def err_memtag_arg_must_be_integer : Error< + "%0 argument of MTE builtin function must be an integer type (%1 invalid)">; +def err_memtag_arg_must_be_unsigned : Error< + "%0 argument of MTE builtin function must be an unsigned integer type (%1 invalid)">; + def warn_dereference_of_noderef_type : Warning< "dereferencing %0; was declared with a 'noderef' type">, InGroup; def warn_dereference_of_noderef_type_no_decl : Warning< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -10759,6 +10759,7 @@ bool SemaBuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall, int ArgNum, unsigned ExpectedFieldNum, bool AllowName); + bool SemaBuiltinARMMemoryTaggingCall(unsigned BuiltinID, CallExpr *TheCall); public: enum FormatStringType { FST_Scanf, Index: lib/Basic/Targets/AArch64.h =================================================================== --- lib/Basic/Targets/AArch64.h +++ lib/Basic/Targets/AArch64.h @@ -34,6 +34,7 @@ unsigned HasFullFP16; unsigned HasDotProd; unsigned HasFP16FML; + unsigned HasMTE; llvm::AArch64::ArchKind ArchKind; static const Builtin::Info BuiltinInfo[]; Index: lib/Basic/Targets/AArch64.cpp =================================================================== --- lib/Basic/Targets/AArch64.cpp +++ lib/Basic/Targets/AArch64.cpp @@ -194,6 +194,9 @@ if (HasDotProd) Builder.defineMacro("__ARM_FEATURE_DOTPROD", "1"); + if (HasMTE) + Builder.defineMacro("__ARM_FEATURE_MEMORY_TAGGING", "1"); + if ((FPU & NeonMode) && HasFP16FML) Builder.defineMacro("__ARM_FEATURE_FP16FML", "1"); @@ -258,6 +261,8 @@ HasDotProd = 1; if (Feature == "+fp16fml") HasFP16FML = 1; + if (Feature == "+mte") + HasMTE = 1; } setDataLayout(); Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -7077,6 +7077,84 @@ return Builder.CreateCall(F, {Arg0, Arg1}); } + // Memory Tagging Extensions (MTE) Intrinsics + Intrinsic::ID MTEIntrinsicID = Intrinsic::not_intrinsic; + switch (BuiltinID) { + case AArch64::BI__builtin_arm_irg: + MTEIntrinsicID = Intrinsic::aarch64_irg; break; + case AArch64::BI__builtin_arm_addg: + MTEIntrinsicID = Intrinsic::aarch64_addg; break; + case AArch64::BI__builtin_arm_gmi: + MTEIntrinsicID = Intrinsic::aarch64_gmi; break; + case AArch64::BI__builtin_arm_ldg: + MTEIntrinsicID = Intrinsic::aarch64_ldg; break; + case AArch64::BI__builtin_arm_stg: + MTEIntrinsicID = Intrinsic::aarch64_stg; break; + case AArch64::BI__builtin_arm_subp: + MTEIntrinsicID = Intrinsic::aarch64_subp; break; + } + + if (MTEIntrinsicID != Intrinsic::not_intrinsic) { + llvm::Type *T = ConvertType(E->getType()); + + if (MTEIntrinsicID == Intrinsic::aarch64_irg) { + Value *Pointer = EmitScalarExpr(E->getArg(0)); + Value *Mask = EmitScalarExpr(E->getArg(1)); + + Pointer = Builder.CreatePointerCast(Pointer, Int8PtrTy); + Mask = Builder.CreateZExt(Mask, Int64Ty); + Value *RV = Builder.CreateCall( + CGM.getIntrinsic(MTEIntrinsicID), {Pointer, Mask}); + return Builder.CreatePointerCast(RV, T); + } + if (MTEIntrinsicID == Intrinsic::aarch64_addg) { + Value *Pointer = EmitScalarExpr(E->getArg(0)); + Value *TagOffset = EmitScalarExpr(E->getArg(1)); + + Pointer = Builder.CreatePointerCast(Pointer, Int8PtrTy); + TagOffset = Builder.CreateZExt(TagOffset, Int64Ty); + Value *RV = Builder.CreateCall( + CGM.getIntrinsic(MTEIntrinsicID), {Pointer, TagOffset}); + return Builder.CreatePointerCast(RV, T); + } + if (MTEIntrinsicID == Intrinsic::aarch64_gmi) { + Value *Pointer = EmitScalarExpr(E->getArg(0)); + Value *ExcludedMask = EmitScalarExpr(E->getArg(1)); + + ExcludedMask = Builder.CreateZExt(ExcludedMask, Int64Ty); + Pointer = Builder.CreatePointerCast(Pointer, Int8PtrTy); + return Builder.CreateCall( + CGM.getIntrinsic(MTEIntrinsicID), {Pointer, ExcludedMask}); + } + // Although it is possible to supply a different return + // address (first arg) to this intrinsic, for now we set + // return address same as input address. + if (MTEIntrinsicID == Intrinsic::aarch64_ldg) { + Value *TagAddress = EmitScalarExpr(E->getArg(0)); + TagAddress = Builder.CreatePointerCast(TagAddress, Int8PtrTy); + Value *RV = Builder.CreateCall( + CGM.getIntrinsic(MTEIntrinsicID), {TagAddress, TagAddress}); + return Builder.CreatePointerCast(RV, T); + } + // Although it is possible to supply a different tag (to set) + // to this intrinsic (as first arg), for now we supply + // the tag that is in input address arg (common use case). + if (MTEIntrinsicID == Intrinsic::aarch64_stg) { + Value *TagAddress = EmitScalarExpr(E->getArg(0)); + TagAddress = Builder.CreatePointerCast(TagAddress, Int8PtrTy); + return Builder.CreateCall( + CGM.getIntrinsic(MTEIntrinsicID), {TagAddress, TagAddress}); + } + if (MTEIntrinsicID == Intrinsic::aarch64_subp) { + Value *PointerA = EmitScalarExpr(E->getArg(0)); + Value *PointerB = EmitScalarExpr(E->getArg(1)); + PointerA = Builder.CreatePointerCast(PointerA, Int8PtrTy); + PointerB = Builder.CreatePointerCast(PointerB, Int8PtrTy); + return Builder.CreateCall( + CGM.getIntrinsic(MTEIntrinsicID), {PointerA, PointerB}); + } + } + if (BuiltinID == AArch64::BI__builtin_arm_rsr || BuiltinID == AArch64::BI__builtin_arm_rsr64 || BuiltinID == AArch64::BI__builtin_arm_rsrp || Index: lib/Headers/arm_acle.h =================================================================== --- lib/Headers/arm_acle.h +++ lib/Headers/arm_acle.h @@ -605,6 +605,16 @@ #define __arm_wsr64(sysreg, v) __builtin_arm_wsr64(sysreg, v) #define __arm_wsrp(sysreg, v) __builtin_arm_wsrp(sysreg, v) +// Memory Tagging Extensions (MTE) Intrinsics +#if __ARM_FEATURE_MEMORY_TAGGING +#define __arm_mte_create_random_tag(__ptr, __mask) __builtin_arm_irg(__ptr, __mask) +#define __arm_mte_increment_tag(__ptr, __tag_offset) __builtin_arm_addg(__ptr, __tag_offset) +#define __arm_mte_exclude_tag(__ptr, __excluded) __builtin_arm_gmi(__ptr, __excluded) +#define __arm_mte_get_tag(__ptr) __builtin_arm_ldg(__ptr) +#define __arm_mte_set_tag(__ptr) __builtin_arm_stg(__ptr) +#define __arm_mte_ptrdiff(__ptra, __ptrb) __builtin_arm_subp(__ptra, __ptrb) +#endif + #if defined(__cplusplus) } #endif Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -1875,6 +1875,16 @@ BuiltinID == AArch64::BI__builtin_arm_wsr64) return SemaBuiltinARMSpecialReg(BuiltinID, TheCall, 0, 5, true); + // Memory Tagging Extensions (MTE) Intrinsics + if (BuiltinID == AArch64::BI__builtin_arm_irg || + BuiltinID == AArch64::BI__builtin_arm_addg || + BuiltinID == AArch64::BI__builtin_arm_gmi || + BuiltinID == AArch64::BI__builtin_arm_ldg || + BuiltinID == AArch64::BI__builtin_arm_stg || + BuiltinID == AArch64::BI__builtin_arm_subp) { + return SemaBuiltinARMMemoryTaggingCall(BuiltinID, TheCall); + } + if (BuiltinID == AArch64::BI__builtin_arm_rsr || BuiltinID == AArch64::BI__builtin_arm_rsrp || BuiltinID == AArch64::BI__builtin_arm_wsr || @@ -6102,6 +6112,160 @@ return false; } +/// SemaBuiltinARMMemoryTaggingCall - Handle calls of memory tagging extensions +bool Sema::SemaBuiltinARMMemoryTaggingCall(unsigned BuiltinID, CallExpr *TheCall) { + if (BuiltinID == AArch64::BI__builtin_arm_irg) { + if (checkArgCount(*this, TheCall, 2)) + return true; + Expr *Arg0 = TheCall->getArg(0); + Expr *Arg1 = TheCall->getArg(1); + + ExprResult FirstArg = DefaultFunctionArrayLvalueConversion(Arg0); + if (FirstArg.isInvalid()) + return true; + QualType FirstArgType = FirstArg.get()->getType(); + if (!FirstArgType->isAnyPointerType()) + return Diag(TheCall->getBeginLoc(), diag::err_memtag_arg_must_be_pointer) + << "first" << FirstArgType << Arg0->getSourceRange(); + TheCall->setArg(0, FirstArg.get()); + + ExprResult SecArg = DefaultLvalueConversion(Arg1); + if (SecArg.isInvalid()) + return true; + QualType SecArgType = SecArg.get()->getType(); + if (!SecArgType->isIntegerType()) + return Diag(TheCall->getBeginLoc(), diag::err_memtag_arg_must_be_integer) + << "second" << SecArgType << Arg1->getSourceRange(); + + // Derive the return type from the pointer argument. + TheCall->setType(FirstArgType); + return false; + } + + if (BuiltinID == AArch64::BI__builtin_arm_addg) { + if (checkArgCount(*this, TheCall, 2)) + return true; + + Expr *Arg0 = TheCall->getArg(0); + ExprResult FirstArg = DefaultFunctionArrayLvalueConversion(Arg0); + if (FirstArg.isInvalid()) + return true; + QualType FirstArgType = FirstArg.get()->getType(); + if (!FirstArgType->isAnyPointerType()) + return Diag(TheCall->getBeginLoc(), diag::err_memtag_arg_must_be_pointer) + << "first" << FirstArgType << Arg0->getSourceRange(); + TheCall->setArg(0, FirstArg.get()); + + // Derive the return type from the pointer argument. + TheCall->setType(FirstArgType); + + // Second arg must be an constant in range [0,15] + return SemaBuiltinConstantArgRange(TheCall, 1, 0, 15); + } + + if (BuiltinID == AArch64::BI__builtin_arm_gmi) { + if (checkArgCount(*this, TheCall, 2)) + return true; + Expr *Arg0 = TheCall->getArg(0); + Expr *Arg1 = TheCall->getArg(1); + + ExprResult FirstArg = DefaultFunctionArrayLvalueConversion(Arg0); + if (FirstArg.isInvalid()) + return true; + QualType FirstArgType = FirstArg.get()->getType(); + if (!FirstArgType->isAnyPointerType()) + return Diag(TheCall->getBeginLoc(), diag::err_memtag_arg_must_be_pointer) + << "first" << FirstArgType << Arg0->getSourceRange(); + + QualType SecArgType = Arg1->getType(); + if (!SecArgType->isIntegerType()) + return Diag(TheCall->getBeginLoc(), diag::err_memtag_arg_must_be_integer) + << "second" << SecArgType << Arg1->getSourceRange(); + TheCall->setType(Context.IntTy); + return false; + } + + if (BuiltinID == AArch64::BI__builtin_arm_ldg || + BuiltinID == AArch64::BI__builtin_arm_stg) { + if (checkArgCount(*this, TheCall, 1)) + return true; + Expr *Arg0 = TheCall->getArg(0); + ExprResult FirstArg = DefaultFunctionArrayLvalueConversion(Arg0); + if (FirstArg.isInvalid()) + return true; + + QualType FirstArgType = FirstArg.get()->getType(); + if (!FirstArgType->isAnyPointerType()) + return Diag(TheCall->getBeginLoc(), diag::err_memtag_arg_must_be_pointer) + << "first" << FirstArgType << Arg0->getSourceRange(); + TheCall->setArg(0, FirstArg.get()); + + // Derive the return type from the pointer argument. + if (BuiltinID == AArch64::BI__builtin_arm_ldg) + TheCall->setType(FirstArgType); + return false; + } + + if (BuiltinID == AArch64::BI__builtin_arm_subp) { + Expr *ArgA = TheCall->getArg(0); + Expr *ArgB = TheCall->getArg(1); + + ExprResult ArgExprA = DefaultFunctionArrayLvalueConversion(ArgA); + ExprResult ArgExprB = DefaultFunctionArrayLvalueConversion(ArgB); + + if (ArgExprA.isInvalid() || ArgExprB.isInvalid()) + return true; + + QualType ArgTypeA = ArgExprA.get()->getType(); + QualType ArgTypeB = ArgExprB.get()->getType(); + + auto isNull = [&] (Expr *E) -> bool { + return E->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull); }; + + // argument should be either a pointer or null + if (!ArgTypeA->isAnyPointerType() && !isNull(ArgA)) + return Diag(TheCall->getBeginLoc(), diag::err_memtag_arg_null_or_pointer) + << "first" << ArgTypeA << ArgA->getSourceRange(); + + if (!ArgTypeB->isAnyPointerType() && !isNull(ArgB)) + return Diag(TheCall->getBeginLoc(), diag::err_memtag_arg_null_or_pointer) + << "second" << ArgTypeB << ArgB->getSourceRange(); + + // Ensure Pointee types are compatible + if (ArgTypeA->isAnyPointerType() && !isNull(ArgA) && + ArgTypeB->isAnyPointerType() && !isNull(ArgB)) { + QualType pointeeA = ArgTypeA->getPointeeType(); + QualType pointeeB = ArgTypeB->getPointeeType(); + if (!Context.typesAreCompatible( + Context.getCanonicalType(pointeeA).getUnqualifiedType(), + Context.getCanonicalType(pointeeB).getUnqualifiedType())) { + return Diag(TheCall->getBeginLoc(), diag::err_typecheck_sub_ptr_compatible) + << ArgTypeA << ArgTypeB << ArgA->getSourceRange() + << ArgB->getSourceRange(); + } + } + + // at least one argument should be pointer type + if (!ArgTypeA->isAnyPointerType() && !ArgTypeB->isAnyPointerType()) + return Diag(TheCall->getBeginLoc(), diag::err_memtag_any2arg_pointer) + << ArgTypeA << ArgTypeB << ArgA->getSourceRange(); + + if (isNull(ArgA)) // adopt type of the other pointer + ArgExprA = ImpCastExprToType(ArgExprA.get(), ArgTypeB, CK_NullToPointer); + + if (isNull(ArgB)) + ArgExprB = ImpCastExprToType(ArgExprB.get(), ArgTypeA, CK_NullToPointer); + + TheCall->setArg(0, ArgExprA.get()); + TheCall->setArg(1, ArgExprB.get()); + TheCall->setType(Context.LongLongTy); + return false; + } + assert(false && "Unhandled ARM MTE intrinsic"); + return true; +} + /// SemaBuiltinARMSpecialReg - Handle a check if argument ArgNum of CallExpr /// TheCall is an ARM/AArch64 special register string literal. bool Sema::SemaBuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall, Index: test/CodeGen/arm64-mte.c =================================================================== --- test/CodeGen/arm64-mte.c +++ test/CodeGen/arm64-mte.c @@ -0,0 +1,110 @@ +// Test memory tagging extension intrinsics +// RUN: %clang_cc1 -triple aarch64-none-linux-eabi -target-feature +mte -O3 -S -emit-llvm -o - %s | FileCheck %s +#include +#include + +// CHECK-LABEL: define i32* @create_tag1 +int *create_tag1(int *a, unsigned b) { +// CHECK: [[T0:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[T1:%[0-9]+]] = zext i32 %b to i64 +// CHECK: [[T2:%[0-9]+]] = tail call i8* @llvm.aarch64.irg(i8* [[T0]], i64 [[T1]]) +// CHECK: bitcast i8* [[T2]] to i32* + return __arm_mte_create_random_tag(a,b); +} + +// CHECK-LABEL: define i16* @create_tag2 +short *create_tag2(short *a, unsigned b) { +// CHECK: [[T0:%[0-9]+]] = bitcast i16* %a to i8* +// CHECK: [[T1:%[0-9]+]] = zext i32 %b to i64 +// CHECK: [[T2:%[0-9]+]] = tail call i8* @llvm.aarch64.irg(i8* [[T0]], i64 [[T1]]) +// CHECK: bitcast i8* [[T2]] to i16* + return __arm_mte_create_random_tag(a,b); +} + +// CHECK-LABEL: define i8* @create_tag3 +char *create_tag3(char *a, unsigned b) { +// CHECK: [[T1:%[0-9]+]] = zext i32 %b to i64 +// CHECK: [[T2:%[0-9]+]] = tail call i8* @llvm.aarch64.irg(i8* %a, i64 [[T1]]) +// CHECK: ret i8* [[T2:%[0-9]+]] + return __arm_mte_create_random_tag(a,b); +} + +// CHECK-LABEL: define i8* @increment_tag1 +char *increment_tag1(char *a) { +// CHECK: call i8* @llvm.aarch64.addg(i8* %a, i64 3) + return __arm_mte_increment_tag(a,3); +} + +// CHECK-LABEL: define i16* @increment_tag2 +short *increment_tag2(short *a) { +// CHECK: [[T0:%[0-9]+]] = bitcast i16* %a to i8* +// CHECK: [[T1:%[0-9]+]] = tail call i8* @llvm.aarch64.addg(i8* [[T0]], i64 3) +// CHECK: [[T2:%[0-9]+]] = bitcast i8* [[T1]] to i16* + return __arm_mte_increment_tag(a,3); +} + +// CHECK-LABEL: define i32 @exclude_tag +unsigned exclude_tag(int *a, unsigned m) { +// CHECK: [[T0:%[0-9]+]] = zext i32 %m to i64 +// CHECK: [[T1:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[T2:%[0-9]+]] = tail call i64 @llvm.aarch64.gmi(i8* [[T1]], i64 [[T0]]) +// CHECK: trunc i64 [[T2]] to i32 + return __arm_mte_exclude_tag(a, m); +} + +// CHECK-LABEL: define i32* @get_tag1 +int *get_tag1(int *a) { +// CHECK: [[T0:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[T1:%[0-9]+]] = tail call i8* @llvm.aarch64.ldg(i8* [[T0]], i8* [[T0]]) +// CHECK: [[T2:%[0-9]+]] = bitcast i8* [[T1]] to i32* + return __arm_mte_get_tag(a); +} + +// CHECK-LABEL: define i16* @get_tag2 +short *get_tag2(short *a) { +// CHECK: [[T0:%[0-9]+]] = bitcast i16* %a to i8* +// CHECK: [[T1:%[0-9]+]] = tail call i8* @llvm.aarch64.ldg(i8* [[T0]], i8* [[T0]]) +// CHECK: [[T2:%[0-9]+]] = bitcast i8* [[T1]] to i16* + return __arm_mte_get_tag(a); +} + +// CHECK-LABEL: define void @set_tag1 +void set_tag1(int *a) { +// CHECK: [[T0:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: tail call void @llvm.aarch64.stg(i8* [[T0]], i8* [[T0]]) + __arm_mte_set_tag(a); +} + +// CHECK-LABEL: define i64 @subtract_pointers +ptrdiff_t subtract_pointers(int *a, int *b) { +// CHECK: [[T0:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[T1:%[0-9]+]] = bitcast i32* %b to i8* +// CHECK: [[T2:%[0-9]+]] = tail call i64 @llvm.aarch64.subp(i8* [[T0]], i8* [[T1]]) +// CHECK: ret i64 [[T2]] + return __arm_mte_ptrdiff(a, b); +} + +// CHECK-LABEL: define i64 @subtract_pointers_null_1 +ptrdiff_t subtract_pointers_null_1(int *a) { +// CHECK: [[T0:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[T1:%[0-9]+]] = tail call i64 @llvm.aarch64.subp(i8* [[T0]], i8* null) +// CHECK: ret i64 [[T1]] + return __arm_mte_ptrdiff(a, NULL); +} + +// CHECK-LABEL: define i64 @subtract_pointers_null_2 +ptrdiff_t subtract_pointers_null_2(int *a) { +// CHECK: [[T0:%[0-9]+]] = bitcast i32* %a to i8* +// CHECK: [[T1:%[0-9]+]] = tail call i64 @llvm.aarch64.subp(i8* null, i8* [[T0]]) +// CHECK: ret i64 [[T1]] + return __arm_mte_ptrdiff(NULL, a); +} + +// Check arithmetic promotion on return type +// CHECK-LABEL: define i32 @subtract_pointers4 +int subtract_pointers4(void* a, void *b) { +// CHECK: [[T0:%[0-9]+]] = tail call i64 @llvm.aarch64.subp(i8* %a, i8* %b) +// CHECK-NEXT: %cmp = icmp slt i64 [[T0]], 1 +// CHECK-NEXT: = zext i1 %cmp to i32 + return __arm_mte_ptrdiff(a,b) <= 0; +} Index: test/Preprocessor/aarch64-target-features.c =================================================================== --- test/Preprocessor/aarch64-target-features.c +++ test/Preprocessor/aarch64-target-features.c @@ -316,3 +316,6 @@ // CHECK-V81A-FEATURE-2: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "+v8.1a" "-target-feature" "-crypto" // CHECK-V81A-FEATURE-3: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+v8.1a" "-target-feature" "-neon" +// ================== Check Memory Tagging Extensions (MTE). +// RUN: %clang -target arm64-none-linux-gnu -march=armv8.5-a+memtag -x c -E -dM %s -o - 2>&1 | FileCheck -check-prefix=CHECK-MEMTAG %s +// CHECK-MEMTAG: __ARM_FEATURE_MEMORY_TAGGING 1 Index: test/Sema/builtins-arm64-mte.c =================================================================== --- test/Sema/builtins-arm64-mte.c +++ test/Sema/builtins-arm64-mte.c @@ -0,0 +1,136 @@ +// RUN: %clang_cc1 -triple arm64-arm-eabi %s -target-feature +mte -fsyntax-only -verify +// RUN: %clang_cc1 -triple arm64-arm-eabi %s -target-feature +mte -x c++ -fsyntax-only -verify +#include +#include + +int *create_tag1(int a, unsigned b) { + // expected-error@+1 {{first argument of MTE builtin function must be a pointer ('int' invalid)}} + return __arm_mte_create_random_tag(a,b); +} + +int *create_tag2(int *a, unsigned *b) { + // expected-error@+1 {{second argument of MTE builtin function must be an integer type ('unsigned int *' invalid)}} + return __arm_mte_create_random_tag(a,b); +} + +int *create_tag3(const int *a, unsigned b) { +#ifdef __cplusplus + // expected-error@+1 {{cannot initialize return object of type 'int *' with an rvalue of type 'const int *'}} + return __arm_mte_create_random_tag(a,b); +#else + // expected-warning@+1 {{returning 'const int *' from a function with result type 'int *' discards qualifiers}} + return __arm_mte_create_random_tag(a,b); +#endif +} + +int *create_tag4(volatile int *a, unsigned b) { +#ifdef __cplusplus + // expected-error@+1 {{cannot initialize return object of type 'int *' with an rvalue of type 'volatile int *'}} + return __arm_mte_create_random_tag(a,b); +#else + // expected-warning@+1 {{returning 'volatile int *' from a function with result type 'int *' discards qualifiers}} + return __arm_mte_create_random_tag(a,b); +#endif +} + +int *increment_tag1(int *a, unsigned b) { + // expected-error@+1 {{argument to '__builtin_arm_addg' must be a constant integer}} + return __arm_mte_increment_tag(a,b); +} + +int *increment_tag2(int *a) { + // expected-error@+1 {{argument value 16 is outside the valid range [0, 15]}} + return __arm_mte_increment_tag(a,16); +} + +int *increment_tag3(int *a) { + // expected-error@+1 {{argument value -1 is outside the valid range [0, 15]}} + return __arm_mte_increment_tag(a,-1); +} + +int *increment_tag4(const int *a) { +#ifdef __cplusplus + // expected-error@+1 {{cannot initialize return object of type 'int *' with an rvalue of type 'const int *'}} + return __arm_mte_increment_tag(a,5); +#else + // expected-warning@+1 {{returning 'const int *' from a function with result type 'int *' discards qualifiers}} + return __arm_mte_increment_tag(a,5); +#endif +} + +int *increment_tag5(const volatile int *a) { +#ifdef __cplusplus + // expected-error@+1 {{cannot initialize return object of type 'int *' with an rvalue of type 'const volatile int *'}} + return __arm_mte_increment_tag(a,5); +#else + // expected-warning@+1 {{returning 'const volatile int *' from a function with result type 'int *' discards qualifiers}} + return __arm_mte_increment_tag(a,5); +#endif +} + +unsigned exclude_tag1(int *ptr, unsigned m) { + // expected-error@+1 {{first argument of MTE builtin function must be a pointer ('int' invalid)}} + return __arm_mte_exclude_tag(*ptr, m); +} + +unsigned exclude_tag2(int *ptr, int *m) { + // expected-error@+1 {{second argument of MTE builtin function must be an integer type ('int *' invalid)}} + return __arm_mte_exclude_tag(ptr, m); +} + +void get_tag1() { + // expected-error@+1 {{too few arguments to function call, expected 1, have 0}} + __arm_mte_get_tag(); +} + +int *get_tag2(int ptr) { + // expected-error@+1 {{first argument of MTE builtin function must be a pointer ('int' invalid)}} + return __arm_mte_get_tag(ptr); +} + +int *get_tag3(const volatile int *ptr) { +#ifdef __cplusplus + // expected-error@+1 {{cannot initialize return object of type 'int *' with an rvalue of type 'const volatile int *'}} + return __arm_mte_get_tag(ptr); +#else + // expected-warning@+1 {{returning 'const volatile int *' from a function with result type 'int *' discards qualifiers}} + return __arm_mte_get_tag(ptr); +#endif +} + +void set_tag1() { + // expected-error@+1 {{too few arguments to function call, expected 1, have 0}} + __arm_mte_set_tag(); +} + +void set_tag2(int ptr) { + // expected-error@+1 {{first argument of MTE builtin function must be a pointer ('int' invalid)}} + __arm_mte_set_tag(ptr); +} + +ptrdiff_t subtract_pointers1(int a, int *b) { + // expected-error@+1 {{first argument of MTE builtin function must be a null or a pointer ('int' invalid)}} + return __arm_mte_ptrdiff(a, b); +} + +ptrdiff_t subtract_pointers2(int *a, int b) { + // expected-error@+1 {{second argument of MTE builtin function must be a null or a pointer ('int' invalid)}} + return __arm_mte_ptrdiff(a, b); +} + +ptrdiff_t subtract_pointers3(char *a, int *b) { + // expected-error@+1 {{'char *' and 'int *' are not pointers to compatible types}} + return __arm_mte_ptrdiff(a, b); +} + +ptrdiff_t subtract_pointers4(int *a, char *b) { + // expected-error@+1 {{'int *' and 'char *' are not pointers to compatible types}} + return __arm_mte_ptrdiff(a, b); +} + +#ifdef __cplusplus +ptrdiff_t subtract_pointers5() { + // expected-error@+1 {{at least one argument of MTE builtin function must be a pointer ('nullptr_t', 'nullptr_t' invalid)}} + return __arm_mte_ptrdiff(nullptr, nullptr); +} +#endif