
Fix code quality issues reported by static analysis tool, such as: - Rule of Three/Five. - Dereference after null check. - Unchecked return value. - Variable copied when it could be moved.
2091 lines
83 KiB
C++
2091 lines
83 KiB
C++
//===-- 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/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 {
|
|
if (Width > 64)
|
|
report_fatal_error("Unsupported integer width!");
|
|
const SPIRVSubtarget &ST = cast<SPIRVSubtarget>(CurMF->getSubtarget());
|
|
if (ST.canUseExtension(
|
|
SPIRV::Extension::SPV_INTEL_arbitrary_precision_integers) ||
|
|
ST.canUseExtension(SPIRV::Extension::SPV_INTEL_int4))
|
|
return Width;
|
|
if (Width <= 8)
|
|
Width = 8;
|
|
else if (Width <= 16)
|
|
Width = 16;
|
|
else if (Width <= 32)
|
|
Width = 32;
|
|
else
|
|
Width = 64;
|
|
return 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_INTEL_arbitrary_precision_integers)) {
|
|
MIRBuilder.buildInstr(SPIRV::OpExtension)
|
|
.addImm(SPIRV::Extension::SPV_INTEL_arbitrary_precision_integers);
|
|
MIRBuilder.buildInstr(SPIRV::OpCapability)
|
|
.addImm(SPIRV::Capability::ArbitraryPrecisionIntegersINTEL);
|
|
}
|
|
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::getOpTypeVoid(MachineIRBuilder &MIRBuilder) {
|
|
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
|
|
return MIRBuilder.buildInstr(SPIRV::OpTypeVoid)
|
|
.addDef(createTypeVReg(MIRBuilder));
|
|
});
|
|
}
|
|
|
|
void SPIRVGlobalRegistry::invalidateMachineInstr(MachineInstr *MI) {
|
|
// TODO:
|
|
// - review other data structure wrt. possible issues related to removal
|
|
// of a machine instruction during instruction selection.
|
|
const MachineFunction *MF = MI->getMF();
|
|
auto It = LastInsertedTypeMap.find(MF);
|
|
if (It == LastInsertedTypeMap.end())
|
|
return;
|
|
if (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);
|
|
assignFloatTypeToVReg(BitWidth, Res, I, TII);
|
|
|
|
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(APInt(BitWidth, CI->getZExtValue()), 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));
|
|
auto *const CI = ConstantInt::get(const_cast<IntegerType *>(Ty), Val);
|
|
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, bool HasLinkageTy,
|
|
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 != 0)
|
|
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 (HasLinkageTy)
|
|
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);
|
|
|
|
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, false,
|
|
SPIRV::LinkageType::Import, 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 {
|
|
assert(ST.isShader() && "Runtime arrays are not allowed in non-shader "
|
|
"SPIR-V modules.");
|
|
if (!ST.isShader())
|
|
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) {
|
|
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(
|
|
SPIRVType *RetType, const SmallVectorImpl<SPIRVType *> &ArgTypes,
|
|
MachineIRBuilder &MIRBuilder) {
|
|
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(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())
|
|
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(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::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) {
|
|
return BuildMI(MIRBuilder.getMBB(), *MIRBuilder.getInsertPt(),
|
|
MIRBuilder.getDL(), TII.get(SPIRVOPcode))
|
|
.addDef(createTypeVReg(CurMF->getRegInfo()))
|
|
.addImm(BitWidth)
|
|
.addImm(0);
|
|
});
|
|
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;
|
|
}
|