//===-- SPIRVGlobalRegistry.cpp - SPIR-V Global Registry --------*- C++ -*-===//
//
// 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 file contains the implementation of the SPIRVGlobalRegistry class,
// which is used to maintain rich type information required for SPIR-V even
// after lowering from LLVM IR to GMIR. It can convert an llvm::Type into
// an OpTypeXXX instruction, and map it to a virtual register. Also it builds
// and supports consistency of constants and global variables.
//
//===----------------------------------------------------------------------===//

#include "SPIRVGlobalRegistry.h"
#include "SPIRV.h"
#include "SPIRVBuiltins.h"
#include "SPIRVSubtarget.h"
#include "SPIRVUtils.h"
#include "llvm/ADT/APInt.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsSPIRV.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/MathExtras.h"
#include <cassert>
#include <functional>

using namespace llvm;

static bool allowEmitFakeUse(const Value *Arg) {
  if (isSpvIntrinsic(Arg))
    return false;
  if (isa<AtomicCmpXchgInst, InsertValueInst, UndefValue>(Arg))
    return false;
  if (const auto *LI = dyn_cast<LoadInst>(Arg))
    if (LI->getType()->isAggregateType())
      return false;
  return true;
}

static unsigned typeToAddressSpace(const Type *Ty) {
  if (auto PType = dyn_cast<TypedPointerType>(Ty))
    return PType->getAddressSpace();
  if (auto PType = dyn_cast<PointerType>(Ty))
    return PType->getAddressSpace();
  if (auto *ExtTy = dyn_cast<TargetExtType>(Ty);
      ExtTy && isTypedPointerWrapper(ExtTy))
    return ExtTy->getIntParameter(0);
  reportFatalInternalError("Unable to convert LLVM type to SPIRVType");
}

static bool
storageClassRequiresExplictLayout(SPIRV::StorageClass::StorageClass SC) {
  switch (SC) {
  case SPIRV::StorageClass::Uniform:
  case SPIRV::StorageClass::PushConstant:
  case SPIRV::StorageClass::StorageBuffer:
  case SPIRV::StorageClass::PhysicalStorageBufferEXT:
    return true;
  case SPIRV::StorageClass::UniformConstant:
  case SPIRV::StorageClass::Input:
  case SPIRV::StorageClass::Output:
  case SPIRV::StorageClass::Workgroup:
  case SPIRV::StorageClass::CrossWorkgroup:
  case SPIRV::StorageClass::Private:
  case SPIRV::StorageClass::Function:
  case SPIRV::StorageClass::Generic:
  case SPIRV::StorageClass::AtomicCounter:
  case SPIRV::StorageClass::Image:
  case SPIRV::StorageClass::CallableDataNV:
  case SPIRV::StorageClass::IncomingCallableDataNV:
  case SPIRV::StorageClass::RayPayloadNV:
  case SPIRV::StorageClass::HitAttributeNV:
  case SPIRV::StorageClass::IncomingRayPayloadNV:
  case SPIRV::StorageClass::ShaderRecordBufferNV:
  case SPIRV::StorageClass::CodeSectionINTEL:
  case SPIRV::StorageClass::DeviceOnlyINTEL:
  case SPIRV::StorageClass::HostOnlyINTEL:
    return false;
  }
  llvm_unreachable("Unknown SPIRV::StorageClass enum");
}

SPIRVGlobalRegistry::SPIRVGlobalRegistry(unsigned PointerSize)
    : PointerSize(PointerSize), Bound(0), CurMF(nullptr) {}

SPIRVType *SPIRVGlobalRegistry::assignIntTypeToVReg(unsigned BitWidth,
                                                    Register VReg,
                                                    MachineInstr &I,
                                                    const SPIRVInstrInfo &TII) {
  SPIRVType *SpirvType = getOrCreateSPIRVIntegerType(BitWidth, I, TII);
  assignSPIRVTypeToVReg(SpirvType, VReg, *CurMF);
  return SpirvType;
}

SPIRVType *
SPIRVGlobalRegistry::assignFloatTypeToVReg(unsigned BitWidth, Register VReg,
                                           MachineInstr &I,
                                           const SPIRVInstrInfo &TII) {
  SPIRVType *SpirvType = getOrCreateSPIRVFloatType(BitWidth, I, TII);
  assignSPIRVTypeToVReg(SpirvType, VReg, *CurMF);
  return SpirvType;
}

SPIRVType *SPIRVGlobalRegistry::assignVectTypeToVReg(
    SPIRVType *BaseType, unsigned NumElements, Register VReg, MachineInstr &I,
    const SPIRVInstrInfo &TII) {
  SPIRVType *SpirvType =
      getOrCreateSPIRVVectorType(BaseType, NumElements, I, TII);
  assignSPIRVTypeToVReg(SpirvType, VReg, *CurMF);
  return SpirvType;
}

SPIRVType *SPIRVGlobalRegistry::assignTypeToVReg(
    const Type *Type, Register VReg, MachineIRBuilder &MIRBuilder,
    SPIRV::AccessQualifier::AccessQualifier AccessQual, bool EmitIR) {
  SPIRVType *SpirvType =
      getOrCreateSPIRVType(Type, MIRBuilder, AccessQual, EmitIR);
  assignSPIRVTypeToVReg(SpirvType, VReg, MIRBuilder.getMF());
  return SpirvType;
}

void SPIRVGlobalRegistry::assignSPIRVTypeToVReg(SPIRVType *SpirvType,
                                                Register VReg,
                                                const MachineFunction &MF) {
  VRegToTypeMap[&MF][VReg] = SpirvType;
}

static Register createTypeVReg(MachineRegisterInfo &MRI) {
  auto Res = MRI.createGenericVirtualRegister(LLT::scalar(64));
  MRI.setRegClass(Res, &SPIRV::TYPERegClass);
  return Res;
}

inline Register createTypeVReg(MachineIRBuilder &MIRBuilder) {
  return createTypeVReg(MIRBuilder.getMF().getRegInfo());
}

SPIRVType *SPIRVGlobalRegistry::getOpTypeBool(MachineIRBuilder &MIRBuilder) {
  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    return MIRBuilder.buildInstr(SPIRV::OpTypeBool)
        .addDef(createTypeVReg(MIRBuilder));
  });
}

unsigned SPIRVGlobalRegistry::adjustOpTypeIntWidth(unsigned Width) const {
  const SPIRVSubtarget &ST = cast<SPIRVSubtarget>(CurMF->getSubtarget());
  if (ST.canUseExtension(
          SPIRV::Extension::SPV_ALTERA_arbitrary_precision_integers) ||
      (Width == 4 && ST.canUseExtension(SPIRV::Extension::SPV_INTEL_int4)))
    return Width;
  if (Width <= 8)
    return 8;
  else if (Width <= 16)
    return 16;
  else if (Width <= 32)
    return 32;
  else if (Width <= 64)
    return 64;
  else if (Width <= 128)
    return 128;
  reportFatalUsageError("Unsupported Integer width!");
}

SPIRVType *SPIRVGlobalRegistry::getOpTypeInt(unsigned Width,
                                             MachineIRBuilder &MIRBuilder,
                                             bool IsSigned) {
  Width = adjustOpTypeIntWidth(Width);
  const SPIRVSubtarget &ST =
      cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    if (Width == 4 && ST.canUseExtension(SPIRV::Extension::SPV_INTEL_int4)) {
      MIRBuilder.buildInstr(SPIRV::OpExtension)
          .addImm(SPIRV::Extension::SPV_INTEL_int4);
      MIRBuilder.buildInstr(SPIRV::OpCapability)
          .addImm(SPIRV::Capability::Int4TypeINTEL);
    } else if ((!isPowerOf2_32(Width) || Width < 8) &&
               ST.canUseExtension(
                   SPIRV::Extension::SPV_ALTERA_arbitrary_precision_integers)) {
      MIRBuilder.buildInstr(SPIRV::OpExtension)
          .addImm(SPIRV::Extension::SPV_ALTERA_arbitrary_precision_integers);
      MIRBuilder.buildInstr(SPIRV::OpCapability)
          .addImm(SPIRV::Capability::ArbitraryPrecisionIntegersALTERA);
    }
    return MIRBuilder.buildInstr(SPIRV::OpTypeInt)
        .addDef(createTypeVReg(MIRBuilder))
        .addImm(Width)
        .addImm(IsSigned ? 1 : 0);
  });
}

SPIRVType *SPIRVGlobalRegistry::getOpTypeFloat(uint32_t Width,
                                               MachineIRBuilder &MIRBuilder) {
  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    return MIRBuilder.buildInstr(SPIRV::OpTypeFloat)
        .addDef(createTypeVReg(MIRBuilder))
        .addImm(Width);
  });
}

SPIRVType *
SPIRVGlobalRegistry::getOpTypeFloat(uint32_t Width,
                                    MachineIRBuilder &MIRBuilder,
                                    SPIRV::FPEncoding::FPEncoding FPEncode) {
  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    return MIRBuilder.buildInstr(SPIRV::OpTypeFloat)
        .addDef(createTypeVReg(MIRBuilder))
        .addImm(Width)
        .addImm(FPEncode);
  });
}

SPIRVType *SPIRVGlobalRegistry::getOpTypeVoid(MachineIRBuilder &MIRBuilder) {
  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    return MIRBuilder.buildInstr(SPIRV::OpTypeVoid)
        .addDef(createTypeVReg(MIRBuilder));
  });
}

void SPIRVGlobalRegistry::invalidateMachineInstr(MachineInstr *MI) {
  // Other maps that may hold MachineInstr*:
  // - VRegToTypeMap: We cannot remove the definitions of `MI` from
  // VRegToTypeMap because some calls to invalidateMachineInstr are replacing MI
  // with another instruction defining the same register. We expect that if MI
  // is a type instruction, and it is still referenced in VRegToTypeMap, then
  // those registers are dead or the VRegToTypeMap is out-of-date. We do not
  // expect passes to ask for the SPIR-V type of a dead register. If the
  // VRegToTypeMap is out-of-date already, then there was an error before. We
  // cannot add an assert to verify this because the VRegToTypeMap can be
  // out-of-date.
  // - FunctionToInstr & FunctionToInstrRev: At this point, we should not be
  // deleting functions. No need to update.
  // - AliasInstMDMap: Would require a linear search, and the Intel Alias
  // instruction are not instructions instruction selection will be able to
  // remove.

  const SPIRVSubtarget &ST = MI->getMF()->getSubtarget<SPIRVSubtarget>();
  [[maybe_unused]] const SPIRVInstrInfo *TII = ST.getInstrInfo();
  assert(!TII->isAliasingInstr(*MI) &&
         "Cannot invalidate aliasing instructions.");
  assert(MI->getOpcode() != SPIRV::OpFunction &&
         "Cannot invalidate OpFunction.");

  if (MI->getOpcode() == SPIRV::OpFunctionCall) {
    if (const auto *F = dyn_cast<Function>(MI->getOperand(2).getGlobal())) {
      auto It = ForwardCalls.find(F);
      if (It != ForwardCalls.end()) {
        It->second.erase(MI);
        if (It->second.empty())
          ForwardCalls.erase(It);
      }
    }
  }

  const MachineFunction *MF = MI->getMF();
  auto It = LastInsertedTypeMap.find(MF);
  if (It != LastInsertedTypeMap.end() && It->second == MI)
    LastInsertedTypeMap.erase(MF);
  // remove from the duplicate tracker to avoid incorrect reuse
  erase(MI);
}

SPIRVType *SPIRVGlobalRegistry::createOpType(
    MachineIRBuilder &MIRBuilder,
    std::function<MachineInstr *(MachineIRBuilder &)> Op) {
  auto oldInsertPoint = MIRBuilder.getInsertPt();
  MachineBasicBlock *OldMBB = &MIRBuilder.getMBB();
  MachineBasicBlock *NewMBB = &*MIRBuilder.getMF().begin();

  auto LastInsertedType = LastInsertedTypeMap.find(CurMF);
  if (LastInsertedType != LastInsertedTypeMap.end()) {
    auto It = LastInsertedType->second->getIterator();
    // It might happen that this instruction was removed from the first MBB,
    // hence the Parent's check.
    MachineBasicBlock::iterator InsertAt;
    if (It->getParent() != NewMBB)
      InsertAt = oldInsertPoint->getParent() == NewMBB
                     ? oldInsertPoint
                     : getInsertPtValidEnd(NewMBB);
    else if (It->getNextNode())
      InsertAt = It->getNextNode()->getIterator();
    else
      InsertAt = getInsertPtValidEnd(NewMBB);
    MIRBuilder.setInsertPt(*NewMBB, InsertAt);
  } else {
    MIRBuilder.setInsertPt(*NewMBB, NewMBB->begin());
    auto Result = LastInsertedTypeMap.try_emplace(CurMF, nullptr);
    assert(Result.second);
    LastInsertedType = Result.first;
  }

  MachineInstr *Type = Op(MIRBuilder);
  // We expect all users of this function to insert definitions at the insertion
  // point set above that is always the first MBB.
  assert(Type->getParent() == NewMBB);
  LastInsertedType->second = Type;

  MIRBuilder.setInsertPt(*OldMBB, oldInsertPoint);
  return Type;
}

SPIRVType *SPIRVGlobalRegistry::getOpTypeVector(uint32_t NumElems,
                                                SPIRVType *ElemType,
                                                MachineIRBuilder &MIRBuilder) {
  auto EleOpc = ElemType->getOpcode();
  (void)EleOpc;
  assert((EleOpc == SPIRV::OpTypeInt || EleOpc == SPIRV::OpTypeFloat ||
          EleOpc == SPIRV::OpTypeBool) &&
         "Invalid vector element type");

  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    return MIRBuilder.buildInstr(SPIRV::OpTypeVector)
        .addDef(createTypeVReg(MIRBuilder))
        .addUse(getSPIRVTypeID(ElemType))
        .addImm(NumElems);
  });
}

Register SPIRVGlobalRegistry::getOrCreateConstFP(APFloat Val, MachineInstr &I,
                                                 SPIRVType *SpvType,
                                                 const SPIRVInstrInfo &TII,
                                                 bool ZeroAsNull) {
  LLVMContext &Ctx = CurMF->getFunction().getContext();
  auto *const CF = ConstantFP::get(Ctx, Val);
  const MachineInstr *MI = findMI(CF, CurMF);
  if (MI && (MI->getOpcode() == SPIRV::OpConstantNull ||
             MI->getOpcode() == SPIRV::OpConstantF))
    return MI->getOperand(0).getReg();
  return createConstFP(CF, I, SpvType, TII, ZeroAsNull);
}

Register SPIRVGlobalRegistry::createConstFP(const ConstantFP *CF,
                                            MachineInstr &I, SPIRVType *SpvType,
                                            const SPIRVInstrInfo &TII,
                                            bool ZeroAsNull) {
  unsigned BitWidth = getScalarOrVectorBitWidth(SpvType);
  LLT LLTy = LLT::scalar(BitWidth);
  Register Res = CurMF->getRegInfo().createGenericVirtualRegister(LLTy);
  CurMF->getRegInfo().setRegClass(Res, &SPIRV::fIDRegClass);
  assignSPIRVTypeToVReg(SpvType, Res, *CurMF);

  MachineInstr *DepMI = const_cast<MachineInstr *>(SpvType);
  MachineIRBuilder MIRBuilder(*DepMI->getParent(), DepMI->getIterator());
  SPIRVType *NewType =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        MachineInstrBuilder MIB;
        // In OpenCL OpConstantNull - Scalar floating point: +0.0 (all bits 0)
        if (CF->getValue().isPosZero() && ZeroAsNull) {
          MIB = MIRBuilder.buildInstr(SPIRV::OpConstantNull)
                    .addDef(Res)
                    .addUse(getSPIRVTypeID(SpvType));
        } else {
          MIB = MIRBuilder.buildInstr(SPIRV::OpConstantF)
                    .addDef(Res)
                    .addUse(getSPIRVTypeID(SpvType));
          addNumImm(APInt(BitWidth,
                          CF->getValueAPF().bitcastToAPInt().getZExtValue()),
                    MIB);
        }
        const auto &ST = CurMF->getSubtarget();
        constrainSelectedInstRegOperands(*MIB, *ST.getInstrInfo(),
                                         *ST.getRegisterInfo(),
                                         *ST.getRegBankInfo());
        return MIB;
      });
  add(CF, NewType);
  return Res;
}

Register SPIRVGlobalRegistry::getOrCreateConstInt(uint64_t Val, MachineInstr &I,
                                                  SPIRVType *SpvType,
                                                  const SPIRVInstrInfo &TII,
                                                  bool ZeroAsNull) {
  const IntegerType *Ty = cast<IntegerType>(getTypeForSPIRVType(SpvType));
  auto *const CI = ConstantInt::get(const_cast<IntegerType *>(Ty), Val);
  const MachineInstr *MI = findMI(CI, CurMF);
  if (MI && (MI->getOpcode() == SPIRV::OpConstantNull ||
             MI->getOpcode() == SPIRV::OpConstantI))
    return MI->getOperand(0).getReg();
  return createConstInt(CI, I, SpvType, TII, ZeroAsNull);
}

Register SPIRVGlobalRegistry::createConstInt(const ConstantInt *CI,
                                             MachineInstr &I,
                                             SPIRVType *SpvType,
                                             const SPIRVInstrInfo &TII,
                                             bool ZeroAsNull) {
  unsigned BitWidth = getScalarOrVectorBitWidth(SpvType);
  LLT LLTy = LLT::scalar(BitWidth);
  Register Res = CurMF->getRegInfo().createGenericVirtualRegister(LLTy);
  CurMF->getRegInfo().setRegClass(Res, &SPIRV::iIDRegClass);
  assignIntTypeToVReg(BitWidth, Res, I, TII);

  MachineInstr *DepMI = const_cast<MachineInstr *>(SpvType);
  MachineIRBuilder MIRBuilder(*DepMI->getParent(), DepMI->getIterator());
  SPIRVType *NewType =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        MachineInstrBuilder MIB;
        if (BitWidth == 1) {
          MIB = MIRBuilder
                    .buildInstr(CI->isZero() ? SPIRV::OpConstantFalse
                                             : SPIRV::OpConstantTrue)
                    .addDef(Res)
                    .addUse(getSPIRVTypeID(SpvType));
        } else if (!CI->isZero() || !ZeroAsNull) {
          MIB = MIRBuilder.buildInstr(SPIRV::OpConstantI)
                    .addDef(Res)
                    .addUse(getSPIRVTypeID(SpvType));
          addNumImm(CI->getValue(), MIB);
        } else {
          MIB = MIRBuilder.buildInstr(SPIRV::OpConstantNull)
                    .addDef(Res)
                    .addUse(getSPIRVTypeID(SpvType));
        }
        const auto &ST = CurMF->getSubtarget();
        constrainSelectedInstRegOperands(*MIB, *ST.getInstrInfo(),
                                         *ST.getRegisterInfo(),
                                         *ST.getRegBankInfo());
        return MIB;
      });
  add(CI, NewType);
  return Res;
}

Register SPIRVGlobalRegistry::buildConstantInt(uint64_t Val,
                                               MachineIRBuilder &MIRBuilder,
                                               SPIRVType *SpvType, bool EmitIR,
                                               bool ZeroAsNull) {
  assert(SpvType);
  auto &MF = MIRBuilder.getMF();
  const IntegerType *Ty = cast<IntegerType>(getTypeForSPIRVType(SpvType));
  // TODO: Avoid implicit trunc?
  // See https://github.com/llvm/llvm-project/issues/112510.
  auto *const CI = ConstantInt::get(const_cast<IntegerType *>(Ty), Val,
                                    /*IsSigned=*/false, /*ImplicitTrunc=*/true);
  Register Res = find(CI, &MF);
  if (Res.isValid())
    return Res;

  unsigned BitWidth = getScalarOrVectorBitWidth(SpvType);
  LLT LLTy = LLT::scalar(BitWidth);
  MachineRegisterInfo &MRI = MF.getRegInfo();
  Res = MRI.createGenericVirtualRegister(LLTy);
  MRI.setRegClass(Res, &SPIRV::iIDRegClass);
  assignTypeToVReg(Ty, Res, MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
                   EmitIR);

  SPIRVType *NewType =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        if (EmitIR)
          return MIRBuilder.buildConstant(Res, *CI);
        Register SpvTypeReg = getSPIRVTypeID(SpvType);
        MachineInstrBuilder MIB;
        if (Val || !ZeroAsNull) {
          MIB = MIRBuilder.buildInstr(SPIRV::OpConstantI)
                    .addDef(Res)
                    .addUse(SpvTypeReg);
          addNumImm(APInt(BitWidth, Val), MIB);
        } else {
          MIB = MIRBuilder.buildInstr(SPIRV::OpConstantNull)
                    .addDef(Res)
                    .addUse(SpvTypeReg);
        }
        const auto &Subtarget = CurMF->getSubtarget();
        constrainSelectedInstRegOperands(*MIB, *Subtarget.getInstrInfo(),
                                         *Subtarget.getRegisterInfo(),
                                         *Subtarget.getRegBankInfo());
        return MIB;
      });
  add(CI, NewType);
  return Res;
}

Register SPIRVGlobalRegistry::buildConstantFP(APFloat Val,
                                              MachineIRBuilder &MIRBuilder,
                                              SPIRVType *SpvType) {
  auto &MF = MIRBuilder.getMF();
  LLVMContext &Ctx = MF.getFunction().getContext();
  if (!SpvType)
    SpvType = getOrCreateSPIRVType(Type::getFloatTy(Ctx), MIRBuilder,
                                   SPIRV::AccessQualifier::ReadWrite, true);
  auto *const CF = ConstantFP::get(Ctx, Val);
  Register Res = find(CF, &MF);
  if (Res.isValid())
    return Res;

  LLT LLTy = LLT::scalar(getScalarOrVectorBitWidth(SpvType));
  Res = MF.getRegInfo().createGenericVirtualRegister(LLTy);
  MF.getRegInfo().setRegClass(Res, &SPIRV::fIDRegClass);
  assignSPIRVTypeToVReg(SpvType, Res, MF);

  SPIRVType *NewType =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        MachineInstrBuilder MIB;
        MIB = MIRBuilder.buildInstr(SPIRV::OpConstantF)
                  .addDef(Res)
                  .addUse(getSPIRVTypeID(SpvType));
        addNumImm(CF->getValueAPF().bitcastToAPInt(), MIB);
        return MIB;
      });
  add(CF, NewType);
  return Res;
}

Register SPIRVGlobalRegistry::getOrCreateBaseRegister(
    Constant *Val, MachineInstr &I, SPIRVType *SpvType,
    const SPIRVInstrInfo &TII, unsigned BitWidth, bool ZeroAsNull) {
  SPIRVType *Type = SpvType;
  if (SpvType->getOpcode() == SPIRV::OpTypeVector ||
      SpvType->getOpcode() == SPIRV::OpTypeArray) {
    auto EleTypeReg = SpvType->getOperand(1).getReg();
    Type = getSPIRVTypeForVReg(EleTypeReg);
  }
  if (Type->getOpcode() == SPIRV::OpTypeFloat) {
    SPIRVType *SpvBaseType = getOrCreateSPIRVFloatType(BitWidth, I, TII);
    return getOrCreateConstFP(cast<ConstantFP>(Val)->getValue(), I, SpvBaseType,
                              TII, ZeroAsNull);
  }
  assert(Type->getOpcode() == SPIRV::OpTypeInt);
  SPIRVType *SpvBaseType = getOrCreateSPIRVIntegerType(BitWidth, I, TII);
  return getOrCreateConstInt(Val->getUniqueInteger().getZExtValue(), I,
                             SpvBaseType, TII, ZeroAsNull);
}

Register SPIRVGlobalRegistry::getOrCreateCompositeOrNull(
    Constant *Val, MachineInstr &I, SPIRVType *SpvType,
    const SPIRVInstrInfo &TII, Constant *CA, unsigned BitWidth,
    unsigned ElemCnt, bool ZeroAsNull) {
  if (Register R = find(CA, CurMF); R.isValid())
    return R;

  bool IsNull = Val->isNullValue() && ZeroAsNull;
  Register ElemReg;
  if (!IsNull)
    ElemReg =
        getOrCreateBaseRegister(Val, I, SpvType, TII, BitWidth, ZeroAsNull);

  LLT LLTy = LLT::scalar(64);
  Register Res = CurMF->getRegInfo().createGenericVirtualRegister(LLTy);
  CurMF->getRegInfo().setRegClass(Res, getRegClass(SpvType));
  assignSPIRVTypeToVReg(SpvType, Res, *CurMF);

  MachineInstr *DepMI = const_cast<MachineInstr *>(SpvType);
  MachineIRBuilder MIRBuilder(*DepMI->getParent(), DepMI->getIterator());
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        MachineInstrBuilder MIB;
        if (!IsNull) {
          MIB = MIRBuilder.buildInstr(SPIRV::OpConstantComposite)
                    .addDef(Res)
                    .addUse(getSPIRVTypeID(SpvType));
          for (unsigned i = 0; i < ElemCnt; ++i)
            MIB.addUse(ElemReg);
        } else {
          MIB = MIRBuilder.buildInstr(SPIRV::OpConstantNull)
                    .addDef(Res)
                    .addUse(getSPIRVTypeID(SpvType));
        }
        const auto &Subtarget = CurMF->getSubtarget();
        constrainSelectedInstRegOperands(*MIB, *Subtarget.getInstrInfo(),
                                         *Subtarget.getRegisterInfo(),
                                         *Subtarget.getRegBankInfo());
        return MIB;
      });
  add(CA, NewMI);
  return Res;
}

Register SPIRVGlobalRegistry::getOrCreateConstVector(uint64_t Val,
                                                     MachineInstr &I,
                                                     SPIRVType *SpvType,
                                                     const SPIRVInstrInfo &TII,
                                                     bool ZeroAsNull) {
  const Type *LLVMTy = getTypeForSPIRVType(SpvType);
  assert(LLVMTy->isVectorTy());
  const FixedVectorType *LLVMVecTy = cast<FixedVectorType>(LLVMTy);
  Type *LLVMBaseTy = LLVMVecTy->getElementType();
  assert(LLVMBaseTy->isIntegerTy());
  auto *ConstVal = ConstantInt::get(LLVMBaseTy, Val);
  auto *ConstVec =
      ConstantVector::getSplat(LLVMVecTy->getElementCount(), ConstVal);
  unsigned BW = getScalarOrVectorBitWidth(SpvType);
  return getOrCreateCompositeOrNull(ConstVal, I, SpvType, TII, ConstVec, BW,
                                    SpvType->getOperand(2).getImm(),
                                    ZeroAsNull);
}

Register SPIRVGlobalRegistry::getOrCreateConstVector(APFloat Val,
                                                     MachineInstr &I,
                                                     SPIRVType *SpvType,
                                                     const SPIRVInstrInfo &TII,
                                                     bool ZeroAsNull) {
  const Type *LLVMTy = getTypeForSPIRVType(SpvType);
  assert(LLVMTy->isVectorTy());
  const FixedVectorType *LLVMVecTy = cast<FixedVectorType>(LLVMTy);
  Type *LLVMBaseTy = LLVMVecTy->getElementType();
  assert(LLVMBaseTy->isFloatingPointTy());
  auto *ConstVal = ConstantFP::get(LLVMBaseTy, Val);
  auto *ConstVec =
      ConstantVector::getSplat(LLVMVecTy->getElementCount(), ConstVal);
  unsigned BW = getScalarOrVectorBitWidth(SpvType);
  return getOrCreateCompositeOrNull(ConstVal, I, SpvType, TII, ConstVec, BW,
                                    SpvType->getOperand(2).getImm(),
                                    ZeroAsNull);
}

Register SPIRVGlobalRegistry::getOrCreateConstIntArray(
    uint64_t Val, size_t Num, MachineInstr &I, SPIRVType *SpvType,
    const SPIRVInstrInfo &TII) {
  const Type *LLVMTy = getTypeForSPIRVType(SpvType);
  assert(LLVMTy->isArrayTy());
  const ArrayType *LLVMArrTy = cast<ArrayType>(LLVMTy);
  Type *LLVMBaseTy = LLVMArrTy->getElementType();
  Constant *CI = ConstantInt::get(LLVMBaseTy, Val);
  SPIRVType *SpvBaseTy = getSPIRVTypeForVReg(SpvType->getOperand(1).getReg());
  unsigned BW = getScalarOrVectorBitWidth(SpvBaseTy);
  // The following is reasonably unique key that is better that [Val]. The naive
  // alternative would be something along the lines of:
  //   SmallVector<Constant *> NumCI(Num, CI);
  //   Constant *UniqueKey =
  //     ConstantArray::get(const_cast<ArrayType*>(LLVMArrTy), NumCI);
  // that would be a truly unique but dangerous key, because it could lead to
  // the creation of constants of arbitrary length (that is, the parameter of
  // memset) which were missing in the original module.
  Constant *UniqueKey = ConstantStruct::getAnon(
      {PoisonValue::get(const_cast<ArrayType *>(LLVMArrTy)),
       ConstantInt::get(LLVMBaseTy, Val), ConstantInt::get(LLVMBaseTy, Num)});
  return getOrCreateCompositeOrNull(CI, I, SpvType, TII, UniqueKey, BW,
                                    LLVMArrTy->getNumElements());
}

Register SPIRVGlobalRegistry::getOrCreateIntCompositeOrNull(
    uint64_t Val, MachineIRBuilder &MIRBuilder, SPIRVType *SpvType, bool EmitIR,
    Constant *CA, unsigned BitWidth, unsigned ElemCnt) {
  if (Register R = find(CA, CurMF); R.isValid())
    return R;

  Register ElemReg;
  if (Val || EmitIR) {
    SPIRVType *SpvBaseType = getOrCreateSPIRVIntegerType(BitWidth, MIRBuilder);
    ElemReg = buildConstantInt(Val, MIRBuilder, SpvBaseType, EmitIR);
  }
  LLT LLTy = EmitIR ? LLT::fixed_vector(ElemCnt, BitWidth) : LLT::scalar(64);
  Register Res = CurMF->getRegInfo().createGenericVirtualRegister(LLTy);
  CurMF->getRegInfo().setRegClass(Res, &SPIRV::iIDRegClass);
  assignSPIRVTypeToVReg(SpvType, Res, *CurMF);

  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        if (EmitIR)
          return MIRBuilder.buildSplatBuildVector(Res, ElemReg);

        if (Val) {
          auto MIB = MIRBuilder.buildInstr(SPIRV::OpConstantComposite)
                         .addDef(Res)
                         .addUse(getSPIRVTypeID(SpvType));
          for (unsigned i = 0; i < ElemCnt; ++i)
            MIB.addUse(ElemReg);
          return MIB;
        }

        return MIRBuilder.buildInstr(SPIRV::OpConstantNull)
            .addDef(Res)
            .addUse(getSPIRVTypeID(SpvType));
      });
  add(CA, NewMI);
  return Res;
}

Register
SPIRVGlobalRegistry::getOrCreateConsIntVector(uint64_t Val,
                                              MachineIRBuilder &MIRBuilder,
                                              SPIRVType *SpvType, bool EmitIR) {
  const Type *LLVMTy = getTypeForSPIRVType(SpvType);
  assert(LLVMTy->isVectorTy());
  const FixedVectorType *LLVMVecTy = cast<FixedVectorType>(LLVMTy);
  Type *LLVMBaseTy = LLVMVecTy->getElementType();
  const auto ConstInt = ConstantInt::get(LLVMBaseTy, Val);
  auto ConstVec =
      ConstantVector::getSplat(LLVMVecTy->getElementCount(), ConstInt);
  unsigned BW = getScalarOrVectorBitWidth(SpvType);
  return getOrCreateIntCompositeOrNull(Val, MIRBuilder, SpvType, EmitIR,
                                       ConstVec, BW,
                                       SpvType->getOperand(2).getImm());
}

Register
SPIRVGlobalRegistry::getOrCreateConstNullPtr(MachineIRBuilder &MIRBuilder,
                                             SPIRVType *SpvType) {
  const Type *Ty = getTypeForSPIRVType(SpvType);
  unsigned AddressSpace = typeToAddressSpace(Ty);
  Type *ElemTy = ::getPointeeType(Ty);
  assert(ElemTy);
  const Constant *CP = ConstantTargetNone::get(
      dyn_cast<TargetExtType>(getTypedPointerWrapper(ElemTy, AddressSpace)));
  Register Res = find(CP, CurMF);
  if (Res.isValid())
    return Res;

  LLT LLTy = LLT::pointer(AddressSpace, PointerSize);
  Res = CurMF->getRegInfo().createGenericVirtualRegister(LLTy);
  CurMF->getRegInfo().setRegClass(Res, &SPIRV::pIDRegClass);
  assignSPIRVTypeToVReg(SpvType, Res, *CurMF);

  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        return MIRBuilder.buildInstr(SPIRV::OpConstantNull)
            .addDef(Res)
            .addUse(getSPIRVTypeID(SpvType));
      });
  add(CP, NewMI);
  return Res;
}

Register
SPIRVGlobalRegistry::buildConstantSampler(Register ResReg, unsigned AddrMode,
                                          unsigned Param, unsigned FilerMode,
                                          MachineIRBuilder &MIRBuilder) {
  auto Sampler =
      ResReg.isValid()
          ? ResReg
          : MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);
  SPIRVType *TypeSampler = getOrCreateOpTypeSampler(MIRBuilder);
  Register TypeSamplerReg = getSPIRVTypeID(TypeSampler);
  // We cannot use createOpType() logic here, because of the
  // GlobalISel/IRTranslator.cpp check for a tail call that expects that
  // MIRBuilder.getInsertPt() has a previous instruction. If this constant is
  // inserted as a result of "__translate_sampler_initializer()" this would
  // break this IRTranslator assumption.
  MIRBuilder.buildInstr(SPIRV::OpConstantSampler)
      .addDef(Sampler)
      .addUse(TypeSamplerReg)
      .addImm(AddrMode)
      .addImm(Param)
      .addImm(FilerMode);
  return Sampler;
}

Register SPIRVGlobalRegistry::buildGlobalVariable(
    Register ResVReg, SPIRVType *BaseType, StringRef Name,
    const GlobalValue *GV, SPIRV::StorageClass::StorageClass Storage,
    const MachineInstr *Init, bool IsConst,
    const std::optional<SPIRV::LinkageType::LinkageType> &LinkageType,
    MachineIRBuilder &MIRBuilder, bool IsInstSelector) {
  const GlobalVariable *GVar = nullptr;
  if (GV) {
    GVar = cast<const GlobalVariable>(GV);
  } else {
    // If GV is not passed explicitly, use the name to find or construct
    // the global variable.
    Module *M = MIRBuilder.getMF().getFunction().getParent();
    GVar = M->getGlobalVariable(Name);
    if (GVar == nullptr) {
      const Type *Ty = getTypeForSPIRVType(BaseType); // TODO: check type.
      // Module takes ownership of the global var.
      GVar = new GlobalVariable(*M, const_cast<Type *>(Ty), false,
                                GlobalValue::ExternalLinkage, nullptr,
                                Twine(Name));
    }
    GV = GVar;
  }

  const MachineFunction *MF = &MIRBuilder.getMF();
  Register Reg = find(GVar, MF);
  if (Reg.isValid()) {
    if (Reg != ResVReg)
      MIRBuilder.buildCopy(ResVReg, Reg);
    return ResVReg;
  }

  auto MIB = MIRBuilder.buildInstr(SPIRV::OpVariable)
                 .addDef(ResVReg)
                 .addUse(getSPIRVTypeID(BaseType))
                 .addImm(static_cast<uint32_t>(Storage));
  if (Init)
    MIB.addUse(Init->getOperand(0).getReg());
  // ISel may introduce a new register on this step, so we need to add it to
  // DT and correct its type avoiding fails on the next stage.
  if (IsInstSelector) {
    const auto &Subtarget = CurMF->getSubtarget();
    constrainSelectedInstRegOperands(*MIB, *Subtarget.getInstrInfo(),
                                     *Subtarget.getRegisterInfo(),
                                     *Subtarget.getRegBankInfo());
  }
  add(GVar, MIB);

  Reg = MIB->getOperand(0).getReg();
  addGlobalObject(GVar, MF, Reg);

  // Set to Reg the same type as ResVReg has.
  auto MRI = MIRBuilder.getMRI();
  if (Reg != ResVReg) {
    LLT RegLLTy =
        LLT::pointer(MRI->getType(ResVReg).getAddressSpace(), getPointerSize());
    MRI->setType(Reg, RegLLTy);
    assignSPIRVTypeToVReg(BaseType, Reg, MIRBuilder.getMF());
  } else {
    // Our knowledge about the type may be updated.
    // If that's the case, we need to update a type
    // associated with the register.
    SPIRVType *DefType = getSPIRVTypeForVReg(ResVReg);
    if (!DefType || DefType != BaseType)
      assignSPIRVTypeToVReg(BaseType, Reg, MIRBuilder.getMF());
  }

  // If it's a global variable with name, output OpName for it.
  if (GVar && GVar->hasName())
    buildOpName(Reg, GVar->getName(), MIRBuilder);

  // Output decorations for the GV.
  // TODO: maybe move to GenerateDecorations pass.
  const SPIRVSubtarget &ST =
      cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
  if (IsConst && !ST.isShader())
    buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::Constant, {});

  if (GVar && GVar->getAlign().valueOrOne().value() != 1 && !ST.isShader()) {
    unsigned Alignment = (unsigned)GVar->getAlign().valueOrOne().value();
    buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::Alignment, {Alignment});
  }

  if (LinkageType)
    buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::LinkageAttributes,
                    {static_cast<uint32_t>(*LinkageType)}, Name);

  SPIRV::BuiltIn::BuiltIn BuiltInId;
  if (getSpirvBuiltInIdByName(Name, BuiltInId))
    buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::BuiltIn,
                    {static_cast<uint32_t>(BuiltInId)});

  // If it's a global variable with "spirv.Decorations" metadata node
  // recognize it as a SPIR-V friendly LLVM IR and parse "spirv.Decorations"
  // arguments.
  MDNode *GVarMD = nullptr;
  if (GVar && (GVarMD = GVar->getMetadata("spirv.Decorations")) != nullptr)
    buildOpSpirvDecorations(Reg, MIRBuilder, GVarMD, ST);

  return Reg;
}

// Returns a name based on the Type. Notes that this does not look at
// decorations, and will return the same string for two types that are the same
// except for decorations.
Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
    const SPIRVType *VarType, uint32_t Set, uint32_t Binding, StringRef Name,
    MachineIRBuilder &MIRBuilder) {
  Register VarReg =
      MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);

  buildGlobalVariable(VarReg, VarType, Name, nullptr,
                      getPointerStorageClass(VarType), nullptr, false,
                      std::nullopt, MIRBuilder, false);

  buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::DescriptorSet, {Set});
  buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::Binding, {Binding});
  return VarReg;
}

// TODO: Double check the calls to getOpTypeArray to make sure that `ElemType`
// is explicitly laid out when required.
SPIRVType *SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems,
                                               SPIRVType *ElemType,
                                               MachineIRBuilder &MIRBuilder,
                                               bool ExplicitLayoutRequired,
                                               bool EmitIR) {
  assert((ElemType->getOpcode() != SPIRV::OpTypeVoid) &&
         "Invalid array element type");
  SPIRVType *SpvTypeInt32 = getOrCreateSPIRVIntegerType(32, MIRBuilder);
  SPIRVType *ArrayType = nullptr;
  const SPIRVSubtarget &ST =
      cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
  if (NumElems != 0) {
    Register NumElementsVReg =
        buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
    ArrayType = createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
      return MIRBuilder.buildInstr(SPIRV::OpTypeArray)
          .addDef(createTypeVReg(MIRBuilder))
          .addUse(getSPIRVTypeID(ElemType))
          .addUse(NumElementsVReg);
    });
  } else {
    if (!ST.isShader()) {
      llvm::reportFatalUsageError(
          "Runtime arrays are not allowed in non-shader "
          "SPIR-V modules");
      return nullptr;
    }
    ArrayType = createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
      return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray)
          .addDef(createTypeVReg(MIRBuilder))
          .addUse(getSPIRVTypeID(ElemType));
    });
  }

  if (ExplicitLayoutRequired && !isResourceType(ElemType)) {
    Type *ET = const_cast<Type *>(getTypeForSPIRVType(ElemType));
    addArrayStrideDecorations(ArrayType->defs().begin()->getReg(), ET,
                              MIRBuilder);
  }

  return ArrayType;
}

SPIRVType *SPIRVGlobalRegistry::getOpTypeOpaque(const StructType *Ty,
                                                MachineIRBuilder &MIRBuilder) {
  assert(Ty->hasName());
  const StringRef Name = Ty->hasName() ? Ty->getName() : "";
  Register ResVReg = createTypeVReg(MIRBuilder);
  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    auto MIB = MIRBuilder.buildInstr(SPIRV::OpTypeOpaque).addDef(ResVReg);
    addStringImm(Name, MIB);
    buildOpName(ResVReg, Name, MIRBuilder);
    return MIB;
  });
}

SPIRVType *SPIRVGlobalRegistry::getOpTypeStruct(
    const StructType *Ty, MachineIRBuilder &MIRBuilder,
    SPIRV::AccessQualifier::AccessQualifier AccQual,
    StructOffsetDecorator Decorator, bool EmitIR) {
  Type *OriginalElementType = nullptr;
  uint64_t TotalSize = 0;
  if (matchPeeledArrayPattern(Ty, OriginalElementType, TotalSize)) {
    SPIRVType *ElementSPIRVType = findSPIRVType(
        OriginalElementType, MIRBuilder, AccQual,
        /* ExplicitLayoutRequired= */ Decorator != nullptr, EmitIR);
    return getOpTypeArray(TotalSize, ElementSPIRVType, MIRBuilder,
                          /*ExplicitLayoutRequired=*/Decorator != nullptr,
                          EmitIR);
  }

  const SPIRVSubtarget &ST =
      cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
  SmallVector<Register, 4> FieldTypes;
  constexpr unsigned MaxWordCount = UINT16_MAX;
  const size_t NumElements = Ty->getNumElements();

  size_t MaxNumElements = MaxWordCount - 2;
  size_t SPIRVStructNumElements = NumElements;
  if (NumElements > MaxNumElements) {
    // Do adjustments for continued instructions.
    SPIRVStructNumElements = MaxNumElements;
    MaxNumElements = MaxWordCount - 1;
  }

  for (const auto &Elem : Ty->elements()) {
    SPIRVType *ElemTy = findSPIRVType(
        toTypedPointer(Elem), MIRBuilder, AccQual,
        /* ExplicitLayoutRequired= */ Decorator != nullptr, EmitIR);
    assert(ElemTy && ElemTy->getOpcode() != SPIRV::OpTypeVoid &&
           "Invalid struct element type");
    FieldTypes.push_back(getSPIRVTypeID(ElemTy));
  }
  Register ResVReg = createTypeVReg(MIRBuilder);
  if (Ty->hasName())
    buildOpName(ResVReg, Ty->getName(), MIRBuilder);
  if (Ty->isPacked() && !ST.isShader())
    buildOpDecorate(ResVReg, MIRBuilder, SPIRV::Decoration::CPacked, {});

  SPIRVType *SPVType =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        auto MIBStruct =
            MIRBuilder.buildInstr(SPIRV::OpTypeStruct).addDef(ResVReg);
        for (size_t I = 0; I < SPIRVStructNumElements; ++I)
          MIBStruct.addUse(FieldTypes[I]);
        for (size_t I = SPIRVStructNumElements; I < NumElements;
             I += MaxNumElements) {
          auto MIBCont =
              MIRBuilder.buildInstr(SPIRV::OpTypeStructContinuedINTEL);
          for (size_t J = I; J < std::min(I + MaxNumElements, NumElements); ++J)
            MIBCont.addUse(FieldTypes[I]);
        }
        return MIBStruct;
      });

  if (Decorator)
    Decorator(SPVType->defs().begin()->getReg());

  return SPVType;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSpecialType(
    const Type *Ty, MachineIRBuilder &MIRBuilder,
    SPIRV::AccessQualifier::AccessQualifier AccQual) {
  assert(isSpecialOpaqueType(Ty) && "Not a special opaque builtin type");
  return SPIRV::lowerBuiltinType(Ty, AccQual, MIRBuilder, this);
}

SPIRVType *SPIRVGlobalRegistry::getOpTypePointer(
    SPIRV::StorageClass::StorageClass SC, SPIRVType *ElemType,
    MachineIRBuilder &MIRBuilder, Register Reg) {
  if (!Reg.isValid())
    Reg = createTypeVReg(MIRBuilder);

  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    return MIRBuilder.buildInstr(SPIRV::OpTypePointer)
        .addDef(Reg)
        .addImm(static_cast<uint32_t>(SC))
        .addUse(getSPIRVTypeID(ElemType));
  });
}

SPIRVType *SPIRVGlobalRegistry::getOpTypeForwardPointer(
    SPIRV::StorageClass::StorageClass SC, MachineIRBuilder &MIRBuilder) {
  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    return MIRBuilder.buildInstr(SPIRV::OpTypeForwardPointer)
        .addUse(createTypeVReg(MIRBuilder))
        .addImm(static_cast<uint32_t>(SC));
  });
}

SPIRVType *SPIRVGlobalRegistry::getOpTypeFunction(
    const FunctionType *Ty, SPIRVType *RetType,
    const SmallVectorImpl<SPIRVType *> &ArgTypes,
    MachineIRBuilder &MIRBuilder) {
  if (Ty->isVarArg()) {
    Function &Fn = MIRBuilder.getMF().getFunction();
    Ty->getContext().diagnose(DiagnosticInfoUnsupported(
        Fn, "SPIR-V does not support variadic functions",
        MIRBuilder.getDebugLoc()));
  }
  return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
    auto MIB = MIRBuilder.buildInstr(SPIRV::OpTypeFunction)
                   .addDef(createTypeVReg(MIRBuilder))
                   .addUse(getSPIRVTypeID(RetType));
    for (const SPIRVType *ArgType : ArgTypes)
      MIB.addUse(getSPIRVTypeID(ArgType));
    return MIB;
  });
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeFunctionWithArgs(
    const Type *Ty, SPIRVType *RetType,
    const SmallVectorImpl<SPIRVType *> &ArgTypes,
    MachineIRBuilder &MIRBuilder) {
  if (const MachineInstr *MI = findMI(Ty, false, &MIRBuilder.getMF()))
    return MI;
  const MachineInstr *NewMI =
      getOpTypeFunction(cast<FunctionType>(Ty), RetType, ArgTypes, MIRBuilder);
  add(Ty, false, NewMI);
  return finishCreatingSPIRVType(Ty, NewMI);
}

SPIRVType *SPIRVGlobalRegistry::findSPIRVType(
    const Type *Ty, MachineIRBuilder &MIRBuilder,
    SPIRV::AccessQualifier::AccessQualifier AccQual,
    bool ExplicitLayoutRequired, bool EmitIR) {
  Ty = adjustIntTypeByWidth(Ty);
  // TODO: findMI needs to know if a layout is required.
  if (const MachineInstr *MI =
          findMI(Ty, ExplicitLayoutRequired, &MIRBuilder.getMF()))
    return MI;
  if (auto It = ForwardPointerTypes.find(Ty); It != ForwardPointerTypes.end())
    return It->second;
  return restOfCreateSPIRVType(Ty, MIRBuilder, AccQual, ExplicitLayoutRequired,
                               EmitIR);
}

Register SPIRVGlobalRegistry::getSPIRVTypeID(const SPIRVType *SpirvType) const {
  assert(SpirvType && "Attempting to get type id for nullptr type.");
  if (SpirvType->getOpcode() == SPIRV::OpTypeForwardPointer ||
      SpirvType->getOpcode() == SPIRV::OpTypeStructContinuedINTEL)
    return SpirvType->uses().begin()->getReg();
  return SpirvType->defs().begin()->getReg();
}

// We need to use a new LLVM integer type if there is a mismatch between
// number of bits in LLVM and SPIRV integer types to let DuplicateTracker
// ensure uniqueness of a SPIRV type by the corresponding LLVM type. Without
// such an adjustment SPIRVGlobalRegistry::getOpTypeInt() could create the
// same "OpTypeInt 8" type for a series of LLVM integer types with number of
// bits less than 8. This would lead to duplicate type definitions
// eventually due to the method that DuplicateTracker utilizes to reason
// about uniqueness of type records.
const Type *SPIRVGlobalRegistry::adjustIntTypeByWidth(const Type *Ty) const {
  if (auto IType = dyn_cast<IntegerType>(Ty)) {
    unsigned SrcBitWidth = IType->getBitWidth();
    if (SrcBitWidth > 1) {
      unsigned BitWidth = adjustOpTypeIntWidth(SrcBitWidth);
      // Maybe change source LLVM type to keep DuplicateTracker consistent.
      if (SrcBitWidth != BitWidth)
        Ty = IntegerType::get(Ty->getContext(), BitWidth);
    }
  }
  return Ty;
}

SPIRVType *SPIRVGlobalRegistry::createSPIRVType(
    const Type *Ty, MachineIRBuilder &MIRBuilder,
    SPIRV::AccessQualifier::AccessQualifier AccQual,
    bool ExplicitLayoutRequired, bool EmitIR) {
  if (isSpecialOpaqueType(Ty))
    return getOrCreateSpecialType(Ty, MIRBuilder, AccQual);

  if (const MachineInstr *MI =
          findMI(Ty, ExplicitLayoutRequired, &MIRBuilder.getMF()))
    return MI;

  if (auto IType = dyn_cast<IntegerType>(Ty)) {
    const unsigned Width = IType->getBitWidth();
    return Width == 1 ? getOpTypeBool(MIRBuilder)
                      : getOpTypeInt(Width, MIRBuilder, false);
  }
  if (Ty->isFloatingPointTy()) {
    if (Ty->isBFloatTy()) {
      return getOpTypeFloat(Ty->getPrimitiveSizeInBits(), MIRBuilder,
                            SPIRV::FPEncoding::BFloat16KHR);
    } else {
      return getOpTypeFloat(Ty->getPrimitiveSizeInBits(), MIRBuilder);
    }
  }
  if (Ty->isVoidTy())
    return getOpTypeVoid(MIRBuilder);
  if (Ty->isVectorTy()) {
    SPIRVType *El =
        findSPIRVType(cast<FixedVectorType>(Ty)->getElementType(), MIRBuilder,
                      AccQual, ExplicitLayoutRequired, EmitIR);
    return getOpTypeVector(cast<FixedVectorType>(Ty)->getNumElements(), El,
                           MIRBuilder);
  }
  if (Ty->isArrayTy()) {
    SPIRVType *El = findSPIRVType(Ty->getArrayElementType(), MIRBuilder,
                                  AccQual, ExplicitLayoutRequired, EmitIR);
    return getOpTypeArray(Ty->getArrayNumElements(), El, MIRBuilder,
                          ExplicitLayoutRequired, EmitIR);
  }
  if (auto SType = dyn_cast<StructType>(Ty)) {
    if (SType->isOpaque())
      return getOpTypeOpaque(SType, MIRBuilder);

    StructOffsetDecorator Decorator = nullptr;
    if (ExplicitLayoutRequired) {
      Decorator = [&MIRBuilder, SType, this](Register Reg) {
        addStructOffsetDecorations(Reg, const_cast<StructType *>(SType),
                                   MIRBuilder);
      };
    }
    return getOpTypeStruct(SType, MIRBuilder, AccQual, std::move(Decorator),
                           EmitIR);
  }
  if (auto FType = dyn_cast<FunctionType>(Ty)) {
    SPIRVType *RetTy = findSPIRVType(FType->getReturnType(), MIRBuilder,
                                     AccQual, ExplicitLayoutRequired, EmitIR);
    SmallVector<SPIRVType *, 4> ParamTypes;
    for (const auto &ParamTy : FType->params())
      ParamTypes.push_back(findSPIRVType(ParamTy, MIRBuilder, AccQual,
                                         ExplicitLayoutRequired, EmitIR));
    return getOpTypeFunction(FType, RetTy, ParamTypes, MIRBuilder);
  }

  unsigned AddrSpace = typeToAddressSpace(Ty);
  SPIRVType *SpvElementType = nullptr;
  if (Type *ElemTy = ::getPointeeType(Ty))
    SpvElementType = getOrCreateSPIRVType(ElemTy, MIRBuilder, AccQual, EmitIR);
  else
    SpvElementType = getOrCreateSPIRVIntegerType(8, MIRBuilder);

  // Get access to information about available extensions
  const SPIRVSubtarget *ST =
      static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
  auto SC = addressSpaceToStorageClass(AddrSpace, *ST);

  Type *ElemTy = ::getPointeeType(Ty);
  if (!ElemTy) {
    ElemTy = Type::getInt8Ty(MIRBuilder.getContext());
  }

  // If we have forward pointer associated with this type, use its register
  // operand to create OpTypePointer.
  if (auto It = ForwardPointerTypes.find(Ty); It != ForwardPointerTypes.end()) {
    Register Reg = getSPIRVTypeID(It->second);
    // TODO: what does getOpTypePointer do?
    return getOpTypePointer(SC, SpvElementType, MIRBuilder, Reg);
  }

  return getOrCreateSPIRVPointerType(ElemTy, MIRBuilder, SC);
}

SPIRVType *SPIRVGlobalRegistry::restOfCreateSPIRVType(
    const Type *Ty, MachineIRBuilder &MIRBuilder,
    SPIRV::AccessQualifier::AccessQualifier AccessQual,
    bool ExplicitLayoutRequired, bool EmitIR) {
  // TODO: Could this create a problem if one requires an explicit layout, and
  // the next time it does not?
  if (TypesInProcessing.count(Ty) && !isPointerTyOrWrapper(Ty))
    return nullptr;
  TypesInProcessing.insert(Ty);
  SPIRVType *SpirvType = createSPIRVType(Ty, MIRBuilder, AccessQual,
                                         ExplicitLayoutRequired, EmitIR);
  TypesInProcessing.erase(Ty);
  VRegToTypeMap[&MIRBuilder.getMF()][getSPIRVTypeID(SpirvType)] = SpirvType;

  // TODO: We could end up with two SPIR-V types pointing to the same llvm type.
  // Is that a problem?
  SPIRVToLLVMType[SpirvType] = unifyPtrType(Ty);

  if (SpirvType->getOpcode() == SPIRV::OpTypeForwardPointer ||
      findMI(Ty, false, &MIRBuilder.getMF()) || isSpecialOpaqueType(Ty))
    return SpirvType;

  if (auto *ExtTy = dyn_cast<TargetExtType>(Ty);
      ExtTy && isTypedPointerWrapper(ExtTy))
    add(ExtTy->getTypeParameter(0), ExtTy->getIntParameter(0), SpirvType);
  else if (!isPointerTy(Ty))
    add(Ty, ExplicitLayoutRequired, SpirvType);
  else if (isTypedPointerTy(Ty))
    add(cast<TypedPointerType>(Ty)->getElementType(),
        getPointerAddressSpace(Ty), SpirvType);
  else
    add(Type::getInt8Ty(MIRBuilder.getMF().getFunction().getContext()),
        getPointerAddressSpace(Ty), SpirvType);
  return SpirvType;
}

SPIRVType *
SPIRVGlobalRegistry::getSPIRVTypeForVReg(Register VReg,
                                         const MachineFunction *MF) const {
  auto t = VRegToTypeMap.find(MF ? MF : CurMF);
  if (t != VRegToTypeMap.end()) {
    auto tt = t->second.find(VReg);
    if (tt != t->second.end())
      return tt->second;
  }
  return nullptr;
}

SPIRVType *SPIRVGlobalRegistry::getResultType(Register VReg,
                                              MachineFunction *MF) {
  if (!MF)
    MF = CurMF;
  MachineInstr *Instr = getVRegDef(MF->getRegInfo(), VReg);
  return getSPIRVTypeForVReg(Instr->getOperand(1).getReg(), MF);
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVType(
    const Type *Ty, MachineIRBuilder &MIRBuilder,
    SPIRV::AccessQualifier::AccessQualifier AccessQual,
    bool ExplicitLayoutRequired, bool EmitIR) {
  const MachineFunction *MF = &MIRBuilder.getMF();
  Register Reg;
  if (auto *ExtTy = dyn_cast<TargetExtType>(Ty);
      ExtTy && isTypedPointerWrapper(ExtTy))
    Reg = find(ExtTy->getTypeParameter(0), ExtTy->getIntParameter(0), MF);
  else if (!isPointerTy(Ty))
    Reg = find(Ty = adjustIntTypeByWidth(Ty), ExplicitLayoutRequired, MF);
  else if (isTypedPointerTy(Ty))
    Reg = find(cast<TypedPointerType>(Ty)->getElementType(),
               getPointerAddressSpace(Ty), MF);
  else
    Reg = find(Type::getInt8Ty(MIRBuilder.getMF().getFunction().getContext()),
               getPointerAddressSpace(Ty), MF);
  if (Reg.isValid() && !isSpecialOpaqueType(Ty))
    return getSPIRVTypeForVReg(Reg);

  TypesInProcessing.clear();
  SPIRVType *STy = restOfCreateSPIRVType(Ty, MIRBuilder, AccessQual,
                                         ExplicitLayoutRequired, EmitIR);
  // Create normal pointer types for the corresponding OpTypeForwardPointers.
  for (auto &CU : ForwardPointerTypes) {
    // Pointer type themselves do not require an explicit layout. The types
    // they pointer to might, but that is taken care of when creating the type.
    bool PtrNeedsLayout = false;
    const Type *Ty2 = CU.first;
    SPIRVType *STy2 = CU.second;
    if ((Reg = find(Ty2, PtrNeedsLayout, MF)).isValid())
      STy2 = getSPIRVTypeForVReg(Reg);
    else
      STy2 = restOfCreateSPIRVType(Ty2, MIRBuilder, AccessQual, PtrNeedsLayout,
                                   EmitIR);
    if (Ty == Ty2)
      STy = STy2;
  }
  ForwardPointerTypes.clear();
  return STy;
}

bool SPIRVGlobalRegistry::isScalarOfType(Register VReg,
                                         unsigned TypeOpcode) const {
  SPIRVType *Type = getSPIRVTypeForVReg(VReg);
  assert(Type && "isScalarOfType VReg has no type assigned");
  return Type->getOpcode() == TypeOpcode;
}

bool SPIRVGlobalRegistry::isScalarOrVectorOfType(Register VReg,
                                                 unsigned TypeOpcode) const {
  SPIRVType *Type = getSPIRVTypeForVReg(VReg);
  assert(Type && "isScalarOrVectorOfType VReg has no type assigned");
  if (Type->getOpcode() == TypeOpcode)
    return true;
  if (Type->getOpcode() == SPIRV::OpTypeVector) {
    Register ScalarTypeVReg = Type->getOperand(1).getReg();
    SPIRVType *ScalarType = getSPIRVTypeForVReg(ScalarTypeVReg);
    return ScalarType->getOpcode() == TypeOpcode;
  }
  return false;
}

bool SPIRVGlobalRegistry::isResourceType(SPIRVType *Type) const {
  switch (Type->getOpcode()) {
  case SPIRV::OpTypeImage:
  case SPIRV::OpTypeSampler:
  case SPIRV::OpTypeSampledImage:
    return true;
  case SPIRV::OpTypeStruct:
    return hasBlockDecoration(Type);
  default:
    return false;
  }
  return false;
}
unsigned
SPIRVGlobalRegistry::getScalarOrVectorComponentCount(Register VReg) const {
  return getScalarOrVectorComponentCount(getSPIRVTypeForVReg(VReg));
}

unsigned
SPIRVGlobalRegistry::getScalarOrVectorComponentCount(SPIRVType *Type) const {
  if (!Type)
    return 0;
  return Type->getOpcode() == SPIRV::OpTypeVector
             ? static_cast<unsigned>(Type->getOperand(2).getImm())
             : 1;
}

SPIRVType *
SPIRVGlobalRegistry::getScalarOrVectorComponentType(Register VReg) const {
  return getScalarOrVectorComponentType(getSPIRVTypeForVReg(VReg));
}

SPIRVType *
SPIRVGlobalRegistry::getScalarOrVectorComponentType(SPIRVType *Type) const {
  if (!Type)
    return nullptr;
  Register ScalarReg = Type->getOpcode() == SPIRV::OpTypeVector
                           ? Type->getOperand(1).getReg()
                           : Type->getOperand(0).getReg();
  SPIRVType *ScalarType = getSPIRVTypeForVReg(ScalarReg);
  assert(isScalarOrVectorOfType(Type->getOperand(0).getReg(),
                                ScalarType->getOpcode()));
  return ScalarType;
}

unsigned
SPIRVGlobalRegistry::getScalarOrVectorBitWidth(const SPIRVType *Type) const {
  assert(Type && "Invalid Type pointer");
  if (Type->getOpcode() == SPIRV::OpTypeVector) {
    auto EleTypeReg = Type->getOperand(1).getReg();
    Type = getSPIRVTypeForVReg(EleTypeReg);
  }
  if (Type->getOpcode() == SPIRV::OpTypeInt ||
      Type->getOpcode() == SPIRV::OpTypeFloat)
    return Type->getOperand(1).getImm();
  if (Type->getOpcode() == SPIRV::OpTypeBool)
    return 1;
  llvm_unreachable("Attempting to get bit width of non-integer/float type.");
}

unsigned SPIRVGlobalRegistry::getNumScalarOrVectorTotalBitWidth(
    const SPIRVType *Type) const {
  assert(Type && "Invalid Type pointer");
  unsigned NumElements = 1;
  if (Type->getOpcode() == SPIRV::OpTypeVector) {
    NumElements = static_cast<unsigned>(Type->getOperand(2).getImm());
    Type = getSPIRVTypeForVReg(Type->getOperand(1).getReg());
  }
  return Type->getOpcode() == SPIRV::OpTypeInt ||
                 Type->getOpcode() == SPIRV::OpTypeFloat
             ? NumElements * Type->getOperand(1).getImm()
             : 0;
}

const SPIRVType *SPIRVGlobalRegistry::retrieveScalarOrVectorIntType(
    const SPIRVType *Type) const {
  if (Type && Type->getOpcode() == SPIRV::OpTypeVector)
    Type = getSPIRVTypeForVReg(Type->getOperand(1).getReg());
  return Type && Type->getOpcode() == SPIRV::OpTypeInt ? Type : nullptr;
}

bool SPIRVGlobalRegistry::isScalarOrVectorSigned(const SPIRVType *Type) const {
  const SPIRVType *IntType = retrieveScalarOrVectorIntType(Type);
  return IntType && IntType->getOperand(2).getImm() != 0;
}

SPIRVType *SPIRVGlobalRegistry::getPointeeType(SPIRVType *PtrType) {
  return PtrType && PtrType->getOpcode() == SPIRV::OpTypePointer
             ? getSPIRVTypeForVReg(PtrType->getOperand(2).getReg())
             : nullptr;
}

unsigned SPIRVGlobalRegistry::getPointeeTypeOp(Register PtrReg) {
  SPIRVType *ElemType = getPointeeType(getSPIRVTypeForVReg(PtrReg));
  return ElemType ? ElemType->getOpcode() : 0;
}

bool SPIRVGlobalRegistry::isBitcastCompatible(const SPIRVType *Type1,
                                              const SPIRVType *Type2) const {
  if (!Type1 || !Type2)
    return false;
  auto Op1 = Type1->getOpcode(), Op2 = Type2->getOpcode();
  // Ignore difference between <1.5 and >=1.5 protocol versions:
  // it's valid if either Result Type or Operand is a pointer, and the other
  // is a pointer, an integer scalar, or an integer vector.
  if (Op1 == SPIRV::OpTypePointer &&
      (Op2 == SPIRV::OpTypePointer || retrieveScalarOrVectorIntType(Type2)))
    return true;
  if (Op2 == SPIRV::OpTypePointer &&
      (Op1 == SPIRV::OpTypePointer || retrieveScalarOrVectorIntType(Type1)))
    return true;
  unsigned Bits1 = getNumScalarOrVectorTotalBitWidth(Type1),
           Bits2 = getNumScalarOrVectorTotalBitWidth(Type2);
  return Bits1 > 0 && Bits1 == Bits2;
}

SPIRV::StorageClass::StorageClass
SPIRVGlobalRegistry::getPointerStorageClass(Register VReg) const {
  SPIRVType *Type = getSPIRVTypeForVReg(VReg);
  assert(Type && Type->getOpcode() == SPIRV::OpTypePointer &&
         Type->getOperand(1).isImm() && "Pointer type is expected");
  return getPointerStorageClass(Type);
}

SPIRV::StorageClass::StorageClass
SPIRVGlobalRegistry::getPointerStorageClass(const SPIRVType *Type) const {
  return static_cast<SPIRV::StorageClass::StorageClass>(
      Type->getOperand(1).getImm());
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateVulkanBufferType(
    MachineIRBuilder &MIRBuilder, Type *ElemType,
    SPIRV::StorageClass::StorageClass SC, bool IsWritable, bool EmitIr) {
  auto Key = SPIRV::irhandle_vkbuffer(ElemType, SC, IsWritable);
  if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
    return MI;

  bool ExplicitLayoutRequired = storageClassRequiresExplictLayout(SC);
  // We need to get the SPIR-V type for the element here, so we can add the
  // decoration to it.
  auto *T = StructType::create(ElemType);
  auto *BlockType =
      getOrCreateSPIRVType(T, MIRBuilder, SPIRV::AccessQualifier::None,
                           ExplicitLayoutRequired, EmitIr);

  buildOpDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
                  SPIRV::Decoration::Block, {});

  if (!IsWritable) {
    buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
                          SPIRV::Decoration::NonWritable, 0, {});
  }

  SPIRVType *R = getOrCreateSPIRVPointerTypeInternal(BlockType, MIRBuilder, SC);
  add(Key, R);
  return R;
}

SPIRVType *
SPIRVGlobalRegistry::getOrCreatePaddingType(MachineIRBuilder &MIRBuilder) {
  auto Key = SPIRV::irhandle_padding();
  if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
    return MI;
  auto *T = Type::getInt8Ty(MIRBuilder.getContext());
  SPIRVType *R = getOrCreateSPIRVIntegerType(8, MIRBuilder);
  finishCreatingSPIRVType(T, R);
  add(Key, R);
  return R;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateVulkanPushConstantType(
    MachineIRBuilder &MIRBuilder, Type *T) {
  const auto SC = SPIRV::StorageClass::PushConstant;

  auto Key = SPIRV::irhandle_vkbuffer(T, SC, /* IsWritable= */ false);
  if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
    return MI;

  // We need to get the SPIR-V type for the element here, so we can add the
  // decoration to it.
  auto *BlockType = getOrCreateSPIRVType(
      T, MIRBuilder, SPIRV::AccessQualifier::None,
      /* ExplicitLayoutRequired= */ true, /* EmitIr= */ false);

  buildOpDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
                  SPIRV::Decoration::Block, {});
  SPIRVType *R = BlockType;
  add(Key, R);
  return R;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateLayoutType(
    MachineIRBuilder &MIRBuilder, const TargetExtType *T, bool EmitIr) {
  auto Key = SPIRV::handle(T);
  if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
    return MI;

  StructType *ST = cast<StructType>(T->getTypeParameter(0));
  ArrayRef<uint32_t> Offsets = T->int_params().slice(1);
  assert(ST->getNumElements() == Offsets.size());

  StructOffsetDecorator Decorator = [&MIRBuilder, &Offsets](Register Reg) {
    for (uint32_t I = 0; I < Offsets.size(); ++I) {
      buildOpMemberDecorate(Reg, MIRBuilder, SPIRV::Decoration::Offset, I,
                            {Offsets[I]});
    }
  };

  // We need a new OpTypeStruct instruction because decorations will be
  // different from a struct with an explicit layout created from a different
  // entry point.
  SPIRVType *SPIRVStructType =
      getOpTypeStruct(ST, MIRBuilder, SPIRV::AccessQualifier::None,
                      std::move(Decorator), EmitIr);
  add(Key, SPIRVStructType);
  return SPIRVStructType;
}

SPIRVType *SPIRVGlobalRegistry::getImageType(
    const TargetExtType *ExtensionType,
    const SPIRV::AccessQualifier::AccessQualifier Qualifier,
    MachineIRBuilder &MIRBuilder) {
  assert(ExtensionType->getNumTypeParameters() == 1 &&
         "SPIR-V image builtin type must have sampled type parameter!");
  const SPIRVType *SampledType =
      getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder,
                           SPIRV::AccessQualifier::ReadWrite, true);
  assert((ExtensionType->getNumIntParameters() == 7 ||
          ExtensionType->getNumIntParameters() == 6) &&
         "Invalid number of parameters for SPIR-V image builtin!");

  SPIRV::AccessQualifier::AccessQualifier accessQualifier =
      SPIRV::AccessQualifier::None;
  if (ExtensionType->getNumIntParameters() == 7) {
    accessQualifier = Qualifier == SPIRV::AccessQualifier::WriteOnly
                          ? SPIRV::AccessQualifier::WriteOnly
                          : SPIRV::AccessQualifier::AccessQualifier(
                                ExtensionType->getIntParameter(6));
  }

  // Create or get an existing type from GlobalRegistry.
  SPIRVType *R = getOrCreateOpTypeImage(
      MIRBuilder, SampledType,
      SPIRV::Dim::Dim(ExtensionType->getIntParameter(0)),
      ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
      ExtensionType->getIntParameter(3), ExtensionType->getIntParameter(4),
      SPIRV::ImageFormat::ImageFormat(ExtensionType->getIntParameter(5)),
      accessQualifier);
  SPIRVToLLVMType[R] = ExtensionType;
  return R;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeImage(
    MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim,
    uint32_t Depth, uint32_t Arrayed, uint32_t Multisampled, uint32_t Sampled,
    SPIRV::ImageFormat::ImageFormat ImageFormat,
    SPIRV::AccessQualifier::AccessQualifier AccessQual) {
  auto Key = SPIRV::irhandle_image(SPIRVToLLVMType.lookup(SampledType), Dim,
                                   Depth, Arrayed, Multisampled, Sampled,
                                   ImageFormat, AccessQual);
  if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
    return MI;
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        auto MIB =
            MIRBuilder.buildInstr(SPIRV::OpTypeImage)
                .addDef(createTypeVReg(MIRBuilder))
                .addUse(getSPIRVTypeID(SampledType))
                .addImm(Dim)
                .addImm(Depth)   // Depth (whether or not it is a Depth image).
                .addImm(Arrayed) // Arrayed.
                .addImm(Multisampled) // Multisampled (0 = only single-sample).
                .addImm(Sampled)      // Sampled (0 = usage known at runtime).
                .addImm(ImageFormat);
        if (AccessQual != SPIRV::AccessQualifier::None)
          MIB.addImm(AccessQual);
        return MIB;
      });
  add(Key, NewMI);
  return NewMI;
}

