//===-- 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(ClassKind CK = ClassS) const {
    return mangleName(CK);
  }

  /// 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) {
      assert(Proto[I] != '4' &&
             "Handling for '4' prototype modifier not implemented");
      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;

      if (Proto[I] == '2')
        Param += 1;

      // Multivector modifier can be skipped
      if (Proto[I] == '.') {
        Param -= 1; // Adjust for the increment at the top of the loop
        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;
  std::vector<llvm::StringRef> ImmCheckTypeNames;

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")) {
      auto [it, inserted] = ImmCheckTypes.try_emplace(
          RV->getNameInitAsString(), RV->getValueAsInt("Value"));
      if (!inserted)
        llvm_unreachable("Duplicate imm check");
      if ((size_t)it->second >= ImmCheckTypeNames.size())
        ImmCheckTypeNames.resize((size_t)it->second + 1);
      ImmCheckTypeNames[it->second] = it->first();
    }
  }

  /// 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 name for the immcheck type
  StringRef getImmCheckForEnumValue(unsigned Id) {
    if ((size_t)Id < ImmCheckTypeNames.size())
      return ImmCheckTypeNames[Id];
    llvm_unreachable("Unsupported imm check");
  }

  // 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 __builtin prototypes in JSON format.
  void createBuiltinsJSON(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();

    unsigned Depth = 0;
    for (auto &C : Guard) {
      switch (C) {
      default:
        break;
      case '|':
        if (Depth == 0)
          // Group top-level ORs before ANDing with the base feature.
          return Base.str() + ",(" + Guard.str() + ")";
        break;
      case '(':
        ++Depth;
        break;
      case ')':
        if (Depth == 0)
          llvm_unreachable("Mismatched parentheses!");

        --Depth;
        break;
      }
    }

    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::createBuiltinsJSON(raw_ostream &OS) {
  SmallVector<std::unique_ptr<Intrinsic>, 128> Defs;
  std::vector<const Record *> RV = Records.getAllDerivedDefinitions("Inst");
  for (auto *R : RV)
    createIntrinsic(R, Defs);

  OS << "[\n";
  bool FirstDef = true;

  for (auto &Def : Defs) {
    std::vector<std::string> Flags;

    if (Def->isFlagSet(getEnumValueForFlag("IsStreaming")))
      Flags.push_back("streaming-only");
    else if (Def->isFlagSet(getEnumValueForFlag("IsStreamingCompatible")))
      Flags.push_back("streaming-compatible");
    else if (Def->isFlagSet(getEnumValueForFlag("VerifyRuntimeMode")))
      Flags.push_back("feature-dependent");

    if (Def->isFlagSet(getEnumValueForFlag("IsInZA")) ||
        Def->isFlagSet(getEnumValueForFlag("IsOutZA")) ||
        Def->isFlagSet(getEnumValueForFlag("IsInOutZA")))
      Flags.push_back("requires-za");

    if (Def->isFlagSet(getEnumValueForFlag("IsInZT0")) ||
        Def->isFlagSet(getEnumValueForFlag("IsOutZT0")) ||
        Def->isFlagSet(getEnumValueForFlag("IsInOutZT0")))
      Flags.push_back("requires-zt");

    if (!FirstDef)
      OS << ",\n";

    OS << "{ ";
    OS << "\"guard\": \"" << Def->getSVEGuard() << "\",";
    OS << "\"streaming_guard\": \"" << Def->getSMEGuard() << "\",";
    OS << "\"flags\": \"";

    for (size_t I = 0; I < Flags.size(); ++I) {
      if (I != 0)
        OS << ',';
      OS << Flags[I];
    }

    OS << "\",\"builtin\": \"";

    std::string BuiltinName = Def->getMangledName(Def->getClassKind());

    OS << Def->getReturnType().str() << " " << BuiltinName << "(";
    for (unsigned I = 0; I < Def->getTypes().size() - 1; ++I) {
      if (I != 0)
        OS << ", ";

      SVEType ParamType = Def->getParamType(I);

      // These are ImmCheck'd but their type names are sufficiently clear.
      if (ParamType.isPredicatePattern() || ParamType.isPrefetchOp()) {
        OS << ParamType.str();
        continue;
      }

      // Pass ImmCheck information by pretending it's a type.
      auto Iter = llvm::find_if(Def->getImmChecks(), [I](const auto &Chk) {
        return (unsigned)Chk.getImmArgIdx() == I;
      });
      if (Iter != Def->getImmChecks().end())
        OS << getImmCheckForEnumValue(Iter->getKind());
      else
        OS << ParamType.str();
    }
    OS << ");\" }";
    FirstDef = false;
  }

  OS << "\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 EmitSveBuiltinsJSON(const RecordKeeper &Records, raw_ostream &OS) {
  SVEEmitter(Records).createBuiltinsJSON(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 EmitSmeBuiltinsJSON(const RecordKeeper &Records, raw_ostream &OS) {
  SVEEmitter(Records).createBuiltinsJSON(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
