|  | //===-- SveEmitter.cpp - Generate arm_sve.h for use with clang ------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This tablegen backend is responsible for emitting arm_sve.h, which includes | 
|  | // a declaration and definition of each function specified by the ARM C/C++ | 
|  | // Language Extensions (ACLE). | 
|  | // | 
|  | // For details, visit: | 
|  | //  https://developer.arm.com/architectures/system-architectures/software-standards/acle | 
|  | // | 
|  | // Each SVE instruction is implemented in terms of 1 or more functions which | 
|  | // are suffixed with the element type of the input vectors.  Functions may be | 
|  | // implemented in terms of generic vector operations such as +, *, -, etc. or | 
|  | // by calling a __builtin_-prefixed function which will be handled by clang's | 
|  | // CodeGen library. | 
|  | // | 
|  | // See also the documentation in include/clang/Basic/arm_sve.td. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/ADT/ArrayRef.h" | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  | #include "llvm/ADT/StringExtras.h" | 
|  | #include "llvm/ADT/StringMap.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include "llvm/TableGen/AArch64ImmCheck.h" | 
|  | #include "llvm/TableGen/Error.h" | 
|  | #include "llvm/TableGen/Record.h" | 
|  | #include "llvm/TableGen/StringToOffsetTable.h" | 
|  | #include <array> | 
|  | #include <cctype> | 
|  | #include <set> | 
|  | #include <string> | 
|  | #include <tuple> | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | enum ClassKind { | 
|  | ClassNone, | 
|  | ClassS,     // signed/unsigned, e.g., "_s8", "_u8" suffix | 
|  | ClassG,     // Overloaded name without type suffix | 
|  | }; | 
|  |  | 
|  | enum class ACLEKind { SVE, SME }; | 
|  |  | 
|  | using TypeSpec = std::string; | 
|  |  | 
|  | namespace { | 
|  | class SVEType { | 
|  |  | 
|  | enum TypeKind { | 
|  | Invalid, | 
|  | Void, | 
|  | Float, | 
|  | SInt, | 
|  | UInt, | 
|  | BFloat16, | 
|  | MFloat8, | 
|  | Svcount, | 
|  | PrefetchOp, | 
|  | PredicatePattern, | 
|  | Predicate, | 
|  | Fpm | 
|  | }; | 
|  | TypeKind Kind; | 
|  | bool Immediate, Constant, Pointer, DefaultType, IsScalable; | 
|  | unsigned Bitwidth, ElementBitwidth, NumVectors; | 
|  |  | 
|  | public: | 
|  | SVEType() : SVEType("", 'v') {} | 
|  |  | 
|  | SVEType(StringRef TS, char CharMod, unsigned NumVectors = 1) | 
|  | : Kind(Invalid), Immediate(false), Constant(false), Pointer(false), | 
|  | DefaultType(false), IsScalable(true), Bitwidth(128), | 
|  | ElementBitwidth(~0U), NumVectors(NumVectors) { | 
|  | if (!TS.empty()) | 
|  | applyTypespec(TS); | 
|  | applyModifier(CharMod); | 
|  | } | 
|  |  | 
|  | SVEType(const SVEType &Base, unsigned NumV) : SVEType(Base) { | 
|  | NumVectors = NumV; | 
|  | } | 
|  |  | 
|  | bool isPointer() const { return Pointer; } | 
|  | bool isConstant() const { return Constant; } | 
|  | bool isImmediate() const { return Immediate; } | 
|  | bool isScalar() const { return NumVectors == 0; } | 
|  | bool isVector() const { return NumVectors > 0; } | 
|  | bool isScalableVector() const { return isVector() && IsScalable; } | 
|  | bool isFixedLengthVector() const { return isVector() && !IsScalable; } | 
|  | bool isChar() const { return ElementBitwidth == 8 && isInteger(); } | 
|  | bool isVoid() const { return Kind == Void; } | 
|  | bool isDefault() const { return DefaultType; } | 
|  | bool isFloat() const { return Kind == Float; } | 
|  | bool isBFloat() const { return Kind == BFloat16; } | 
|  | bool isMFloat() const { return Kind == MFloat8; } | 
|  | bool isFloatingPoint() const { | 
|  | return Kind == Float || Kind == BFloat16 || Kind == MFloat8; | 
|  | } | 
|  | bool isInteger() const { return Kind == SInt || Kind == UInt; } | 
|  | bool isSignedInteger() const { return Kind == SInt; } | 
|  | bool isUnsignedInteger() const { return Kind == UInt; } | 
|  | bool isScalarPredicate() const { | 
|  | return Kind == Predicate && NumVectors == 0; | 
|  | } | 
|  | bool isPredicate() const { return Kind == Predicate; } | 
|  | bool isPredicatePattern() const { return Kind == PredicatePattern; } | 
|  | bool isPrefetchOp() const { return Kind == PrefetchOp; } | 
|  | bool isSvcount() const { return Kind == Svcount; } | 
|  | bool isFpm() const { return Kind == Fpm; } | 
|  | bool isInvalid() const { return Kind == Invalid; } | 
|  | unsigned getElementSizeInBits() const { return ElementBitwidth; } | 
|  | unsigned getNumVectors() const { return NumVectors; } | 
|  |  | 
|  | unsigned getNumElements() const { | 
|  | assert(ElementBitwidth != ~0U); | 
|  | return isPredicate() ? 16 : (Bitwidth / ElementBitwidth); | 
|  | } | 
|  | unsigned getSizeInBits() const { | 
|  | return Bitwidth; | 
|  | } | 
|  |  | 
|  | /// Return the string representation of a type, which is an encoded | 
|  | /// string for passing to the BUILTIN() macro in Builtins.def. | 
|  | std::string builtin_str() const; | 
|  |  | 
|  | /// Return the C/C++ string representation of a type for use in the | 
|  | /// arm_sve.h header file. | 
|  | std::string str() const; | 
|  |  | 
|  | private: | 
|  | /// Creates the type based on the typespec string in TS. | 
|  | void applyTypespec(StringRef TS); | 
|  |  | 
|  | /// Applies a prototype modifier to the type. | 
|  | void applyModifier(char Mod); | 
|  |  | 
|  | /// Get the builtin base for this SVEType, e.g. 'Wi' for svint64_t. | 
|  | std::string builtinBaseType() const; | 
|  | }; | 
|  |  | 
|  | class SVEEmitter; | 
|  |  | 
|  | /// The main grunt class. This represents an instantiation of an intrinsic with | 
|  | /// a particular typespec and prototype. | 
|  | class Intrinsic { | 
|  | /// The unmangled name. | 
|  | std::string Name; | 
|  |  | 
|  | /// The name of the corresponding LLVM IR intrinsic. | 
|  | std::string LLVMName; | 
|  |  | 
|  | /// Intrinsic prototype. | 
|  | std::string Proto; | 
|  |  | 
|  | /// The base type spec for this intrinsic. | 
|  | TypeSpec BaseTypeSpec; | 
|  |  | 
|  | /// The base class kind. Most intrinsics use ClassS, which has full type | 
|  | /// info for integers (_s32/_u32), or ClassG which is used for overloaded | 
|  | /// intrinsics. | 
|  | ClassKind Class; | 
|  |  | 
|  | /// The architectural #ifdef guard. | 
|  | std::string SVEGuard, SMEGuard; | 
|  |  | 
|  | // The merge suffix such as _m, _x or _z. | 
|  | std::string MergeSuffix; | 
|  |  | 
|  | /// The types of return value [0] and parameters [1..]. | 
|  | std::vector<SVEType> Types; | 
|  |  | 
|  | /// The "base type", which is VarType('d', BaseTypeSpec). | 
|  | SVEType BaseType; | 
|  |  | 
|  | uint64_t Flags; | 
|  |  | 
|  | SmallVector<ImmCheck, 2> ImmChecks; | 
|  |  | 
|  | bool SetsFPMR; | 
|  |  | 
|  | public: | 
|  | Intrinsic(StringRef Name, StringRef Proto, uint64_t MergeTy, | 
|  | StringRef MergeSuffix, uint64_t MemoryElementTy, StringRef LLVMName, | 
|  | uint64_t Flags, ArrayRef<ImmCheck> ImmChecks, TypeSpec BT, | 
|  | ClassKind Class, SVEEmitter &Emitter, StringRef SVEGuard, | 
|  | StringRef SMEGuard); | 
|  |  | 
|  | ~Intrinsic()=default; | 
|  |  | 
|  | std::string getName() const { return Name; } | 
|  | std::string getLLVMName() const { return LLVMName; } | 
|  | std::string getProto() const { return Proto; } | 
|  | TypeSpec getBaseTypeSpec() const { return BaseTypeSpec; } | 
|  | SVEType getBaseType() const { return BaseType; } | 
|  |  | 
|  | StringRef getSVEGuard() const { return SVEGuard; } | 
|  | StringRef getSMEGuard() const { return SMEGuard; } | 
|  | std::string getGuard() const { | 
|  | std::string Guard; | 
|  | llvm::raw_string_ostream OS(Guard); | 
|  | if (!SVEGuard.empty() && SMEGuard.empty()) | 
|  | OS << SVEGuard; | 
|  | else if (SVEGuard.empty() && !SMEGuard.empty()) | 
|  | OS << SMEGuard; | 
|  | else { | 
|  | if (SVEGuard.find(",") != std::string::npos || | 
|  | SVEGuard.find("|") != std::string::npos) | 
|  | OS << "(" << SVEGuard << ")"; | 
|  | else | 
|  | OS << SVEGuard; | 
|  | OS << "|"; | 
|  | if (SMEGuard.find(",") != std::string::npos || | 
|  | SMEGuard.find("|") != std::string::npos) | 
|  | OS << "(" << SMEGuard << ")"; | 
|  | else | 
|  | OS << SMEGuard; | 
|  | } | 
|  | return Guard; | 
|  | } | 
|  | ClassKind getClassKind() const { return Class; } | 
|  |  | 
|  | SVEType getReturnType() const { return Types[0]; } | 
|  | ArrayRef<SVEType> getTypes() const { return Types; } | 
|  | SVEType getParamType(unsigned I) const { return Types[I + 1]; } | 
|  | unsigned getNumParams() const { | 
|  | return Proto.size() - (2 * count(Proto, '.')) - 1; | 
|  | } | 
|  |  | 
|  | uint64_t getFlags() const { return Flags; } | 
|  | bool isFlagSet(uint64_t Flag) const { return Flags & Flag;} | 
|  |  | 
|  | ArrayRef<ImmCheck> getImmChecks() const { return ImmChecks; } | 
|  |  | 
|  | /// Return the type string for a BUILTIN() macro in Builtins.def. | 
|  | std::string getBuiltinTypeStr(); | 
|  |  | 
|  | /// Return the name, mangled with type information. The name is mangled for | 
|  | /// ClassS, so will add type suffixes such as _u32/_s32. | 
|  | std::string getMangledName() const { return mangleName(ClassS); } | 
|  |  | 
|  | /// As above, but mangles the LLVM name instead. | 
|  | std::string getMangledLLVMName() const { return mangleLLVMName(); } | 
|  |  | 
|  | /// Returns true if the intrinsic is overloaded, in that it should also generate | 
|  | /// a short form without the type-specifiers, e.g. 'svld1(..)' instead of | 
|  | /// 'svld1_u32(..)'. | 
|  | static bool isOverloadedIntrinsic(StringRef Name) { | 
|  | return Name.contains('[') && Name.contains(']'); | 
|  | } | 
|  |  | 
|  | /// Return true if the intrinsic takes a splat operand. | 
|  | bool hasSplat() const { | 
|  | // These prototype modifiers are described in arm_sve.td. | 
|  | return Proto.find_first_of("ajfrKLR@!") != std::string::npos; | 
|  | } | 
|  |  | 
|  | /// Return the parameter index of the splat operand. | 
|  | unsigned getSplatIdx() const { | 
|  | unsigned I = 1, Param = 0; | 
|  | for (; I < Proto.size(); ++I, ++Param) { | 
|  | if (Proto[I] == 'a' || Proto[I] == 'j' || Proto[I] == 'f' || | 
|  | Proto[I] == 'r' || Proto[I] == 'K' || Proto[I] == 'L' || | 
|  | Proto[I] == 'R' || Proto[I] == '@' || Proto[I] == '!') | 
|  | break; | 
|  |  | 
|  | // Multivector modifier can be skipped | 
|  | if (Proto[I] == '.') | 
|  | I += 2; | 
|  | } | 
|  | assert(I != Proto.size() && "Prototype has no splat operand"); | 
|  | return Param; | 
|  | } | 
|  |  | 
|  | /// Emits the intrinsic declaration to the ostream. | 
|  | void emitIntrinsic(raw_ostream &OS, SVEEmitter &Emitter, ACLEKind Kind) const; | 
|  |  | 
|  | private: | 
|  | std::string getMergeSuffix() const { return MergeSuffix; } | 
|  | StringRef getFPMSuffix() const { return SetsFPMR ? "_fpm" : ""; } | 
|  | std::string mangleName(ClassKind LocalCK) const; | 
|  | std::string mangleLLVMName() const; | 
|  | std::string replaceTemplatedArgs(std::string Name, TypeSpec TS, | 
|  | std::string Proto) const; | 
|  | }; | 
|  |  | 
|  | class SVEEmitter { | 
|  | private: | 
|  | // The reinterpret builtins are generated separately because they | 
|  | // need the cross product of all types (121 functions in total), | 
|  | // which is inconvenient to specify in the arm_sve.td file or | 
|  | // generate in CGBuiltin.cpp. | 
|  | struct ReinterpretTypeInfo { | 
|  | SVEType BaseType; | 
|  | const char *Suffix; | 
|  | }; | 
|  |  | 
|  | static const std::array<ReinterpretTypeInfo, 13> Reinterprets; | 
|  |  | 
|  | const RecordKeeper &Records; | 
|  | StringMap<uint64_t> EltTypes; | 
|  | StringMap<uint64_t> MemEltTypes; | 
|  | StringMap<uint64_t> FlagTypes; | 
|  | StringMap<uint64_t> MergeTypes; | 
|  | StringMap<uint64_t> ImmCheckTypes; | 
|  |  | 
|  | public: | 
|  | SVEEmitter(const RecordKeeper &R) : Records(R) { | 
|  | for (auto *RV : Records.getAllDerivedDefinitions("EltType")) | 
|  | EltTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value"); | 
|  | for (auto *RV : Records.getAllDerivedDefinitions("MemEltType")) | 
|  | MemEltTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value"); | 
|  | for (auto *RV : Records.getAllDerivedDefinitions("FlagType")) | 
|  | FlagTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value"); | 
|  | for (auto *RV : Records.getAllDerivedDefinitions("MergeType")) | 
|  | MergeTypes[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 It = ImmCheckTypes.find(C); | 
|  | if (It != ImmCheckTypes.end()) | 
|  | return It->getValue(); | 
|  | llvm_unreachable("Unsupported imm check"); | 
|  | } | 
|  |  | 
|  | /// Returns the enum value for the flag type | 
|  | uint64_t getEnumValueForFlag(StringRef C) const { | 
|  | auto Res = FlagTypes.find(C); | 
|  | if (Res != FlagTypes.end()) | 
|  | return Res->getValue(); | 
|  | llvm_unreachable("Unsupported flag"); | 
|  | } | 
|  |  | 
|  | // Returns the SVETypeFlags for a given value and mask. | 
|  | uint64_t encodeFlag(uint64_t V, StringRef MaskName) const { | 
|  | auto It = FlagTypes.find(MaskName); | 
|  | if (It != FlagTypes.end()) { | 
|  | uint64_t Mask = It->getValue(); | 
|  | unsigned Shift = countr_zero(Mask); | 
|  | assert(Shift < 64 && "Mask value produced an invalid shift value"); | 
|  | return (V << Shift) & Mask; | 
|  | } | 
|  | llvm_unreachable("Unsupported flag"); | 
|  | } | 
|  |  | 
|  | // Returns the SVETypeFlags for the given element type. | 
|  | uint64_t encodeEltType(StringRef EltName) { | 
|  | auto It = EltTypes.find(EltName); | 
|  | if (It != EltTypes.end()) | 
|  | return encodeFlag(It->getValue(), "EltTypeMask"); | 
|  | llvm_unreachable("Unsupported EltType"); | 
|  | } | 
|  |  | 
|  | // Returns the SVETypeFlags for the given memory element type. | 
|  | uint64_t encodeMemoryElementType(uint64_t MT) { | 
|  | return encodeFlag(MT, "MemEltTypeMask"); | 
|  | } | 
|  |  | 
|  | // Returns the SVETypeFlags for the given merge type. | 
|  | uint64_t encodeMergeType(uint64_t MT) { | 
|  | return encodeFlag(MT, "MergeTypeMask"); | 
|  | } | 
|  |  | 
|  | // Returns the SVETypeFlags for the given splat operand. | 
|  | unsigned encodeSplatOperand(unsigned SplatIdx) { | 
|  | assert(SplatIdx < 7 && "SplatIdx out of encodable range"); | 
|  | return encodeFlag(SplatIdx + 1, "SplatOperandMask"); | 
|  | } | 
|  |  | 
|  | // Returns the SVETypeFlags value for the given SVEType. | 
|  | uint64_t encodeTypeFlags(const SVEType &T); | 
|  |  | 
|  | /// Emit arm_sve.h. | 
|  | void createHeader(raw_ostream &o); | 
|  |  | 
|  | // Emits core intrinsics in both arm_sme.h and arm_sve.h | 
|  | void createCoreHeaderIntrinsics(raw_ostream &o, SVEEmitter &Emitter, | 
|  | ACLEKind Kind); | 
|  |  | 
|  | /// Emit all the __builtin prototypes and code needed by Sema. | 
|  | void createBuiltins(raw_ostream &o); | 
|  |  | 
|  | /// 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); | 
|  |  | 
|  | // Emit all the ImmCheckTypes to arm_immcheck_types.inc | 
|  | void createImmCheckTypes(raw_ostream &OS); | 
|  |  | 
|  | /// Create the SVETypeFlags used in CGBuiltins | 
|  | void createTypeFlags(raw_ostream &o); | 
|  |  | 
|  | /// Emit arm_sme.h. | 
|  | void createSMEHeader(raw_ostream &o); | 
|  |  | 
|  | /// Emit all the SME __builtin prototypes and code needed by Sema. | 
|  | void createSMEBuiltins(raw_ostream &o); | 
|  |  | 
|  | /// Emit all the information needed to map builtin -> LLVM IR intrinsic. | 
|  | void createSMECodeGenMap(raw_ostream &o); | 
|  |  | 
|  | /// Create a table for a builtin's requirement for PSTATE.SM. | 
|  | void createStreamingAttrs(raw_ostream &o, ACLEKind Kind); | 
|  |  | 
|  | /// Emit all the range checks for the immediates. | 
|  | void createSMERangeChecks(raw_ostream &o); | 
|  |  | 
|  | /// Create a table for a builtin's requirement for PSTATE.ZA. | 
|  | void createBuiltinZAState(raw_ostream &OS); | 
|  |  | 
|  | /// Create intrinsic and add it to \p Out | 
|  | void createIntrinsic(const Record *R, | 
|  | SmallVectorImpl<std::unique_ptr<Intrinsic>> &Out); | 
|  | }; | 
|  |  | 
|  | const std::array<SVEEmitter::ReinterpretTypeInfo, 13> SVEEmitter::Reinterprets = | 
|  | {{{SVEType("c", 'd'), "s8"}, | 
|  | {SVEType("Uc", 'd'), "u8"}, | 
|  | {SVEType("m", 'd'), "mf8"}, | 
|  | {SVEType("s", 'd'), "s16"}, | 
|  | {SVEType("Us", 'd'), "u16"}, | 
|  | {SVEType("i", 'd'), "s32"}, | 
|  | {SVEType("Ui", 'd'), "u32"}, | 
|  | {SVEType("l", 'd'), "s64"}, | 
|  | {SVEType("Ul", 'd'), "u64"}, | 
|  | {SVEType("h", 'd'), "f16"}, | 
|  | {SVEType("b", 'd'), "bf16"}, | 
|  | {SVEType("f", 'd'), "f32"}, | 
|  | {SVEType("d", 'd'), "f64"}}}; | 
|  |  | 
|  | } // end anonymous namespace | 
|  |  | 
|  | //===----------------------------------------------------------------------===// | 
|  | // Type implementation | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | std::string SVEType::builtinBaseType() const { | 
|  | switch (Kind) { | 
|  | case TypeKind::Void: | 
|  | return "v"; | 
|  | case TypeKind::Svcount: | 
|  | return "Qa"; | 
|  | case TypeKind::PrefetchOp: | 
|  | case TypeKind::PredicatePattern: | 
|  | return "i"; | 
|  | case TypeKind::Fpm: | 
|  | return "UWi"; | 
|  | case TypeKind::Predicate: | 
|  | return "b"; | 
|  | case TypeKind::BFloat16: | 
|  | assert(ElementBitwidth == 16 && "Invalid BFloat16!"); | 
|  | return "y"; | 
|  | case TypeKind::MFloat8: | 
|  | assert(ElementBitwidth == 8 && "Invalid MFloat8!"); | 
|  | return "m"; | 
|  | case TypeKind::Float: | 
|  | switch (ElementBitwidth) { | 
|  | case 16: | 
|  | return "h"; | 
|  | case 32: | 
|  | return "f"; | 
|  | case 64: | 
|  | return "d"; | 
|  | default: | 
|  | llvm_unreachable("Unhandled float width!"); | 
|  | } | 
|  | case TypeKind::SInt: | 
|  | case TypeKind::UInt: | 
|  | switch (ElementBitwidth) { | 
|  | case 1: | 
|  | return "b"; | 
|  | case 8: | 
|  | return "c"; | 
|  | case 16: | 
|  | return "s"; | 
|  | case 32: | 
|  | return "i"; | 
|  | case 64: | 
|  | return "Wi"; | 
|  | case 128: | 
|  | return "LLLi"; | 
|  | default: | 
|  | llvm_unreachable("Unhandled bitwidth!"); | 
|  | } | 
|  | case TypeKind::Invalid: | 
|  | llvm_unreachable("Attempting to resolve builtin string from Invalid type!"); | 
|  | } | 
|  | llvm_unreachable("Unhandled TypeKind!"); | 
|  | } | 
|  |  | 
|  | std::string SVEType::builtin_str() const { | 
|  | std::string Prefix; | 
|  |  | 
|  | if (isScalableVector()) | 
|  | Prefix = "q" + llvm::utostr(getNumElements() * NumVectors); | 
|  | else if (isFixedLengthVector()) | 
|  | Prefix = "V" + llvm::utostr(getNumElements() * NumVectors); | 
|  | else if (isImmediate()) { | 
|  | assert(!isFloatingPoint() && "fp immediates are not supported"); | 
|  | Prefix = "I"; | 
|  | } | 
|  |  | 
|  | // Make chars and integer pointers explicitly signed. | 
|  | if ((ElementBitwidth == 8 || isPointer()) && isSignedInteger()) | 
|  | Prefix += "S"; | 
|  | else if (isUnsignedInteger()) | 
|  | Prefix += "U"; | 
|  |  | 
|  | std::string BuiltinStr = Prefix + builtinBaseType(); | 
|  | if (isConstant()) | 
|  | BuiltinStr += "C"; | 
|  | if (isPointer()) | 
|  | BuiltinStr += "*"; | 
|  |  | 
|  | return BuiltinStr; | 
|  | } | 
|  |  | 
|  | std::string SVEType::str() const { | 
|  | std::string TypeStr; | 
|  |  | 
|  | switch (Kind) { | 
|  | case TypeKind::PrefetchOp: | 
|  | return "enum svprfop"; | 
|  | case TypeKind::PredicatePattern: | 
|  | return "enum svpattern"; | 
|  | case TypeKind::Fpm: | 
|  | TypeStr += "fpm"; | 
|  | break; | 
|  | case TypeKind::Void: | 
|  | TypeStr += "void"; | 
|  | break; | 
|  | case TypeKind::Float: | 
|  | TypeStr += "float" + llvm::utostr(ElementBitwidth); | 
|  | break; | 
|  | case TypeKind::Svcount: | 
|  | TypeStr += "svcount"; | 
|  | break; | 
|  | case TypeKind::Predicate: | 
|  | TypeStr += "bool"; | 
|  | break; | 
|  | case TypeKind::BFloat16: | 
|  | TypeStr += "bfloat16"; | 
|  | break; | 
|  | case TypeKind::MFloat8: | 
|  | TypeStr += "mfloat8"; | 
|  | break; | 
|  | case TypeKind::SInt: | 
|  | TypeStr += "int" + llvm::utostr(ElementBitwidth); | 
|  | break; | 
|  | case TypeKind::UInt: | 
|  | TypeStr += "uint" + llvm::utostr(ElementBitwidth); | 
|  | break; | 
|  | case TypeKind::Invalid: | 
|  | llvm_unreachable("Attempting to resolve type name from Invalid type!"); | 
|  | } | 
|  |  | 
|  | if (isFixedLengthVector()) | 
|  | TypeStr += "x" + llvm::utostr(getNumElements()); | 
|  | else if (isScalableVector()) | 
|  | TypeStr = "sv" + TypeStr; | 
|  |  | 
|  | if (NumVectors > 1) | 
|  | TypeStr += "x" + llvm::utostr(NumVectors); | 
|  | if (!isScalarPredicate() && !isVoid()) | 
|  | TypeStr += "_t"; | 
|  | if (isConstant()) | 
|  | TypeStr += " const"; | 
|  | if (isPointer()) | 
|  | TypeStr += " *"; | 
|  |  | 
|  | return TypeStr; | 
|  | } | 
|  |  | 
|  | void SVEType::applyTypespec(StringRef TS) { | 
|  | for (char I : TS) { | 
|  | switch (I) { | 
|  | case 'Q': | 
|  | assert(isInvalid() && "Unexpected use of typespec modifier"); | 
|  | Kind = Svcount; | 
|  | break; | 
|  | case 'P': | 
|  | assert(isInvalid() && "Unexpected use of typespec modifier"); | 
|  | Kind = Predicate; | 
|  | break; | 
|  | case 'U': | 
|  | assert(isInvalid() && "Unexpected use of typespec modifier"); | 
|  | Kind = UInt; | 
|  | break; | 
|  | case 'c': | 
|  | Kind = isInvalid() ? SInt : Kind; | 
|  | ElementBitwidth = 8; | 
|  | break; | 
|  | case 's': | 
|  | Kind = isInvalid() ? SInt : Kind; | 
|  | ElementBitwidth = 16; | 
|  | break; | 
|  | case 'i': | 
|  | Kind = isInvalid() ? SInt : Kind; | 
|  | ElementBitwidth = 32; | 
|  | break; | 
|  | case 'l': | 
|  | Kind = isInvalid() ? SInt : Kind; | 
|  | ElementBitwidth = 64; | 
|  | break; | 
|  | case 'q': | 
|  | Kind = isInvalid() ? SInt : Kind; | 
|  | ElementBitwidth = 128; | 
|  | break; | 
|  | case 'h': | 
|  | assert(isInvalid() && "Unexpected use of typespec modifier"); | 
|  | Kind = Float; | 
|  | ElementBitwidth = 16; | 
|  | break; | 
|  | case 'f': | 
|  | assert(isInvalid() && "Unexpected use of typespec modifier"); | 
|  | Kind = Float; | 
|  | ElementBitwidth = 32; | 
|  | break; | 
|  | case 'd': | 
|  | assert(isInvalid() && "Unexpected use of typespec modifier"); | 
|  | Kind = Float; | 
|  | ElementBitwidth = 64; | 
|  | break; | 
|  | case 'b': | 
|  | assert(isInvalid() && "Unexpected use of typespec modifier"); | 
|  | Kind = BFloat16; | 
|  | ElementBitwidth = 16; | 
|  | break; | 
|  | case 'm': | 
|  | assert(isInvalid() && "Unexpected use of typespec modifier"); | 
|  | Kind = MFloat8; | 
|  | ElementBitwidth = 8; | 
|  | break; | 
|  | default: | 
|  | llvm_unreachable("Unhandled type code!"); | 
|  | } | 
|  | } | 
|  | assert(ElementBitwidth != ~0U && "Bad element bitwidth!"); | 
|  | } | 
|  |  | 
|  | void SVEType::applyModifier(char Mod) { | 
|  | switch (Mod) { | 
|  | case 'v': | 
|  | Kind = Void; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'd': | 
|  | DefaultType = true; | 
|  | break; | 
|  | case 'c': | 
|  | Constant = true; | 
|  | [[fallthrough]]; | 
|  | case 'p': | 
|  | Pointer = true; | 
|  | Bitwidth = ElementBitwidth; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'e': | 
|  | Kind = UInt; | 
|  | ElementBitwidth /= 2; | 
|  | break; | 
|  | case 'h': | 
|  | ElementBitwidth /= 2; | 
|  | break; | 
|  | case 'q': | 
|  | ElementBitwidth /= 4; | 
|  | break; | 
|  | case 'b': | 
|  | Kind = UInt; | 
|  | ElementBitwidth /= 4; | 
|  | break; | 
|  | case 'o': | 
|  | ElementBitwidth *= 4; | 
|  | break; | 
|  | case 'P': | 
|  | Kind = Predicate; | 
|  | Bitwidth = 16; | 
|  | ElementBitwidth = 1; | 
|  | break; | 
|  | case '{': | 
|  | IsScalable = false; | 
|  | Bitwidth = 128; | 
|  | NumVectors = 1; | 
|  | break; | 
|  | case 's': | 
|  | case 'a': | 
|  | Bitwidth = ElementBitwidth; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'R': | 
|  | ElementBitwidth /= 2; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'r': | 
|  | ElementBitwidth /= 4; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case '@': | 
|  | Kind = UInt; | 
|  | ElementBitwidth /= 4; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'K': | 
|  | Kind = SInt; | 
|  | Bitwidth = ElementBitwidth; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'L': | 
|  | Kind = UInt; | 
|  | Bitwidth = ElementBitwidth; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'u': | 
|  | Kind = UInt; | 
|  | break; | 
|  | case 'x': | 
|  | Kind = SInt; | 
|  | break; | 
|  | case 'i': | 
|  | Kind = UInt; | 
|  | ElementBitwidth = Bitwidth = 64; | 
|  | NumVectors = 0; | 
|  | Immediate = true; | 
|  | break; | 
|  | case 'I': | 
|  | Kind = PredicatePattern; | 
|  | ElementBitwidth = Bitwidth = 32; | 
|  | NumVectors = 0; | 
|  | Immediate = true; | 
|  | break; | 
|  | case 'J': | 
|  | Kind = PrefetchOp; | 
|  | ElementBitwidth = Bitwidth = 32; | 
|  | NumVectors = 0; | 
|  | Immediate = true; | 
|  | break; | 
|  | case 'k': | 
|  | Kind = SInt; | 
|  | ElementBitwidth = Bitwidth = 32; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'l': | 
|  | Kind = SInt; | 
|  | ElementBitwidth = Bitwidth = 64; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'm': | 
|  | Kind = UInt; | 
|  | ElementBitwidth = Bitwidth = 32; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case '>': | 
|  | Kind = Fpm; | 
|  | ElementBitwidth = Bitwidth = 64; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'n': | 
|  | Kind = UInt; | 
|  | ElementBitwidth = Bitwidth = 64; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'w': | 
|  | ElementBitwidth = 64; | 
|  | break; | 
|  | case 'j': | 
|  | ElementBitwidth = Bitwidth = 64; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'f': | 
|  | Kind = UInt; | 
|  | ElementBitwidth = Bitwidth = 64; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'g': | 
|  | Kind = UInt; | 
|  | ElementBitwidth = 64; | 
|  | break; | 
|  | case '#': | 
|  | Kind = SInt; | 
|  | ElementBitwidth = 64; | 
|  | break; | 
|  | case '[': | 
|  | Kind = UInt; | 
|  | ElementBitwidth = 8; | 
|  | break; | 
|  | case 't': | 
|  | Kind = SInt; | 
|  | ElementBitwidth = 32; | 
|  | break; | 
|  | case 'z': | 
|  | Kind = UInt; | 
|  | ElementBitwidth = 32; | 
|  | break; | 
|  | case 'O': | 
|  | Kind = Float; | 
|  | ElementBitwidth = 16; | 
|  | break; | 
|  | case 'M': | 
|  | Kind = Float; | 
|  | ElementBitwidth = 32; | 
|  | break; | 
|  | case 'N': | 
|  | Kind = Float; | 
|  | ElementBitwidth = 64; | 
|  | break; | 
|  | case 'Q': | 
|  | Kind = Void; | 
|  | Constant = true; | 
|  | Pointer = true; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'S': | 
|  | Kind = SInt; | 
|  | Constant = true; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 8; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'W': | 
|  | Kind = UInt; | 
|  | Constant = true; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 8; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'T': | 
|  | Kind = SInt; | 
|  | Constant = true; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 16; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'X': | 
|  | Kind = UInt; | 
|  | Constant = true; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 16; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'Y': | 
|  | Kind = UInt; | 
|  | Constant = true; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 32; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'U': | 
|  | Kind = SInt; | 
|  | Constant = true; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 32; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case '%': | 
|  | Kind = Void; | 
|  | Pointer = true; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'A': | 
|  | Kind = SInt; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 8; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'B': | 
|  | Kind = SInt; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 16; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'C': | 
|  | Kind = SInt; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 32; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'D': | 
|  | Kind = SInt; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 64; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'E': | 
|  | Kind = UInt; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 8; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'F': | 
|  | Kind = UInt; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 16; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case 'G': | 
|  | Kind = UInt; | 
|  | Pointer = true; | 
|  | ElementBitwidth = Bitwidth = 32; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case '$': | 
|  | Kind = BFloat16; | 
|  | ElementBitwidth = 16; | 
|  | break; | 
|  | case '}': | 
|  | Kind = Svcount; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case '~': | 
|  | Kind = MFloat8; | 
|  | ElementBitwidth = 8; | 
|  | break; | 
|  | case '!': | 
|  | Kind = MFloat8; | 
|  | Bitwidth = ElementBitwidth = 8; | 
|  | NumVectors = 0; | 
|  | break; | 
|  | case '.': | 
|  | llvm_unreachable(". is never a type in itself"); | 
|  | break; | 
|  | default: | 
|  | llvm_unreachable("Unhandled character!"); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Returns the modifier and number of vectors for the given operand \p Op. | 
|  | std::pair<char, unsigned> getProtoModifier(StringRef Proto, unsigned Op) { | 
|  | for (unsigned P = 0; !Proto.empty(); ++P) { | 
|  | unsigned NumVectors = 1; | 
|  | unsigned CharsToSkip = 1; | 
|  | char Mod = Proto[0]; | 
|  | if (Mod == '2' || Mod == '3' || Mod == '4') { | 
|  | NumVectors = Mod - '0'; | 
|  | Mod = 'd'; | 
|  | if (Proto.size() > 1 && Proto[1] == '.') { | 
|  | Mod = Proto[2]; | 
|  | CharsToSkip = 3; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (P == Op) | 
|  | return {Mod, NumVectors}; | 
|  |  | 
|  | Proto = Proto.drop_front(CharsToSkip); | 
|  | } | 
|  | llvm_unreachable("Unexpected Op"); | 
|  | } | 
|  |  | 
|  | //===----------------------------------------------------------------------===// | 
|  | // Intrinsic implementation | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | Intrinsic::Intrinsic(StringRef Name, StringRef Proto, uint64_t MergeTy, | 
|  | StringRef MergeSuffix, uint64_t MemoryElementTy, | 
|  | StringRef LLVMName, uint64_t Flags, | 
|  | ArrayRef<ImmCheck> Checks, TypeSpec BT, ClassKind Class, | 
|  | SVEEmitter &Emitter, StringRef SVEGuard, | 
|  | StringRef SMEGuard) | 
|  | : Name(Name.str()), LLVMName(LLVMName), Proto(Proto.str()), | 
|  | BaseTypeSpec(BT), Class(Class), MergeSuffix(MergeSuffix.str()), | 
|  | BaseType(BT, 'd'), Flags(Flags), ImmChecks(Checks) { | 
|  |  | 
|  | auto FormatGuard = [](StringRef Guard, StringRef Base) -> std::string { | 
|  | if (Guard.empty() || Guard == Base) | 
|  | return Guard.str(); | 
|  | if (Guard.contains('|')) | 
|  | return Base.str() + ",(" + Guard.str() + ")"; | 
|  | return Base.str() + "," + Guard.str(); | 
|  | }; | 
|  |  | 
|  | this->SVEGuard = FormatGuard(SVEGuard, "sve"); | 
|  | this->SMEGuard = FormatGuard(SMEGuard, "sme"); | 
|  |  | 
|  | // Types[0] is the return value. | 
|  | for (unsigned I = 0; I < (getNumParams() + 1); ++I) { | 
|  | char Mod; | 
|  | unsigned NumVectors; | 
|  | std::tie(Mod, NumVectors) = getProtoModifier(Proto, I); | 
|  | SVEType T(BaseTypeSpec, Mod, NumVectors); | 
|  | Types.push_back(T); | 
|  | SetsFPMR = T.isFpm(); | 
|  |  | 
|  | // Add range checks for immediates | 
|  | if (I > 0) { | 
|  | if (T.isPredicatePattern()) | 
|  | ImmChecks.emplace_back( | 
|  | I - 1, Emitter.getEnumValueForImmCheck("ImmCheck0_31")); | 
|  | else if (T.isPrefetchOp()) | 
|  | ImmChecks.emplace_back( | 
|  | I - 1, Emitter.getEnumValueForImmCheck("ImmCheck0_13")); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Set flags based on properties | 
|  | this->Flags |= Emitter.encodeTypeFlags(BaseType); | 
|  | this->Flags |= Emitter.encodeMemoryElementType(MemoryElementTy); | 
|  | this->Flags |= Emitter.encodeMergeType(MergeTy); | 
|  | if (hasSplat()) | 
|  | this->Flags |= Emitter.encodeSplatOperand(getSplatIdx()); | 
|  | if (SetsFPMR) | 
|  | this->Flags |= Emitter.getEnumValueForFlag("SetsFPMR"); | 
|  | } | 
|  |  | 
|  | std::string Intrinsic::getBuiltinTypeStr() { | 
|  | std::string S = getReturnType().builtin_str(); | 
|  | for (unsigned I = 0; I < getNumParams(); ++I) | 
|  | S += getParamType(I).builtin_str(); | 
|  |  | 
|  | return S; | 
|  | } | 
|  |  | 
|  | std::string Intrinsic::replaceTemplatedArgs(std::string Name, TypeSpec TS, | 
|  | std::string Proto) const { | 
|  | std::string Ret = Name; | 
|  | while (Ret.find('{') != std::string::npos) { | 
|  | size_t Pos = Ret.find('{'); | 
|  | size_t End = Ret.find('}'); | 
|  | unsigned NumChars = End - Pos + 1; | 
|  | assert(NumChars == 3 && "Unexpected template argument"); | 
|  |  | 
|  | SVEType T; | 
|  | char C = Ret[Pos+1]; | 
|  | switch(C) { | 
|  | default: | 
|  | llvm_unreachable("Unknown predication specifier"); | 
|  | case 'd': | 
|  | T = SVEType(TS, 'd'); | 
|  | break; | 
|  | case '0': | 
|  | case '1': | 
|  | case '2': | 
|  | case '3': | 
|  | // Extract the modifier before passing to SVEType to handle numeric | 
|  | // modifiers | 
|  | auto [Mod, NumVectors] = getProtoModifier(Proto, (C - '0')); | 
|  | T = SVEType(TS, Mod); | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Replace templated arg with the right suffix (e.g. u32) | 
|  | std::string TypeCode; | 
|  |  | 
|  | if (T.isSignedInteger()) | 
|  | TypeCode = 's'; | 
|  | else if (T.isUnsignedInteger()) | 
|  | TypeCode = 'u'; | 
|  | else if (T.isSvcount()) | 
|  | TypeCode = 'c'; | 
|  | else if (T.isPredicate()) | 
|  | TypeCode = 'b'; | 
|  | else if (T.isBFloat()) | 
|  | TypeCode = "bf"; | 
|  | else if (T.isMFloat()) | 
|  | TypeCode = "mf"; | 
|  | else | 
|  | TypeCode = 'f'; | 
|  | Ret.replace(Pos, NumChars, TypeCode + utostr(T.getElementSizeInBits())); | 
|  | } | 
|  |  | 
|  | return Ret; | 
|  | } | 
|  |  | 
|  | std::string Intrinsic::mangleLLVMName() const { | 
|  | std::string S = getLLVMName(); | 
|  |  | 
|  | // Replace all {d} like expressions with e.g. 'u32' | 
|  | return replaceTemplatedArgs(S, getBaseTypeSpec(), getProto()); | 
|  | } | 
|  |  | 
|  | std::string Intrinsic::mangleName(ClassKind LocalCK) const { | 
|  | std::string S = getName(); | 
|  |  | 
|  | if (LocalCK == ClassG) { | 
|  | // Remove the square brackets and everything in between. | 
|  | while (S.find('[') != std::string::npos) { | 
|  | auto Start = S.find('['); | 
|  | auto End = S.find(']'); | 
|  | S.erase(Start, (End-Start)+1); | 
|  | } | 
|  | } else { | 
|  | // Remove the square brackets. | 
|  | while (S.find('[') != std::string::npos) { | 
|  | auto BrPos = S.find('['); | 
|  | if (BrPos != std::string::npos) | 
|  | S.erase(BrPos, 1); | 
|  | BrPos = S.find(']'); | 
|  | if (BrPos != std::string::npos) | 
|  | S.erase(BrPos, 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Replace all {d} like expressions with e.g. 'u32' | 
|  | return replaceTemplatedArgs(S, getBaseTypeSpec(), getProto()) | 
|  | .append(getMergeSuffix()) | 
|  | .append(getFPMSuffix()); | 
|  | } | 
|  |  | 
|  | void Intrinsic::emitIntrinsic(raw_ostream &OS, SVEEmitter &Emitter, | 
|  | ACLEKind Kind) const { | 
|  | bool IsOverloaded = getClassKind() == ClassG && getProto().size() > 1; | 
|  |  | 
|  | std::string FullName = mangleName(ClassS); | 
|  | std::string ProtoName = mangleName(getClassKind()); | 
|  | OS << (IsOverloaded ? "__aio " : "__ai ") | 
|  | << "__attribute__((__clang_arm_builtin_alias("; | 
|  |  | 
|  | switch (Kind) { | 
|  | case ACLEKind::SME: | 
|  | OS << "__builtin_sme_" << FullName << ")"; | 
|  | break; | 
|  | case ACLEKind::SVE: | 
|  | OS << "__builtin_sve_" << FullName << ")"; | 
|  | break; | 
|  | } | 
|  |  | 
|  | OS << "))\n"; | 
|  |  | 
|  | OS << getTypes()[0].str() << " " << ProtoName << "("; | 
|  | for (unsigned I = 0; I < getTypes().size() - 1; ++I) { | 
|  | if (I != 0) | 
|  | OS << ", "; | 
|  | OS << getTypes()[I + 1].str(); | 
|  | } | 
|  | OS << ");\n"; | 
|  | } | 
|  |  | 
|  | //===----------------------------------------------------------------------===// | 
|  | // SVEEmitter implementation | 
|  | //===----------------------------------------------------------------------===// | 
|  | uint64_t SVEEmitter::encodeTypeFlags(const SVEType &T) { | 
|  | if (T.isFloat()) { | 
|  | switch (T.getElementSizeInBits()) { | 
|  | case 16: | 
|  | return encodeEltType("EltTyFloat16"); | 
|  | case 32: | 
|  | return encodeEltType("EltTyFloat32"); | 
|  | case 64: | 
|  | return encodeEltType("EltTyFloat64"); | 
|  | default: | 
|  | llvm_unreachable("Unhandled float element bitwidth!"); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (T.isBFloat()) { | 
|  | assert(T.getElementSizeInBits() == 16 && "Not a valid BFloat."); | 
|  | return encodeEltType("EltTyBFloat16"); | 
|  | } | 
|  |  | 
|  | if (T.isMFloat()) { | 
|  | assert(T.getElementSizeInBits() == 8 && "Not a valid MFloat."); | 
|  | return encodeEltType("EltTyMFloat8"); | 
|  | } | 
|  |  | 
|  | if (T.isPredicate() || T.isSvcount()) { | 
|  | switch (T.getElementSizeInBits()) { | 
|  | case 8: | 
|  | return encodeEltType("EltTyBool8"); | 
|  | case 16: | 
|  | return encodeEltType("EltTyBool16"); | 
|  | case 32: | 
|  | return encodeEltType("EltTyBool32"); | 
|  | case 64: | 
|  | return encodeEltType("EltTyBool64"); | 
|  | default: | 
|  | llvm_unreachable("Unhandled predicate element bitwidth!"); | 
|  | } | 
|  | } | 
|  |  | 
|  | switch (T.getElementSizeInBits()) { | 
|  | case 8: | 
|  | return encodeEltType("EltTyInt8"); | 
|  | case 16: | 
|  | return encodeEltType("EltTyInt16"); | 
|  | case 32: | 
|  | return encodeEltType("EltTyInt32"); | 
|  | case 64: | 
|  | return encodeEltType("EltTyInt64"); | 
|  | case 128: | 
|  | return encodeEltType("EltTyInt128"); | 
|  | default: | 
|  | llvm_unreachable("Unhandled integer element bitwidth!"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createIntrinsic( | 
|  | const Record *R, SmallVectorImpl<std::unique_ptr<Intrinsic>> &Out) { | 
|  | StringRef Name = R->getValueAsString("Name"); | 
|  | StringRef Proto = R->getValueAsString("Prototype"); | 
|  | StringRef Types = R->getValueAsString("Types"); | 
|  | StringRef SVEGuard = R->getValueAsString("SVETargetGuard"); | 
|  | StringRef SMEGuard = R->getValueAsString("SMETargetGuard"); | 
|  | StringRef LLVMName = R->getValueAsString("LLVMIntrinsic"); | 
|  | uint64_t Merge = R->getValueAsInt("Merge"); | 
|  | StringRef MergeSuffix = R->getValueAsString("MergeSuffix"); | 
|  | uint64_t MemEltType = R->getValueAsInt("MemEltType"); | 
|  |  | 
|  | int64_t Flags = 0; | 
|  | for (const Record *FlagRec : R->getValueAsListOfDefs("Flags")) | 
|  | Flags |= FlagRec->getValueAsInt("Value"); | 
|  |  | 
|  | // Create a dummy TypeSpec for non-overloaded builtins. | 
|  | if (Types.empty()) { | 
|  | assert((Flags & getEnumValueForFlag("IsOverloadNone")) && | 
|  | "Expect TypeSpec for overloaded builtin!"); | 
|  | Types = "i"; | 
|  | } | 
|  |  | 
|  | // Extract type specs from string | 
|  | SmallVector<TypeSpec, 8> TypeSpecs; | 
|  | TypeSpec Acc; | 
|  | for (char I : Types) { | 
|  | Acc.push_back(I); | 
|  | if (islower(I)) { | 
|  | TypeSpecs.push_back(TypeSpec(Acc)); | 
|  | Acc.clear(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Remove duplicate type specs. | 
|  | sort(TypeSpecs); | 
|  | TypeSpecs.erase(llvm::unique(TypeSpecs), TypeSpecs.end()); | 
|  |  | 
|  | // Create an Intrinsic for each type spec. | 
|  | for (auto TS : TypeSpecs) { | 
|  | // Collate a list of range/option checks for the immediates. | 
|  | SmallVector<ImmCheck, 2> ImmChecks; | 
|  | for (const Record *ImmR : R->getValueAsListOfDefs("ImmChecks")) { | 
|  | int64_t ArgIdx = ImmR->getValueAsInt("ImmArgIdx"); | 
|  | int64_t EltSizeArgIdx = ImmR->getValueAsInt("TypeContextArgIdx"); | 
|  | int64_t Kind = ImmR->getValueAsDef("Kind")->getValueAsInt("Value"); | 
|  | assert(ArgIdx >= 0 && Kind >= 0 && | 
|  | "ImmArgIdx and Kind must be nonnegative"); | 
|  |  | 
|  | unsigned ElementSizeInBits = 0; | 
|  | auto [Mod, NumVectors] = getProtoModifier(Proto, EltSizeArgIdx + 1); | 
|  | if (EltSizeArgIdx >= 0) | 
|  | ElementSizeInBits = SVEType(TS, Mod, NumVectors).getElementSizeInBits(); | 
|  | ImmChecks.push_back(ImmCheck(ArgIdx, Kind, ElementSizeInBits)); | 
|  | } | 
|  |  | 
|  | Out.push_back(std::make_unique<Intrinsic>( | 
|  | Name, Proto, Merge, MergeSuffix, MemEltType, LLVMName, Flags, ImmChecks, | 
|  | TS, ClassS, *this, SVEGuard, SMEGuard)); | 
|  |  | 
|  | // Also generate the short-form (e.g. svadd_m) for the given type-spec. | 
|  | if (Intrinsic::isOverloadedIntrinsic(Name)) | 
|  | Out.push_back(std::make_unique<Intrinsic>( | 
|  | Name, Proto, Merge, MergeSuffix, MemEltType, LLVMName, Flags, | 
|  | ImmChecks, TS, ClassG, *this, SVEGuard, SMEGuard)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createCoreHeaderIntrinsics(raw_ostream &OS, | 
|  | SVEEmitter &Emitter, | 
|  | ACLEKind Kind) { | 
|  | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; | 
|  | std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst"); | 
|  | for (auto *R : RV) | 
|  | createIntrinsic(R, Defs); | 
|  |  | 
|  | // Sort intrinsics in header file by following order/priority: | 
|  | // - Architectural guard (i.e. does it require SVE2 or SVE2_AES) | 
|  | // - Class (is intrinsic overloaded or not) | 
|  | // - Intrinsic name | 
|  | llvm::stable_sort(Defs, [](const std::unique_ptr<Intrinsic> &A, | 
|  | const std::unique_ptr<Intrinsic> &B) { | 
|  | auto ToTuple = [](const std::unique_ptr<Intrinsic> &I) { | 
|  | return std::make_tuple(I->getSVEGuard().str() + I->getSMEGuard().str(), | 
|  | (unsigned)I->getClassKind(), I->getName()); | 
|  | }; | 
|  | return ToTuple(A) < ToTuple(B); | 
|  | }); | 
|  |  | 
|  | // Actually emit the intrinsic declarations. | 
|  | for (auto &I : Defs) | 
|  | I->emitIntrinsic(OS, Emitter, Kind); | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createHeader(raw_ostream &OS) { | 
|  | OS << "/*===---- arm_sve.h - ARM SVE intrinsics " | 
|  | "-----------------------------------===\n" | 
|  | " *\n" | 
|  | " *\n" | 
|  | " * Part of the LLVM Project, under the Apache License v2.0 with LLVM " | 
|  | "Exceptions.\n" | 
|  | " * See https://llvm.org/LICENSE.txt for license information.\n" | 
|  | " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" | 
|  | " *\n" | 
|  | " *===-----------------------------------------------------------------" | 
|  | "------===\n" | 
|  | " */\n\n"; | 
|  |  | 
|  | OS << "#ifndef __ARM_SVE_H\n"; | 
|  | OS << "#define __ARM_SVE_H\n\n"; | 
|  |  | 
|  | OS << "#if !defined(__LITTLE_ENDIAN__)\n"; | 
|  | OS << "#error \"Big endian is currently not supported for arm_sve.h\"\n"; | 
|  | OS << "#endif\n"; | 
|  |  | 
|  | OS << "#include <stdint.h>\n\n"; | 
|  | OS << "#ifdef  __cplusplus\n"; | 
|  | OS << "extern \"C\" {\n"; | 
|  | OS << "#else\n"; | 
|  | OS << "#include <stdbool.h>\n"; | 
|  | OS << "#endif\n\n"; | 
|  |  | 
|  | OS << "typedef __fp16 float16_t;\n"; | 
|  | OS << "typedef float float32_t;\n"; | 
|  | OS << "typedef double float64_t;\n"; | 
|  |  | 
|  | OS << "typedef __SVInt8_t svint8_t;\n"; | 
|  | OS << "typedef __SVInt16_t svint16_t;\n"; | 
|  | OS << "typedef __SVInt32_t svint32_t;\n"; | 
|  | OS << "typedef __SVInt64_t svint64_t;\n"; | 
|  | OS << "typedef __SVUint8_t svuint8_t;\n"; | 
|  | OS << "typedef __SVUint16_t svuint16_t;\n"; | 
|  | OS << "typedef __SVUint32_t svuint32_t;\n"; | 
|  | OS << "typedef __SVUint64_t svuint64_t;\n"; | 
|  | OS << "typedef __SVFloat16_t svfloat16_t;\n\n"; | 
|  |  | 
|  | OS << "typedef __SVBfloat16_t svbfloat16_t;\n"; | 
|  |  | 
|  | OS << "#include <arm_bf16.h>\n"; | 
|  | OS << "#include <arm_vector_types.h>\n"; | 
|  |  | 
|  | OS << "typedef __SVMfloat8_t svmfloat8_t;\n\n"; | 
|  |  | 
|  | OS << "typedef __SVFloat32_t svfloat32_t;\n"; | 
|  | OS << "typedef __SVFloat64_t svfloat64_t;\n"; | 
|  | OS << "typedef __clang_svint8x2_t svint8x2_t;\n"; | 
|  | OS << "typedef __clang_svint16x2_t svint16x2_t;\n"; | 
|  | OS << "typedef __clang_svint32x2_t svint32x2_t;\n"; | 
|  | OS << "typedef __clang_svint64x2_t svint64x2_t;\n"; | 
|  | OS << "typedef __clang_svuint8x2_t svuint8x2_t;\n"; | 
|  | OS << "typedef __clang_svuint16x2_t svuint16x2_t;\n"; | 
|  | OS << "typedef __clang_svuint32x2_t svuint32x2_t;\n"; | 
|  | OS << "typedef __clang_svuint64x2_t svuint64x2_t;\n"; | 
|  | OS << "typedef __clang_svfloat16x2_t svfloat16x2_t;\n"; | 
|  | OS << "typedef __clang_svfloat32x2_t svfloat32x2_t;\n"; | 
|  | OS << "typedef __clang_svfloat64x2_t svfloat64x2_t;\n"; | 
|  | OS << "typedef __clang_svint8x3_t svint8x3_t;\n"; | 
|  | OS << "typedef __clang_svint16x3_t svint16x3_t;\n"; | 
|  | OS << "typedef __clang_svint32x3_t svint32x3_t;\n"; | 
|  | OS << "typedef __clang_svint64x3_t svint64x3_t;\n"; | 
|  | OS << "typedef __clang_svuint8x3_t svuint8x3_t;\n"; | 
|  | OS << "typedef __clang_svuint16x3_t svuint16x3_t;\n"; | 
|  | OS << "typedef __clang_svuint32x3_t svuint32x3_t;\n"; | 
|  | OS << "typedef __clang_svuint64x3_t svuint64x3_t;\n"; | 
|  | OS << "typedef __clang_svfloat16x3_t svfloat16x3_t;\n"; | 
|  | OS << "typedef __clang_svfloat32x3_t svfloat32x3_t;\n"; | 
|  | OS << "typedef __clang_svfloat64x3_t svfloat64x3_t;\n"; | 
|  | OS << "typedef __clang_svint8x4_t svint8x4_t;\n"; | 
|  | OS << "typedef __clang_svint16x4_t svint16x4_t;\n"; | 
|  | OS << "typedef __clang_svint32x4_t svint32x4_t;\n"; | 
|  | OS << "typedef __clang_svint64x4_t svint64x4_t;\n"; | 
|  | OS << "typedef __clang_svuint8x4_t svuint8x4_t;\n"; | 
|  | OS << "typedef __clang_svuint16x4_t svuint16x4_t;\n"; | 
|  | OS << "typedef __clang_svuint32x4_t svuint32x4_t;\n"; | 
|  | OS << "typedef __clang_svuint64x4_t svuint64x4_t;\n"; | 
|  | OS << "typedef __clang_svfloat16x4_t svfloat16x4_t;\n"; | 
|  | OS << "typedef __clang_svfloat32x4_t svfloat32x4_t;\n"; | 
|  | OS << "typedef __clang_svfloat64x4_t svfloat64x4_t;\n"; | 
|  | OS << "typedef __SVBool_t  svbool_t;\n"; | 
|  | OS << "typedef __clang_svboolx2_t  svboolx2_t;\n"; | 
|  | OS << "typedef __clang_svboolx4_t  svboolx4_t;\n\n"; | 
|  |  | 
|  | OS << "typedef __clang_svbfloat16x2_t svbfloat16x2_t;\n"; | 
|  | OS << "typedef __clang_svbfloat16x3_t svbfloat16x3_t;\n"; | 
|  | OS << "typedef __clang_svbfloat16x4_t svbfloat16x4_t;\n"; | 
|  |  | 
|  | OS << "typedef __clang_svmfloat8x2_t svmfloat8x2_t;\n"; | 
|  | OS << "typedef __clang_svmfloat8x3_t svmfloat8x3_t;\n"; | 
|  | OS << "typedef __clang_svmfloat8x4_t svmfloat8x4_t;\n"; | 
|  |  | 
|  | OS << "typedef __SVCount_t svcount_t;\n\n"; | 
|  |  | 
|  | OS << "enum svpattern\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 << "};\n\n"; | 
|  |  | 
|  | OS << "enum svprfop\n"; | 
|  | OS << "{\n"; | 
|  | OS << "  SV_PLDL1KEEP = 0,\n"; | 
|  | OS << "  SV_PLDL1STRM = 1,\n"; | 
|  | OS << "  SV_PLDL2KEEP = 2,\n"; | 
|  | OS << "  SV_PLDL2STRM = 3,\n"; | 
|  | OS << "  SV_PLDL3KEEP = 4,\n"; | 
|  | OS << "  SV_PLDL3STRM = 5,\n"; | 
|  | OS << "  SV_PSTL1KEEP = 8,\n"; | 
|  | OS << "  SV_PSTL1STRM = 9,\n"; | 
|  | OS << "  SV_PSTL2KEEP = 10,\n"; | 
|  | OS << "  SV_PSTL2STRM = 11,\n"; | 
|  | OS << "  SV_PSTL3KEEP = 12,\n"; | 
|  | OS << "  SV_PSTL3STRM = 13\n"; | 
|  | OS << "};\n\n"; | 
|  |  | 
|  | OS << "/* Function attributes */\n"; | 
|  | OS << "#define __ai static __inline__ __attribute__((__always_inline__, " | 
|  | "__nodebug__))\n\n"; | 
|  | OS << "#define __aio static __inline__ __attribute__((__always_inline__, " | 
|  | "__nodebug__, __overloadable__))\n\n"; | 
|  |  | 
|  | // Add reinterpret functions. | 
|  | for (auto [N, Suffix] : | 
|  | std::initializer_list<std::pair<unsigned, const char *>>{ | 
|  | {1, ""}, {2, "_x2"}, {3, "_x3"}, {4, "_x4"}}) { | 
|  | for (auto ShortForm : {false, true}) | 
|  | for (const ReinterpretTypeInfo &To : Reinterprets) { | 
|  | SVEType ToV(To.BaseType, N); | 
|  | for (const ReinterpretTypeInfo &From : Reinterprets) { | 
|  | SVEType FromV(From.BaseType, N); | 
|  | OS << "__aio " | 
|  | "__attribute__((__clang_arm_builtin_alias(__builtin_sve_" | 
|  | "reinterpret_" | 
|  | << To.Suffix << "_" << From.Suffix << Suffix << ")))\n" | 
|  | << ToV.str() << " svreinterpret_" << To.Suffix; | 
|  | if (!ShortForm) | 
|  | OS << "_" << From.Suffix << Suffix; | 
|  | OS << "(" << FromV.str() << " op);\n"; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | createCoreHeaderIntrinsics(OS, *this, ACLEKind::SVE); | 
|  |  | 
|  | OS << "#define svcvtnt_bf16_x      svcvtnt_bf16_m\n"; | 
|  | OS << "#define svcvtnt_bf16_f32_x  svcvtnt_bf16_f32_m\n"; | 
|  |  | 
|  | OS << "#define svcvtnt_f16_x      svcvtnt_f16_m\n"; | 
|  | OS << "#define svcvtnt_f16_f32_x  svcvtnt_f16_f32_m\n"; | 
|  | OS << "#define svcvtnt_f32_x      svcvtnt_f32_m\n"; | 
|  | OS << "#define svcvtnt_f32_f64_x  svcvtnt_f32_f64_m\n\n"; | 
|  |  | 
|  | OS << "#define svcvtxnt_f32_x     svcvtxnt_f32_m\n"; | 
|  | OS << "#define svcvtxnt_f32_f64_x svcvtxnt_f32_f64_m\n\n"; | 
|  |  | 
|  | OS << "#ifdef __cplusplus\n"; | 
|  | OS << "} // extern \"C\"\n"; | 
|  | OS << "#endif\n\n"; | 
|  | OS << "#undef __ai\n\n"; | 
|  | OS << "#undef __aio\n\n"; | 
|  | OS << "#endif /* __ARM_SVE_H */\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createBuiltins(raw_ostream &OS) { | 
|  | std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst"); | 
|  | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; | 
|  | for (auto *R : RV) | 
|  | createIntrinsic(R, Defs); | 
|  |  | 
|  | // The mappings must be sorted based on BuiltinID. | 
|  | sort(Defs, [](const std::unique_ptr<Intrinsic> &A, | 
|  | const std::unique_ptr<Intrinsic> &B) { | 
|  | return A->getMangledName() < B->getMangledName(); | 
|  | }); | 
|  |  | 
|  | llvm::StringToOffsetTable Table; | 
|  | Table.GetOrAddStringOffset(""); | 
|  | Table.GetOrAddStringOffset("n"); | 
|  |  | 
|  | for (const auto &Def : Defs) | 
|  | if (Def->getClassKind() != ClassG) { | 
|  | Table.GetOrAddStringOffset(Def->getMangledName()); | 
|  | Table.GetOrAddStringOffset(Def->getBuiltinTypeStr()); | 
|  | Table.GetOrAddStringOffset(Def->getGuard()); | 
|  | } | 
|  |  | 
|  | Table.GetOrAddStringOffset("sme|sve"); | 
|  | SmallVector<std::pair<std::string, std::string>> ReinterpretBuiltins; | 
|  | for (auto [N, Suffix] : | 
|  | std::initializer_list<std::pair<unsigned, const char *>>{ | 
|  | {1, ""}, {2, "_x2"}, {3, "_x3"}, {4, "_x4"}}) { | 
|  | for (const ReinterpretTypeInfo &To : Reinterprets) { | 
|  | SVEType ToV(To.BaseType, N); | 
|  | for (const ReinterpretTypeInfo &From : Reinterprets) { | 
|  | SVEType FromV(From.BaseType, N); | 
|  | std::string Name = | 
|  | (Twine("reinterpret_") + To.Suffix + "_" + From.Suffix + Suffix) | 
|  | .str(); | 
|  | std::string Type = ToV.builtin_str() + FromV.builtin_str(); | 
|  | Table.GetOrAddStringOffset(Name); | 
|  | Table.GetOrAddStringOffset(Type); | 
|  | ReinterpretBuiltins.push_back({Name, Type}); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | OS << "#ifdef GET_SVE_BUILTIN_ENUMERATORS\n"; | 
|  | for (const auto &Def : Defs) | 
|  | if (Def->getClassKind() != ClassG) | 
|  | OS << "  BI__builtin_sve_" << Def->getMangledName() << ",\n"; | 
|  | for (const auto &[Name, _] : ReinterpretBuiltins) | 
|  | OS << "  BI__builtin_sve_" << Name << ",\n"; | 
|  | OS << "#endif // GET_SVE_BUILTIN_ENUMERATORS\n\n"; | 
|  |  | 
|  | OS << "#ifdef GET_SVE_BUILTIN_STR_TABLE\n"; | 
|  | Table.EmitStringTableDef(OS, "BuiltinStrings"); | 
|  | OS << "#endif // GET_SVE_BUILTIN_STR_TABLE\n\n"; | 
|  |  | 
|  | OS << "#ifdef GET_SVE_BUILTIN_INFOS\n"; | 
|  | for (const auto &Def : Defs) { | 
|  | // Only create BUILTINs for non-overloaded intrinsics, as overloaded | 
|  | // declarations only live in the header file. | 
|  | if (Def->getClassKind() != ClassG) { | 
|  | OS << "    Builtin::Info{Builtin::Info::StrOffsets{" | 
|  | << Table.GetStringOffset(Def->getMangledName()) << " /* " | 
|  | << Def->getMangledName() << " */, "; | 
|  | OS << Table.GetStringOffset(Def->getBuiltinTypeStr()) << " /* " | 
|  | << Def->getBuiltinTypeStr() << " */, "; | 
|  | OS << Table.GetStringOffset("n") << " /* n */, "; | 
|  | OS << Table.GetStringOffset(Def->getGuard()) << " /* " << Def->getGuard() | 
|  | << " */}, "; | 
|  | OS << "HeaderDesc::NO_HEADER, ALL_LANGUAGES},\n"; | 
|  | } | 
|  | } | 
|  | for (const auto &[Name, Type] : ReinterpretBuiltins) { | 
|  | OS << "    Builtin::Info{Builtin::Info::StrOffsets{" | 
|  | << Table.GetStringOffset(Name) << " /* " << Name << " */, "; | 
|  | OS << Table.GetStringOffset(Type) << " /* " << Type << " */, "; | 
|  | OS << Table.GetStringOffset("n") << " /* n */, "; | 
|  | OS << Table.GetStringOffset("sme|sve") << " /* sme|sve */}, "; | 
|  | OS << "HeaderDesc::NO_HEADER, ALL_LANGUAGES},\n"; | 
|  | } | 
|  | OS << "#endif // GET_SVE_BUILTIN_INFOS\n\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createCodeGenMap(raw_ostream &OS) { | 
|  | std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst"); | 
|  | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; | 
|  | for (auto *R : RV) | 
|  | createIntrinsic(R, Defs); | 
|  |  | 
|  | // The mappings must be sorted based on BuiltinID. | 
|  | sort(Defs, [](const std::unique_ptr<Intrinsic> &A, | 
|  | const std::unique_ptr<Intrinsic> &B) { | 
|  | return A->getMangledName() < B->getMangledName(); | 
|  | }); | 
|  |  | 
|  | OS << "#ifdef GET_SVE_LLVM_INTRINSIC_MAP\n"; | 
|  | for (auto &Def : Defs) { | 
|  | // Builtins only exist for non-overloaded intrinsics, overloaded | 
|  | // declarations only live in the header file. | 
|  | if (Def->getClassKind() == ClassG) | 
|  | continue; | 
|  |  | 
|  | uint64_t Flags = Def->getFlags(); | 
|  | auto FlagString = std::to_string(Flags); | 
|  |  | 
|  | std::string LLVMName = Def->getMangledLLVMName(); | 
|  | std::string Builtin = Def->getMangledName(); | 
|  | if (!LLVMName.empty()) | 
|  | OS << "SVEMAP1(" << Builtin << ", " << LLVMName << ", " << FlagString | 
|  | << "),\n"; | 
|  | else | 
|  | OS << "SVEMAP2(" << Builtin << ", " << FlagString << "),\n"; | 
|  | } | 
|  | OS << "#endif\n\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createRangeChecks(raw_ostream &OS) { | 
|  | std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst"); | 
|  | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; | 
|  | for (auto *R : RV) | 
|  | createIntrinsic(R, Defs); | 
|  |  | 
|  | // The mappings must be sorted based on BuiltinID. | 
|  | sort(Defs, [](const std::unique_ptr<Intrinsic> &A, | 
|  | const std::unique_ptr<Intrinsic> &B) { | 
|  | return A->getMangledName() < B->getMangledName(); | 
|  | }); | 
|  |  | 
|  | OS << "#ifdef GET_SVE_IMMEDIATE_CHECK\n"; | 
|  |  | 
|  | // Ensure these are only emitted once. | 
|  | std::set<std::string> 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.emplace_back(" << Check.getImmArgIdx() << ", " | 
|  | << 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"; | 
|  | for (auto &KV : FlagTypes) | 
|  | OS << "const uint64_t " << KV.getKey() << " = " << KV.getValue() << ";\n"; | 
|  | OS << "#endif\n\n"; | 
|  |  | 
|  | OS << "#ifdef LLVM_GET_SVE_ELTTYPES\n"; | 
|  | for (auto &KV : EltTypes) | 
|  | OS << "  " << KV.getKey() << " = " << KV.getValue() << ",\n"; | 
|  | OS << "#endif\n\n"; | 
|  |  | 
|  | OS << "#ifdef LLVM_GET_SVE_MEMELTTYPES\n"; | 
|  | for (auto &KV : MemEltTypes) | 
|  | OS << "  " << KV.getKey() << " = " << KV.getValue() << ",\n"; | 
|  | OS << "#endif\n\n"; | 
|  |  | 
|  | OS << "#ifdef LLVM_GET_SVE_MERGETYPES\n"; | 
|  | for (auto &KV : MergeTypes) | 
|  | OS << "  " << KV.getKey() << " = " << KV.getValue() << ",\n"; | 
|  | OS << "#endif\n\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createImmCheckTypes(raw_ostream &OS) { | 
|  | OS << "#ifdef LLVM_GET_ARM_INTRIN_IMMCHECKTYPES\n"; | 
|  | for (auto &KV : ImmCheckTypes) | 
|  | OS << "  " << KV.getKey() << " = " << KV.getValue() << ",\n"; | 
|  | OS << "#endif\n\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createSMEHeader(raw_ostream &OS) { | 
|  | OS << "/*===---- arm_sme.h - ARM SME intrinsics " | 
|  | "------===\n" | 
|  | " *\n" | 
|  | " *\n" | 
|  | " * Part of the LLVM Project, under the Apache License v2.0 with LLVM " | 
|  | "Exceptions.\n" | 
|  | " * See https://llvm.org/LICENSE.txt for license information.\n" | 
|  | " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" | 
|  | " *\n" | 
|  | " *===-----------------------------------------------------------------" | 
|  | "------===\n" | 
|  | " */\n\n"; | 
|  |  | 
|  | OS << "#ifndef __ARM_SME_H\n"; | 
|  | OS << "#define __ARM_SME_H\n\n"; | 
|  |  | 
|  | OS << "#if !defined(__LITTLE_ENDIAN__)\n"; | 
|  | OS << "#error \"Big endian is currently not supported for arm_sme.h\"\n"; | 
|  | OS << "#endif\n"; | 
|  |  | 
|  | OS << "#include <arm_sve.h>\n\n"; | 
|  | OS << "#include <stddef.h>\n\n"; | 
|  |  | 
|  | OS << "/* Function attributes */\n"; | 
|  | OS << "#define __ai static __inline__ __attribute__((__always_inline__, " | 
|  | "__nodebug__))\n\n"; | 
|  | OS << "#define __aio static __inline__ __attribute__((__always_inline__, " | 
|  | "__nodebug__, __overloadable__))\n\n"; | 
|  |  | 
|  | OS << "#ifdef  __cplusplus\n"; | 
|  | OS << "extern \"C\" {\n"; | 
|  | OS << "#endif\n\n"; | 
|  |  | 
|  | OS << "void __arm_za_disable(void) __arm_streaming_compatible;\n\n"; | 
|  |  | 
|  | OS << "__ai bool __arm_has_sme(void) __arm_streaming_compatible {\n"; | 
|  | OS << "  uint64_t x0, x1;\n"; | 
|  | OS << "  __builtin_arm_get_sme_state(&x0, &x1);\n"; | 
|  | OS << "  return x0 & (1ULL << 63);\n"; | 
|  | OS << "}\n\n"; | 
|  |  | 
|  | OS << "void *__arm_sc_memcpy(void *dest, const void *src, size_t n) __arm_streaming_compatible;\n"; | 
|  | OS << "void *__arm_sc_memmove(void *dest, const void *src, size_t n) __arm_streaming_compatible;\n"; | 
|  | OS << "void *__arm_sc_memset(void *s, int c, size_t n) __arm_streaming_compatible;\n"; | 
|  | OS << "void *__arm_sc_memchr(void *s, int c, size_t n) __arm_streaming_compatible;\n\n"; | 
|  |  | 
|  | OS << "__ai __attribute__((target(\"sme\"))) void svundef_za(void) " | 
|  | "__arm_streaming_compatible __arm_out(\"za\") " | 
|  | "{ }\n\n"; | 
|  |  | 
|  | createCoreHeaderIntrinsics(OS, *this, ACLEKind::SME); | 
|  |  | 
|  | OS << "#ifdef __cplusplus\n"; | 
|  | OS << "} // extern \"C\"\n"; | 
|  | OS << "#endif\n\n"; | 
|  | OS << "#undef __ai\n\n"; | 
|  | OS << "#endif /* __ARM_SME_H */\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createSMEBuiltins(raw_ostream &OS) { | 
|  | std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst"); | 
|  | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; | 
|  | for (auto *R : RV) { | 
|  | createIntrinsic(R, Defs); | 
|  | } | 
|  |  | 
|  | // The mappings must be sorted based on BuiltinID. | 
|  | sort(Defs, [](const std::unique_ptr<Intrinsic> &A, | 
|  | const std::unique_ptr<Intrinsic> &B) { | 
|  | return A->getMangledName() < B->getMangledName(); | 
|  | }); | 
|  |  | 
|  | llvm::StringToOffsetTable Table; | 
|  | Table.GetOrAddStringOffset(""); | 
|  | Table.GetOrAddStringOffset("n"); | 
|  |  | 
|  | for (const auto &Def : Defs) | 
|  | if (Def->getClassKind() != ClassG) { | 
|  | Table.GetOrAddStringOffset(Def->getMangledName()); | 
|  | Table.GetOrAddStringOffset(Def->getBuiltinTypeStr()); | 
|  | Table.GetOrAddStringOffset(Def->getGuard()); | 
|  | } | 
|  |  | 
|  | OS << "#ifdef GET_SME_BUILTIN_ENUMERATORS\n"; | 
|  | for (const auto &Def : Defs) | 
|  | if (Def->getClassKind() != ClassG) | 
|  | OS << "  BI__builtin_sme_" << Def->getMangledName() << ",\n"; | 
|  | OS << "#endif // GET_SME_BUILTIN_ENUMERATORS\n\n"; | 
|  |  | 
|  | OS << "#ifdef GET_SME_BUILTIN_STR_TABLE\n"; | 
|  | Table.EmitStringTableDef(OS, "BuiltinStrings"); | 
|  | OS << "#endif // GET_SME_BUILTIN_STR_TABLE\n\n"; | 
|  |  | 
|  | OS << "#ifdef GET_SME_BUILTIN_INFOS\n"; | 
|  | for (const auto &Def : Defs) { | 
|  | // Only create BUILTINs for non-overloaded intrinsics, as overloaded | 
|  | // declarations only live in the header file. | 
|  | if (Def->getClassKind() != ClassG) { | 
|  | OS << "    Builtin::Info{Builtin::Info::StrOffsets{" | 
|  | << Table.GetStringOffset(Def->getMangledName()) << " /* " | 
|  | << Def->getMangledName() << " */, "; | 
|  | OS << Table.GetStringOffset(Def->getBuiltinTypeStr()) << " /* " | 
|  | << Def->getBuiltinTypeStr() << " */, "; | 
|  | OS << Table.GetStringOffset("n") << " /* n */, "; | 
|  | OS << Table.GetStringOffset(Def->getGuard()) << " /* " << Def->getGuard() | 
|  | << " */}, "; | 
|  | OS << "HeaderDesc::NO_HEADER, ALL_LANGUAGES},\n"; | 
|  | } | 
|  | } | 
|  | OS << "#endif // GET_SME_BUILTIN_INFOS\n\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createSMECodeGenMap(raw_ostream &OS) { | 
|  | std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst"); | 
|  | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; | 
|  | for (auto *R : RV) { | 
|  | createIntrinsic(R, Defs); | 
|  | } | 
|  |  | 
|  | // The mappings must be sorted based on BuiltinID. | 
|  | sort(Defs, [](const std::unique_ptr<Intrinsic> &A, | 
|  | const std::unique_ptr<Intrinsic> &B) { | 
|  | return A->getMangledName() < B->getMangledName(); | 
|  | }); | 
|  |  | 
|  | OS << "#ifdef GET_SME_LLVM_INTRINSIC_MAP\n"; | 
|  | for (auto &Def : Defs) { | 
|  | // Builtins only exist for non-overloaded intrinsics, overloaded | 
|  | // declarations only live in the header file. | 
|  | if (Def->getClassKind() == ClassG) | 
|  | continue; | 
|  |  | 
|  | uint64_t Flags = Def->getFlags(); | 
|  | auto FlagString = std::to_string(Flags); | 
|  |  | 
|  | std::string LLVMName = Def->getLLVMName(); | 
|  | std::string Builtin = Def->getMangledName(); | 
|  | if (!LLVMName.empty()) | 
|  | OS << "SMEMAP1(" << Builtin << ", " << LLVMName << ", " << FlagString | 
|  | << "),\n"; | 
|  | else | 
|  | OS << "SMEMAP2(" << Builtin << ", " << FlagString << "),\n"; | 
|  | } | 
|  | OS << "#endif\n\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createSMERangeChecks(raw_ostream &OS) { | 
|  | std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst"); | 
|  | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; | 
|  | for (auto *R : RV) { | 
|  | createIntrinsic(R, Defs); | 
|  | } | 
|  |  | 
|  | // The mappings must be sorted based on BuiltinID. | 
|  | sort(Defs, [](const std::unique_ptr<Intrinsic> &A, | 
|  | const std::unique_ptr<Intrinsic> &B) { | 
|  | return A->getMangledName() < B->getMangledName(); | 
|  | }); | 
|  |  | 
|  | OS << "#ifdef GET_SME_IMMEDIATE_CHECK\n"; | 
|  |  | 
|  | // Ensure these are only emitted once. | 
|  | std::set<std::string> Emitted; | 
|  |  | 
|  | for (auto &Def : Defs) { | 
|  | if (Emitted.find(Def->getMangledName()) != Emitted.end() || | 
|  | Def->getImmChecks().empty()) | 
|  | continue; | 
|  |  | 
|  | OS << "case SME::BI__builtin_sme_" << Def->getMangledName() << ":\n"; | 
|  | for (auto &Check : Def->getImmChecks()) | 
|  | OS << "ImmChecks.push_back(std::make_tuple(" << Check.getImmArgIdx() | 
|  | << ", " << Check.getKind() << ", " << Check.getElementSizeInBits() | 
|  | << "));\n"; | 
|  | OS << "  break;\n"; | 
|  |  | 
|  | Emitted.insert(Def->getMangledName()); | 
|  | } | 
|  |  | 
|  | OS << "#endif\n\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createBuiltinZAState(raw_ostream &OS) { | 
|  | std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst"); | 
|  | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; | 
|  | for (auto *R : RV) | 
|  | createIntrinsic(R, Defs); | 
|  |  | 
|  | std::map<std::string, std::set<std::string>> IntrinsicsPerState; | 
|  | for (auto &Def : Defs) { | 
|  | std::string Key; | 
|  | auto AddToKey = [&Key](const std::string &S) -> void { | 
|  | Key = Key.empty() ? S : (Key + " | " + S); | 
|  | }; | 
|  |  | 
|  | if (Def->isFlagSet(getEnumValueForFlag("IsInZA"))) | 
|  | AddToKey("ArmInZA"); | 
|  | else if (Def->isFlagSet(getEnumValueForFlag("IsOutZA"))) | 
|  | AddToKey("ArmOutZA"); | 
|  | else if (Def->isFlagSet(getEnumValueForFlag("IsInOutZA"))) | 
|  | AddToKey("ArmInOutZA"); | 
|  |  | 
|  | if (Def->isFlagSet(getEnumValueForFlag("IsInZT0"))) | 
|  | AddToKey("ArmInZT0"); | 
|  | else if (Def->isFlagSet(getEnumValueForFlag("IsOutZT0"))) | 
|  | AddToKey("ArmOutZT0"); | 
|  | else if (Def->isFlagSet(getEnumValueForFlag("IsInOutZT0"))) | 
|  | AddToKey("ArmInOutZT0"); | 
|  |  | 
|  | if (!Key.empty()) | 
|  | IntrinsicsPerState[Key].insert(Def->getMangledName()); | 
|  | } | 
|  |  | 
|  | OS << "#ifdef GET_SME_BUILTIN_GET_STATE\n"; | 
|  | for (auto &KV : IntrinsicsPerState) { | 
|  | for (StringRef Name : KV.second) | 
|  | OS << "case SME::BI__builtin_sme_" << Name << ":\n"; | 
|  | OS << "  return " << KV.first << ";\n"; | 
|  | } | 
|  | OS << "#endif\n\n"; | 
|  | } | 
|  |  | 
|  | void SVEEmitter::createStreamingAttrs(raw_ostream &OS, ACLEKind Kind) { | 
|  | std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst"); | 
|  | SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; | 
|  | for (auto *R : RV) | 
|  | createIntrinsic(R, Defs); | 
|  |  | 
|  | StringRef ExtensionKind; | 
|  | switch (Kind) { | 
|  | case ACLEKind::SME: | 
|  | ExtensionKind = "SME"; | 
|  | break; | 
|  | case ACLEKind::SVE: | 
|  | ExtensionKind = "SVE"; | 
|  | break; | 
|  | } | 
|  |  | 
|  | OS << "#ifdef GET_" << ExtensionKind << "_STREAMING_ATTRS\n"; | 
|  |  | 
|  | StringMap<std::set<std::string>> StreamingMap; | 
|  |  | 
|  | uint64_t IsStreamingFlag = getEnumValueForFlag("IsStreaming"); | 
|  | uint64_t VerifyRuntimeMode = getEnumValueForFlag("VerifyRuntimeMode"); | 
|  | uint64_t IsStreamingCompatibleFlag = | 
|  | getEnumValueForFlag("IsStreamingCompatible"); | 
|  |  | 
|  | for (auto &Def : Defs) { | 
|  | if (!Def->isFlagSet(VerifyRuntimeMode) && !Def->getSVEGuard().empty() && | 
|  | !Def->getSMEGuard().empty()) | 
|  | report_fatal_error("Missing VerifyRuntimeMode flag"); | 
|  | if (Def->isFlagSet(VerifyRuntimeMode) && | 
|  | (Def->getSVEGuard().empty() || Def->getSMEGuard().empty())) | 
|  | report_fatal_error("VerifyRuntimeMode requires SVE and SME guards"); | 
|  |  | 
|  | if (Def->isFlagSet(IsStreamingFlag)) | 
|  | StreamingMap["ArmStreaming"].insert(Def->getMangledName()); | 
|  | else if (Def->isFlagSet(VerifyRuntimeMode)) | 
|  | StreamingMap["VerifyRuntimeMode"].insert(Def->getMangledName()); | 
|  | else if (Def->isFlagSet(IsStreamingCompatibleFlag)) | 
|  | StreamingMap["ArmStreamingCompatible"].insert(Def->getMangledName()); | 
|  | else | 
|  | StreamingMap["ArmNonStreaming"].insert(Def->getMangledName()); | 
|  | } | 
|  |  | 
|  | for (auto BuiltinType : StreamingMap.keys()) { | 
|  | for (auto Name : StreamingMap[BuiltinType]) { | 
|  | OS << "case " << ExtensionKind << "::BI__builtin_" | 
|  | << ExtensionKind.lower() << "_"; | 
|  | OS << Name << ":\n"; | 
|  | } | 
|  | OS << "  BuiltinType = " << BuiltinType << ";\n"; | 
|  | OS << "  break;\n"; | 
|  | } | 
|  |  | 
|  | OS << "#endif\n\n"; | 
|  | } | 
|  |  | 
|  | namespace clang { | 
|  | void EmitSveHeader(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createHeader(OS); | 
|  | } | 
|  |  | 
|  | void EmitSveBuiltins(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createBuiltins(OS); | 
|  | } | 
|  |  | 
|  | void EmitSveBuiltinCG(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createCodeGenMap(OS); | 
|  | } | 
|  |  | 
|  | void EmitSveRangeChecks(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createRangeChecks(OS); | 
|  | } | 
|  |  | 
|  | void EmitSveTypeFlags(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createTypeFlags(OS); | 
|  | } | 
|  |  | 
|  | void EmitImmCheckTypes(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createImmCheckTypes(OS); | 
|  | } | 
|  |  | 
|  | void EmitSveStreamingAttrs(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createStreamingAttrs(OS, ACLEKind::SVE); | 
|  | } | 
|  |  | 
|  | void EmitSmeHeader(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createSMEHeader(OS); | 
|  | } | 
|  |  | 
|  | void EmitSmeBuiltins(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createSMEBuiltins(OS); | 
|  | } | 
|  |  | 
|  | void EmitSmeBuiltinCG(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createSMECodeGenMap(OS); | 
|  | } | 
|  |  | 
|  | void EmitSmeRangeChecks(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createSMERangeChecks(OS); | 
|  | } | 
|  |  | 
|  | void EmitSmeStreamingAttrs(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createStreamingAttrs(OS, ACLEKind::SME); | 
|  | } | 
|  |  | 
|  | void EmitSmeBuiltinZAState(const RecordKeeper &Records, raw_ostream &OS) { | 
|  | SVEEmitter(Records).createBuiltinZAState(OS); | 
|  | } | 
|  | } // End namespace clang |