Index: clang/include/clang/Basic/CMakeLists.txt =================================================================== --- clang/include/clang/Basic/CMakeLists.txt +++ clang/include/clang/Basic/CMakeLists.txt @@ -69,6 +69,9 @@ clang_tablegen(arm_sve_typeflags.inc -gen-arm-sve-typeflags SOURCE arm_sve.td TARGET ClangARMSveTypeFlags) +clang_tablegen(arm_sve_sema_rangechecks.inc -gen-arm-sve-sema-rangechecks + SOURCE arm_sve.td + TARGET ClangARMSveSemaRangeChecks) clang_tablegen(arm_cde_builtins.inc -gen-arm-cde-builtin-def SOURCE arm_cde.td TARGET ClangARMCdeBuiltinsDef) Index: clang/include/clang/Basic/TargetBuiltins.h =================================================================== --- clang/include/clang/Basic/TargetBuiltins.h +++ clang/include/clang/Basic/TargetBuiltins.h @@ -184,6 +184,12 @@ #undef LLVM_GET_SVE_MEMELTTYPES }; + enum ImmCheckType { +#define LLVM_GET_SVE_IMMCHECKTYPES +#include "clang/Basic/arm_sve_typeflags.inc" +#undef LLVM_GET_SVE_IMMCHECKTYPES + }; + SVETypeFlags(uint64_t F) : Flags(F), EltTypeShift(0), MemEltTypeShift(0) { EltTypeShift = llvm::countTrailingZeros(EltTypeMask); MemEltTypeShift = llvm::countTrailingZeros(MemEltTypeMask); Index: clang/include/clang/Basic/arm_sve.td =================================================================== --- clang/include/clang/Basic/arm_sve.td +++ clang/include/clang/Basic/arm_sve.td @@ -61,6 +61,10 @@ // d: default // c: const pointer type // P: predicate type +// +// i: constant uint64_t +// +// I: Predicate Pattern (sv_pattern) // l: int64_t @@ -142,9 +146,22 @@ def IsStructStore : FlagType<0x00001000>; def IsZExtReturn : FlagType<0x00002000>; // Return value is sign-extend by default +// These must be kept in sync with the flags in include/clang/Basic/TargetBuiltins.h +class ImmCheckType { + int Value = val; +} +def ImmCheckPredicatePattern : ImmCheckType<0>; // 0..31 +def ImmCheck1_16 : ImmCheckType<1>; // 1..16 + +class ImmCheck { + int Arg = arg; + int EltSizeArg = eltSizeArg; + ImmCheckType Kind = kind; +} + // Every intrinsic subclasses Inst. class Inst ft, MemEltType met> { + list ft, list ch, MemEltType met> { string Name = n; string Prototype = p; string Types = t; @@ -152,13 +169,21 @@ int Merge = mt.Value; string LLVMIntrinsic = i; list Flags = ft; + list ImmChecks = ch; int MemEltType = met.Value; } +// SInst: Instruction with signed/unsigned suffix (e.g., "s8", "u8") +class SInst ft = [], list ch = []> + : Inst { +} + // MInst: Instructions which access memory class MInst f, - MemEltType met=MemEltTyDefault, string i=""> - : Inst {} + MemEltType met = MemEltTyDefault, string i = ""> + : Inst { +} //////////////////////////////////////////////////////////////////////////////// // Loads @@ -250,3 +275,8 @@ // Store one vector, with no truncation, non-temporal (scalar base, VL displacement) def SVSTNT1_VNUM : MInst<"svstnt1_vnum[_{d}]", "vPpld", "csilUcUsUiUlhfd", [IsStore], MemEltTyDefault, "aarch64_sve_stnt1">; + +//////////////////////////////////////////////////////////////////////////////// +// Saturating scalar arithmetic +def SVQDECH_S : SInst<"svqdech_pat[_{d}]", "ddIi", "s", MergeNone, "aarch64_sve_sqdech", [], [ImmCheck<2, ImmCheck1_16>]>; +def SVQDECH_U : SInst<"svqdech_pat[_{d}]", "ddIi", "Us", MergeNone, "aarch64_sve_uqdech", [], [ImmCheck<2, ImmCheck1_16>]>; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -11858,6 +11858,7 @@ unsigned MaxWidth); bool CheckNeonBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckMVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); + bool CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckCDEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckARMCoprocessorImmediate(const Expr *CoprocArg, bool WantCDE); bool CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); Index: clang/lib/CodeGen/CGBuiltin.cpp =================================================================== --- clang/lib/CodeGen/CGBuiltin.cpp +++ clang/lib/CodeGen/CGBuiltin.cpp @@ -7401,6 +7401,39 @@ return Op; } +// Return the llvm vector type corresponding to the specified element TypeFlags. +llvm::Type *CodeGenFunction::getSVEType(const SVETypeFlags &TypeFlags) { + switch (TypeFlags.getEltType()) { + default: + llvm_unreachable("Invalid SVETypeFlag!"); + + case SVETypeFlags::EltTyInt8: + return llvm::VectorType::get(Builder.getInt8Ty(), {16, true}); + case SVETypeFlags::EltTyInt16: + return llvm::VectorType::get(Builder.getInt16Ty(), {8, true}); + case SVETypeFlags::EltTyInt32: + return llvm::VectorType::get(Builder.getInt32Ty(), {4, true}); + case SVETypeFlags::EltTyInt64: + return llvm::VectorType::get(Builder.getInt64Ty(), {2, true}); + + case SVETypeFlags::EltTyFloat16: + return llvm::VectorType::get(Builder.getHalfTy(), {8, true}); + case SVETypeFlags::EltTyFloat32: + return llvm::VectorType::get(Builder.getFloatTy(), {4, true}); + case SVETypeFlags::EltTyFloat64: + return llvm::VectorType::get(Builder.getDoubleTy(), {2, true}); + + case SVETypeFlags::EltTyBool8: + return llvm::VectorType::get(Builder.getInt1Ty(), {16, true}); + case SVETypeFlags::EltTyBool16: + return llvm::VectorType::get(Builder.getInt1Ty(), {8, true}); + case SVETypeFlags::EltTyBool32: + return llvm::VectorType::get(Builder.getInt1Ty(), {4, true}); + case SVETypeFlags::EltTyBool64: + return llvm::VectorType::get(Builder.getInt1Ty(), {2, true}); + } +} + // Reinterpret the input predicate so that it can be used to correctly isolate // the elements of the specified datatype. Value *CodeGenFunction::EmitSVEPredicateCast(Value *Pred, @@ -7510,8 +7543,19 @@ for (unsigned i = 0, e = E->getNumArgs(); i != e; i++) { if ((ICEArguments & (1 << i)) == 0) Ops.push_back(EmitScalarExpr(E->getArg(i))); - else - llvm_unreachable("Not yet implemented"); + else { + // If this is required to be a constant, constant fold it so that we know + // that the generated intrinsic gets a ConstantInt. + llvm::APSInt Result; + if (!E->getArg(i)->isIntegerConstantExpr(Result, getContext())) + llvm_unreachable("Expected argument to be a constant"); + + // Immediates for SVE llvm intrinsics are always 32bit. We can safely + // truncate because the immediate has been range checked and no valid + // immediate requires more than a handful of bits. + Result = Result.extOrTrunc(32); + Ops.push_back(llvm::ConstantInt::get(getLLVMContext(), Result)); + } } auto *Builtin = findARMVectorIntrinsicInMap(AArch64SVEIntrinsicMap, BuiltinID, @@ -7523,6 +7567,13 @@ TypeFlags.isZExtReturn()); else if (TypeFlags.isStore()) return EmitSVEMaskedStore(E, Ops, Builtin->LLVMIntrinsic); + else if (Builtin->LLVMIntrinsic != 0) { + llvm::Type* OverloadedTy = getSVEType(TypeFlags); + + Function *F = CGM.getIntrinsic(Builtin->LLVMIntrinsic, OverloadedTy); + Value *Call = Builder.CreateCall(F, Ops); + return Call; + } /// Should not happen return nullptr; Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -77,6 +77,7 @@ class ObjCAtSynchronizedStmt; class ObjCAutoreleasePoolStmt; class ReturnsNonNullAttr; +class SVETypeFlags; namespace analyze_os_log { class OSLogBufferLayout; @@ -3901,6 +3902,7 @@ llvm::Type *Ty, bool usgn, const char *name); llvm::Value *vectorWrapScalar16(llvm::Value *Op); + llvm::Type *getSVEType(const SVETypeFlags &TypeFlags); llvm::Value *EmitSVEPredicateCast(llvm::Value *Pred, llvm::VectorType *VTy); llvm::Value *EmitSVEMaskedLoad(const CallExpr *, llvm::Type *ReturnTy, SmallVectorImpl &Ops, Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -1993,6 +1993,39 @@ llvm_unreachable("Invalid NeonTypeFlag!"); } +bool Sema::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { + // Range check SVE intrinsics that take immediate values. + SmallVector, 3> ImmChecks; + + switch (BuiltinID) { + default: + return false; +#define GET_SVE_IMMEDIATE_CHECK +#include "clang/Basic/arm_sve_sema_rangechecks.inc" +#undef GET_SVE_IMMEDIATE_CHECK + } + + // Perform all the immediate checks for this builtin call. + bool HasError = false; + for (auto &I : ImmChecks) { + int ArgNum, CheckTy, ElementSizeInBits; + std::tie(ArgNum, CheckTy, ElementSizeInBits) = I; + + switch ((SVETypeFlags::ImmCheckType)CheckTy) { + case SVETypeFlags::ImmCheckPredicatePattern: + if (SemaBuiltinConstantArgRange(TheCall, ArgNum, 0, 31)) + HasError = true; + break; + case SVETypeFlags::ImmCheck1_16: + if (SemaBuiltinConstantArgRange(TheCall, ArgNum, 1, 16)) + HasError = true; + break; + } + } + + return HasError; +} + bool Sema::CheckNeonBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { llvm::APSInt Result; uint64_t mask = 0; @@ -2347,6 +2380,9 @@ if (CheckNeonBuiltinFunctionCall(BuiltinID, TheCall)) return true; + if (CheckSVEBuiltinFunctionCall(BuiltinID, TheCall)) + return true; + // For intrinsics which take an immediate value as part of the instruction, // range check them here. unsigned i = 0, l = 0, u = 0; Index: clang/test/CodeGen/aarch64-sve-intrinsics/acle_sve_qdech.c =================================================================== --- /dev/null +++ clang/test/CodeGen/aarch64-sve-intrinsics/acle_sve_qdech.c @@ -0,0 +1,155 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -fallow-half-arguments-and-returns -S -O1 -Werror -Wall -emit-llvm -o - -D__ARM_FEATURE_SVE %s | FileCheck %s + +#include + +svint16_t test_svqdech_pat_s16(svint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_s16 + // CHECK: @llvm.aarch64.sve.sqdech.nxv8i16( %op, i32 0, i32 1) + // CHECK: ret + return svqdech_pat_s16(op, SV_POW2, 1); +} + +svint16_t test_svqdech_pat_s16_all(svint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_s16_all + // CHECK: @llvm.aarch64.sve.sqdech.nxv8i16( %op, i32 31, i32 16) + // CHECK: ret + return svqdech_pat_s16(op, SV_ALL, 16); +} + +svuint16_t test_svqdech_pat_u16_pow2(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_pow2 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 0, i32 1) + // CHECK: ret + return svqdech_pat_u16(op, SV_POW2, 1); +} + +svuint16_t test_svqdech_pat_u16_vl1(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl1 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 1, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL1, 16); +} + +svuint16_t test_svqdech_pat_u16_vl2(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl2 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 2, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL2, 16); +} + +svuint16_t test_svqdech_pat_u16_vl3(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl3 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 3, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL3, 16); +} + +svuint16_t test_svqdech_pat_u16_vl4(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl4 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 4, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL4, 16); +} + +svuint16_t test_svqdech_pat_u16_vl5(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl5 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 5, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL5, 16); +} + +svuint16_t test_svqdech_pat_u16_vl6(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl6 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 6, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL6, 16); +} + +svuint16_t test_svqdech_pat_u16_vl7(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl7 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 7, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL7, 16); +} + +svuint16_t test_svqdech_pat_u16_vl8(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl8 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 8, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL8, 16); +} + +svuint16_t test_svqdech_pat_u16_vl16(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl16 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 9, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL16, 16); +} + +svuint16_t test_svqdech_pat_u16_vl32(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl32 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 10, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL32, 16); +} + +svuint16_t test_svqdech_pat_u16_vl64(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl64 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 11, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL64, 16); +} + +svuint16_t test_svqdech_pat_u16_vl128(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl128 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 12, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL128, 16); +} + +svuint16_t test_svqdech_pat_u16_vl256(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_vl256 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 13, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_VL256, 16); +} + +svuint16_t test_svqdech_pat_u16_mul4(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_mul4 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 29, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_MUL4, 16); +} + +svuint16_t test_svqdech_pat_u16_mul3(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_mul3 + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 30, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_MUL3, 16); +} + +svuint16_t test_svqdech_pat_u16_all(svuint16_t op) +{ + // CHECK-LABEL: test_svqdech_pat_u16_all + // CHECK: @llvm.aarch64.sve.uqdech.nxv8i16( %op, i32 31, i32 16) + // CHECK: ret + return svqdech_pat_u16(op, SV_ALL, 16); +} Index: clang/test/CodeGen/aarch64-sve-intrinsics/negative/acle_sve_qdech.c =================================================================== --- /dev/null +++ clang/test/CodeGen/aarch64-sve-intrinsics/negative/acle_sve_qdech.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -fallow-half-arguments-and-returns -fsyntax-only -verify -D__ARM_FEATURE_SVE %s + +#include +// +// qdech +// + +svint16_t test_svqdech_pat_s16(svint16_t op) +{ + return svqdech_pat_s16(op, SV_VL8, 0); // expected-error-re {{argument value {{[0-9]+}} is outside the valid range [1, 16]}} +} + +svint16_t test_svqdech_pat_8(svint16_t op) +{ + return svqdech_pat(op, SV_VL8, 0); // expected-error-re {{argument value {{[0-9]+}} is outside the valid range [1, 16]}} +} + +svint16_t test_svqdech_pat_s16_2(svint16_t op) +{ + return svqdech_pat_s16(op, SV_VL16, 17); // expected-error-re {{argument value {{[0-9]+}} is outside the valid range [1, 16]}} +} + +svint16_t test_svqdech_pat_9(svint16_t op) +{ + return svqdech_pat(op, SV_VL16, 17); // expected-error-re {{argument value {{[0-9]+}} is outside the valid range [1, 16]}} +} + +svuint16_t test_svqdech_pat_u16(svuint16_t op) +{ + return svqdech_pat_u16(op, SV_VL32, 0); // expected-error-re {{argument value {{[0-9]+}} is outside the valid range [1, 16]}} +} + +svuint16_t test_svqdech_pat_10(svuint16_t op) +{ + return svqdech_pat(op, SV_VL32, 0); // expected-error-re {{argument value {{[0-9]+}} is outside the valid range [1, 16]}} +} + +svuint16_t test_svqdech_pat_u16_2(svuint16_t op) +{ + return svqdech_pat_u16(op, SV_VL64, 17); // expected-error-re {{argument value {{[0-9]+}} is outside the valid range [1, 16]}} +} + +svuint16_t test_svqdech_pat_11(svuint16_t op) +{ + return svqdech_pat(op, SV_VL64, 17); // expected-error-re {{argument value {{[0-9]+}} is outside the valid range [1, 16]}} +} Index: clang/utils/TableGen/SveEmitter.cpp =================================================================== --- clang/utils/TableGen/SveEmitter.cpp +++ clang/utils/TableGen/SveEmitter.cpp @@ -46,6 +46,22 @@ namespace { +class ImmCheck { + int Arg; + int Kind; + int ElementSizeInBits; + +public: + ImmCheck(int Arg, int Kind, int ElementSizeInBits = 0) + : Arg(Arg), Kind(Kind), ElementSizeInBits(ElementSizeInBits) {} + ImmCheck(const ImmCheck &Other) = default; + ~ImmCheck() = default; + + int getArg() const { return Arg; } + int getKind() const { return Kind; } + int getElementSizeInBits() const { return ElementSizeInBits; } +}; + class SVEType { TypeSpec TS; bool Float, Signed, Immediate, Void, Constant, Pointer; @@ -146,6 +162,8 @@ unsigned Flags; + SmallVector ImmChecks; + public: /// The type of predication. enum MergeType { @@ -159,16 +177,18 @@ } Merge; Intrinsic(StringRef Name, StringRef Proto, int64_t MT, StringRef LLVMName, - unsigned Flags, TypeSpec BT, ClassKind Class, SVEEmitter &Emitter, - StringRef Guard) + unsigned Flags, ArrayRef ImmChecks, TypeSpec BT, + ClassKind Class, SVEEmitter &Emitter, StringRef Guard) : Name(Name.str()), LLVMName(LLVMName), Proto(Proto.str()), BaseTypeSpec(BT), Class(Class), Guard(Guard.str()), BaseType(BT, 'd'), - Flags(Flags), Merge(MergeType(MT)) { - // Types[0] is the return value. - for (unsigned I = 0; I < Proto.size(); ++I) - Types.emplace_back(BaseTypeSpec, Proto[I]); + Flags(Flags), ImmChecks(ImmChecks.begin(), ImmChecks.end()), + Merge(MergeType(MT)) { + initialize(Emitter); } + // Initialize the types for this intrinsic. + void initialize(const SVEEmitter &Emitter); + ~Intrinsic()=default; std::string getName() const { return Name; } @@ -189,6 +209,8 @@ unsigned getFlags() const { return Flags; } bool isFlagSet(uint64_t Flag) const { return Flags & Flag;} + ArrayRef getImmChecks() const { return ImmChecks; } + /// Return the type string for a BUILTIN() macro in Builtins.def. std::string getBuiltinTypeStr(); @@ -221,6 +243,7 @@ llvm::StringMap EltTypes; llvm::StringMap MemEltTypes; llvm::StringMap FlagTypes; + llvm::StringMap ImmCheckTypes; unsigned getTypeFlags(const SVEType &T); public: @@ -231,6 +254,16 @@ MemEltTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value"); for (auto *RV : Records.getAllDerivedDefinitions("FlagType")) FlagTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value"); + for (auto *RV : Records.getAllDerivedDefinitions("ImmCheckType")) + ImmCheckTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value"); + } + + /// Returns the enum value for the immcheck type + unsigned getEnumValueForImmCheck(StringRef C) const { + auto Res = ImmCheckTypes.find(C); + if (Res != ImmCheckTypes.end()) + return Res->getValue(); + llvm_unreachable("Unsupported imm check"); } /// Emit arm_sve.h. @@ -242,6 +275,9 @@ /// Emit all the information needed to map builtin -> LLVM IR intrinsic. void createCodeGenMap(raw_ostream &o); + /// Emit all the range checks for the immediates. + void createRangeChecks(raw_ostream &o); + /// Create the SVETypeFlags used in CGBuiltins void createTypeFlags(raw_ostream &o); @@ -442,6 +478,23 @@ Bitwidth = 16; ElementBitwidth = 1; break; + case 'i': + Predicate = false; + Float = false; + ElementBitwidth = Bitwidth = 64; + NumVectors = 0; + Signed = false; + Immediate = true; + break; + case 'I': + Predicate = false; + Float = false; + ElementBitwidth = Bitwidth = 32; + NumVectors = 0; + Signed = true; + Immediate = true; + PredicatePattern = true; + break; case 'l': Predicate = false; Signed = true; @@ -543,6 +596,21 @@ // Intrinsic implementation //===----------------------------------------------------------------------===// +void Intrinsic::initialize(const SVEEmitter &Emitter) { + // Types[0] is the return value. + for (unsigned I = 0; I < Proto.size(); ++I) { + SVEType T(BaseTypeSpec, Proto[I]); + Types.push_back(T); + + // Add range checks for immediates + if (I > 0) { + if (T.isPredicatePattern()) + ImmChecks.emplace_back( + I - 1, Emitter.getEnumValueForImmCheck("ImmCheckPredicatePattern")); + } + } +} + std::string Intrinsic::getBuiltinTypeStr() { std::string S; @@ -677,6 +745,7 @@ StringRef LLVMName = R->getValueAsString("LLVMIntrinsic"); int64_t Merge = R->getValueAsInt("Merge"); std::vector FlagsList = R->getValueAsListOfDefs("Flags"); + std::vector ImmCheckList = R->getValueAsListOfDefs("ImmChecks"); int64_t Flags = 0; for (auto FlagRec : FlagsList) @@ -702,14 +771,30 @@ // Create an Intrinsic for each type spec. for (auto TS : TypeSpecs) { + // Collate a list of range/option checks for the immediates. + SmallVector ImmChecks; + for (auto *R : ImmCheckList) { + int Arg = R->getValueAsInt("Arg"); + int EltSizeArg = R->getValueAsInt("EltSizeArg"); + int Kind = R->getValueAsDef("Kind")->getValueAsInt("Value"); + + int ElementSizeInBits = 0; + if (EltSizeArg >= 0) + ElementSizeInBits = + SVEType(TS, Proto[EltSizeArg + /* offset by return arg */ 1]) + .getElementSizeInBits(); + ImmChecks.push_back(ImmCheck(Arg, Kind, ElementSizeInBits)); + } + Out.push_back(std::make_unique(Name, Proto, Merge, - LLVMName, Flags, TS, ClassS, - *this, Guard)); + LLVMName, Flags, ImmChecks, TS, + ClassS, *this, Guard)); // Also generate the short-form (e.g. svadd_m) for the given type-spec. if (Intrinsic::isOverloadedIntrinsic(Name)) Out.push_back(std::make_unique( - Name, Proto, Merge, LLVMName, Flags, TS, ClassG, *this, Guard)); + Name, Proto, Merge, LLVMName, Flags, ImmChecks, TS, ClassG, + *this, Guard)); } } @@ -759,6 +844,27 @@ OS << "typedef __SVFloat64_t svfloat64_t;\n"; OS << "typedef __SVBool_t svbool_t;\n\n"; + OS << "typedef enum\n"; + OS << "{\n"; + OS << " SV_POW2 = 0,\n"; + OS << " SV_VL1 = 1,\n"; + OS << " SV_VL2 = 2,\n"; + OS << " SV_VL3 = 3,\n"; + OS << " SV_VL4 = 4,\n"; + OS << " SV_VL5 = 5,\n"; + OS << " SV_VL6 = 6,\n"; + OS << " SV_VL7 = 7,\n"; + OS << " SV_VL8 = 8,\n"; + OS << " SV_VL16 = 9,\n"; + OS << " SV_VL32 = 10,\n"; + OS << " SV_VL64 = 11,\n"; + OS << " SV_VL128 = 12,\n"; + OS << " SV_VL256 = 13,\n"; + OS << " SV_MUL4 = 29,\n"; + OS << " SV_MUL3 = 30,\n"; + OS << " SV_ALL = 31\n"; + OS << "} sv_pattern;\n\n"; + OS << "/* Function attributes */\n"; OS << "#define __aio static inline __attribute__((__always_inline__, " "__nodebug__, __overloadable__))\n\n"; @@ -861,6 +967,41 @@ OS << "#endif\n\n"; } +void SVEEmitter::createRangeChecks(raw_ostream &OS) { + std::vector RV = Records.getAllDerivedDefinitions("Inst"); + SmallVector, 128> Defs; + for (auto *R : RV) + createIntrinsic(R, Defs); + + // The mappings must be sorted based on BuiltinID. + llvm::sort(Defs, [](const std::unique_ptr &A, + const std::unique_ptr &B) { + return A->getMangledName() < B->getMangledName(); + }); + + + OS << "#ifdef GET_SVE_IMMEDIATE_CHECK\n"; + + // Ensure these are only emitted once. + std::set Emitted; + + for (auto &Def : Defs) { + if (Emitted.find(Def->getMangledName()) != Emitted.end() || + Def->getImmChecks().empty()) + continue; + + OS << "case SVE::BI__builtin_sve_" << Def->getMangledName() << ":\n"; + for (auto &Check : Def->getImmChecks()) + OS << "ImmChecks.push_back(std::make_tuple(" << Check.getArg() << ", " + << Check.getKind() << ", " << Check.getElementSizeInBits() << "));\n"; + OS << " break;\n"; + + Emitted.insert(Def->getMangledName()); + } + + OS << "#endif\n\n"; +} + /// Create the SVETypeFlags used in CGBuiltins void SVEEmitter::createTypeFlags(raw_ostream &OS) { OS << "#ifdef LLVM_GET_SVE_TYPEFLAGS\n"; @@ -877,6 +1018,11 @@ for (auto &KV : MemEltTypes) OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n"; OS << "#endif\n\n"; + + OS << "#ifdef LLVM_GET_SVE_IMMCHECKTYPES\n"; + for (auto &KV : ImmCheckTypes) + OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n"; + OS << "#endif\n\n"; } namespace clang { @@ -891,6 +1037,11 @@ void EmitSveBuiltinCG(RecordKeeper &Records, raw_ostream &OS) { SVEEmitter(Records).createCodeGenMap(OS); } + +void EmitSveRangeChecks(RecordKeeper &Records, raw_ostream &OS) { + SVEEmitter(Records).createRangeChecks(OS); +} + void EmitSveTypeFlags(RecordKeeper &Records, raw_ostream &OS) { SVEEmitter(Records).createTypeFlags(OS); } Index: clang/utils/TableGen/TableGen.cpp =================================================================== --- clang/utils/TableGen/TableGen.cpp +++ clang/utils/TableGen/TableGen.cpp @@ -74,6 +74,7 @@ GenArmSveBuiltins, GenArmSveBuiltinCG, GenArmSveTypeFlags, + GenArmSveRangeChecks, GenArmCdeHeader, GenArmCdeBuiltinDef, GenArmCdeBuiltinSema, @@ -197,6 +198,8 @@ "Generate arm_sve_builtin_cg_map.inc for clang"), clEnumValN(GenArmSveTypeFlags, "gen-arm-sve-typeflags", "Generate arm_sve_typeflags.inc for clang"), + clEnumValN(GenArmSveRangeChecks, "gen-arm-sve-sema-rangechecks", + "Generate arm_sve_sema_rangechecks.inc for clang"), clEnumValN(GenArmMveHeader, "gen-arm-mve-header", "Generate arm_mve.h for clang"), clEnumValN(GenArmMveBuiltinDef, "gen-arm-mve-builtin-def", @@ -390,6 +393,9 @@ case GenArmSveTypeFlags: EmitSveTypeFlags(Records, OS); break; + case GenArmSveRangeChecks: + EmitSveRangeChecks(Records, OS); + break; case GenArmCdeHeader: EmitCdeHeader(Records, OS); break; Index: clang/utils/TableGen/TableGenBackends.h =================================================================== --- clang/utils/TableGen/TableGenBackends.h +++ clang/utils/TableGen/TableGenBackends.h @@ -95,6 +95,7 @@ void EmitSveBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitSveBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitSveTypeFlags(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitSveRangeChecks(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitMveHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitMveBuiltinDef(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);