c-rhodes created this revision. c-rhodes added reviewers: efriedma, sdesmalen, rsandifo-arm, aaron.ballman, paulwalker-arm. Herald added subscribers: danielkiss, kristof.beyls, tschuett. Herald added a reviewer: rengolin. Herald added a project: clang. c-rhodes requested review of this revision.
This patch implements the semantics for the 'arm_sve_vector_bits' type attribute, defined by the Arm C Language Extensions (ACLE) for SVE [1]. The purpose of this attribute is to define vector-length-specific (VLS) versions of existing vector-length-agnostic (VLA) types. The semantics were already implemented by D83551 <https://reviews.llvm.org/D83551>, although the implementation approach has since changed to represent VLSTs as VectorType in the AST and fixed-length vectors in the IR everywhere except in function args/returns. This is described in the prototype patch D85128 <https://reviews.llvm.org/D85128> demonstrating the new approach. The semantic changes added in D83551 <https://reviews.llvm.org/D83551> are changed since the AttributedType is replaced by VectorType in the AST. Minimal changes were necessary in the previous patch as the canonical type for both VLA and VLS was the same (i.e. sizeless), except in constructs such as globals and structs where sizeless types are unsupported. This patch reverts the changes that permitted VLS types that were represented as sizeless types in such circumstances, and adds support for implicit casting between VLA <-> VLS types as described in section 3.7.3.2 of the ACLE. Since the SVE builtin types for bool and uint8 are both represented as BuiltinType::UChar in VLSTs, two new vector kinds are implemented to distinguish predicate and data vectors. [1] https://developer.arm.com/documentation/100987/latest Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D85736 Files: clang/include/clang/AST/ASTContext.h clang/include/clang/AST/Type.h clang/include/clang/Basic/Attr.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/AST/ASTContext.cpp clang/lib/AST/JSONNodeDumper.cpp clang/lib/AST/TextNodeDumper.cpp clang/lib/AST/Type.cpp clang/lib/AST/TypePrinter.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaType.cpp clang/test/Sema/attr-arm-sve-vector-bits.c
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 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<BuiltinType>(); + + 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/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<BuiltinType>()) { + if (const auto *VT = RHSType->getAs<VectorType>()) { + // 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/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/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/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<BuiltinType>(); + 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/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/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/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<TypedefType>()) { - const TypedefNameDecl *Typedef = TT->getDecl(); - TypeSourceInfo *TInfo = Typedef->getTypeSourceInfo(); - TypeLoc TL = TInfo->getTypeLoc(); - if (AttributedTypeLoc ATL = TL.getAs<AttributedTypeLoc>()) - if (const auto *Attr = ATL.getAttrAs<ArmSveVectorBitsAttr>()) - 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<BuiltinType>()->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/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<Ts...> 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<Ts...> 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/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/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<TargetARM> { 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/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; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits