Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -2098,10 +2098,6 @@ 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; } Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -1886,14 +1886,16 @@ 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. bool isVLSTBuiltinType() const; + /// Returns the representive type for the element of an SVE builtin type. + /// This is used to represent fixed-length SVE vectors created with the + /// 'arm_sve_vector_bits' type attribute as VectorType. + QualType getFixedLengthSveEltType(const ASTContext &Ctx) const; + /// Types are partitioned into 3 broad categories (C99 6.2.5p1): /// object types, function types, and incomplete types. @@ -3219,7 +3221,12 @@ NeonVector, /// is ARM Neon polynomial vector - NeonPolyVector + NeonPolyVector, + + /// is AArch64 SVE fixed-length data vector + SveFixedLengthDataVector, + /// is AArch64 SVE fixed-length predicate vector + SveFixedLengthPredicateVector }; protected: Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1548,6 +1548,8 @@ let Args = [UnsignedArgument<"NumBits">]; let Documentation = [ArmSveVectorBitsDocs]; let PragmaAttributeSupport = 0; + // Represented as VectorType instead. + let ASTNode = 0; } def ArmMveStrictPolymorphism : TypeAttr, TargetSpecificAttr { Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2924,6 +2924,8 @@ "vector size not an integral multiple of component size">; def err_attribute_zero_size : Error<"zero %0 size">; def err_attribute_size_too_large : Error<"%0 size too large">; +def err_typecheck_vector_not_convertable_sizeless : Error< + "cannot convert between fixed-length and sizeless vector (%0 and %1)">; def err_typecheck_vector_not_convertable_implict_truncation : Error< "cannot convert between %select{scalar|vector}0 type %1 and vector type" " %2 as implicit conversion would cause truncation">; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -1997,10 +1997,7 @@ bool RequireCompleteSizedType(SourceLocation Loc, QualType T, unsigned DiagID, const Ts &... Args) { SizelessTypeDiagnoser Diagnoser(DiagID, Args...); - CompleteTypeKind Kind = CompleteTypeKind::Normal; - if (T->isVLST()) - Kind = CompleteTypeKind::AcceptSizeless; - return RequireCompleteType(Loc, T, Kind, Diagnoser); + return RequireCompleteType(Loc, T, CompleteTypeKind::Normal, Diagnoser); } void completeExprArrayBound(Expr *E); @@ -2018,10 +2015,7 @@ bool RequireCompleteSizedExprType(Expr *E, unsigned DiagID, const Ts &... Args) { SizelessTypeDiagnoser Diagnoser(DiagID, Args...); - CompleteTypeKind Kind = CompleteTypeKind::Normal; - if (E->getType()->isVLST()) - Kind = CompleteTypeKind::AcceptSizeless; - return RequireCompleteExprType(E, Kind, Diagnoser); + return RequireCompleteExprType(E, CompleteTypeKind::Normal, Diagnoser); } bool RequireLiteralType(SourceLocation Loc, QualType T, Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -1871,50 +1871,6 @@ 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. /// @@ -1981,6 +1937,9 @@ uint64_t TargetVectorAlign = Target->getMaxVectorAlign(); if (TargetVectorAlign && TargetVectorAlign < Align) Align = TargetVectorAlign; + // Adjust the alignment for fixed-length SVE predicates. + if (VT->getVectorKind() == VectorType::SveFixedLengthPredicateVector) + Align = 16; break; } @@ -2319,10 +2278,7 @@ Align = Info.Align; AlignIsRequired = Info.AlignIsRequired; } - if (T->isVLST()) - Width = getBitwidthForAttributedSveType(T); - else - Width = Info.Width; + Width = Info.Width; break; } Index: clang/lib/AST/JSONNodeDumper.cpp =================================================================== --- clang/lib/AST/JSONNodeDumper.cpp +++ clang/lib/AST/JSONNodeDumper.cpp @@ -616,6 +616,9 @@ case VectorType::NeonPolyVector: JOS.attribute("vectorKind", "neon poly"); break; + case VectorType::SveFixedLengthDataVector: + case VectorType::SveFixedLengthPredicateVector: + JOS.attribute("vectorKind", "fixed-length sve"); } } Index: clang/lib/AST/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -1408,6 +1408,9 @@ case VectorType::NeonPolyVector: OS << " neon poly"; break; + case VectorType::SveFixedLengthDataVector: + case VectorType::SveFixedLengthPredicateVector: + OS << " fixed-length sve"; } OS << " " << T->getNumElements(); } Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -2313,11 +2313,42 @@ return false; } -bool Type::isVLST() const { - if (!isVLSTBuiltinType()) - return false; +QualType Type::getFixedLengthSveEltType(const ASTContext &Ctx) const { + assert(isVLSTBuiltinType() && "unsupported type!"); - return hasAttr(attr::ArmSveVectorBits); + const BuiltinType *BTy = getAs(); + switch (BTy->getKind()) { + default: + llvm_unreachable("Unknown builtin SVE type!"); + case BuiltinType::SveInt8: + return Ctx.SignedCharTy; + case BuiltinType::SveUint8: + case BuiltinType::SveBool: + // Represent predicates as i8 rather than i1 to avoid any layout issues. + // The type is bitcasted to a scalable predicate type when casting between + // scalable and fixed-length vectors. + return Ctx.UnsignedCharTy; + case BuiltinType::SveInt16: + return Ctx.ShortTy; + case BuiltinType::SveUint16: + return Ctx.UnsignedShortTy; + case BuiltinType::SveInt32: + return Ctx.IntTy; + case BuiltinType::SveUint32: + return Ctx.UnsignedIntTy; + case BuiltinType::SveInt64: + return Ctx.LongTy; + case BuiltinType::SveUint64: + return Ctx.UnsignedLongTy; + case BuiltinType::SveFloat16: + return Ctx.Float16Ty; + case BuiltinType::SveBFloat16: + return Ctx.BFloat16Ty; + case BuiltinType::SveFloat32: + return Ctx.FloatTy; + case BuiltinType::SveFloat64: + return Ctx.DoubleTy; + } } bool QualType::isPODType(const ASTContext &Context) const { Index: clang/lib/AST/TypePrinter.cpp =================================================================== --- clang/lib/AST/TypePrinter.cpp +++ clang/lib/AST/TypePrinter.cpp @@ -655,6 +655,24 @@ printBefore(T->getElementType(), OS); break; } + case VectorType::SveFixedLengthDataVector: + case VectorType::SveFixedLengthPredicateVector: + // FIXME: We prefer to print the size directly here, but have no way + // to get the size of the type. + OS << "__attribute__((__arm_sve_vector_bits__("; + + if (T->getVectorKind() == VectorType::SveFixedLengthPredicateVector) + // Predicates take a bit per byte of the vector size, multiply by 8 to + // get the number of bits passed to the attribute. + OS << T->getNumElements() * 8; + else + OS << T->getNumElements(); + + OS << " * sizeof("; + print(T->getElementType(), OS, StringRef()); + // Multiply by 8 for the number of bits. + OS << ") * 8))) "; + printBefore(T->getElementType(), OS); } } @@ -702,6 +720,24 @@ printBefore(T->getElementType(), OS); break; } + case VectorType::SveFixedLengthDataVector: + case VectorType::SveFixedLengthPredicateVector: + // FIXME: We prefer to print the size directly here, but have no way + // to get the size of the type. + OS << "__attribute__((__arm_sve_vector_bits__("; + if (T->getSizeExpr()) { + T->getSizeExpr()->printPretty(OS, nullptr, Policy); + if (T->getVectorKind() == VectorType::SveFixedLengthPredicateVector) + // Predicates take a bit per byte of the vector size, multiply by 8 to + // get the number of bits passed to the attribute. + OS << " * 8"; + OS << " * sizeof("; + print(T->getElementType(), OS, StringRef()); + // Multiply by 8 for the number of bits. + OS << ") * 8"; + } + OS << "))) "; + printBefore(T->getElementType(), OS); } } @@ -1634,9 +1670,6 @@ case attr::ArmMveStrictPolymorphism: OS << "__clang_arm_mve_strict_polymorphism"; break; - case attr::ArmSveVectorBits: - OS << "arm_sve_vector_bits"; - break; } OS << "))"; } Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -8032,7 +8032,7 @@ return; } - if (!NewVD->hasLocalStorage() && T->isSizelessType() && !T->isVLST()) { + if (!NewVD->hasLocalStorage() && T->isSizelessType()) { Diag(NewVD->getLocation(), diag::err_sizeless_nonlocal) << T; NewVD->setInvalidDecl(); return; Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -8927,6 +8927,33 @@ return false; } +/// This helper function returns true if LHSType is an SVE builtin type and +/// RHSType is a valid fixed-length representation of LHSType, and vice versa. +static bool areCompatibleSveTypes(QualType LHSType, QualType RHSType, + ASTContext &Context) { + auto IsValidCast = [](QualType LHSType, QualType RHSType, + ASTContext &Context) { + if (const auto *BT = LHSType->getAs()) { + if (const auto *VT = RHSType->getAs()) { + // Predicates have the same representation as uint8 so we also have to + // check the kind to make these types incompatible. + if (BT->getKind() == BuiltinType::SveBool && + VT->getVectorKind() == VectorType::SveFixedLengthPredicateVector && + isVector(RHSType, LHSType->getFixedLengthSveEltType(Context))) + return true; + + if (VT->getVectorKind() == VectorType::SveFixedLengthDataVector && + isVector(RHSType, LHSType->getFixedLengthSveEltType(Context))) + return true; + } + } + return false; + }; + + return IsValidCast(LHSType, RHSType, Context) || + IsValidCast(RHSType, LHSType, Context); +} + /// CheckAssignmentConstraints (C99 6.5.16) - This routine currently /// has code to accommodate several GCC extensions when type checking /// pointers. Here are some objectionable examples that GCC considers warnings: @@ -9037,6 +9064,15 @@ } } + if ((LHSType->isSizelessBuiltinType() && RHSType->isVectorType()) || + (LHSType->isVectorType() && RHSType->isSizelessBuiltinType())) { + // Allow assignments between fixed-length and sizeless SVE vectors. + if (areCompatibleSveTypes(LHSType, RHSType, Context)) { + Kind = CK_BitCast; + return Compatible; + } + } + return Incompatible; } @@ -9927,6 +9963,22 @@ // Okay, the expression is invalid. + // If there's a sizeless operand, diagnose that. + if ((LHSVecType && + ((LHSVecType->getVectorKind() == VectorType::SveFixedLengthDataVector) || + (LHSVecType->getVectorKind() == + VectorType::SveFixedLengthPredicateVector)) && + RHSType->isSizelessBuiltinType()) || + (RHSVecType && + ((RHSVecType->getVectorKind() == VectorType::SveFixedLengthDataVector) || + (RHSVecType->getVectorKind() == + VectorType::SveFixedLengthPredicateVector)) && + LHSType->isSizelessBuiltinType())) { + Diag(Loc, diag::err_typecheck_vector_not_convertable_sizeless) + << LHSType << RHSType; + return QualType(); + } + // If there's a non-vector, non-real operand, diagnose that. if ((!RHSVecType && !RHSType->isRealType()) || (!LHSVecType && !LHSType->isRealType())) { Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -2305,7 +2305,7 @@ return QualType(); } - if (T->isSizelessType() && !T->isVLST()) { + if (T->isSizelessType()) { Diag(Loc, diag::err_array_incomplete_or_sizeless_type) << 1 << T; return QualType(); } @@ -7760,14 +7760,10 @@ /// 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(TypeProcessingState &State, - QualType &CurType, - ParsedAttr &Attr) { - Sema &S = State.getSema(); - ASTContext &Ctx = S.Context; - +static void HandleArmSveVectorBitsTypeAttr(QualType &CurType, ParsedAttr &Attr, + Sema &S) { // Target must have SVE. - if (!Ctx.getTargetInfo().hasFeature("sve")) { + if (!S.Context.getTargetInfo().hasFeature("sve")) { S.Diag(Attr.getLoc(), diag::err_attribute_unsupported) << Attr; Attr.setInvalid(); return; @@ -7812,8 +7808,18 @@ return; } - auto *A = ::new (Ctx) ArmSveVectorBitsAttr(Ctx, Attr, VecSize); - CurType = State.getAttributedType(A, CurType, CurType); + const auto *BT = CurType->castAs(); + + QualType EltType = CurType->getFixedLengthSveEltType(S.Context); + unsigned TypeSize = S.Context.getTypeSize(EltType); + VectorType::VectorKind VecKind = VectorType::SveFixedLengthDataVector; + if (BT->getKind() == BuiltinType::SveBool) { + // Predicates are represented as i8 + VecSize /= S.Context.getCharWidth() * S.Context.getCharWidth(); + VecKind = VectorType::SveFixedLengthPredicateVector; + } else + VecSize /= TypeSize; + CurType = S.Context.getVectorType(EltType, VecSize, VecKind); } static void HandleArmMveStrictPolymorphismAttr(TypeProcessingState &State, @@ -8084,7 +8090,7 @@ attr.setUsedAsTypeAttr(); break; case ParsedAttr::AT_ArmSveVectorBits: - HandleArmSveVectorBitsTypeAttr(state, type, attr); + HandleArmSveVectorBitsTypeAttr(type, attr, state.getSema()); 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 @@ -102,8 +102,11 @@ 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'))}} + sel = c ? ss8 : fs8; // expected-error {{cannot convert between fixed-length and sizeless vector}} + sel = c ? fs8 : ss8; // expected-error {{cannot convert between fixed-length and sizeless vector}} + + sel = fs8 + ss8; // expected-error {{cannot convert between fixed-length and sizeless vector}} + sel = ss8 + fs8; // expected-error {{cannot convert between fixed-length and sizeless vector}} } // --------------------------------------------------------------------------// @@ -192,14 +195,18 @@ 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')}} +fixed_int8_t to_fixed_int8_t__from_svuint8_t(svuint8_t x) { return x; } // expected-error-re {{returning 'svuint8_t' (aka '__SVUint8_t') from a function with incompatible result type 'fixed_int8_t' (vector of {{[0-9]+}} 'signed char' values)}} +fixed_bool_t to_fixed_bool_t__from_svint32_t(svint32_t x) { return x; } // expected-error-re {{returning 'svint32_t' (aka '__SVInt32_t') from a function with incompatible result type 'fixed_bool_t' (vector of {{[0-9]+}} 'unsigned char' values)}} + +// Test conversion between predicate and uint8 is invalid, both have the same +// memory representation. +fixed_bool_t to_fixed_bool_t__from_svuint8_t(svuint8_t x) { return x; } // expected-error-re {{returning 'svuint8_t' (aka '__SVUint8_t') from a function with incompatible result type 'fixed_bool_t' (vector of {{[0-9]+}} 'unsigned char' values)}} // 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')}} +svint32_t to_svint32_t_from_gnut(vSInt32 x) { return x; } // expected-error-re {{returning 'vSInt32' (vector of {{[0-9]+}} '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)}} +vSInt32 to_gnut_from_svint32_t(svint32_t x) { return x; } // expected-error-re {{returning 'svint32_t' (aka '__SVInt32_t') from a function with incompatible result type 'vSInt32' (vector of {{[0-9]+}} 'int' values)}} // --------------------------------------------------------------------------// // Test the scalable and fixed-length types can be used interchangeably