Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -2086,6 +2086,10 @@ return getTypeSizeInCharsIfKnown(QualType(Ty, 0)); } + /// Returns true if the 'arm_sve_vector_bits(N)' type attribute is applied to + /// \p T and updates \p Width to the vector size (N), specified in bits. + bool getArmSveVectorBits(const Type *T, unsigned &Width) const; + /// Return the ABI-specified alignment of a (complete) type \p T, in /// bits. unsigned getTypeAlign(QualType T) const { return getTypeInfo(T).Align; } Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -1925,6 +1925,9 @@ bool isSizelessType() const; bool isSizelessBuiltinType() const; + /// Determines if this is vector-length sized typed (VLST), i.e. a + /// sizeless type with the 'arm_sve_vector_bits(N)' 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. Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1538,6 +1538,31 @@ let Documentation = [Undocumented]; } +def ArmSveVectorBits128 : TypeAttr { + let Spellings = []; + let Documentation = [Undocumented]; +} + +def ArmSveVectorBits256 : TypeAttr { + let Spellings = []; + let Documentation = [Undocumented]; +} + +def ArmSveVectorBits512 : TypeAttr { + let Spellings = []; + let Documentation = [Undocumented]; +} + +def ArmSveVectorBits1024 : TypeAttr { + let Spellings = []; + let Documentation = [Undocumented]; +} + +def ArmSveVectorBits2048 : TypeAttr { + let Spellings = []; + let Documentation = [Undocumented]; +} + def ArmMveStrictPolymorphism : TypeAttr, TargetSpecificAttr { let Spellings = [Clang<"__clang_arm_mve_strict_polymorphism">]; let Documentation = [ArmMveStrictPolymorphismDocs]; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ 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, Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -1869,6 +1869,56 @@ return TI; } +bool getSveVectorWidth(const Type *T, unsigned &Width) { + if (T->hasAttr(attr::ArmSveVectorBits128)) + Width = 128; + else if (T->hasAttr(attr::ArmSveVectorBits256)) + Width = 256; + else if (T->hasAttr(attr::ArmSveVectorBits512)) + Width = 512; + else if (T->hasAttr(attr::ArmSveVectorBits1024)) + Width = 1024; + else if (T->hasAttr(attr::ArmSveVectorBits2048)) + Width = 2048; + else + return false; + return true; +} + +bool getSvePredWidth(const Type *T, unsigned &Width) { + // Bit per byte + if (getSveVectorWidth(T, Width)) { + Width /= 8; + return true; + } + return false; +} + +bool ASTContext::getArmSveVectorBits(const Type *T, unsigned &Width) const { + if (!T->isVLST()) + return false; + + 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, Width); + case BuiltinType::SveBool: + return getSvePredWidth(T, Width); + } +} + /// getTypeInfoImpl - Return the size of the specified type, in bits. This /// method does not work on incomplete types. /// @@ -2280,9 +2330,16 @@ case Type::Elaborated: return getTypeInfo(cast(T)->getNamedType().getTypePtr()); - case Type::Attributed: - return getTypeInfo( - cast(T)->getEquivalentType().getTypePtr()); + case Type::Attributed: { + TypeInfo Info = + getTypeInfo(cast(T)->getEquivalentType().getTypePtr()); + unsigned VectorSize; + if (!getArmSveVectorBits(T, VectorSize)) + return Info; + Width = VectorSize; + Align = Info.Align; + break; + } case Type::Atomic: { // Start with the base type information. Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -2318,6 +2318,20 @@ return false; } +bool Type::isVLST() const { + if (!isVLSTBuiltinType()) + return false; + + if (hasAttr(attr::ArmSveVectorBits128) || + hasAttr(attr::ArmSveVectorBits256) || + hasAttr(attr::ArmSveVectorBits512) || + hasAttr(attr::ArmSveVectorBits1024) || + hasAttr(attr::ArmSveVectorBits2048)) + return true; + + return false; +} + bool QualType::isPODType(const ASTContext &Context) const { // C++11 has a more relaxed definition of POD. if (Context.getLangOpts().CPlusPlus11) Index: clang/lib/AST/TypePrinter.cpp =================================================================== --- clang/lib/AST/TypePrinter.cpp +++ clang/lib/AST/TypePrinter.cpp @@ -1633,7 +1633,21 @@ OS << "__clang_arm_mve_strict_polymorphism"; break; case attr::ArmSveVectorBits: - OS << "arm_sve_vector_bits"; + llvm_unreachable("unsupported 'arm_sve_vector_bits' attribute!"); + case attr::ArmSveVectorBits128: + OS << "arm_sve_vector_bits(128)"; + break; + case attr::ArmSveVectorBits256: + OS << "arm_sve_vector_bits(256)"; + break; + case attr::ArmSveVectorBits512: + OS << "arm_sve_vector_bits(512)"; + break; + case attr::ArmSveVectorBits1024: + OS << "arm_sve_vector_bits(1024)"; + break; + case attr::ArmSveVectorBits2048: + OS << "arm_sve_vector_bits(2048)"; break; } OS << "))"; Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -8001,7 +8001,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; Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -2303,7 +2303,7 @@ return QualType(); } - if (T->isSizelessType()) { + if (T->isSizelessType() && !T->isVLST()) { Diag(Loc, diag::err_array_incomplete_or_sizeless_type) << 1 << T; return QualType(); } @@ -7751,10 +7751,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; @@ -7772,7 +7776,7 @@ Expr *VecSizeExpr = static_cast(Attr.getArgAsExpr(0)); llvm::APSInt SveVectorSizeInBits(32); if (VecSizeExpr->isTypeDependent() || VecSizeExpr->isValueDependent() || - !VecSizeExpr->isIntegerConstantExpr(SveVectorSizeInBits, S.Context)) { + !VecSizeExpr->isIntegerConstantExpr(SveVectorSizeInBits, Ctx)) { S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << Attr << AANT_ArgumentIntegerConstant << VecSizeExpr->getSourceRange(); @@ -7828,6 +7832,29 @@ Attr.setInvalid(); return; } + + clang::Attr *A; + switch (SveVectorSizeInBits.getZExtValue()) { + default: + llvm_unreachable("unsupported vector size!"); + case 128: + A = createSimpleAttr(Ctx, Attr); + break; + case 256: + A = createSimpleAttr(Ctx, Attr); + break; + case 512: + A = createSimpleAttr(Ctx, Attr); + break; + case 1024: + A = createSimpleAttr(Ctx, Attr); + break; + case 2048: + A = createSimpleAttr(Ctx, Attr); + break; + } + + CurType = State.getAttributedType(A, CurType, CurType); } static void HandleArmMveStrictPolymorphismAttr(TypeProcessingState &State, @@ -8094,7 +8121,7 @@ attr.setUsedAsTypeAttr(); break; case ParsedAttr::AT_ArmSveVectorBits: - HandleArmSveVectorBitsTypeAttr(type, attr, state.getSema()); + HandleArmSveVectorBitsTypeAttr(state, type, attr); attr.setUsedAsTypeAttr(); break; case ParsedAttr::AT_ArmMveStrictPolymorphism: { Index: clang/test/Sema/attr-arm-sve-vector-bits.c =================================================================== --- clang/test/Sema/attr-arm-sve-vector-bits.c +++ clang/test/Sema/attr-arm-sve-vector-bits.c @@ -41,3 +41,194 @@ 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')}} + +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; + +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(bool 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_int8 { fixed_int8_t x, y[5]; }; +struct struct_int16 { fixed_int16_t x, y[5]; }; +struct struct_int32 { fixed_int32_t x, y[5]; }; +struct struct_int64 { fixed_int64_t x, y[5]; }; + +struct struct_uint8 { fixed_uint8_t x, y[5]; }; +struct struct_uint16 { fixed_uint16_t x, y[5]; }; +struct struct_uint32 { fixed_uint32_t x, y[5]; }; +struct struct_uint64 { fixed_uint64_t x, y[5]; }; + +struct struct_float16 { fixed_float16_t x, y[5]; }; +struct struct_float32 { fixed_float32_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_int8 { fixed_int8_t x, y[5]; }; +union union_int16 { fixed_int16_t x, y[5]; }; +union union_int32 { fixed_int32_t x, y[5]; }; +union union_int64 { fixed_int64_t x, y[5]; }; + +union union_uint8 { fixed_uint8_t x, y[5]; }; +union union_uint16 { fixed_uint16_t x, y[5]; }; +union union_uint32 { fixed_uint32_t x, y[5]; }; +union union_uint64 { fixed_uint64_t x, y[5]; }; + +union union_float16 { fixed_float16_t x, y[5]; }; +union union_float32 { fixed_float32_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 call + +#define TEST_CALL(TYPE) \ + fixed_##TYPE##_t \ + call_##TYPE##_ff(fixed_bool_t pg, fixed_##TYPE##_t op1, fixed_##TYPE##_t op2) { \ + return svsel(pg, op1, op2); \ + } \ + fixed_##TYPE##_t \ + call_##TYPE##_fs(fixed_bool_t pg, fixed_##TYPE##_t op1, sv##TYPE##_t op2) { \ + return svsel(pg, op1, op2); \ + } \ + fixed_##TYPE##_t \ + call_##TYPE##_sf(svbool_t pg, sv##TYPE##_t op1, fixed_##TYPE##_t op2) { \ + return svsel(pg, op1, op2); \ + } + +TEST_CALL(int8) +TEST_CALL(int16) +TEST_CALL(int32) +TEST_CALL(int64) +TEST_CALL(uint8) +TEST_CALL(uint16) +TEST_CALL(uint32) +TEST_CALL(uint64) +TEST_CALL(float16) +TEST_CALL(float32) +TEST_CALL(float64) +TEST_CALL(bfloat16) +TEST_CALL(bool)