SPIRVType *
SPIRVGlobalRegistry::getOrCreateOpTypeSampler(MachineIRBuilder &MIRBuilder) {
  auto Key = SPIRV::irhandle_sampler();
  const MachineFunction *MF = &MIRBuilder.getMF();
  if (const MachineInstr *MI = findMI(Key, MF))
    return MI;
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        return MIRBuilder.buildInstr(SPIRV::OpTypeSampler)
            .addDef(createTypeVReg(MIRBuilder));
      });
  add(Key, NewMI);
  return NewMI;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypePipe(
    MachineIRBuilder &MIRBuilder,
    SPIRV::AccessQualifier::AccessQualifier AccessQual) {
  auto Key = SPIRV::irhandle_pipe(AccessQual);
  if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
    return MI;
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        return MIRBuilder.buildInstr(SPIRV::OpTypePipe)
            .addDef(createTypeVReg(MIRBuilder))
            .addImm(AccessQual);
      });
  add(Key, NewMI);
  return NewMI;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeDeviceEvent(
    MachineIRBuilder &MIRBuilder) {
  auto Key = SPIRV::irhandle_event();
  if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
    return MI;
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        return MIRBuilder.buildInstr(SPIRV::OpTypeDeviceEvent)
            .addDef(createTypeVReg(MIRBuilder));
      });
  add(Key, NewMI);
  return NewMI;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeSampledImage(
    SPIRVType *ImageType, MachineIRBuilder &MIRBuilder) {
  auto Key = SPIRV::irhandle_sampled_image(
      SPIRVToLLVMType.lookup(MIRBuilder.getMF().getRegInfo().getVRegDef(
          ImageType->getOperand(1).getReg())),
      ImageType);
  if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
    return MI;
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        return MIRBuilder.buildInstr(SPIRV::OpTypeSampledImage)
            .addDef(createTypeVReg(MIRBuilder))
            .addUse(getSPIRVTypeID(ImageType));
      });
  add(Key, NewMI);
  return NewMI;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeCoopMatr(
    MachineIRBuilder &MIRBuilder, const TargetExtType *ExtensionType,
    const SPIRVType *ElemType, uint32_t Scope, uint32_t Rows, uint32_t Columns,
    uint32_t Use, bool EmitIR) {
  if (const MachineInstr *MI =
          findMI(ExtensionType, false, &MIRBuilder.getMF()))
    return MI;
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        SPIRVType *SpvTypeInt32 = getOrCreateSPIRVIntegerType(32, MIRBuilder);
        const Type *ET = getTypeForSPIRVType(ElemType);
        if (ET->isIntegerTy() && ET->getIntegerBitWidth() == 4 &&
            cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget())
                .canUseExtension(SPIRV::Extension::SPV_INTEL_int4)) {
          MIRBuilder.buildInstr(SPIRV::OpCapability)
              .addImm(SPIRV::Capability::Int4CooperativeMatrixINTEL);
        }
        return MIRBuilder.buildInstr(SPIRV::OpTypeCooperativeMatrixKHR)
            .addDef(createTypeVReg(MIRBuilder))
            .addUse(getSPIRVTypeID(ElemType))
            .addUse(buildConstantInt(Scope, MIRBuilder, SpvTypeInt32, EmitIR))
            .addUse(buildConstantInt(Rows, MIRBuilder, SpvTypeInt32, EmitIR))
            .addUse(buildConstantInt(Columns, MIRBuilder, SpvTypeInt32, EmitIR))
            .addUse(buildConstantInt(Use, MIRBuilder, SpvTypeInt32, EmitIR));
      });
  add(ExtensionType, false, NewMI);
  return NewMI;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeByOpcode(
    const Type *Ty, MachineIRBuilder &MIRBuilder, unsigned Opcode) {
  if (const MachineInstr *MI = findMI(Ty, false, &MIRBuilder.getMF()))
    return MI;
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        return MIRBuilder.buildInstr(Opcode).addDef(createTypeVReg(MIRBuilder));
      });
  add(Ty, false, NewMI);
  return NewMI;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateUnknownType(
    const Type *Ty, MachineIRBuilder &MIRBuilder, unsigned Opcode,
    const ArrayRef<MCOperand> Operands) {
  if (const MachineInstr *MI = findMI(Ty, false, &MIRBuilder.getMF()))
    return MI;
  Register ResVReg = createTypeVReg(MIRBuilder);
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        MachineInstrBuilder MIB = MIRBuilder.buildInstr(SPIRV::UNKNOWN_type)
                                      .addDef(ResVReg)
                                      .addImm(Opcode);
        for (MCOperand Operand : Operands) {
          if (Operand.isReg()) {
            MIB.addUse(Operand.getReg());
          } else if (Operand.isImm()) {
            MIB.addImm(Operand.getImm());
          }
        }
        return MIB;
      });
  add(Ty, false, NewMI);
  return NewMI;
}

// Returns nullptr if unable to recognize SPIRV type name
SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVTypeByName(
    StringRef TypeStr, MachineIRBuilder &MIRBuilder, bool EmitIR,
    SPIRV::StorageClass::StorageClass SC,
    SPIRV::AccessQualifier::AccessQualifier AQ) {
  unsigned VecElts = 0;
  auto &Ctx = MIRBuilder.getMF().getFunction().getContext();

  // Parse strings representing either a SPIR-V or OpenCL builtin type.
  if (hasBuiltinTypePrefix(TypeStr))
    return getOrCreateSPIRVType(SPIRV::parseBuiltinTypeNameToTargetExtType(
                                    TypeStr.str(), MIRBuilder.getContext()),
                                MIRBuilder, AQ, false, true);

  // Parse type name in either "typeN" or "type vector[N]" format, where
  // N is the number of elements of the vector.
  Type *Ty;

  Ty = parseBasicTypeName(TypeStr, Ctx);
  if (!Ty)
    // Unable to recognize SPIRV type name
    return nullptr;

  const SPIRVType *SpirvTy =
      getOrCreateSPIRVType(Ty, MIRBuilder, AQ, false, true);

  // Handle "type*" or  "type* vector[N]".
  if (TypeStr.consume_front("*"))
    SpirvTy = getOrCreateSPIRVPointerType(Ty, MIRBuilder, SC);

  // Handle "typeN*" or  "type vector[N]*".
  bool IsPtrToVec = TypeStr.consume_back("*");

  if (TypeStr.consume_front(" vector[")) {
    TypeStr = TypeStr.substr(0, TypeStr.find(']'));
  }
  TypeStr.getAsInteger(10, VecElts);
  if (VecElts > 0)
    SpirvTy = getOrCreateSPIRVVectorType(SpirvTy, VecElts, MIRBuilder, EmitIR);

  if (IsPtrToVec)
    SpirvTy = getOrCreateSPIRVPointerType(SpirvTy, MIRBuilder, SC);

  return SpirvTy;
}

SPIRVType *
SPIRVGlobalRegistry::getOrCreateSPIRVIntegerType(unsigned BitWidth,
                                                 MachineIRBuilder &MIRBuilder) {
  return getOrCreateSPIRVType(
      IntegerType::get(MIRBuilder.getMF().getFunction().getContext(), BitWidth),
      MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false, true);
}

SPIRVType *SPIRVGlobalRegistry::finishCreatingSPIRVType(const Type *LLVMTy,
                                                        SPIRVType *SpirvType) {
  assert(CurMF == SpirvType->getMF());
  VRegToTypeMap[CurMF][getSPIRVTypeID(SpirvType)] = SpirvType;
  SPIRVToLLVMType[SpirvType] = unifyPtrType(LLVMTy);
  return SpirvType;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVType(unsigned BitWidth,
                                                     MachineInstr &I,
                                                     const SPIRVInstrInfo &TII,
                                                     unsigned SPIRVOPcode,
                                                     Type *Ty) {
  if (const MachineInstr *MI = findMI(Ty, false, CurMF))
    return MI;
  MachineBasicBlock &DepMBB = I.getMF()->front();
  MachineIRBuilder MIRBuilder(DepMBB, DepMBB.getFirstNonPHI());
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        auto NewTypeMI = BuildMI(MIRBuilder.getMBB(), *MIRBuilder.getInsertPt(),
                                 MIRBuilder.getDL(), TII.get(SPIRVOPcode))
                             .addDef(createTypeVReg(CurMF->getRegInfo()))
                             .addImm(BitWidth);
        // Don't add Encoding to FP type
        if (!Ty->isFloatTy()) {
          return NewTypeMI.addImm(0);
        } else {
          return NewTypeMI;
        }
      });
  add(Ty, false, NewMI);
  return finishCreatingSPIRVType(Ty, NewMI);
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVIntegerType(
    unsigned BitWidth, MachineInstr &I, const SPIRVInstrInfo &TII) {
  // Maybe adjust bit width to keep DuplicateTracker consistent. Without
  // such an adjustment SPIRVGlobalRegistry::getOpTypeInt() could create, for
  // example, the same "OpTypeInt 8" type for a series of LLVM integer types
  // with number of bits less than 8, causing duplicate type definitions.
  if (BitWidth > 1)
    BitWidth = adjustOpTypeIntWidth(BitWidth);
  Type *LLVMTy = IntegerType::get(CurMF->getFunction().getContext(), BitWidth);
  return getOrCreateSPIRVType(BitWidth, I, TII, SPIRV::OpTypeInt, LLVMTy);
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVFloatType(
    unsigned BitWidth, MachineInstr &I, const SPIRVInstrInfo &TII) {
  LLVMContext &Ctx = CurMF->getFunction().getContext();
  Type *LLVMTy;
  switch (BitWidth) {
  case 16:
    LLVMTy = Type::getHalfTy(Ctx);
    break;
  case 32:
    LLVMTy = Type::getFloatTy(Ctx);
    break;
  case 64:
    LLVMTy = Type::getDoubleTy(Ctx);
    break;
  default:
    llvm_unreachable("Bit width is of unexpected size.");
  }
  return getOrCreateSPIRVType(BitWidth, I, TII, SPIRV::OpTypeFloat, LLVMTy);
}

SPIRVType *
SPIRVGlobalRegistry::getOrCreateSPIRVBoolType(MachineIRBuilder &MIRBuilder,
                                              bool EmitIR) {
  return getOrCreateSPIRVType(
      IntegerType::get(MIRBuilder.getMF().getFunction().getContext(), 1),
      MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false, EmitIR);
}

SPIRVType *
SPIRVGlobalRegistry::getOrCreateSPIRVBoolType(MachineInstr &I,
                                              const SPIRVInstrInfo &TII) {
  Type *Ty = IntegerType::get(CurMF->getFunction().getContext(), 1);
  if (const MachineInstr *MI = findMI(Ty, false, CurMF))
    return MI;
  MachineBasicBlock &DepMBB = I.getMF()->front();
  MachineIRBuilder MIRBuilder(DepMBB, DepMBB.getFirstNonPHI());
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        return BuildMI(MIRBuilder.getMBB(), *MIRBuilder.getInsertPt(),
                       MIRBuilder.getDL(), TII.get(SPIRV::OpTypeBool))
            .addDef(createTypeVReg(CurMF->getRegInfo()));
      });
  add(Ty, false, NewMI);
  return finishCreatingSPIRVType(Ty, NewMI);
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVVectorType(
    SPIRVType *BaseType, unsigned NumElements, MachineIRBuilder &MIRBuilder,
    bool EmitIR) {
  return getOrCreateSPIRVType(
      FixedVectorType::get(const_cast<Type *>(getTypeForSPIRVType(BaseType)),
                           NumElements),
      MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false, EmitIR);
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVVectorType(
    SPIRVType *BaseType, unsigned NumElements, MachineInstr &I,
    const SPIRVInstrInfo &TII) {
  Type *Ty = FixedVectorType::get(
      const_cast<Type *>(getTypeForSPIRVType(BaseType)), NumElements);
  if (const MachineInstr *MI = findMI(Ty, false, CurMF))
    return MI;
  MachineInstr *DepMI = const_cast<MachineInstr *>(BaseType);
  MachineIRBuilder MIRBuilder(*DepMI->getParent(), DepMI->getIterator());
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        return BuildMI(MIRBuilder.getMBB(), *MIRBuilder.getInsertPt(),
                       MIRBuilder.getDL(), TII.get(SPIRV::OpTypeVector))
            .addDef(createTypeVReg(CurMF->getRegInfo()))
            .addUse(getSPIRVTypeID(BaseType))
            .addImm(NumElements);
      });
  add(Ty, false, NewMI);
  return finishCreatingSPIRVType(Ty, NewMI);
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
    const Type *BaseType, MachineInstr &I,
    SPIRV::StorageClass::StorageClass SC) {
  MachineIRBuilder MIRBuilder(I);
  return getOrCreateSPIRVPointerType(BaseType, MIRBuilder, SC);
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
    const Type *BaseType, MachineIRBuilder &MIRBuilder,
    SPIRV::StorageClass::StorageClass SC) {
  // TODO: Need to check if EmitIr should always be true.
  SPIRVType *SpirvBaseType = getOrCreateSPIRVType(
      BaseType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
      storageClassRequiresExplictLayout(SC), true);
  assert(SpirvBaseType);
  return getOrCreateSPIRVPointerTypeInternal(SpirvBaseType, MIRBuilder, SC);
}

SPIRVType *SPIRVGlobalRegistry::changePointerStorageClass(
    SPIRVType *PtrType, SPIRV::StorageClass::StorageClass SC, MachineInstr &I) {
  [[maybe_unused]] SPIRV::StorageClass::StorageClass OldSC =
      getPointerStorageClass(PtrType);
  assert(storageClassRequiresExplictLayout(OldSC) ==
         storageClassRequiresExplictLayout(SC));

  SPIRVType *PointeeType = getPointeeType(PtrType);
  MachineIRBuilder MIRBuilder(I);
  return getOrCreateSPIRVPointerTypeInternal(PointeeType, MIRBuilder, SC);
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
    SPIRVType *BaseType, MachineIRBuilder &MIRBuilder,
    SPIRV::StorageClass::StorageClass SC) {
  const Type *LLVMType = getTypeForSPIRVType(BaseType);
  assert(!storageClassRequiresExplictLayout(SC));
  SPIRVType *R = getOrCreateSPIRVPointerType(LLVMType, MIRBuilder, SC);
  assert(
      getPointeeType(R) == BaseType &&
      "The base type was not correctly laid out for the given storage class.");
  return R;
}

SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVPointerTypeInternal(
    SPIRVType *BaseType, MachineIRBuilder &MIRBuilder,
    SPIRV::StorageClass::StorageClass SC) {
  const Type *PointerElementType = getTypeForSPIRVType(BaseType);
  unsigned AddressSpace = storageClassToAddressSpace(SC);
  if (const MachineInstr *MI = findMI(PointerElementType, AddressSpace, CurMF))
    return MI;
  Type *Ty = TypedPointerType::get(const_cast<Type *>(PointerElementType),
                                   AddressSpace);
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        return BuildMI(MIRBuilder.getMBB(), MIRBuilder.getInsertPt(),
                       MIRBuilder.getDebugLoc(),
                       MIRBuilder.getTII().get(SPIRV::OpTypePointer))
            .addDef(createTypeVReg(CurMF->getRegInfo()))
            .addImm(static_cast<uint32_t>(SC))
            .addUse(getSPIRVTypeID(BaseType));
      });
  add(PointerElementType, AddressSpace, NewMI);
  return finishCreatingSPIRVType(Ty, NewMI);
}

Register SPIRVGlobalRegistry::getOrCreateUndef(MachineInstr &I,
                                               SPIRVType *SpvType,
                                               const SPIRVInstrInfo &TII) {
  UndefValue *UV =
      UndefValue::get(const_cast<Type *>(getTypeForSPIRVType(SpvType)));
  Register Res = find(UV, CurMF);
  if (Res.isValid())
    return Res;

  LLT LLTy = LLT::scalar(64);
  Res = CurMF->getRegInfo().createGenericVirtualRegister(LLTy);
  CurMF->getRegInfo().setRegClass(Res, &SPIRV::iIDRegClass);
  assignSPIRVTypeToVReg(SpvType, Res, *CurMF);

  MachineInstr *DepMI = const_cast<MachineInstr *>(SpvType);
  MachineIRBuilder MIRBuilder(*DepMI->getParent(), DepMI->getIterator());
  const MachineInstr *NewMI =
      createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
        auto MIB = BuildMI(MIRBuilder.getMBB(), *MIRBuilder.getInsertPt(),
                           MIRBuilder.getDL(), TII.get(SPIRV::OpUndef))
                       .addDef(Res)
                       .addUse(getSPIRVTypeID(SpvType));
        const auto &ST = CurMF->getSubtarget();
        constrainSelectedInstRegOperands(*MIB, *ST.getInstrInfo(),
                                         *ST.getRegisterInfo(),
                                         *ST.getRegBankInfo());
        return MIB;
      });
  add(UV, NewMI);
  return Res;
}

const TargetRegisterClass *
SPIRVGlobalRegistry::getRegClass(SPIRVType *SpvType) const {
  unsigned Opcode = SpvType->getOpcode();
  switch (Opcode) {
  case SPIRV::OpTypeFloat:
    return &SPIRV::fIDRegClass;
  case SPIRV::OpTypePointer:
    return &SPIRV::pIDRegClass;
  case SPIRV::OpTypeVector: {
    SPIRVType *ElemType = getSPIRVTypeForVReg(SpvType->getOperand(1).getReg());
    unsigned ElemOpcode = ElemType ? ElemType->getOpcode() : 0;
    if (ElemOpcode == SPIRV::OpTypeFloat)
      return &SPIRV::vfIDRegClass;
    if (ElemOpcode == SPIRV::OpTypePointer)
      return &SPIRV::vpIDRegClass;
    return &SPIRV::vIDRegClass;
  }
  }
  return &SPIRV::iIDRegClass;
}

inline unsigned getAS(SPIRVType *SpvType) {
  return storageClassToAddressSpace(
      static_cast<SPIRV::StorageClass::StorageClass>(
          SpvType->getOperand(1).getImm()));
}

LLT SPIRVGlobalRegistry::getRegType(SPIRVType *SpvType) const {
  unsigned Opcode = SpvType ? SpvType->getOpcode() : 0;
  switch (Opcode) {
  case SPIRV::OpTypeInt:
  case SPIRV::OpTypeFloat:
  case SPIRV::OpTypeBool:
    return LLT::scalar(getScalarOrVectorBitWidth(SpvType));
  case SPIRV::OpTypePointer:
    return LLT::pointer(getAS(SpvType), getPointerSize());
  case SPIRV::OpTypeVector: {
    SPIRVType *ElemType = getSPIRVTypeForVReg(SpvType->getOperand(1).getReg());
    LLT ET;
    switch (ElemType ? ElemType->getOpcode() : 0) {
    case SPIRV::OpTypePointer:
      ET = LLT::pointer(getAS(ElemType), getPointerSize());
      break;
    case SPIRV::OpTypeInt:
    case SPIRV::OpTypeFloat:
    case SPIRV::OpTypeBool:
      ET = LLT::scalar(getScalarOrVectorBitWidth(ElemType));
      break;
    default:
      ET = LLT::scalar(64);
    }
    return LLT::fixed_vector(
        static_cast<unsigned>(SpvType->getOperand(2).getImm()), ET);
  }
  }
  return LLT::scalar(64);
}

// Aliasing list MD contains several scope MD nodes whithin it. Each scope MD
// has a selfreference and an extra MD node for aliasing domain and also it
// can contain an optional string operand. Domain MD contains a self-reference
// with an optional string operand. Here we unfold the list, creating SPIR-V
// aliasing instructions.
// TODO: add support for an optional string operand.
MachineInstr *SPIRVGlobalRegistry::getOrAddMemAliasingINTELInst(
    MachineIRBuilder &MIRBuilder, const MDNode *AliasingListMD) {
  if (AliasingListMD->getNumOperands() == 0)
    return nullptr;
  if (auto L = AliasInstMDMap.find(AliasingListMD); L != AliasInstMDMap.end())
    return L->second;

  SmallVector<MachineInstr *> ScopeList;
  MachineRegisterInfo *MRI = MIRBuilder.getMRI();
  for (const MDOperand &MDListOp : AliasingListMD->operands()) {
    if (MDNode *ScopeMD = dyn_cast<MDNode>(MDListOp)) {
      if (ScopeMD->getNumOperands() < 2)
        return nullptr;
      MDNode *DomainMD = dyn_cast<MDNode>(ScopeMD->getOperand(1));
      if (!DomainMD)
        return nullptr;
      auto *Domain = [&] {
        auto D = AliasInstMDMap.find(DomainMD);
        if (D != AliasInstMDMap.end())
          return D->second;
        const Register Ret = MRI->createVirtualRegister(&SPIRV::IDRegClass);
        auto MIB =
            MIRBuilder.buildInstr(SPIRV::OpAliasDomainDeclINTEL).addDef(Ret);
        return MIB.getInstr();
      }();
      AliasInstMDMap.insert(std::make_pair(DomainMD, Domain));
      auto *Scope = [&] {
        auto S = AliasInstMDMap.find(ScopeMD);
        if (S != AliasInstMDMap.end())
          return S->second;
        const Register Ret = MRI->createVirtualRegister(&SPIRV::IDRegClass);
        auto MIB = MIRBuilder.buildInstr(SPIRV::OpAliasScopeDeclINTEL)
                       .addDef(Ret)
                       .addUse(Domain->getOperand(0).getReg());
        return MIB.getInstr();
      }();
      AliasInstMDMap.insert(std::make_pair(ScopeMD, Scope));
      ScopeList.push_back(Scope);
    }
  }

  const Register Ret = MRI->createVirtualRegister(&SPIRV::IDRegClass);
  auto MIB =
      MIRBuilder.buildInstr(SPIRV::OpAliasScopeListDeclINTEL).addDef(Ret);
  for (auto *Scope : ScopeList)
    MIB.addUse(Scope->getOperand(0).getReg());
  auto List = MIB.getInstr();
  AliasInstMDMap.insert(std::make_pair(AliasingListMD, List));
  return List;
}

void SPIRVGlobalRegistry::buildMemAliasingOpDecorate(
    Register Reg, MachineIRBuilder &MIRBuilder, uint32_t Dec,
    const MDNode *AliasingListMD) {
  MachineInstr *AliasList =
      getOrAddMemAliasingINTELInst(MIRBuilder, AliasingListMD);
  if (!AliasList)
    return;
  MIRBuilder.buildInstr(SPIRV::OpDecorate)
      .addUse(Reg)
      .addImm(Dec)
      .addUse(AliasList->getOperand(0).getReg());
}
void SPIRVGlobalRegistry::replaceAllUsesWith(Value *Old, Value *New,
                                             bool DeleteOld) {
  Old->replaceAllUsesWith(New);
  updateIfExistDeducedElementType(Old, New, DeleteOld);
  updateIfExistAssignPtrTypeInstr(Old, New, DeleteOld);
}

void SPIRVGlobalRegistry::buildAssignType(IRBuilder<> &B, Type *Ty,
                                          Value *Arg) {
  Value *OfType = getNormalizedPoisonValue(Ty);
  CallInst *AssignCI = nullptr;
  if (Arg->getType()->isAggregateType() && Ty->isAggregateType() &&
      allowEmitFakeUse(Arg)) {
    LLVMContext &Ctx = Arg->getContext();
    SmallVector<Metadata *, 2> ArgMDs{
        MDNode::get(Ctx, ValueAsMetadata::getConstant(OfType)),
        MDString::get(Ctx, Arg->getName())};
    B.CreateIntrinsic(Intrinsic::spv_value_md,
                      {MetadataAsValue::get(Ctx, MDTuple::get(Ctx, ArgMDs))});
    AssignCI = B.CreateIntrinsic(Intrinsic::fake_use, {Arg});
  } else {
    AssignCI = buildIntrWithMD(Intrinsic::spv_assign_type, {Arg->getType()},
                               OfType, Arg, {}, B);
  }
  addAssignPtrTypeInstr(Arg, AssignCI);
}

void SPIRVGlobalRegistry::buildAssignPtr(IRBuilder<> &B, Type *ElemTy,
                                         Value *Arg) {
  Value *OfType = PoisonValue::get(ElemTy);
  CallInst *AssignPtrTyCI = findAssignPtrTypeInstr(Arg);
  Function *CurrF =
      B.GetInsertBlock() ? B.GetInsertBlock()->getParent() : nullptr;
  if (AssignPtrTyCI == nullptr ||
      AssignPtrTyCI->getParent()->getParent() != CurrF) {
    AssignPtrTyCI = buildIntrWithMD(
        Intrinsic::spv_assign_ptr_type, {Arg->getType()}, OfType, Arg,
        {B.getInt32(getPointerAddressSpace(Arg->getType()))}, B);
    addDeducedElementType(AssignPtrTyCI, ElemTy);
    addDeducedElementType(Arg, ElemTy);
    addAssignPtrTypeInstr(Arg, AssignPtrTyCI);
  } else {
    updateAssignType(AssignPtrTyCI, Arg, OfType);
  }
}

void SPIRVGlobalRegistry::updateAssignType(CallInst *AssignCI, Value *Arg,
                                           Value *OfType) {
  AssignCI->setArgOperand(1, buildMD(OfType));
  if (cast<IntrinsicInst>(AssignCI)->getIntrinsicID() !=
      Intrinsic::spv_assign_ptr_type)
    return;

  // update association with the pointee type
  Type *ElemTy = OfType->getType();
  addDeducedElementType(AssignCI, ElemTy);
  addDeducedElementType(Arg, ElemTy);
}

void SPIRVGlobalRegistry::addStructOffsetDecorations(
    Register Reg, StructType *Ty, MachineIRBuilder &MIRBuilder) {
  DataLayout DL;
  ArrayRef<TypeSize> Offsets = DL.getStructLayout(Ty)->getMemberOffsets();
  for (uint32_t I = 0; I < Ty->getNumElements(); ++I) {
    buildOpMemberDecorate(Reg, MIRBuilder, SPIRV::Decoration::Offset, I,
                          {static_cast<uint32_t>(Offsets[I])});
  }
}

void SPIRVGlobalRegistry::addArrayStrideDecorations(
    Register Reg, Type *ElementType, MachineIRBuilder &MIRBuilder) {
  uint32_t SizeInBytes = DataLayout().getTypeSizeInBits(ElementType) / 8;
  buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::ArrayStride,
                  {SizeInBytes});
}

bool SPIRVGlobalRegistry::hasBlockDecoration(SPIRVType *Type) const {
  Register Def = getSPIRVTypeID(Type);
  for (const MachineInstr &Use :
       Type->getMF()->getRegInfo().use_instructions(Def)) {
    if (Use.getOpcode() != SPIRV::OpDecorate)
      continue;

    if (Use.getOperand(1).getImm() == SPIRV::Decoration::Block)
      return true;
  }
  return false;
}
