diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2086,6 +2086,10 @@ return getTypeSizeInCharsIfKnown(QualType(Ty, 0)); } + /// Returns the bitwidth of \p T, an SVE type attributed with + /// 'arm_sve_vector_bits'. Should only be called if T->isVLST(). + unsigned getBitwidthForAttributedSveType(const Type *T) const; + /// Return the ABI-specified alignment of a (complete) type \p T, in /// bits. unsigned getTypeAlign(QualType T) const { return getTypeInfo(T).Align; } diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1925,6 +1925,9 @@ bool isSizelessType() const; bool isSizelessBuiltinType() const; + /// Determines if this is a vector-length-specific type (VLST), i.e. a + /// sizeless type with the 'arm_sve_vector_bits' attribute applied. + bool isVLST() const; /// Determines if this is a sizeless type supported by the /// 'arm_sve_vector_bits' type attribute, which can be applied to a single /// SVE vector or predicate, excluding tuple types such as svint32x4_t. diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1534,8 +1534,10 @@ def ArmSveVectorBits : TypeAttr { let Spellings = [GNU<"arm_sve_vector_bits">]; - let Args = [IntArgument<"NumBits">]; + let Subjects = SubjectList<[TypedefName], ErrorDiag>; + let Args = [UnsignedArgument<"NumBits">]; let Documentation = [ArmSveVectorBitsDocs]; + let PragmaAttributeSupport = 0; } def ArmMveStrictPolymorphism : TypeAttr, TargetSpecificAttr { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1997,7 +1997,10 @@ bool RequireCompleteSizedType(SourceLocation Loc, QualType T, unsigned DiagID, const Ts &... Args) { SizelessTypeDiagnoser Diagnoser(DiagID, Args...); - return RequireCompleteType(Loc, T, CompleteTypeKind::Normal, Diagnoser); + CompleteTypeKind Kind = CompleteTypeKind::Normal; + if (T->isVLST()) + Kind = CompleteTypeKind::AcceptSizeless; + return RequireCompleteType(Loc, T, Kind, Diagnoser); } void completeExprArrayBound(Expr *E); @@ -2015,7 +2018,10 @@ bool RequireCompleteSizedExprType(Expr *E, unsigned DiagID, const Ts &... Args) { SizelessTypeDiagnoser Diagnoser(DiagID, Args...); - return RequireCompleteExprType(E, CompleteTypeKind::Normal, Diagnoser); + CompleteTypeKind Kind = CompleteTypeKind::Normal; + if (E->getType()->isVLST()) + Kind = CompleteTypeKind::AcceptSizeless; + return RequireCompleteExprType(E, Kind, Diagnoser); } bool RequireLiteralType(SourceLocation Loc, QualType T, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1869,6 +1869,50 @@ return TI; } +static unsigned getSveVectorWidth(const Type *T) { + // Get the vector size from the 'arm_sve_vector_bits' attribute via the + // AttributedTypeLoc associated with the typedef decl. + if (const auto *TT = T->getAs()) { + const TypedefNameDecl *Typedef = TT->getDecl(); + TypeSourceInfo *TInfo = Typedef->getTypeSourceInfo(); + TypeLoc TL = TInfo->getTypeLoc(); + if (AttributedTypeLoc ATL = TL.getAs()) + if (const auto *Attr = ATL.getAttrAs()) + return Attr->getNumBits(); + } + + llvm_unreachable("bad 'arm_sve_vector_bits' attribute!"); +} + +static unsigned getSvePredWidth(const ASTContext &Context, const Type *T) { + return getSveVectorWidth(T) / Context.getCharWidth(); +} + +unsigned ASTContext::getBitwidthForAttributedSveType(const Type *T) const { + assert(T->isVLST() && + "getBitwidthForAttributedSveType called for non-attributed type!"); + + switch (T->castAs()->getKind()) { + default: + llvm_unreachable("unknown builtin type!"); + case BuiltinType::SveInt8: + case BuiltinType::SveInt16: + case BuiltinType::SveInt32: + case BuiltinType::SveInt64: + case BuiltinType::SveUint8: + case BuiltinType::SveUint16: + case BuiltinType::SveUint32: + case BuiltinType::SveUint64: + case BuiltinType::SveFloat16: + case BuiltinType::SveFloat32: + case BuiltinType::SveFloat64: + case BuiltinType::SveBFloat16: + return getSveVectorWidth(T); + case BuiltinType::SveBool: + return getSvePredWidth(*this, T); + } +} + /// getTypeInfoImpl - Return the size of the specified type, in bits. This /// method does not work on incomplete types. /// @@ -2273,7 +2317,10 @@ Align = Info.Align; AlignIsRequired = Info.AlignIsRequired; } - Width = Info.Width; + if (T->isVLST()) + Width = getBitwidthForAttributedSveType(T); + else + Width = Info.Width; break; } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2318,6 +2318,13 @@ return false; } +bool Type::isVLST() const { + if (!isVLSTBuiltinType()) + return false; + + return hasAttr(attr::ArmSveVectorBits); +} + bool QualType::isPODType(const ASTContext &Context) const { // C++11 has a more relaxed definition of POD. if (Context.getLangOpts().CPlusPlus11) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8004,7 +8004,7 @@ return; } - if (!NewVD->hasLocalStorage() && T->isSizelessType()) { + if (!NewVD->hasLocalStorage() && T->isSizelessType() && !T->isVLST()) { Diag(NewVD->getLocation(), diag::err_sizeless_nonlocal) << T; NewVD->setInvalidDecl(); return; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2304,7 +2304,7 @@ return QualType(); } - if (T->isSizelessType()) { + if (T->isSizelessType() && !T->isVLST()) { Diag(Loc, diag::err_array_incomplete_or_sizeless_type) << 1 << T; return QualType(); } @@ -7754,10 +7754,14 @@ /// HandleArmSveVectorBitsTypeAttr - The "arm_sve_vector_bits" attribute is /// used to create fixed-length versions of sizeless SVE types defined by /// the ACLE, such as svint32_t and svbool_t. -static void HandleArmSveVectorBitsTypeAttr(QualType &CurType, - const ParsedAttr &Attr, Sema &S) { +static void HandleArmSveVectorBitsTypeAttr(TypeProcessingState &State, + QualType &CurType, + ParsedAttr &Attr) { + Sema &S = State.getSema(); + ASTContext &Ctx = S.Context; + // Target must have SVE. - if (!S.Context.getTargetInfo().hasFeature("sve")) { + if (!Ctx.getTargetInfo().hasFeature("sve")) { S.Diag(Attr.getLoc(), diag::err_attribute_unsupported) << Attr; Attr.setInvalid(); return; @@ -7801,6 +7805,9 @@ Attr.setInvalid(); return; } + + auto *A = ::new (Ctx) ArmSveVectorBitsAttr(Ctx, Attr, VecSize); + CurType = State.getAttributedType(A, CurType, CurType); } static void HandleArmMveStrictPolymorphismAttr(TypeProcessingState &State, @@ -8067,7 +8074,7 @@ attr.setUsedAsTypeAttr(); break; case ParsedAttr::AT_ArmSveVectorBits: - HandleArmSveVectorBitsTypeAttr(type, attr, state.getSema()); + HandleArmSveVectorBitsTypeAttr(state, type, attr); attr.setUsedAsTypeAttr(); break; case ParsedAttr::AT_ArmMveStrictPolymorphism: { diff --git a/clang/test/Sema/attr-arm-sve-vector-bits.c b/clang/test/Sema/attr-arm-sve-vector-bits.c --- a/clang/test/Sema/attr-arm-sve-vector-bits.c +++ b/clang/test/Sema/attr-arm-sve-vector-bits.c @@ -60,3 +60,168 @@ typedef float badtype3 __attribute__((arm_sve_vector_bits(N))); // expected-error {{'arm_sve_vector_bits' attribute applied to non-SVE type 'float'}} typedef svint8x2_t badtype4 __attribute__((arm_sve_vector_bits(N))); // expected-error {{'arm_sve_vector_bits' attribute applied to non-SVE type 'svint8x2_t' (aka '__clang_svint8x2_t')}} typedef svfloat32x3_t badtype5 __attribute__((arm_sve_vector_bits(N))); // expected-error {{'arm_sve_vector_bits' attribute applied to non-SVE type 'svfloat32x3_t' (aka '__clang_svfloat32x3_t')}} + +// Attribute only applies to typedefs. +svint8_t non_typedef_type __attribute__((arm_sve_vector_bits(N))); // expected-error {{'arm_sve_vector_bits' attribute only applies to typedefs}} + +// Test that we can define non-local fixed-length SVE types (unsupported for +// sizeless types). +fixed_int8_t global_int8; +fixed_bfloat16_t global_bfloat16; +fixed_bool_t global_bool; + +extern fixed_int8_t extern_int8; +extern fixed_bfloat16_t extern_bfloat16; +extern fixed_bool_t extern_bool; + +static fixed_int8_t static_int8; +static fixed_bfloat16_t static_bfloat16; +static fixed_bool_t static_bool; + +fixed_int8_t *global_int8_ptr; +extern fixed_int8_t *extern_int8_ptr; +static fixed_int8_t *static_int8_ptr; +__thread fixed_int8_t thread_int8; + +typedef fixed_int8_t int8_typedef; +typedef fixed_int8_t *int8_ptr_typedef; + +// Test sized expressions +int sizeof_int8 = sizeof(global_int8); +int sizeof_int8_var = sizeof(*global_int8_ptr); +int sizeof_int8_var_ptr = sizeof(global_int8_ptr); + +extern fixed_int8_t *extern_int8_ptr; + +int alignof_int8 = __alignof__(extern_int8); +int alignof_int8_var = __alignof__(*extern_int8_ptr); +int alignof_int8_var_ptr = __alignof__(extern_int8_ptr); + +void f(int c) { + fixed_int8_t fs8; + svint8_t ss8; + + void *sel __attribute__((unused)); + sel = c ? ss8 : fs8; // expected-error {{incompatible operand types ('svint8_t' (aka '__SVInt8_t') and 'fixed_int8_t' (aka '__SVInt8_t'))}} + sel = c ? fs8 : ss8; // expected-error {{incompatible operand types ('fixed_int8_t' (aka '__SVInt8_t') and 'svint8_t' (aka '__SVInt8_t'))}} +} + +// --------------------------------------------------------------------------// +// Sizeof + +#define VECTOR_SIZE ((N / 8)) +#define PRED_SIZE ((N / 64)) + +_Static_assert(sizeof(fixed_int8_t) == VECTOR_SIZE, ""); + +_Static_assert(sizeof(fixed_int16_t) == VECTOR_SIZE, ""); +_Static_assert(sizeof(fixed_int32_t) == VECTOR_SIZE, ""); +_Static_assert(sizeof(fixed_int64_t) == VECTOR_SIZE, ""); + +_Static_assert(sizeof(fixed_uint8_t) == VECTOR_SIZE, ""); +_Static_assert(sizeof(fixed_uint16_t) == VECTOR_SIZE, ""); +_Static_assert(sizeof(fixed_uint32_t) == VECTOR_SIZE, ""); +_Static_assert(sizeof(fixed_uint64_t) == VECTOR_SIZE, ""); + +_Static_assert(sizeof(fixed_float16_t) == VECTOR_SIZE, ""); +_Static_assert(sizeof(fixed_float32_t) == VECTOR_SIZE, ""); +_Static_assert(sizeof(fixed_float64_t) == VECTOR_SIZE, ""); + +_Static_assert(sizeof(fixed_bfloat16_t) == VECTOR_SIZE, ""); + +_Static_assert(sizeof(fixed_bool_t) == PRED_SIZE, ""); + +// --------------------------------------------------------------------------// +// Alignof + +#define VECTOR_ALIGN 16 +#define PRED_ALIGN 2 + +_Static_assert(__alignof__(fixed_int8_t) == VECTOR_ALIGN, ""); +_Static_assert(__alignof__(fixed_int16_t) == VECTOR_ALIGN, ""); +_Static_assert(__alignof__(fixed_int32_t) == VECTOR_ALIGN, ""); +_Static_assert(__alignof__(fixed_int64_t) == VECTOR_ALIGN, ""); + +_Static_assert(__alignof__(fixed_uint8_t) == VECTOR_ALIGN, ""); +_Static_assert(__alignof__(fixed_uint16_t) == VECTOR_ALIGN, ""); +_Static_assert(__alignof__(fixed_uint32_t) == VECTOR_ALIGN, ""); +_Static_assert(__alignof__(fixed_uint64_t) == VECTOR_ALIGN, ""); + +_Static_assert(__alignof__(fixed_float16_t) == VECTOR_ALIGN, ""); +_Static_assert(__alignof__(fixed_float32_t) == VECTOR_ALIGN, ""); +_Static_assert(__alignof__(fixed_float64_t) == VECTOR_ALIGN, ""); + +_Static_assert(__alignof__(fixed_bfloat16_t) == VECTOR_ALIGN, ""); + +_Static_assert(__alignof__(fixed_bool_t) == PRED_ALIGN, ""); + +// --------------------------------------------------------------------------// +// Structs + +struct struct_int64 { fixed_int64_t x, y[5]; }; +struct struct_float64 { fixed_float64_t x, y[5]; }; +struct struct_bfloat16 { fixed_bfloat16_t x, y[5]; }; +struct struct_bool { fixed_bool_t x, y[5]; }; + +// --------------------------------------------------------------------------// +// Unions +union union_int64 { fixed_int64_t x, y[5]; }; +union union_float64 { fixed_float64_t x, y[5]; }; +union union_bfloat16 { fixed_bfloat16_t x, y[5]; }; +union union_bool { fixed_bool_t x, y[5]; }; + +// --------------------------------------------------------------------------// +// Implicit casts + +#define TEST_CAST(TYPE) \ + sv##TYPE##_t to_sv##TYPE##_t(fixed_##TYPE##_t x) { return x; } \ + fixed_##TYPE##_t from_sv##TYPE##_t(sv##TYPE##_t x) { return x; } + +TEST_CAST(int8) +TEST_CAST(int16) +TEST_CAST(int32) +TEST_CAST(int64) +TEST_CAST(uint8) +TEST_CAST(uint16) +TEST_CAST(uint32) +TEST_CAST(uint64) +TEST_CAST(float16) +TEST_CAST(float32) +TEST_CAST(float64) +TEST_CAST(bfloat16) +TEST_CAST(bool) + +// Test the implicit conversion only applies to valid types +fixed_int8_t to_fixed_int8_t__from_svuint8_t(svuint8_t x) { return x; } // expected-error {{returning 'svuint8_t' (aka '__SVUint8_t') from a function with incompatible result type 'fixed_int8_t' (aka '__SVInt8_t')}} +fixed_bool_t to_fixed_bool_t__from_svint32_t(svint32_t x) { return x; } // expected-error {{returning 'svint32_t' (aka '__SVInt32_t') from a function with incompatible result type 'fixed_bool_t' (aka '__SVBool_t')}} + +// Test the implicit conversion only applies to fixed-length types +typedef signed int vSInt32 __attribute__((__vector_size__(16))); +svint32_t to_svint32_t_from_gnut(vSInt32 x) { return x; } // expected-error {{returning 'vSInt32' (vector of 4 'int' values) from a function with incompatible result type 'svint32_t' (aka '__SVInt32_t')}} + +vSInt32 to_gnut_from_svint32_t(svint32_t x) { return x; } // expected-error {{returning 'svint32_t' (aka '__SVInt32_t') from a function with incompatible result type 'vSInt32' (vector of 4 'int' values)}} + +// --------------------------------------------------------------------------// +// Test the scalable and fixed-length types can be used interchangeably + +svint32_t __attribute__((overloadable)) svfunc(svint32_t op1, svint32_t op2); +svfloat64_t __attribute__((overloadable)) svfunc(svfloat64_t op1, svfloat64_t op2); +svbool_t __attribute__((overloadable)) svfunc(svbool_t op1, svbool_t op2); + +#define TEST_CALL(TYPE) \ + fixed_##TYPE##_t \ + call_##TYPE##_ff(fixed_##TYPE##_t op1, fixed_##TYPE##_t op2) { \ + return svfunc(op1, op2); \ + } \ + fixed_##TYPE##_t \ + call_##TYPE##_fs(fixed_##TYPE##_t op1, sv##TYPE##_t op2) { \ + return svfunc(op1, op2); \ + } \ + fixed_##TYPE##_t \ + call_##TYPE##_sf(sv##TYPE##_t op1, fixed_##TYPE##_t op2) { \ + return svfunc(op1, op2); \ + } + +TEST_CALL(int32) +TEST_CALL(float64) +TEST_CALL(bool)