181ad6265SDimitry Andric //===- SPIRVInstructionSelector.cpp ------------------------------*- C++ -*-==// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric // 981ad6265SDimitry Andric // This file implements the targeting of the InstructionSelector class for 1081ad6265SDimitry Andric // SPIRV. 1181ad6265SDimitry Andric // TODO: This should be generated by TableGen. 1281ad6265SDimitry Andric // 1381ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1481ad6265SDimitry Andric 155f757f3fSDimitry Andric #include "MCTargetDesc/SPIRVBaseInfo.h" 165f757f3fSDimitry Andric #include "MCTargetDesc/SPIRVMCTargetDesc.h" 1781ad6265SDimitry Andric #include "SPIRV.h" 1881ad6265SDimitry Andric #include "SPIRVGlobalRegistry.h" 1981ad6265SDimitry Andric #include "SPIRVInstrInfo.h" 2081ad6265SDimitry Andric #include "SPIRVRegisterBankInfo.h" 2181ad6265SDimitry Andric #include "SPIRVRegisterInfo.h" 2281ad6265SDimitry Andric #include "SPIRVTargetMachine.h" 2381ad6265SDimitry Andric #include "SPIRVUtils.h" 2481ad6265SDimitry Andric #include "llvm/ADT/APFloat.h" 2506c3fb27SDimitry Andric #include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h" 265f757f3fSDimitry Andric #include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h" 2781ad6265SDimitry Andric #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" 2881ad6265SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 29*0fca6ea1SDimitry Andric #include "llvm/CodeGen/MachineModuleInfoImpls.h" 3081ad6265SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 31*0fca6ea1SDimitry Andric #include "llvm/CodeGen/TargetOpcodes.h" 3281ad6265SDimitry Andric #include "llvm/IR/IntrinsicsSPIRV.h" 3381ad6265SDimitry Andric #include "llvm/Support/Debug.h" 3481ad6265SDimitry Andric 35*0fca6ea1SDimitry Andric namespace llvm { 36*0fca6ea1SDimitry Andric 37*0fca6ea1SDimitry Andric class SPIRVMachineModuleInfo : public MachineModuleInfoImpl { 38*0fca6ea1SDimitry Andric public: 39*0fca6ea1SDimitry Andric SyncScope::ID Work_ItemSSID; 40*0fca6ea1SDimitry Andric SyncScope::ID WorkGroupSSID; 41*0fca6ea1SDimitry Andric SyncScope::ID DeviceSSID; 42*0fca6ea1SDimitry Andric SyncScope::ID AllSVMDevicesSSID; 43*0fca6ea1SDimitry Andric SyncScope::ID SubGroupSSID; 44*0fca6ea1SDimitry Andric 45*0fca6ea1SDimitry Andric SPIRVMachineModuleInfo(const MachineModuleInfo &MMI) { 46*0fca6ea1SDimitry Andric LLVMContext &CTX = MMI.getModule()->getContext(); 47*0fca6ea1SDimitry Andric Work_ItemSSID = CTX.getOrInsertSyncScopeID("work_item"); 48*0fca6ea1SDimitry Andric WorkGroupSSID = CTX.getOrInsertSyncScopeID("workgroup"); 49*0fca6ea1SDimitry Andric DeviceSSID = CTX.getOrInsertSyncScopeID("device"); 50*0fca6ea1SDimitry Andric AllSVMDevicesSSID = CTX.getOrInsertSyncScopeID("all_svm_devices"); 51*0fca6ea1SDimitry Andric SubGroupSSID = CTX.getOrInsertSyncScopeID("sub_group"); 52*0fca6ea1SDimitry Andric } 53*0fca6ea1SDimitry Andric }; 54*0fca6ea1SDimitry Andric 55*0fca6ea1SDimitry Andric } // end namespace llvm 56*0fca6ea1SDimitry Andric 5781ad6265SDimitry Andric #define DEBUG_TYPE "spirv-isel" 5881ad6265SDimitry Andric 5981ad6265SDimitry Andric using namespace llvm; 60bdd1243dSDimitry Andric namespace CL = SPIRV::OpenCLExtInst; 61bdd1243dSDimitry Andric namespace GL = SPIRV::GLSLExtInst; 62bdd1243dSDimitry Andric 63bdd1243dSDimitry Andric using ExtInstList = 64bdd1243dSDimitry Andric std::vector<std::pair<SPIRV::InstructionSet::InstructionSet, uint32_t>>; 6581ad6265SDimitry Andric 6681ad6265SDimitry Andric namespace { 6781ad6265SDimitry Andric 6881ad6265SDimitry Andric #define GET_GLOBALISEL_PREDICATE_BITSET 6981ad6265SDimitry Andric #include "SPIRVGenGlobalISel.inc" 7081ad6265SDimitry Andric #undef GET_GLOBALISEL_PREDICATE_BITSET 7181ad6265SDimitry Andric 7281ad6265SDimitry Andric class SPIRVInstructionSelector : public InstructionSelector { 7381ad6265SDimitry Andric const SPIRVSubtarget &STI; 7481ad6265SDimitry Andric const SPIRVInstrInfo &TII; 7581ad6265SDimitry Andric const SPIRVRegisterInfo &TRI; 7681ad6265SDimitry Andric const RegisterBankInfo &RBI; 7781ad6265SDimitry Andric SPIRVGlobalRegistry &GR; 7881ad6265SDimitry Andric MachineRegisterInfo *MRI; 79*0fca6ea1SDimitry Andric SPIRVMachineModuleInfo *MMI = nullptr; 80*0fca6ea1SDimitry Andric 81*0fca6ea1SDimitry Andric /// We need to keep track of the number we give to anonymous global values to 82*0fca6ea1SDimitry Andric /// generate the same name every time when this is needed. 83*0fca6ea1SDimitry Andric mutable DenseMap<const GlobalValue *, unsigned> UnnamedGlobalIDs; 8481ad6265SDimitry Andric 8581ad6265SDimitry Andric public: 8681ad6265SDimitry Andric SPIRVInstructionSelector(const SPIRVTargetMachine &TM, 8781ad6265SDimitry Andric const SPIRVSubtarget &ST, 8881ad6265SDimitry Andric const RegisterBankInfo &RBI); 8981ad6265SDimitry Andric void setupMF(MachineFunction &MF, GISelKnownBits *KB, 9006c3fb27SDimitry Andric CodeGenCoverage *CoverageInfo, ProfileSummaryInfo *PSI, 9181ad6265SDimitry Andric BlockFrequencyInfo *BFI) override; 9281ad6265SDimitry Andric // Common selection code. Instruction-specific selection occurs in spvSelect. 9381ad6265SDimitry Andric bool select(MachineInstr &I) override; 9481ad6265SDimitry Andric static const char *getName() { return DEBUG_TYPE; } 9581ad6265SDimitry Andric 9681ad6265SDimitry Andric #define GET_GLOBALISEL_PREDICATES_DECL 9781ad6265SDimitry Andric #include "SPIRVGenGlobalISel.inc" 9881ad6265SDimitry Andric #undef GET_GLOBALISEL_PREDICATES_DECL 9981ad6265SDimitry Andric 10081ad6265SDimitry Andric #define GET_GLOBALISEL_TEMPORARIES_DECL 10181ad6265SDimitry Andric #include "SPIRVGenGlobalISel.inc" 10281ad6265SDimitry Andric #undef GET_GLOBALISEL_TEMPORARIES_DECL 10381ad6265SDimitry Andric 10481ad6265SDimitry Andric private: 10581ad6265SDimitry Andric // tblgen-erated 'select' implementation, used as the initial selector for 10681ad6265SDimitry Andric // the patterns that don't require complex C++. 10781ad6265SDimitry Andric bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const; 10881ad6265SDimitry Andric 10981ad6265SDimitry Andric // All instruction-specific selection that didn't happen in "select()". 11081ad6265SDimitry Andric // Is basically a large Switch/Case delegating to all other select method. 11181ad6265SDimitry Andric bool spvSelect(Register ResVReg, const SPIRVType *ResType, 11281ad6265SDimitry Andric MachineInstr &I) const; 11381ad6265SDimitry Andric 11481ad6265SDimitry Andric bool selectGlobalValue(Register ResVReg, MachineInstr &I, 11581ad6265SDimitry Andric const MachineInstr *Init = nullptr) const; 11681ad6265SDimitry Andric 11781ad6265SDimitry Andric bool selectUnOpWithSrc(Register ResVReg, const SPIRVType *ResType, 11881ad6265SDimitry Andric MachineInstr &I, Register SrcReg, 11981ad6265SDimitry Andric unsigned Opcode) const; 12081ad6265SDimitry Andric bool selectUnOp(Register ResVReg, const SPIRVType *ResType, MachineInstr &I, 12181ad6265SDimitry Andric unsigned Opcode) const; 12281ad6265SDimitry Andric 123*0fca6ea1SDimitry Andric bool selectBitcast(Register ResVReg, const SPIRVType *ResType, 124*0fca6ea1SDimitry Andric MachineInstr &I) const; 125*0fca6ea1SDimitry Andric 12681ad6265SDimitry Andric bool selectLoad(Register ResVReg, const SPIRVType *ResType, 12781ad6265SDimitry Andric MachineInstr &I) const; 12881ad6265SDimitry Andric bool selectStore(MachineInstr &I) const; 12981ad6265SDimitry Andric 130*0fca6ea1SDimitry Andric bool selectStackSave(Register ResVReg, const SPIRVType *ResType, 131*0fca6ea1SDimitry Andric MachineInstr &I) const; 132*0fca6ea1SDimitry Andric bool selectStackRestore(MachineInstr &I) const; 133*0fca6ea1SDimitry Andric 13481ad6265SDimitry Andric bool selectMemOperation(Register ResVReg, MachineInstr &I) const; 13581ad6265SDimitry Andric 13681ad6265SDimitry Andric bool selectAtomicRMW(Register ResVReg, const SPIRVType *ResType, 137*0fca6ea1SDimitry Andric MachineInstr &I, unsigned NewOpcode, 138*0fca6ea1SDimitry Andric unsigned NegateOpcode = 0) const; 13981ad6265SDimitry Andric 14081ad6265SDimitry Andric bool selectAtomicCmpXchg(Register ResVReg, const SPIRVType *ResType, 14181ad6265SDimitry Andric MachineInstr &I) const; 14281ad6265SDimitry Andric 14381ad6265SDimitry Andric bool selectFence(MachineInstr &I) const; 14481ad6265SDimitry Andric 14581ad6265SDimitry Andric bool selectAddrSpaceCast(Register ResVReg, const SPIRVType *ResType, 14681ad6265SDimitry Andric MachineInstr &I) const; 14781ad6265SDimitry Andric 148*0fca6ea1SDimitry Andric bool selectAnyOrAll(Register ResVReg, const SPIRVType *ResType, 149*0fca6ea1SDimitry Andric MachineInstr &I, unsigned OpType) const; 150*0fca6ea1SDimitry Andric 151*0fca6ea1SDimitry Andric bool selectAll(Register ResVReg, const SPIRVType *ResType, 152*0fca6ea1SDimitry Andric MachineInstr &I) const; 153*0fca6ea1SDimitry Andric 154*0fca6ea1SDimitry Andric bool selectAny(Register ResVReg, const SPIRVType *ResType, 155*0fca6ea1SDimitry Andric MachineInstr &I) const; 156*0fca6ea1SDimitry Andric 15781ad6265SDimitry Andric bool selectBitreverse(Register ResVReg, const SPIRVType *ResType, 15881ad6265SDimitry Andric MachineInstr &I) const; 15981ad6265SDimitry Andric 16081ad6265SDimitry Andric bool selectConstVector(Register ResVReg, const SPIRVType *ResType, 16181ad6265SDimitry Andric MachineInstr &I) const; 162*0fca6ea1SDimitry Andric bool selectSplatVector(Register ResVReg, const SPIRVType *ResType, 163*0fca6ea1SDimitry Andric MachineInstr &I) const; 16481ad6265SDimitry Andric 16581ad6265SDimitry Andric bool selectCmp(Register ResVReg, const SPIRVType *ResType, 16681ad6265SDimitry Andric unsigned comparisonOpcode, MachineInstr &I) const; 16781ad6265SDimitry Andric 16881ad6265SDimitry Andric bool selectICmp(Register ResVReg, const SPIRVType *ResType, 16981ad6265SDimitry Andric MachineInstr &I) const; 17081ad6265SDimitry Andric bool selectFCmp(Register ResVReg, const SPIRVType *ResType, 17181ad6265SDimitry Andric MachineInstr &I) const; 17281ad6265SDimitry Andric 173*0fca6ea1SDimitry Andric bool selectFmix(Register ResVReg, const SPIRVType *ResType, 174*0fca6ea1SDimitry Andric MachineInstr &I) const; 175*0fca6ea1SDimitry Andric 176*0fca6ea1SDimitry Andric bool selectRsqrt(Register ResVReg, const SPIRVType *ResType, 177*0fca6ea1SDimitry Andric MachineInstr &I) const; 178*0fca6ea1SDimitry Andric 17981ad6265SDimitry Andric void renderImm32(MachineInstrBuilder &MIB, const MachineInstr &I, 18081ad6265SDimitry Andric int OpIdx) const; 18181ad6265SDimitry Andric void renderFImm32(MachineInstrBuilder &MIB, const MachineInstr &I, 18281ad6265SDimitry Andric int OpIdx) const; 18381ad6265SDimitry Andric 18481ad6265SDimitry Andric bool selectConst(Register ResVReg, const SPIRVType *ResType, const APInt &Imm, 18581ad6265SDimitry Andric MachineInstr &I) const; 18681ad6265SDimitry Andric 18781ad6265SDimitry Andric bool selectSelect(Register ResVReg, const SPIRVType *ResType, MachineInstr &I, 18881ad6265SDimitry Andric bool IsSigned) const; 18981ad6265SDimitry Andric bool selectIToF(Register ResVReg, const SPIRVType *ResType, MachineInstr &I, 19081ad6265SDimitry Andric bool IsSigned, unsigned Opcode) const; 19181ad6265SDimitry Andric bool selectExt(Register ResVReg, const SPIRVType *ResType, MachineInstr &I, 19281ad6265SDimitry Andric bool IsSigned) const; 19381ad6265SDimitry Andric 19481ad6265SDimitry Andric bool selectTrunc(Register ResVReg, const SPIRVType *ResType, 19581ad6265SDimitry Andric MachineInstr &I) const; 19681ad6265SDimitry Andric 197bdd1243dSDimitry Andric bool selectIntToBool(Register IntReg, Register ResVReg, MachineInstr &I, 198bdd1243dSDimitry Andric const SPIRVType *intTy, const SPIRVType *boolTy) const; 19981ad6265SDimitry Andric 20081ad6265SDimitry Andric bool selectOpUndef(Register ResVReg, const SPIRVType *ResType, 20181ad6265SDimitry Andric MachineInstr &I) const; 202*0fca6ea1SDimitry Andric bool selectFreeze(Register ResVReg, const SPIRVType *ResType, 203*0fca6ea1SDimitry Andric MachineInstr &I) const; 20481ad6265SDimitry Andric bool selectIntrinsic(Register ResVReg, const SPIRVType *ResType, 20581ad6265SDimitry Andric MachineInstr &I) const; 20681ad6265SDimitry Andric bool selectExtractVal(Register ResVReg, const SPIRVType *ResType, 20781ad6265SDimitry Andric MachineInstr &I) const; 20881ad6265SDimitry Andric bool selectInsertVal(Register ResVReg, const SPIRVType *ResType, 20981ad6265SDimitry Andric MachineInstr &I) const; 21081ad6265SDimitry Andric bool selectExtractElt(Register ResVReg, const SPIRVType *ResType, 21181ad6265SDimitry Andric MachineInstr &I) const; 21281ad6265SDimitry Andric bool selectInsertElt(Register ResVReg, const SPIRVType *ResType, 21381ad6265SDimitry Andric MachineInstr &I) const; 21481ad6265SDimitry Andric bool selectGEP(Register ResVReg, const SPIRVType *ResType, 21581ad6265SDimitry Andric MachineInstr &I) const; 21681ad6265SDimitry Andric 21781ad6265SDimitry Andric bool selectFrameIndex(Register ResVReg, const SPIRVType *ResType, 21881ad6265SDimitry Andric MachineInstr &I) const; 219*0fca6ea1SDimitry Andric bool selectAllocaArray(Register ResVReg, const SPIRVType *ResType, 220*0fca6ea1SDimitry Andric MachineInstr &I) const; 22181ad6265SDimitry Andric 22281ad6265SDimitry Andric bool selectBranch(MachineInstr &I) const; 22381ad6265SDimitry Andric bool selectBranchCond(MachineInstr &I) const; 22481ad6265SDimitry Andric 22581ad6265SDimitry Andric bool selectPhi(Register ResVReg, const SPIRVType *ResType, 22681ad6265SDimitry Andric MachineInstr &I) const; 22781ad6265SDimitry Andric 228bdd1243dSDimitry Andric bool selectExtInst(Register ResVReg, const SPIRVType *ResType, 229bdd1243dSDimitry Andric MachineInstr &I, CL::OpenCLExtInst CLInst) const; 230bdd1243dSDimitry Andric bool selectExtInst(Register ResVReg, const SPIRVType *ResType, 231bdd1243dSDimitry Andric MachineInstr &I, CL::OpenCLExtInst CLInst, 232bdd1243dSDimitry Andric GL::GLSLExtInst GLInst) const; 233bdd1243dSDimitry Andric bool selectExtInst(Register ResVReg, const SPIRVType *ResType, 234bdd1243dSDimitry Andric MachineInstr &I, const ExtInstList &ExtInsts) const; 235bdd1243dSDimitry Andric 2365f757f3fSDimitry Andric bool selectLog10(Register ResVReg, const SPIRVType *ResType, 2375f757f3fSDimitry Andric MachineInstr &I) const; 2385f757f3fSDimitry Andric 239*0fca6ea1SDimitry Andric bool selectSpvThreadId(Register ResVReg, const SPIRVType *ResType, 240*0fca6ea1SDimitry Andric MachineInstr &I) const; 241*0fca6ea1SDimitry Andric 242*0fca6ea1SDimitry Andric bool selectUnmergeValues(MachineInstr &I) const; 243*0fca6ea1SDimitry Andric 24481ad6265SDimitry Andric Register buildI32Constant(uint32_t Val, MachineInstr &I, 24581ad6265SDimitry Andric const SPIRVType *ResType = nullptr) const; 24681ad6265SDimitry Andric 24781ad6265SDimitry Andric Register buildZerosVal(const SPIRVType *ResType, MachineInstr &I) const; 248*0fca6ea1SDimitry Andric Register buildZerosValF(const SPIRVType *ResType, MachineInstr &I) const; 24981ad6265SDimitry Andric Register buildOnesVal(bool AllOnes, const SPIRVType *ResType, 25081ad6265SDimitry Andric MachineInstr &I) const; 251*0fca6ea1SDimitry Andric 252*0fca6ea1SDimitry Andric bool wrapIntoSpecConstantOp(MachineInstr &I, 253*0fca6ea1SDimitry Andric SmallVector<Register> &CompositeArgs) const; 25481ad6265SDimitry Andric }; 25581ad6265SDimitry Andric 25681ad6265SDimitry Andric } // end anonymous namespace 25781ad6265SDimitry Andric 25881ad6265SDimitry Andric #define GET_GLOBALISEL_IMPL 25981ad6265SDimitry Andric #include "SPIRVGenGlobalISel.inc" 26081ad6265SDimitry Andric #undef GET_GLOBALISEL_IMPL 26181ad6265SDimitry Andric 26281ad6265SDimitry Andric SPIRVInstructionSelector::SPIRVInstructionSelector(const SPIRVTargetMachine &TM, 26381ad6265SDimitry Andric const SPIRVSubtarget &ST, 26481ad6265SDimitry Andric const RegisterBankInfo &RBI) 26581ad6265SDimitry Andric : InstructionSelector(), STI(ST), TII(*ST.getInstrInfo()), 26681ad6265SDimitry Andric TRI(*ST.getRegisterInfo()), RBI(RBI), GR(*ST.getSPIRVGlobalRegistry()), 26781ad6265SDimitry Andric #define GET_GLOBALISEL_PREDICATES_INIT 26881ad6265SDimitry Andric #include "SPIRVGenGlobalISel.inc" 26981ad6265SDimitry Andric #undef GET_GLOBALISEL_PREDICATES_INIT 27081ad6265SDimitry Andric #define GET_GLOBALISEL_TEMPORARIES_INIT 27181ad6265SDimitry Andric #include "SPIRVGenGlobalISel.inc" 27281ad6265SDimitry Andric #undef GET_GLOBALISEL_TEMPORARIES_INIT 27381ad6265SDimitry Andric { 27481ad6265SDimitry Andric } 27581ad6265SDimitry Andric 27681ad6265SDimitry Andric void SPIRVInstructionSelector::setupMF(MachineFunction &MF, GISelKnownBits *KB, 27706c3fb27SDimitry Andric CodeGenCoverage *CoverageInfo, 27881ad6265SDimitry Andric ProfileSummaryInfo *PSI, 27981ad6265SDimitry Andric BlockFrequencyInfo *BFI) { 280*0fca6ea1SDimitry Andric MMI = &MF.getMMI().getObjFileInfo<SPIRVMachineModuleInfo>(); 28181ad6265SDimitry Andric MRI = &MF.getRegInfo(); 28281ad6265SDimitry Andric GR.setCurrentFunc(MF); 28381ad6265SDimitry Andric InstructionSelector::setupMF(MF, KB, CoverageInfo, PSI, BFI); 28481ad6265SDimitry Andric } 28581ad6265SDimitry Andric 286fcaf7f86SDimitry Andric static bool isImm(const MachineOperand &MO, MachineRegisterInfo *MRI); 287fcaf7f86SDimitry Andric 28881ad6265SDimitry Andric // Defined in SPIRVLegalizerInfo.cpp. 28981ad6265SDimitry Andric extern bool isTypeFoldingSupported(unsigned Opcode); 29081ad6265SDimitry Andric 29181ad6265SDimitry Andric bool SPIRVInstructionSelector::select(MachineInstr &I) { 29281ad6265SDimitry Andric assert(I.getParent() && "Instruction should be in a basic block!"); 29381ad6265SDimitry Andric assert(I.getParent()->getParent() && "Instruction should be in a function!"); 29481ad6265SDimitry Andric 29581ad6265SDimitry Andric Register Opcode = I.getOpcode(); 29681ad6265SDimitry Andric // If it's not a GMIR instruction, we've selected it already. 29781ad6265SDimitry Andric if (!isPreISelGenericOpcode(Opcode)) { 29881ad6265SDimitry Andric if (Opcode == SPIRV::ASSIGN_TYPE) { // These pseudos aren't needed any more. 299*0fca6ea1SDimitry Andric Register DstReg = I.getOperand(0).getReg(); 300*0fca6ea1SDimitry Andric Register SrcReg = I.getOperand(1).getReg(); 301*0fca6ea1SDimitry Andric auto *Def = MRI->getVRegDef(SrcReg); 30281ad6265SDimitry Andric if (isTypeFoldingSupported(Def->getOpcode())) { 303*0fca6ea1SDimitry Andric if (MRI->getType(DstReg).isPointer()) 304*0fca6ea1SDimitry Andric MRI->setType(DstReg, LLT::scalar(32)); 305*0fca6ea1SDimitry Andric bool Res = selectImpl(I, *CoverageInfo); 30681ad6265SDimitry Andric assert(Res || Def->getOpcode() == TargetOpcode::G_CONSTANT); 30781ad6265SDimitry Andric if (Res) 30881ad6265SDimitry Andric return Res; 30981ad6265SDimitry Andric } 310*0fca6ea1SDimitry Andric MRI->replaceRegWith(SrcReg, DstReg); 31181ad6265SDimitry Andric I.removeFromParent(); 312bdd1243dSDimitry Andric return true; 31381ad6265SDimitry Andric } else if (I.getNumDefs() == 1) { 31481ad6265SDimitry Andric // Make all vregs 32 bits (for SPIR-V IDs). 31581ad6265SDimitry Andric MRI->setType(I.getOperand(0).getReg(), LLT::scalar(32)); 31681ad6265SDimitry Andric } 317bdd1243dSDimitry Andric return constrainSelectedInstRegOperands(I, TII, TRI, RBI); 31881ad6265SDimitry Andric } 31981ad6265SDimitry Andric 32081ad6265SDimitry Andric if (I.getNumOperands() != I.getNumExplicitOperands()) { 32181ad6265SDimitry Andric LLVM_DEBUG(errs() << "Generic instr has unexpected implicit operands\n"); 32281ad6265SDimitry Andric return false; 32381ad6265SDimitry Andric } 32481ad6265SDimitry Andric 32581ad6265SDimitry Andric // Common code for getting return reg+type, and removing selected instr 32681ad6265SDimitry Andric // from parent occurs here. Instr-specific selection happens in spvSelect(). 32781ad6265SDimitry Andric bool HasDefs = I.getNumDefs() > 0; 32881ad6265SDimitry Andric Register ResVReg = HasDefs ? I.getOperand(0).getReg() : Register(0); 32981ad6265SDimitry Andric SPIRVType *ResType = HasDefs ? GR.getSPIRVTypeForVReg(ResVReg) : nullptr; 33081ad6265SDimitry Andric assert(!HasDefs || ResType || I.getOpcode() == TargetOpcode::G_GLOBAL_VALUE); 33181ad6265SDimitry Andric if (spvSelect(ResVReg, ResType, I)) { 33281ad6265SDimitry Andric if (HasDefs) // Make all vregs 32 bits (for SPIR-V IDs). 333*0fca6ea1SDimitry Andric for (unsigned i = 0; i < I.getNumDefs(); ++i) 334*0fca6ea1SDimitry Andric MRI->setType(I.getOperand(i).getReg(), LLT::scalar(32)); 33581ad6265SDimitry Andric I.removeFromParent(); 33681ad6265SDimitry Andric return true; 33781ad6265SDimitry Andric } 33881ad6265SDimitry Andric return false; 33981ad6265SDimitry Andric } 34081ad6265SDimitry Andric 34181ad6265SDimitry Andric bool SPIRVInstructionSelector::spvSelect(Register ResVReg, 34281ad6265SDimitry Andric const SPIRVType *ResType, 34381ad6265SDimitry Andric MachineInstr &I) const { 34481ad6265SDimitry Andric const unsigned Opcode = I.getOpcode(); 345*0fca6ea1SDimitry Andric if (isTypeFoldingSupported(Opcode) && Opcode != TargetOpcode::G_CONSTANT) 346*0fca6ea1SDimitry Andric return selectImpl(I, *CoverageInfo); 34781ad6265SDimitry Andric switch (Opcode) { 34881ad6265SDimitry Andric case TargetOpcode::G_CONSTANT: 34981ad6265SDimitry Andric return selectConst(ResVReg, ResType, I.getOperand(1).getCImm()->getValue(), 35081ad6265SDimitry Andric I); 35181ad6265SDimitry Andric case TargetOpcode::G_GLOBAL_VALUE: 35281ad6265SDimitry Andric return selectGlobalValue(ResVReg, I); 35381ad6265SDimitry Andric case TargetOpcode::G_IMPLICIT_DEF: 35481ad6265SDimitry Andric return selectOpUndef(ResVReg, ResType, I); 355*0fca6ea1SDimitry Andric case TargetOpcode::G_FREEZE: 356*0fca6ea1SDimitry Andric return selectFreeze(ResVReg, ResType, I); 35781ad6265SDimitry Andric 358*0fca6ea1SDimitry Andric case TargetOpcode::G_INTRINSIC: 35981ad6265SDimitry Andric case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: 3605f757f3fSDimitry Andric case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: 36181ad6265SDimitry Andric return selectIntrinsic(ResVReg, ResType, I); 36281ad6265SDimitry Andric case TargetOpcode::G_BITREVERSE: 36381ad6265SDimitry Andric return selectBitreverse(ResVReg, ResType, I); 36481ad6265SDimitry Andric 36581ad6265SDimitry Andric case TargetOpcode::G_BUILD_VECTOR: 36681ad6265SDimitry Andric return selectConstVector(ResVReg, ResType, I); 367*0fca6ea1SDimitry Andric case TargetOpcode::G_SPLAT_VECTOR: 368*0fca6ea1SDimitry Andric return selectSplatVector(ResVReg, ResType, I); 36981ad6265SDimitry Andric 37081ad6265SDimitry Andric case TargetOpcode::G_SHUFFLE_VECTOR: { 37181ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 37281ad6265SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpVectorShuffle)) 37381ad6265SDimitry Andric .addDef(ResVReg) 37481ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 37581ad6265SDimitry Andric .addUse(I.getOperand(1).getReg()) 37681ad6265SDimitry Andric .addUse(I.getOperand(2).getReg()); 37781ad6265SDimitry Andric for (auto V : I.getOperand(3).getShuffleMask()) 37881ad6265SDimitry Andric MIB.addImm(V); 37981ad6265SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 38081ad6265SDimitry Andric } 38181ad6265SDimitry Andric case TargetOpcode::G_MEMMOVE: 38281ad6265SDimitry Andric case TargetOpcode::G_MEMCPY: 383bdd1243dSDimitry Andric case TargetOpcode::G_MEMSET: 38481ad6265SDimitry Andric return selectMemOperation(ResVReg, I); 38581ad6265SDimitry Andric 38681ad6265SDimitry Andric case TargetOpcode::G_ICMP: 38781ad6265SDimitry Andric return selectICmp(ResVReg, ResType, I); 38881ad6265SDimitry Andric case TargetOpcode::G_FCMP: 38981ad6265SDimitry Andric return selectFCmp(ResVReg, ResType, I); 39081ad6265SDimitry Andric 39181ad6265SDimitry Andric case TargetOpcode::G_FRAME_INDEX: 39281ad6265SDimitry Andric return selectFrameIndex(ResVReg, ResType, I); 39381ad6265SDimitry Andric 39481ad6265SDimitry Andric case TargetOpcode::G_LOAD: 39581ad6265SDimitry Andric return selectLoad(ResVReg, ResType, I); 39681ad6265SDimitry Andric case TargetOpcode::G_STORE: 39781ad6265SDimitry Andric return selectStore(I); 39881ad6265SDimitry Andric 39981ad6265SDimitry Andric case TargetOpcode::G_BR: 40081ad6265SDimitry Andric return selectBranch(I); 40181ad6265SDimitry Andric case TargetOpcode::G_BRCOND: 40281ad6265SDimitry Andric return selectBranchCond(I); 40381ad6265SDimitry Andric 40481ad6265SDimitry Andric case TargetOpcode::G_PHI: 40581ad6265SDimitry Andric return selectPhi(ResVReg, ResType, I); 40681ad6265SDimitry Andric 40781ad6265SDimitry Andric case TargetOpcode::G_FPTOSI: 40881ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpConvertFToS); 40981ad6265SDimitry Andric case TargetOpcode::G_FPTOUI: 41081ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpConvertFToU); 41181ad6265SDimitry Andric 41281ad6265SDimitry Andric case TargetOpcode::G_SITOFP: 41381ad6265SDimitry Andric return selectIToF(ResVReg, ResType, I, true, SPIRV::OpConvertSToF); 41481ad6265SDimitry Andric case TargetOpcode::G_UITOFP: 41581ad6265SDimitry Andric return selectIToF(ResVReg, ResType, I, false, SPIRV::OpConvertUToF); 41681ad6265SDimitry Andric 41781ad6265SDimitry Andric case TargetOpcode::G_CTPOP: 41881ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpBitCount); 419bdd1243dSDimitry Andric case TargetOpcode::G_SMIN: 420bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::s_min, GL::SMin); 421bdd1243dSDimitry Andric case TargetOpcode::G_UMIN: 422bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::u_min, GL::UMin); 423bdd1243dSDimitry Andric 424bdd1243dSDimitry Andric case TargetOpcode::G_SMAX: 425bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::s_max, GL::SMax); 426bdd1243dSDimitry Andric case TargetOpcode::G_UMAX: 427bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::u_max, GL::UMax); 428bdd1243dSDimitry Andric 429bdd1243dSDimitry Andric case TargetOpcode::G_FMA: 430bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::fma, GL::Fma); 431bdd1243dSDimitry Andric 432bdd1243dSDimitry Andric case TargetOpcode::G_FPOW: 433bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::pow, GL::Pow); 434bdd1243dSDimitry Andric case TargetOpcode::G_FPOWI: 435bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::pown); 436bdd1243dSDimitry Andric 437bdd1243dSDimitry Andric case TargetOpcode::G_FEXP: 438bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::exp, GL::Exp); 439bdd1243dSDimitry Andric case TargetOpcode::G_FEXP2: 440bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::exp2, GL::Exp2); 441bdd1243dSDimitry Andric 442bdd1243dSDimitry Andric case TargetOpcode::G_FLOG: 443bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::log, GL::Log); 444bdd1243dSDimitry Andric case TargetOpcode::G_FLOG2: 445bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::log2, GL::Log2); 446bdd1243dSDimitry Andric case TargetOpcode::G_FLOG10: 4475f757f3fSDimitry Andric return selectLog10(ResVReg, ResType, I); 448bdd1243dSDimitry Andric 449bdd1243dSDimitry Andric case TargetOpcode::G_FABS: 450bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::fabs, GL::FAbs); 451bdd1243dSDimitry Andric case TargetOpcode::G_ABS: 452bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::s_abs, GL::SAbs); 453bdd1243dSDimitry Andric 454bdd1243dSDimitry Andric case TargetOpcode::G_FMINNUM: 455bdd1243dSDimitry Andric case TargetOpcode::G_FMINIMUM: 456*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::fmin, GL::NMin); 457bdd1243dSDimitry Andric case TargetOpcode::G_FMAXNUM: 458bdd1243dSDimitry Andric case TargetOpcode::G_FMAXIMUM: 459*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::fmax, GL::NMax); 460bdd1243dSDimitry Andric 461bdd1243dSDimitry Andric case TargetOpcode::G_FCOPYSIGN: 462bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::copysign); 463bdd1243dSDimitry Andric 464bdd1243dSDimitry Andric case TargetOpcode::G_FCEIL: 465bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::ceil, GL::Ceil); 466bdd1243dSDimitry Andric case TargetOpcode::G_FFLOOR: 467bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::floor, GL::Floor); 468bdd1243dSDimitry Andric 469bdd1243dSDimitry Andric case TargetOpcode::G_FCOS: 470bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::cos, GL::Cos); 471bdd1243dSDimitry Andric case TargetOpcode::G_FSIN: 472bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::sin, GL::Sin); 473*0fca6ea1SDimitry Andric case TargetOpcode::G_FTAN: 474*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::tan, GL::Tan); 475*0fca6ea1SDimitry Andric case TargetOpcode::G_FACOS: 476*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::acos, GL::Acos); 477*0fca6ea1SDimitry Andric case TargetOpcode::G_FASIN: 478*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::asin, GL::Asin); 479*0fca6ea1SDimitry Andric case TargetOpcode::G_FATAN: 480*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::atan, GL::Atan); 481*0fca6ea1SDimitry Andric case TargetOpcode::G_FCOSH: 482*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::cosh, GL::Cosh); 483*0fca6ea1SDimitry Andric case TargetOpcode::G_FSINH: 484*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::sinh, GL::Sinh); 485*0fca6ea1SDimitry Andric case TargetOpcode::G_FTANH: 486*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::tanh, GL::Tanh); 487bdd1243dSDimitry Andric 488bdd1243dSDimitry Andric case TargetOpcode::G_FSQRT: 489bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::sqrt, GL::Sqrt); 490bdd1243dSDimitry Andric 491bdd1243dSDimitry Andric case TargetOpcode::G_CTTZ: 492bdd1243dSDimitry Andric case TargetOpcode::G_CTTZ_ZERO_UNDEF: 493bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::ctz); 494bdd1243dSDimitry Andric case TargetOpcode::G_CTLZ: 495bdd1243dSDimitry Andric case TargetOpcode::G_CTLZ_ZERO_UNDEF: 496bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::clz); 497bdd1243dSDimitry Andric 498bdd1243dSDimitry Andric case TargetOpcode::G_INTRINSIC_ROUND: 499bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::round, GL::Round); 500bdd1243dSDimitry Andric case TargetOpcode::G_INTRINSIC_ROUNDEVEN: 501bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::rint, GL::RoundEven); 502bdd1243dSDimitry Andric case TargetOpcode::G_INTRINSIC_TRUNC: 503bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::trunc, GL::Trunc); 504bdd1243dSDimitry Andric case TargetOpcode::G_FRINT: 505bdd1243dSDimitry Andric case TargetOpcode::G_FNEARBYINT: 506bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::rint, GL::RoundEven); 507bdd1243dSDimitry Andric 508bdd1243dSDimitry Andric case TargetOpcode::G_SMULH: 509bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::s_mul_hi); 510bdd1243dSDimitry Andric case TargetOpcode::G_UMULH: 511bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::u_mul_hi); 51281ad6265SDimitry Andric 513*0fca6ea1SDimitry Andric case TargetOpcode::G_SADDSAT: 514*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::s_add_sat); 515*0fca6ea1SDimitry Andric case TargetOpcode::G_UADDSAT: 516*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::u_add_sat); 517*0fca6ea1SDimitry Andric case TargetOpcode::G_SSUBSAT: 518*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::s_sub_sat); 519*0fca6ea1SDimitry Andric case TargetOpcode::G_USUBSAT: 520*0fca6ea1SDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::u_sub_sat); 521*0fca6ea1SDimitry Andric 52281ad6265SDimitry Andric case TargetOpcode::G_SEXT: 52381ad6265SDimitry Andric return selectExt(ResVReg, ResType, I, true); 52481ad6265SDimitry Andric case TargetOpcode::G_ANYEXT: 52581ad6265SDimitry Andric case TargetOpcode::G_ZEXT: 52681ad6265SDimitry Andric return selectExt(ResVReg, ResType, I, false); 52781ad6265SDimitry Andric case TargetOpcode::G_TRUNC: 52881ad6265SDimitry Andric return selectTrunc(ResVReg, ResType, I); 52981ad6265SDimitry Andric case TargetOpcode::G_FPTRUNC: 53081ad6265SDimitry Andric case TargetOpcode::G_FPEXT: 53181ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpFConvert); 53281ad6265SDimitry Andric 53381ad6265SDimitry Andric case TargetOpcode::G_PTRTOINT: 53481ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpConvertPtrToU); 53581ad6265SDimitry Andric case TargetOpcode::G_INTTOPTR: 53681ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpConvertUToPtr); 53781ad6265SDimitry Andric case TargetOpcode::G_BITCAST: 538*0fca6ea1SDimitry Andric return selectBitcast(ResVReg, ResType, I); 53981ad6265SDimitry Andric case TargetOpcode::G_ADDRSPACE_CAST: 54081ad6265SDimitry Andric return selectAddrSpaceCast(ResVReg, ResType, I); 541fcaf7f86SDimitry Andric case TargetOpcode::G_PTR_ADD: { 542fcaf7f86SDimitry Andric // Currently, we get G_PTR_ADD only as a result of translating 543fcaf7f86SDimitry Andric // global variables, initialized with constant expressions like GV + Const 544fcaf7f86SDimitry Andric // (see test opencl/basic/progvar_prog_scope_init.ll). 545fcaf7f86SDimitry Andric // TODO: extend the handler once we have other cases. 546fcaf7f86SDimitry Andric assert(I.getOperand(1).isReg() && I.getOperand(2).isReg()); 547fcaf7f86SDimitry Andric Register GV = I.getOperand(1).getReg(); 548fcaf7f86SDimitry Andric MachineRegisterInfo::def_instr_iterator II = MRI->def_instr_begin(GV); 549*0fca6ea1SDimitry Andric (void)II; 550fcaf7f86SDimitry Andric assert(((*II).getOpcode() == TargetOpcode::G_GLOBAL_VALUE || 551fcaf7f86SDimitry Andric (*II).getOpcode() == TargetOpcode::COPY || 552fcaf7f86SDimitry Andric (*II).getOpcode() == SPIRV::OpVariable) && 553fcaf7f86SDimitry Andric isImm(I.getOperand(2), MRI)); 554fcaf7f86SDimitry Andric Register Idx = buildZerosVal(GR.getOrCreateSPIRVIntegerType(32, I, TII), I); 555fcaf7f86SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 556fcaf7f86SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSpecConstantOp)) 557fcaf7f86SDimitry Andric .addDef(ResVReg) 558fcaf7f86SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 559fcaf7f86SDimitry Andric .addImm(static_cast<uint32_t>( 560fcaf7f86SDimitry Andric SPIRV::Opcode::InBoundsPtrAccessChain)) 561fcaf7f86SDimitry Andric .addUse(GV) 562fcaf7f86SDimitry Andric .addUse(Idx) 563fcaf7f86SDimitry Andric .addUse(I.getOperand(2).getReg()); 564fcaf7f86SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 565fcaf7f86SDimitry Andric } 56681ad6265SDimitry Andric 56781ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_OR: 56881ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicOr); 56981ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_ADD: 57081ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicIAdd); 57181ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_AND: 57281ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicAnd); 57381ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_MAX: 57481ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicSMax); 57581ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_MIN: 57681ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicSMin); 57781ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_SUB: 57881ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicISub); 57981ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_XOR: 58081ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicXor); 58181ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_UMAX: 58281ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicUMax); 58381ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_UMIN: 58481ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicUMin); 58581ad6265SDimitry Andric case TargetOpcode::G_ATOMICRMW_XCHG: 58681ad6265SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicExchange); 58781ad6265SDimitry Andric case TargetOpcode::G_ATOMIC_CMPXCHG: 58881ad6265SDimitry Andric return selectAtomicCmpXchg(ResVReg, ResType, I); 58981ad6265SDimitry Andric 590*0fca6ea1SDimitry Andric case TargetOpcode::G_ATOMICRMW_FADD: 591*0fca6ea1SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicFAddEXT); 592*0fca6ea1SDimitry Andric case TargetOpcode::G_ATOMICRMW_FSUB: 593*0fca6ea1SDimitry Andric // Translate G_ATOMICRMW_FSUB to OpAtomicFAddEXT with negative value operand 594*0fca6ea1SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicFAddEXT, 595*0fca6ea1SDimitry Andric SPIRV::OpFNegate); 596*0fca6ea1SDimitry Andric case TargetOpcode::G_ATOMICRMW_FMIN: 597*0fca6ea1SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicFMinEXT); 598*0fca6ea1SDimitry Andric case TargetOpcode::G_ATOMICRMW_FMAX: 599*0fca6ea1SDimitry Andric return selectAtomicRMW(ResVReg, ResType, I, SPIRV::OpAtomicFMaxEXT); 600*0fca6ea1SDimitry Andric 60181ad6265SDimitry Andric case TargetOpcode::G_FENCE: 60281ad6265SDimitry Andric return selectFence(I); 60381ad6265SDimitry Andric 604*0fca6ea1SDimitry Andric case TargetOpcode::G_STACKSAVE: 605*0fca6ea1SDimitry Andric return selectStackSave(ResVReg, ResType, I); 606*0fca6ea1SDimitry Andric case TargetOpcode::G_STACKRESTORE: 607*0fca6ea1SDimitry Andric return selectStackRestore(I); 608*0fca6ea1SDimitry Andric 609*0fca6ea1SDimitry Andric case TargetOpcode::G_UNMERGE_VALUES: 610*0fca6ea1SDimitry Andric return selectUnmergeValues(I); 611*0fca6ea1SDimitry Andric 61281ad6265SDimitry Andric default: 61381ad6265SDimitry Andric return false; 61481ad6265SDimitry Andric } 61581ad6265SDimitry Andric } 61681ad6265SDimitry Andric 617bdd1243dSDimitry Andric bool SPIRVInstructionSelector::selectExtInst(Register ResVReg, 618bdd1243dSDimitry Andric const SPIRVType *ResType, 619bdd1243dSDimitry Andric MachineInstr &I, 620bdd1243dSDimitry Andric CL::OpenCLExtInst CLInst) const { 621bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, 622bdd1243dSDimitry Andric {{SPIRV::InstructionSet::OpenCL_std, CLInst}}); 623bdd1243dSDimitry Andric } 624bdd1243dSDimitry Andric 625bdd1243dSDimitry Andric bool SPIRVInstructionSelector::selectExtInst(Register ResVReg, 626bdd1243dSDimitry Andric const SPIRVType *ResType, 627bdd1243dSDimitry Andric MachineInstr &I, 628bdd1243dSDimitry Andric CL::OpenCLExtInst CLInst, 629bdd1243dSDimitry Andric GL::GLSLExtInst GLInst) const { 630bdd1243dSDimitry Andric ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CLInst}, 631bdd1243dSDimitry Andric {SPIRV::InstructionSet::GLSL_std_450, GLInst}}; 632bdd1243dSDimitry Andric return selectExtInst(ResVReg, ResType, I, ExtInsts); 633bdd1243dSDimitry Andric } 634bdd1243dSDimitry Andric 635bdd1243dSDimitry Andric bool SPIRVInstructionSelector::selectExtInst(Register ResVReg, 636bdd1243dSDimitry Andric const SPIRVType *ResType, 637bdd1243dSDimitry Andric MachineInstr &I, 638bdd1243dSDimitry Andric const ExtInstList &Insts) const { 639bdd1243dSDimitry Andric 640bdd1243dSDimitry Andric for (const auto &Ex : Insts) { 641bdd1243dSDimitry Andric SPIRV::InstructionSet::InstructionSet Set = Ex.first; 642bdd1243dSDimitry Andric uint32_t Opcode = Ex.second; 643bdd1243dSDimitry Andric if (STI.canUseExtInstSet(Set)) { 644bdd1243dSDimitry Andric MachineBasicBlock &BB = *I.getParent(); 645bdd1243dSDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst)) 646bdd1243dSDimitry Andric .addDef(ResVReg) 647bdd1243dSDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 648bdd1243dSDimitry Andric .addImm(static_cast<uint32_t>(Set)) 649bdd1243dSDimitry Andric .addImm(Opcode); 650bdd1243dSDimitry Andric const unsigned NumOps = I.getNumOperands(); 651bdd1243dSDimitry Andric for (unsigned i = 1; i < NumOps; ++i) 652bdd1243dSDimitry Andric MIB.add(I.getOperand(i)); 653bdd1243dSDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 654bdd1243dSDimitry Andric } 655bdd1243dSDimitry Andric } 656bdd1243dSDimitry Andric return false; 657bdd1243dSDimitry Andric } 658bdd1243dSDimitry Andric 65981ad6265SDimitry Andric bool SPIRVInstructionSelector::selectUnOpWithSrc(Register ResVReg, 66081ad6265SDimitry Andric const SPIRVType *ResType, 66181ad6265SDimitry Andric MachineInstr &I, 66281ad6265SDimitry Andric Register SrcReg, 66381ad6265SDimitry Andric unsigned Opcode) const { 66481ad6265SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcode)) 66581ad6265SDimitry Andric .addDef(ResVReg) 66681ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 66781ad6265SDimitry Andric .addUse(SrcReg) 66881ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 66981ad6265SDimitry Andric } 67081ad6265SDimitry Andric 67181ad6265SDimitry Andric bool SPIRVInstructionSelector::selectUnOp(Register ResVReg, 67281ad6265SDimitry Andric const SPIRVType *ResType, 67381ad6265SDimitry Andric MachineInstr &I, 67481ad6265SDimitry Andric unsigned Opcode) const { 675*0fca6ea1SDimitry Andric if (STI.isOpenCLEnv() && I.getOperand(1).isReg()) { 676*0fca6ea1SDimitry Andric Register SrcReg = I.getOperand(1).getReg(); 677*0fca6ea1SDimitry Andric bool IsGV = false; 678*0fca6ea1SDimitry Andric for (MachineRegisterInfo::def_instr_iterator DefIt = 679*0fca6ea1SDimitry Andric MRI->def_instr_begin(SrcReg); 680*0fca6ea1SDimitry Andric DefIt != MRI->def_instr_end(); DefIt = std::next(DefIt)) { 681*0fca6ea1SDimitry Andric if ((*DefIt).getOpcode() == TargetOpcode::G_GLOBAL_VALUE) { 682*0fca6ea1SDimitry Andric IsGV = true; 683*0fca6ea1SDimitry Andric break; 684*0fca6ea1SDimitry Andric } 685*0fca6ea1SDimitry Andric } 686*0fca6ea1SDimitry Andric if (IsGV) { 687*0fca6ea1SDimitry Andric uint32_t SpecOpcode = 0; 688*0fca6ea1SDimitry Andric switch (Opcode) { 689*0fca6ea1SDimitry Andric case SPIRV::OpConvertPtrToU: 690*0fca6ea1SDimitry Andric SpecOpcode = static_cast<uint32_t>(SPIRV::Opcode::ConvertPtrToU); 691*0fca6ea1SDimitry Andric break; 692*0fca6ea1SDimitry Andric case SPIRV::OpConvertUToPtr: 693*0fca6ea1SDimitry Andric SpecOpcode = static_cast<uint32_t>(SPIRV::Opcode::ConvertUToPtr); 694*0fca6ea1SDimitry Andric break; 695*0fca6ea1SDimitry Andric } 696*0fca6ea1SDimitry Andric if (SpecOpcode) 697*0fca6ea1SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), 698*0fca6ea1SDimitry Andric TII.get(SPIRV::OpSpecConstantOp)) 699*0fca6ea1SDimitry Andric .addDef(ResVReg) 700*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 701*0fca6ea1SDimitry Andric .addImm(SpecOpcode) 702*0fca6ea1SDimitry Andric .addUse(SrcReg) 703*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 704*0fca6ea1SDimitry Andric } 705*0fca6ea1SDimitry Andric } 70681ad6265SDimitry Andric return selectUnOpWithSrc(ResVReg, ResType, I, I.getOperand(1).getReg(), 70781ad6265SDimitry Andric Opcode); 70881ad6265SDimitry Andric } 70981ad6265SDimitry Andric 710*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectBitcast(Register ResVReg, 711*0fca6ea1SDimitry Andric const SPIRVType *ResType, 712*0fca6ea1SDimitry Andric MachineInstr &I) const { 713*0fca6ea1SDimitry Andric Register OpReg = I.getOperand(1).getReg(); 714*0fca6ea1SDimitry Andric SPIRVType *OpType = OpReg.isValid() ? GR.getSPIRVTypeForVReg(OpReg) : nullptr; 715*0fca6ea1SDimitry Andric if (!GR.isBitcastCompatible(ResType, OpType)) 716*0fca6ea1SDimitry Andric report_fatal_error("incompatible result and operand types in a bitcast"); 717*0fca6ea1SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpBitcast); 71881ad6265SDimitry Andric } 719*0fca6ea1SDimitry Andric 720*0fca6ea1SDimitry Andric static SPIRV::Scope::Scope getScope(SyncScope::ID Ord, 721*0fca6ea1SDimitry Andric SPIRVMachineModuleInfo *MMI) { 722*0fca6ea1SDimitry Andric if (Ord == SyncScope::SingleThread || Ord == MMI->Work_ItemSSID) 723*0fca6ea1SDimitry Andric return SPIRV::Scope::Invocation; 724*0fca6ea1SDimitry Andric else if (Ord == SyncScope::System || Ord == MMI->DeviceSSID) 725*0fca6ea1SDimitry Andric return SPIRV::Scope::Device; 726*0fca6ea1SDimitry Andric else if (Ord == MMI->WorkGroupSSID) 727*0fca6ea1SDimitry Andric return SPIRV::Scope::Workgroup; 728*0fca6ea1SDimitry Andric else if (Ord == MMI->AllSVMDevicesSSID) 729*0fca6ea1SDimitry Andric return SPIRV::Scope::CrossDevice; 730*0fca6ea1SDimitry Andric else if (Ord == MMI->SubGroupSSID) 731*0fca6ea1SDimitry Andric return SPIRV::Scope::Subgroup; 732*0fca6ea1SDimitry Andric else 733*0fca6ea1SDimitry Andric // OpenCL approach is: "The functions that do not have memory_scope argument 734*0fca6ea1SDimitry Andric // have the same semantics as the corresponding functions with the 735*0fca6ea1SDimitry Andric // memory_scope argument set to memory_scope_device." See ref.: // 736*0fca6ea1SDimitry Andric // https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_C.html#atomic-functions 737*0fca6ea1SDimitry Andric // In our case if the scope is unknown, assuming that SPIR-V code is to be 738*0fca6ea1SDimitry Andric // consumed in an OpenCL environment, we use the same approach and set the 739*0fca6ea1SDimitry Andric // scope to memory_scope_device. 740*0fca6ea1SDimitry Andric return SPIRV::Scope::Device; 74181ad6265SDimitry Andric } 74281ad6265SDimitry Andric 74381ad6265SDimitry Andric static void addMemoryOperands(MachineMemOperand *MemOp, 74481ad6265SDimitry Andric MachineInstrBuilder &MIB) { 74581ad6265SDimitry Andric uint32_t SpvMemOp = static_cast<uint32_t>(SPIRV::MemoryOperand::None); 74681ad6265SDimitry Andric if (MemOp->isVolatile()) 74781ad6265SDimitry Andric SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Volatile); 74881ad6265SDimitry Andric if (MemOp->isNonTemporal()) 74981ad6265SDimitry Andric SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Nontemporal); 75081ad6265SDimitry Andric if (MemOp->getAlign().value()) 75181ad6265SDimitry Andric SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Aligned); 75281ad6265SDimitry Andric 75381ad6265SDimitry Andric if (SpvMemOp != static_cast<uint32_t>(SPIRV::MemoryOperand::None)) { 75481ad6265SDimitry Andric MIB.addImm(SpvMemOp); 75581ad6265SDimitry Andric if (SpvMemOp & static_cast<uint32_t>(SPIRV::MemoryOperand::Aligned)) 75681ad6265SDimitry Andric MIB.addImm(MemOp->getAlign().value()); 75781ad6265SDimitry Andric } 75881ad6265SDimitry Andric } 75981ad6265SDimitry Andric 76081ad6265SDimitry Andric static void addMemoryOperands(uint64_t Flags, MachineInstrBuilder &MIB) { 76181ad6265SDimitry Andric uint32_t SpvMemOp = static_cast<uint32_t>(SPIRV::MemoryOperand::None); 76281ad6265SDimitry Andric if (Flags & MachineMemOperand::Flags::MOVolatile) 76381ad6265SDimitry Andric SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Volatile); 76481ad6265SDimitry Andric if (Flags & MachineMemOperand::Flags::MONonTemporal) 76581ad6265SDimitry Andric SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Nontemporal); 76681ad6265SDimitry Andric 76781ad6265SDimitry Andric if (SpvMemOp != static_cast<uint32_t>(SPIRV::MemoryOperand::None)) 76881ad6265SDimitry Andric MIB.addImm(SpvMemOp); 76981ad6265SDimitry Andric } 77081ad6265SDimitry Andric 77181ad6265SDimitry Andric bool SPIRVInstructionSelector::selectLoad(Register ResVReg, 77281ad6265SDimitry Andric const SPIRVType *ResType, 77381ad6265SDimitry Andric MachineInstr &I) const { 7745f757f3fSDimitry Andric unsigned OpOffset = isa<GIntrinsic>(I) ? 1 : 0; 77581ad6265SDimitry Andric Register Ptr = I.getOperand(1 + OpOffset).getReg(); 77681ad6265SDimitry Andric auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad)) 77781ad6265SDimitry Andric .addDef(ResVReg) 77881ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 77981ad6265SDimitry Andric .addUse(Ptr); 78081ad6265SDimitry Andric if (!I.getNumMemOperands()) { 7815f757f3fSDimitry Andric assert(I.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS || 7825f757f3fSDimitry Andric I.getOpcode() == 7835f757f3fSDimitry Andric TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS); 78481ad6265SDimitry Andric addMemoryOperands(I.getOperand(2 + OpOffset).getImm(), MIB); 78581ad6265SDimitry Andric } else { 78681ad6265SDimitry Andric addMemoryOperands(*I.memoperands_begin(), MIB); 78781ad6265SDimitry Andric } 78881ad6265SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 78981ad6265SDimitry Andric } 79081ad6265SDimitry Andric 79181ad6265SDimitry Andric bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const { 7925f757f3fSDimitry Andric unsigned OpOffset = isa<GIntrinsic>(I) ? 1 : 0; 79381ad6265SDimitry Andric Register StoreVal = I.getOperand(0 + OpOffset).getReg(); 79481ad6265SDimitry Andric Register Ptr = I.getOperand(1 + OpOffset).getReg(); 79581ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 79681ad6265SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpStore)) 79781ad6265SDimitry Andric .addUse(Ptr) 79881ad6265SDimitry Andric .addUse(StoreVal); 79981ad6265SDimitry Andric if (!I.getNumMemOperands()) { 8005f757f3fSDimitry Andric assert(I.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS || 8015f757f3fSDimitry Andric I.getOpcode() == 8025f757f3fSDimitry Andric TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS); 80381ad6265SDimitry Andric addMemoryOperands(I.getOperand(2 + OpOffset).getImm(), MIB); 80481ad6265SDimitry Andric } else { 80581ad6265SDimitry Andric addMemoryOperands(*I.memoperands_begin(), MIB); 80681ad6265SDimitry Andric } 80781ad6265SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 80881ad6265SDimitry Andric } 80981ad6265SDimitry Andric 810*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectStackSave(Register ResVReg, 811*0fca6ea1SDimitry Andric const SPIRVType *ResType, 812*0fca6ea1SDimitry Andric MachineInstr &I) const { 813*0fca6ea1SDimitry Andric if (!STI.canUseExtension(SPIRV::Extension::SPV_INTEL_variable_length_array)) 814*0fca6ea1SDimitry Andric report_fatal_error( 815*0fca6ea1SDimitry Andric "llvm.stacksave intrinsic: this instruction requires the following " 816*0fca6ea1SDimitry Andric "SPIR-V extension: SPV_INTEL_variable_length_array", 817*0fca6ea1SDimitry Andric false); 818*0fca6ea1SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 819*0fca6ea1SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSaveMemoryINTEL)) 820*0fca6ea1SDimitry Andric .addDef(ResVReg) 821*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 822*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 823*0fca6ea1SDimitry Andric } 824*0fca6ea1SDimitry Andric 825*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectStackRestore(MachineInstr &I) const { 826*0fca6ea1SDimitry Andric if (!STI.canUseExtension(SPIRV::Extension::SPV_INTEL_variable_length_array)) 827*0fca6ea1SDimitry Andric report_fatal_error( 828*0fca6ea1SDimitry Andric "llvm.stackrestore intrinsic: this instruction requires the following " 829*0fca6ea1SDimitry Andric "SPIR-V extension: SPV_INTEL_variable_length_array", 830*0fca6ea1SDimitry Andric false); 831*0fca6ea1SDimitry Andric if (!I.getOperand(0).isReg()) 832*0fca6ea1SDimitry Andric return false; 833*0fca6ea1SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 834*0fca6ea1SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpRestoreMemoryINTEL)) 835*0fca6ea1SDimitry Andric .addUse(I.getOperand(0).getReg()) 836*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 837*0fca6ea1SDimitry Andric } 838*0fca6ea1SDimitry Andric 83981ad6265SDimitry Andric bool SPIRVInstructionSelector::selectMemOperation(Register ResVReg, 84081ad6265SDimitry Andric MachineInstr &I) const { 84181ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 842bdd1243dSDimitry Andric Register SrcReg = I.getOperand(1).getReg(); 843bdd1243dSDimitry Andric if (I.getOpcode() == TargetOpcode::G_MEMSET) { 844bdd1243dSDimitry Andric assert(I.getOperand(1).isReg() && I.getOperand(2).isReg()); 845bdd1243dSDimitry Andric unsigned Val = getIConstVal(I.getOperand(1).getReg(), MRI); 846bdd1243dSDimitry Andric unsigned Num = getIConstVal(I.getOperand(2).getReg(), MRI); 847bdd1243dSDimitry Andric SPIRVType *ValTy = GR.getOrCreateSPIRVIntegerType(8, I, TII); 848bdd1243dSDimitry Andric SPIRVType *ArrTy = GR.getOrCreateSPIRVArrayType(ValTy, Num, I, TII); 849*0fca6ea1SDimitry Andric Register Const = GR.getOrCreateConstIntArray(Val, Num, I, ArrTy, TII); 850bdd1243dSDimitry Andric SPIRVType *VarTy = GR.getOrCreateSPIRVPointerType( 851bdd1243dSDimitry Andric ArrTy, I, TII, SPIRV::StorageClass::UniformConstant); 852bdd1243dSDimitry Andric // TODO: check if we have such GV, add init, use buildGlobalVariable. 853*0fca6ea1SDimitry Andric Function &CurFunction = GR.CurMF->getFunction(); 854*0fca6ea1SDimitry Andric Type *LLVMArrTy = 855*0fca6ea1SDimitry Andric ArrayType::get(IntegerType::get(CurFunction.getContext(), 8), Num); 856*0fca6ea1SDimitry Andric // Module takes ownership of the global var. 857*0fca6ea1SDimitry Andric GlobalVariable *GV = new GlobalVariable(*CurFunction.getParent(), LLVMArrTy, 858*0fca6ea1SDimitry Andric true, GlobalValue::InternalLinkage, 859*0fca6ea1SDimitry Andric Constant::getNullValue(LLVMArrTy)); 860bdd1243dSDimitry Andric Register VarReg = MRI->createGenericVirtualRegister(LLT::scalar(32)); 861bdd1243dSDimitry Andric GR.add(GV, GR.CurMF, VarReg); 862bdd1243dSDimitry Andric 863bdd1243dSDimitry Andric buildOpDecorate(VarReg, I, TII, SPIRV::Decoration::Constant, {}); 864bdd1243dSDimitry Andric BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpVariable)) 865bdd1243dSDimitry Andric .addDef(VarReg) 866bdd1243dSDimitry Andric .addUse(GR.getSPIRVTypeID(VarTy)) 867bdd1243dSDimitry Andric .addImm(SPIRV::StorageClass::UniformConstant) 868bdd1243dSDimitry Andric .addUse(Const) 869bdd1243dSDimitry Andric .constrainAllUses(TII, TRI, RBI); 870bdd1243dSDimitry Andric SPIRVType *SourceTy = GR.getOrCreateSPIRVPointerType( 871bdd1243dSDimitry Andric ValTy, I, TII, SPIRV::StorageClass::UniformConstant); 872bdd1243dSDimitry Andric SrcReg = MRI->createGenericVirtualRegister(LLT::scalar(32)); 873bdd1243dSDimitry Andric selectUnOpWithSrc(SrcReg, SourceTy, I, VarReg, SPIRV::OpBitcast); 874bdd1243dSDimitry Andric } 87581ad6265SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCopyMemorySized)) 876fcaf7f86SDimitry Andric .addUse(I.getOperand(0).getReg()) 877bdd1243dSDimitry Andric .addUse(SrcReg) 87881ad6265SDimitry Andric .addUse(I.getOperand(2).getReg()); 87981ad6265SDimitry Andric if (I.getNumMemOperands()) 88081ad6265SDimitry Andric addMemoryOperands(*I.memoperands_begin(), MIB); 88181ad6265SDimitry Andric bool Result = MIB.constrainAllUses(TII, TRI, RBI); 882fcaf7f86SDimitry Andric if (ResVReg.isValid() && ResVReg != MIB->getOperand(0).getReg()) 88381ad6265SDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(TargetOpcode::COPY), ResVReg) 88481ad6265SDimitry Andric .addUse(MIB->getOperand(0).getReg()); 88581ad6265SDimitry Andric return Result; 88681ad6265SDimitry Andric } 88781ad6265SDimitry Andric 88881ad6265SDimitry Andric bool SPIRVInstructionSelector::selectAtomicRMW(Register ResVReg, 88981ad6265SDimitry Andric const SPIRVType *ResType, 89081ad6265SDimitry Andric MachineInstr &I, 891*0fca6ea1SDimitry Andric unsigned NewOpcode, 892*0fca6ea1SDimitry Andric unsigned NegateOpcode) const { 89381ad6265SDimitry Andric assert(I.hasOneMemOperand()); 89481ad6265SDimitry Andric const MachineMemOperand *MemOp = *I.memoperands_begin(); 895*0fca6ea1SDimitry Andric uint32_t Scope = 896*0fca6ea1SDimitry Andric static_cast<uint32_t>(getScope(MemOp->getSyncScopeID(), MMI)); 89781ad6265SDimitry Andric Register ScopeReg = buildI32Constant(Scope, I); 89881ad6265SDimitry Andric 89981ad6265SDimitry Andric Register Ptr = I.getOperand(1).getReg(); 90081ad6265SDimitry Andric // TODO: Changed as it's implemented in the translator. See test/atomicrmw.ll 90181ad6265SDimitry Andric // auto ScSem = 90281ad6265SDimitry Andric // getMemSemanticsForStorageClass(GR.getPointerStorageClass(Ptr)); 90381ad6265SDimitry Andric AtomicOrdering AO = MemOp->getSuccessOrdering(); 90481ad6265SDimitry Andric uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(AO)); 90581ad6265SDimitry Andric Register MemSemReg = buildI32Constant(MemSem /*| ScSem*/, I); 90681ad6265SDimitry Andric 907*0fca6ea1SDimitry Andric bool Result = false; 908*0fca6ea1SDimitry Andric Register ValueReg = I.getOperand(2).getReg(); 909*0fca6ea1SDimitry Andric if (NegateOpcode != 0) { 910*0fca6ea1SDimitry Andric // Translation with negative value operand is requested 911*0fca6ea1SDimitry Andric Register TmpReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 912*0fca6ea1SDimitry Andric Result |= selectUnOpWithSrc(TmpReg, ResType, I, ValueReg, NegateOpcode); 913*0fca6ea1SDimitry Andric ValueReg = TmpReg; 914*0fca6ea1SDimitry Andric } 915*0fca6ea1SDimitry Andric 916*0fca6ea1SDimitry Andric Result |= BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(NewOpcode)) 91781ad6265SDimitry Andric .addDef(ResVReg) 91881ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 91981ad6265SDimitry Andric .addUse(Ptr) 92081ad6265SDimitry Andric .addUse(ScopeReg) 92181ad6265SDimitry Andric .addUse(MemSemReg) 922*0fca6ea1SDimitry Andric .addUse(ValueReg) 92381ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 924*0fca6ea1SDimitry Andric return Result; 925*0fca6ea1SDimitry Andric } 926*0fca6ea1SDimitry Andric 927*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectUnmergeValues(MachineInstr &I) const { 928*0fca6ea1SDimitry Andric unsigned ArgI = I.getNumOperands() - 1; 929*0fca6ea1SDimitry Andric Register SrcReg = 930*0fca6ea1SDimitry Andric I.getOperand(ArgI).isReg() ? I.getOperand(ArgI).getReg() : Register(0); 931*0fca6ea1SDimitry Andric SPIRVType *DefType = 932*0fca6ea1SDimitry Andric SrcReg.isValid() ? GR.getSPIRVTypeForVReg(SrcReg) : nullptr; 933*0fca6ea1SDimitry Andric if (!DefType || DefType->getOpcode() != SPIRV::OpTypeVector) 934*0fca6ea1SDimitry Andric report_fatal_error( 935*0fca6ea1SDimitry Andric "cannot select G_UNMERGE_VALUES with a non-vector argument"); 936*0fca6ea1SDimitry Andric 937*0fca6ea1SDimitry Andric SPIRVType *ScalarType = 938*0fca6ea1SDimitry Andric GR.getSPIRVTypeForVReg(DefType->getOperand(1).getReg()); 939*0fca6ea1SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 940*0fca6ea1SDimitry Andric bool Res = false; 941*0fca6ea1SDimitry Andric for (unsigned i = 0; i < I.getNumDefs(); ++i) { 942*0fca6ea1SDimitry Andric Register ResVReg = I.getOperand(i).getReg(); 943*0fca6ea1SDimitry Andric SPIRVType *ResType = GR.getSPIRVTypeForVReg(ResVReg); 944*0fca6ea1SDimitry Andric if (!ResType) { 945*0fca6ea1SDimitry Andric // There was no "assign type" actions, let's fix this now 946*0fca6ea1SDimitry Andric ResType = ScalarType; 947*0fca6ea1SDimitry Andric MRI->setRegClass(ResVReg, &SPIRV::IDRegClass); 948*0fca6ea1SDimitry Andric MRI->setType(ResVReg, LLT::scalar(GR.getScalarOrVectorBitWidth(ResType))); 949*0fca6ea1SDimitry Andric GR.assignSPIRVTypeToVReg(ResType, ResVReg, *GR.CurMF); 950*0fca6ea1SDimitry Andric } 951*0fca6ea1SDimitry Andric auto MIB = 952*0fca6ea1SDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeExtract)) 953*0fca6ea1SDimitry Andric .addDef(ResVReg) 954*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 955*0fca6ea1SDimitry Andric .addUse(SrcReg) 956*0fca6ea1SDimitry Andric .addImm(static_cast<int64_t>(i)); 957*0fca6ea1SDimitry Andric Res |= MIB.constrainAllUses(TII, TRI, RBI); 958*0fca6ea1SDimitry Andric } 959*0fca6ea1SDimitry Andric return Res; 96081ad6265SDimitry Andric } 96181ad6265SDimitry Andric 96281ad6265SDimitry Andric bool SPIRVInstructionSelector::selectFence(MachineInstr &I) const { 96381ad6265SDimitry Andric AtomicOrdering AO = AtomicOrdering(I.getOperand(0).getImm()); 96481ad6265SDimitry Andric uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(AO)); 96581ad6265SDimitry Andric Register MemSemReg = buildI32Constant(MemSem, I); 96681ad6265SDimitry Andric SyncScope::ID Ord = SyncScope::ID(I.getOperand(1).getImm()); 967*0fca6ea1SDimitry Andric uint32_t Scope = static_cast<uint32_t>(getScope(Ord, MMI)); 96881ad6265SDimitry Andric Register ScopeReg = buildI32Constant(Scope, I); 96981ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 97081ad6265SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpMemoryBarrier)) 97181ad6265SDimitry Andric .addUse(ScopeReg) 97281ad6265SDimitry Andric .addUse(MemSemReg) 97381ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 97481ad6265SDimitry Andric } 97581ad6265SDimitry Andric 97681ad6265SDimitry Andric bool SPIRVInstructionSelector::selectAtomicCmpXchg(Register ResVReg, 97781ad6265SDimitry Andric const SPIRVType *ResType, 97881ad6265SDimitry Andric MachineInstr &I) const { 979fcaf7f86SDimitry Andric Register ScopeReg; 980fcaf7f86SDimitry Andric Register MemSemEqReg; 981fcaf7f86SDimitry Andric Register MemSemNeqReg; 982fcaf7f86SDimitry Andric Register Ptr = I.getOperand(2).getReg(); 9835f757f3fSDimitry Andric if (!isa<GIntrinsic>(I)) { 98481ad6265SDimitry Andric assert(I.hasOneMemOperand()); 98581ad6265SDimitry Andric const MachineMemOperand *MemOp = *I.memoperands_begin(); 986*0fca6ea1SDimitry Andric unsigned Scope = 987*0fca6ea1SDimitry Andric static_cast<uint32_t>(getScope(MemOp->getSyncScopeID(), MMI)); 988fcaf7f86SDimitry Andric ScopeReg = buildI32Constant(Scope, I); 98981ad6265SDimitry Andric 990fcaf7f86SDimitry Andric unsigned ScSem = static_cast<uint32_t>( 991fcaf7f86SDimitry Andric getMemSemanticsForStorageClass(GR.getPointerStorageClass(Ptr))); 992fcaf7f86SDimitry Andric AtomicOrdering AO = MemOp->getSuccessOrdering(); 993fcaf7f86SDimitry Andric unsigned MemSemEq = static_cast<uint32_t>(getMemSemantics(AO)) | ScSem; 994fcaf7f86SDimitry Andric MemSemEqReg = buildI32Constant(MemSemEq, I); 995fcaf7f86SDimitry Andric AtomicOrdering FO = MemOp->getFailureOrdering(); 996fcaf7f86SDimitry Andric unsigned MemSemNeq = static_cast<uint32_t>(getMemSemantics(FO)) | ScSem; 997fcaf7f86SDimitry Andric MemSemNeqReg = 998fcaf7f86SDimitry Andric MemSemEq == MemSemNeq ? MemSemEqReg : buildI32Constant(MemSemNeq, I); 999fcaf7f86SDimitry Andric } else { 1000fcaf7f86SDimitry Andric ScopeReg = I.getOperand(5).getReg(); 1001fcaf7f86SDimitry Andric MemSemEqReg = I.getOperand(6).getReg(); 1002fcaf7f86SDimitry Andric MemSemNeqReg = I.getOperand(7).getReg(); 1003fcaf7f86SDimitry Andric } 1004fcaf7f86SDimitry Andric 100581ad6265SDimitry Andric Register Cmp = I.getOperand(3).getReg(); 100681ad6265SDimitry Andric Register Val = I.getOperand(4).getReg(); 100781ad6265SDimitry Andric SPIRVType *SpvValTy = GR.getSPIRVTypeForVReg(Val); 1008fcaf7f86SDimitry Andric Register ACmpRes = MRI->createVirtualRegister(&SPIRV::IDRegClass); 100981ad6265SDimitry Andric const DebugLoc &DL = I.getDebugLoc(); 1010fcaf7f86SDimitry Andric bool Result = 1011fcaf7f86SDimitry Andric BuildMI(*I.getParent(), I, DL, TII.get(SPIRV::OpAtomicCompareExchange)) 1012fcaf7f86SDimitry Andric .addDef(ACmpRes) 101381ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(SpvValTy)) 101481ad6265SDimitry Andric .addUse(Ptr) 101581ad6265SDimitry Andric .addUse(ScopeReg) 101681ad6265SDimitry Andric .addUse(MemSemEqReg) 101781ad6265SDimitry Andric .addUse(MemSemNeqReg) 101881ad6265SDimitry Andric .addUse(Val) 101981ad6265SDimitry Andric .addUse(Cmp) 102081ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1021fcaf7f86SDimitry Andric Register CmpSuccReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 1022fcaf7f86SDimitry Andric SPIRVType *BoolTy = GR.getOrCreateSPIRVBoolType(I, TII); 1023fcaf7f86SDimitry Andric Result |= BuildMI(*I.getParent(), I, DL, TII.get(SPIRV::OpIEqual)) 1024fcaf7f86SDimitry Andric .addDef(CmpSuccReg) 1025fcaf7f86SDimitry Andric .addUse(GR.getSPIRVTypeID(BoolTy)) 1026fcaf7f86SDimitry Andric .addUse(ACmpRes) 1027fcaf7f86SDimitry Andric .addUse(Cmp) 1028fcaf7f86SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1029fcaf7f86SDimitry Andric Register TmpReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 1030fcaf7f86SDimitry Andric Result |= BuildMI(*I.getParent(), I, DL, TII.get(SPIRV::OpCompositeInsert)) 1031fcaf7f86SDimitry Andric .addDef(TmpReg) 1032fcaf7f86SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 1033fcaf7f86SDimitry Andric .addUse(ACmpRes) 1034fcaf7f86SDimitry Andric .addUse(GR.getOrCreateUndef(I, ResType, TII)) 1035fcaf7f86SDimitry Andric .addImm(0) 1036fcaf7f86SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1037fcaf7f86SDimitry Andric Result |= BuildMI(*I.getParent(), I, DL, TII.get(SPIRV::OpCompositeInsert)) 1038fcaf7f86SDimitry Andric .addDef(ResVReg) 1039fcaf7f86SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 1040fcaf7f86SDimitry Andric .addUse(CmpSuccReg) 1041fcaf7f86SDimitry Andric .addUse(TmpReg) 1042fcaf7f86SDimitry Andric .addImm(1) 1043fcaf7f86SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1044fcaf7f86SDimitry Andric return Result; 104581ad6265SDimitry Andric } 104681ad6265SDimitry Andric 1047bdd1243dSDimitry Andric static bool isGenericCastablePtr(SPIRV::StorageClass::StorageClass SC) { 104881ad6265SDimitry Andric switch (SC) { 104981ad6265SDimitry Andric case SPIRV::StorageClass::Workgroup: 105081ad6265SDimitry Andric case SPIRV::StorageClass::CrossWorkgroup: 105181ad6265SDimitry Andric case SPIRV::StorageClass::Function: 105281ad6265SDimitry Andric return true; 105381ad6265SDimitry Andric default: 105481ad6265SDimitry Andric return false; 105581ad6265SDimitry Andric } 105681ad6265SDimitry Andric } 105781ad6265SDimitry Andric 1058*0fca6ea1SDimitry Andric static bool isUSMStorageClass(SPIRV::StorageClass::StorageClass SC) { 1059*0fca6ea1SDimitry Andric switch (SC) { 1060*0fca6ea1SDimitry Andric case SPIRV::StorageClass::DeviceOnlyINTEL: 1061*0fca6ea1SDimitry Andric case SPIRV::StorageClass::HostOnlyINTEL: 1062*0fca6ea1SDimitry Andric return true; 1063*0fca6ea1SDimitry Andric default: 1064*0fca6ea1SDimitry Andric return false; 1065*0fca6ea1SDimitry Andric } 1066*0fca6ea1SDimitry Andric } 1067*0fca6ea1SDimitry Andric 106881ad6265SDimitry Andric // In SPIR-V address space casting can only happen to and from the Generic 1069*0fca6ea1SDimitry Andric // storage class. We can also only cast Workgroup, CrossWorkgroup, or Function 107081ad6265SDimitry Andric // pointers to and from Generic pointers. As such, we can convert e.g. from 107181ad6265SDimitry Andric // Workgroup to Function by going via a Generic pointer as an intermediary. All 107281ad6265SDimitry Andric // other combinations can only be done by a bitcast, and are probably not safe. 107381ad6265SDimitry Andric bool SPIRVInstructionSelector::selectAddrSpaceCast(Register ResVReg, 107481ad6265SDimitry Andric const SPIRVType *ResType, 107581ad6265SDimitry Andric MachineInstr &I) const { 1076fcaf7f86SDimitry Andric // If the AddrSpaceCast user is single and in OpConstantComposite or 1077fcaf7f86SDimitry Andric // OpVariable, we should select OpSpecConstantOp. 1078fcaf7f86SDimitry Andric auto UIs = MRI->use_instructions(ResVReg); 1079fcaf7f86SDimitry Andric if (!UIs.empty() && ++UIs.begin() == UIs.end() && 1080fcaf7f86SDimitry Andric (UIs.begin()->getOpcode() == SPIRV::OpConstantComposite || 1081fcaf7f86SDimitry Andric UIs.begin()->getOpcode() == SPIRV::OpVariable || 1082fcaf7f86SDimitry Andric isSpvIntrinsic(*UIs.begin(), Intrinsic::spv_init_global))) { 1083fcaf7f86SDimitry Andric Register NewReg = I.getOperand(1).getReg(); 1084fcaf7f86SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 1085fcaf7f86SDimitry Andric SPIRVType *SpvBaseTy = GR.getOrCreateSPIRVIntegerType(8, I, TII); 1086fcaf7f86SDimitry Andric ResType = GR.getOrCreateSPIRVPointerType(SpvBaseTy, I, TII, 1087fcaf7f86SDimitry Andric SPIRV::StorageClass::Generic); 1088fcaf7f86SDimitry Andric bool Result = 1089fcaf7f86SDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSpecConstantOp)) 1090fcaf7f86SDimitry Andric .addDef(ResVReg) 1091fcaf7f86SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 1092fcaf7f86SDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::Opcode::PtrCastToGeneric)) 1093fcaf7f86SDimitry Andric .addUse(NewReg) 1094fcaf7f86SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1095fcaf7f86SDimitry Andric return Result; 1096fcaf7f86SDimitry Andric } 109781ad6265SDimitry Andric Register SrcPtr = I.getOperand(1).getReg(); 109881ad6265SDimitry Andric SPIRVType *SrcPtrTy = GR.getSPIRVTypeForVReg(SrcPtr); 1099bdd1243dSDimitry Andric SPIRV::StorageClass::StorageClass SrcSC = GR.getPointerStorageClass(SrcPtr); 1100bdd1243dSDimitry Andric SPIRV::StorageClass::StorageClass DstSC = GR.getPointerStorageClass(ResVReg); 110181ad6265SDimitry Andric 1102*0fca6ea1SDimitry Andric // don't generate a cast between identical storage classes 1103*0fca6ea1SDimitry Andric if (SrcSC == DstSC) 1104*0fca6ea1SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), 1105*0fca6ea1SDimitry Andric TII.get(TargetOpcode::COPY)) 1106*0fca6ea1SDimitry Andric .addDef(ResVReg) 1107*0fca6ea1SDimitry Andric .addUse(SrcPtr) 1108*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1109*0fca6ea1SDimitry Andric 1110*0fca6ea1SDimitry Andric // Casting from an eligible pointer to Generic. 111181ad6265SDimitry Andric if (DstSC == SPIRV::StorageClass::Generic && isGenericCastablePtr(SrcSC)) 111281ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpPtrCastToGeneric); 1113*0fca6ea1SDimitry Andric // Casting from Generic to an eligible pointer. 111481ad6265SDimitry Andric if (SrcSC == SPIRV::StorageClass::Generic && isGenericCastablePtr(DstSC)) 111581ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpGenericCastToPtr); 1116*0fca6ea1SDimitry Andric // Casting between 2 eligible pointers using Generic as an intermediary. 111781ad6265SDimitry Andric if (isGenericCastablePtr(SrcSC) && isGenericCastablePtr(DstSC)) { 111881ad6265SDimitry Andric Register Tmp = MRI->createVirtualRegister(&SPIRV::IDRegClass); 111981ad6265SDimitry Andric SPIRVType *GenericPtrTy = GR.getOrCreateSPIRVPointerType( 1120*0fca6ea1SDimitry Andric GR.getPointeeType(SrcPtrTy), I, TII, SPIRV::StorageClass::Generic); 112181ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 112281ad6265SDimitry Andric const DebugLoc &DL = I.getDebugLoc(); 112381ad6265SDimitry Andric bool Success = BuildMI(BB, I, DL, TII.get(SPIRV::OpPtrCastToGeneric)) 112481ad6265SDimitry Andric .addDef(Tmp) 112581ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(GenericPtrTy)) 112681ad6265SDimitry Andric .addUse(SrcPtr) 112781ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 112881ad6265SDimitry Andric return Success && BuildMI(BB, I, DL, TII.get(SPIRV::OpGenericCastToPtr)) 112981ad6265SDimitry Andric .addDef(ResVReg) 113081ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 113181ad6265SDimitry Andric .addUse(Tmp) 113281ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 113381ad6265SDimitry Andric } 1134*0fca6ea1SDimitry Andric 1135*0fca6ea1SDimitry Andric // Check if instructions from the SPV_INTEL_usm_storage_classes extension may 1136*0fca6ea1SDimitry Andric // be applied 1137*0fca6ea1SDimitry Andric if (isUSMStorageClass(SrcSC) && DstSC == SPIRV::StorageClass::CrossWorkgroup) 1138*0fca6ea1SDimitry Andric return selectUnOp(ResVReg, ResType, I, 1139*0fca6ea1SDimitry Andric SPIRV::OpPtrCastToCrossWorkgroupINTEL); 1140*0fca6ea1SDimitry Andric if (SrcSC == SPIRV::StorageClass::CrossWorkgroup && isUSMStorageClass(DstSC)) 1141*0fca6ea1SDimitry Andric return selectUnOp(ResVReg, ResType, I, 1142*0fca6ea1SDimitry Andric SPIRV::OpCrossWorkgroupCastToPtrINTEL); 1143*0fca6ea1SDimitry Andric if (isUSMStorageClass(SrcSC) && DstSC == SPIRV::StorageClass::Generic) 1144*0fca6ea1SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpPtrCastToGeneric); 1145*0fca6ea1SDimitry Andric if (SrcSC == SPIRV::StorageClass::Generic && isUSMStorageClass(DstSC)) 1146*0fca6ea1SDimitry Andric return selectUnOp(ResVReg, ResType, I, SPIRV::OpGenericCastToPtr); 1147*0fca6ea1SDimitry Andric 1148*0fca6ea1SDimitry Andric // Bitcast for pointers requires that the address spaces must match 1149*0fca6ea1SDimitry Andric return false; 115081ad6265SDimitry Andric } 115181ad6265SDimitry Andric 115281ad6265SDimitry Andric static unsigned getFCmpOpcode(unsigned PredNum) { 115381ad6265SDimitry Andric auto Pred = static_cast<CmpInst::Predicate>(PredNum); 115481ad6265SDimitry Andric switch (Pred) { 115581ad6265SDimitry Andric case CmpInst::FCMP_OEQ: 115681ad6265SDimitry Andric return SPIRV::OpFOrdEqual; 115781ad6265SDimitry Andric case CmpInst::FCMP_OGE: 115881ad6265SDimitry Andric return SPIRV::OpFOrdGreaterThanEqual; 115981ad6265SDimitry Andric case CmpInst::FCMP_OGT: 116081ad6265SDimitry Andric return SPIRV::OpFOrdGreaterThan; 116181ad6265SDimitry Andric case CmpInst::FCMP_OLE: 116281ad6265SDimitry Andric return SPIRV::OpFOrdLessThanEqual; 116381ad6265SDimitry Andric case CmpInst::FCMP_OLT: 116481ad6265SDimitry Andric return SPIRV::OpFOrdLessThan; 116581ad6265SDimitry Andric case CmpInst::FCMP_ONE: 116681ad6265SDimitry Andric return SPIRV::OpFOrdNotEqual; 116781ad6265SDimitry Andric case CmpInst::FCMP_ORD: 116881ad6265SDimitry Andric return SPIRV::OpOrdered; 116981ad6265SDimitry Andric case CmpInst::FCMP_UEQ: 117081ad6265SDimitry Andric return SPIRV::OpFUnordEqual; 117181ad6265SDimitry Andric case CmpInst::FCMP_UGE: 117281ad6265SDimitry Andric return SPIRV::OpFUnordGreaterThanEqual; 117381ad6265SDimitry Andric case CmpInst::FCMP_UGT: 117481ad6265SDimitry Andric return SPIRV::OpFUnordGreaterThan; 117581ad6265SDimitry Andric case CmpInst::FCMP_ULE: 117681ad6265SDimitry Andric return SPIRV::OpFUnordLessThanEqual; 117781ad6265SDimitry Andric case CmpInst::FCMP_ULT: 117881ad6265SDimitry Andric return SPIRV::OpFUnordLessThan; 117981ad6265SDimitry Andric case CmpInst::FCMP_UNE: 118081ad6265SDimitry Andric return SPIRV::OpFUnordNotEqual; 118181ad6265SDimitry Andric case CmpInst::FCMP_UNO: 118281ad6265SDimitry Andric return SPIRV::OpUnordered; 118381ad6265SDimitry Andric default: 118481ad6265SDimitry Andric llvm_unreachable("Unknown predicate type for FCmp"); 118581ad6265SDimitry Andric } 118681ad6265SDimitry Andric } 118781ad6265SDimitry Andric 118881ad6265SDimitry Andric static unsigned getICmpOpcode(unsigned PredNum) { 118981ad6265SDimitry Andric auto Pred = static_cast<CmpInst::Predicate>(PredNum); 119081ad6265SDimitry Andric switch (Pred) { 119181ad6265SDimitry Andric case CmpInst::ICMP_EQ: 119281ad6265SDimitry Andric return SPIRV::OpIEqual; 119381ad6265SDimitry Andric case CmpInst::ICMP_NE: 119481ad6265SDimitry Andric return SPIRV::OpINotEqual; 119581ad6265SDimitry Andric case CmpInst::ICMP_SGE: 119681ad6265SDimitry Andric return SPIRV::OpSGreaterThanEqual; 119781ad6265SDimitry Andric case CmpInst::ICMP_SGT: 119881ad6265SDimitry Andric return SPIRV::OpSGreaterThan; 119981ad6265SDimitry Andric case CmpInst::ICMP_SLE: 120081ad6265SDimitry Andric return SPIRV::OpSLessThanEqual; 120181ad6265SDimitry Andric case CmpInst::ICMP_SLT: 120281ad6265SDimitry Andric return SPIRV::OpSLessThan; 120381ad6265SDimitry Andric case CmpInst::ICMP_UGE: 120481ad6265SDimitry Andric return SPIRV::OpUGreaterThanEqual; 120581ad6265SDimitry Andric case CmpInst::ICMP_UGT: 120681ad6265SDimitry Andric return SPIRV::OpUGreaterThan; 120781ad6265SDimitry Andric case CmpInst::ICMP_ULE: 120881ad6265SDimitry Andric return SPIRV::OpULessThanEqual; 120981ad6265SDimitry Andric case CmpInst::ICMP_ULT: 121081ad6265SDimitry Andric return SPIRV::OpULessThan; 121181ad6265SDimitry Andric default: 121281ad6265SDimitry Andric llvm_unreachable("Unknown predicate type for ICmp"); 121381ad6265SDimitry Andric } 121481ad6265SDimitry Andric } 121581ad6265SDimitry Andric 121681ad6265SDimitry Andric static unsigned getPtrCmpOpcode(unsigned Pred) { 121781ad6265SDimitry Andric switch (static_cast<CmpInst::Predicate>(Pred)) { 121881ad6265SDimitry Andric case CmpInst::ICMP_EQ: 121981ad6265SDimitry Andric return SPIRV::OpPtrEqual; 122081ad6265SDimitry Andric case CmpInst::ICMP_NE: 122181ad6265SDimitry Andric return SPIRV::OpPtrNotEqual; 122281ad6265SDimitry Andric default: 122381ad6265SDimitry Andric llvm_unreachable("Unknown predicate type for pointer comparison"); 122481ad6265SDimitry Andric } 122581ad6265SDimitry Andric } 122681ad6265SDimitry Andric 122781ad6265SDimitry Andric // Return the logical operation, or abort if none exists. 122881ad6265SDimitry Andric static unsigned getBoolCmpOpcode(unsigned PredNum) { 122981ad6265SDimitry Andric auto Pred = static_cast<CmpInst::Predicate>(PredNum); 123081ad6265SDimitry Andric switch (Pred) { 123181ad6265SDimitry Andric case CmpInst::ICMP_EQ: 123281ad6265SDimitry Andric return SPIRV::OpLogicalEqual; 123381ad6265SDimitry Andric case CmpInst::ICMP_NE: 123481ad6265SDimitry Andric return SPIRV::OpLogicalNotEqual; 123581ad6265SDimitry Andric default: 123681ad6265SDimitry Andric llvm_unreachable("Unknown predicate type for Bool comparison"); 123781ad6265SDimitry Andric } 123881ad6265SDimitry Andric } 123981ad6265SDimitry Andric 1240*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectAnyOrAll(Register ResVReg, 1241*0fca6ea1SDimitry Andric const SPIRVType *ResType, 1242*0fca6ea1SDimitry Andric MachineInstr &I, 1243*0fca6ea1SDimitry Andric unsigned OpAnyOrAll) const { 1244*0fca6ea1SDimitry Andric assert(I.getNumOperands() == 3); 1245*0fca6ea1SDimitry Andric assert(I.getOperand(2).isReg()); 1246*0fca6ea1SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 1247*0fca6ea1SDimitry Andric Register InputRegister = I.getOperand(2).getReg(); 1248*0fca6ea1SDimitry Andric SPIRVType *InputType = GR.getSPIRVTypeForVReg(InputRegister); 1249*0fca6ea1SDimitry Andric 1250*0fca6ea1SDimitry Andric if (!InputType) 1251*0fca6ea1SDimitry Andric report_fatal_error("Input Type could not be determined."); 1252*0fca6ea1SDimitry Andric 1253*0fca6ea1SDimitry Andric bool IsBoolTy = GR.isScalarOrVectorOfType(InputRegister, SPIRV::OpTypeBool); 1254*0fca6ea1SDimitry Andric bool IsVectorTy = InputType->getOpcode() == SPIRV::OpTypeVector; 1255*0fca6ea1SDimitry Andric if (IsBoolTy && !IsVectorTy) { 1256*0fca6ea1SDimitry Andric assert(ResVReg == I.getOperand(0).getReg()); 1257*0fca6ea1SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), 1258*0fca6ea1SDimitry Andric TII.get(TargetOpcode::COPY)) 1259*0fca6ea1SDimitry Andric .addDef(ResVReg) 1260*0fca6ea1SDimitry Andric .addUse(InputRegister) 1261*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1262*0fca6ea1SDimitry Andric } 1263*0fca6ea1SDimitry Andric 1264*0fca6ea1SDimitry Andric bool IsFloatTy = GR.isScalarOrVectorOfType(InputRegister, SPIRV::OpTypeFloat); 1265*0fca6ea1SDimitry Andric unsigned SpirvNotEqualId = 1266*0fca6ea1SDimitry Andric IsFloatTy ? SPIRV::OpFOrdNotEqual : SPIRV::OpINotEqual; 1267*0fca6ea1SDimitry Andric SPIRVType *SpvBoolScalarTy = GR.getOrCreateSPIRVBoolType(I, TII); 1268*0fca6ea1SDimitry Andric SPIRVType *SpvBoolTy = SpvBoolScalarTy; 1269*0fca6ea1SDimitry Andric Register NotEqualReg = ResVReg; 1270*0fca6ea1SDimitry Andric 1271*0fca6ea1SDimitry Andric if (IsVectorTy) { 1272*0fca6ea1SDimitry Andric NotEqualReg = IsBoolTy ? InputRegister 1273*0fca6ea1SDimitry Andric : MRI->createVirtualRegister(&SPIRV::IDRegClass); 1274*0fca6ea1SDimitry Andric const unsigned NumElts = InputType->getOperand(2).getImm(); 1275*0fca6ea1SDimitry Andric SpvBoolTy = GR.getOrCreateSPIRVVectorType(SpvBoolTy, NumElts, I, TII); 1276*0fca6ea1SDimitry Andric } 1277*0fca6ea1SDimitry Andric 1278*0fca6ea1SDimitry Andric if (!IsBoolTy) { 1279*0fca6ea1SDimitry Andric Register ConstZeroReg = 1280*0fca6ea1SDimitry Andric IsFloatTy ? buildZerosValF(InputType, I) : buildZerosVal(InputType, I); 1281*0fca6ea1SDimitry Andric 1282*0fca6ea1SDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(SpirvNotEqualId)) 1283*0fca6ea1SDimitry Andric .addDef(NotEqualReg) 1284*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(SpvBoolTy)) 1285*0fca6ea1SDimitry Andric .addUse(InputRegister) 1286*0fca6ea1SDimitry Andric .addUse(ConstZeroReg) 1287*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1288*0fca6ea1SDimitry Andric } 1289*0fca6ea1SDimitry Andric 1290*0fca6ea1SDimitry Andric if (!IsVectorTy) 1291*0fca6ea1SDimitry Andric return true; 1292*0fca6ea1SDimitry Andric 1293*0fca6ea1SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(OpAnyOrAll)) 1294*0fca6ea1SDimitry Andric .addDef(ResVReg) 1295*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(SpvBoolScalarTy)) 1296*0fca6ea1SDimitry Andric .addUse(NotEqualReg) 1297*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1298*0fca6ea1SDimitry Andric } 1299*0fca6ea1SDimitry Andric 1300*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectAll(Register ResVReg, 1301*0fca6ea1SDimitry Andric const SPIRVType *ResType, 1302*0fca6ea1SDimitry Andric MachineInstr &I) const { 1303*0fca6ea1SDimitry Andric return selectAnyOrAll(ResVReg, ResType, I, SPIRV::OpAll); 1304*0fca6ea1SDimitry Andric } 1305*0fca6ea1SDimitry Andric 1306*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectAny(Register ResVReg, 1307*0fca6ea1SDimitry Andric const SPIRVType *ResType, 1308*0fca6ea1SDimitry Andric MachineInstr &I) const { 1309*0fca6ea1SDimitry Andric return selectAnyOrAll(ResVReg, ResType, I, SPIRV::OpAny); 1310*0fca6ea1SDimitry Andric } 1311*0fca6ea1SDimitry Andric 1312*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectFmix(Register ResVReg, 1313*0fca6ea1SDimitry Andric const SPIRVType *ResType, 1314*0fca6ea1SDimitry Andric MachineInstr &I) const { 1315*0fca6ea1SDimitry Andric 1316*0fca6ea1SDimitry Andric assert(I.getNumOperands() == 5); 1317*0fca6ea1SDimitry Andric assert(I.getOperand(2).isReg()); 1318*0fca6ea1SDimitry Andric assert(I.getOperand(3).isReg()); 1319*0fca6ea1SDimitry Andric assert(I.getOperand(4).isReg()); 1320*0fca6ea1SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 1321*0fca6ea1SDimitry Andric 1322*0fca6ea1SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst)) 1323*0fca6ea1SDimitry Andric .addDef(ResVReg) 1324*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 1325*0fca6ea1SDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450)) 1326*0fca6ea1SDimitry Andric .addImm(GL::FMix) 1327*0fca6ea1SDimitry Andric .addUse(I.getOperand(2).getReg()) 1328*0fca6ea1SDimitry Andric .addUse(I.getOperand(3).getReg()) 1329*0fca6ea1SDimitry Andric .addUse(I.getOperand(4).getReg()) 1330*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1331*0fca6ea1SDimitry Andric } 1332*0fca6ea1SDimitry Andric 1333*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectRsqrt(Register ResVReg, 1334*0fca6ea1SDimitry Andric const SPIRVType *ResType, 1335*0fca6ea1SDimitry Andric MachineInstr &I) const { 1336*0fca6ea1SDimitry Andric 1337*0fca6ea1SDimitry Andric assert(I.getNumOperands() == 3); 1338*0fca6ea1SDimitry Andric assert(I.getOperand(2).isReg()); 1339*0fca6ea1SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 1340*0fca6ea1SDimitry Andric 1341*0fca6ea1SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst)) 1342*0fca6ea1SDimitry Andric .addDef(ResVReg) 1343*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 1344*0fca6ea1SDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450)) 1345*0fca6ea1SDimitry Andric .addImm(GL::InverseSqrt) 1346*0fca6ea1SDimitry Andric .addUse(I.getOperand(2).getReg()) 1347*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1348*0fca6ea1SDimitry Andric } 1349*0fca6ea1SDimitry Andric 135081ad6265SDimitry Andric bool SPIRVInstructionSelector::selectBitreverse(Register ResVReg, 135181ad6265SDimitry Andric const SPIRVType *ResType, 135281ad6265SDimitry Andric MachineInstr &I) const { 135381ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 135481ad6265SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpBitReverse)) 135581ad6265SDimitry Andric .addDef(ResVReg) 135681ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 135781ad6265SDimitry Andric .addUse(I.getOperand(1).getReg()) 135881ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 135981ad6265SDimitry Andric } 136081ad6265SDimitry Andric 1361*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectFreeze(Register ResVReg, 1362*0fca6ea1SDimitry Andric const SPIRVType *ResType, 1363*0fca6ea1SDimitry Andric MachineInstr &I) const { 1364*0fca6ea1SDimitry Andric // There is no way to implement `freeze` correctly without support on SPIR-V 1365*0fca6ea1SDimitry Andric // standard side, but we may at least address a simple (static) case when 1366*0fca6ea1SDimitry Andric // undef/poison value presence is obvious. The main benefit of even 1367*0fca6ea1SDimitry Andric // incomplete `freeze` support is preventing of translation from crashing due 1368*0fca6ea1SDimitry Andric // to lack of support on legalization and instruction selection steps. 1369*0fca6ea1SDimitry Andric if (!I.getOperand(0).isReg() || !I.getOperand(1).isReg()) 1370*0fca6ea1SDimitry Andric return false; 1371*0fca6ea1SDimitry Andric Register OpReg = I.getOperand(1).getReg(); 1372*0fca6ea1SDimitry Andric if (MachineInstr *Def = MRI->getVRegDef(OpReg)) { 1373*0fca6ea1SDimitry Andric Register Reg; 1374*0fca6ea1SDimitry Andric switch (Def->getOpcode()) { 1375*0fca6ea1SDimitry Andric case SPIRV::ASSIGN_TYPE: 1376*0fca6ea1SDimitry Andric if (MachineInstr *AssignToDef = 1377*0fca6ea1SDimitry Andric MRI->getVRegDef(Def->getOperand(1).getReg())) { 1378*0fca6ea1SDimitry Andric if (AssignToDef->getOpcode() == TargetOpcode::G_IMPLICIT_DEF) 1379*0fca6ea1SDimitry Andric Reg = Def->getOperand(2).getReg(); 1380*0fca6ea1SDimitry Andric } 1381*0fca6ea1SDimitry Andric break; 1382*0fca6ea1SDimitry Andric case SPIRV::OpUndef: 1383*0fca6ea1SDimitry Andric Reg = Def->getOperand(1).getReg(); 1384*0fca6ea1SDimitry Andric break; 1385*0fca6ea1SDimitry Andric } 1386*0fca6ea1SDimitry Andric unsigned DestOpCode; 1387*0fca6ea1SDimitry Andric if (Reg.isValid()) { 1388*0fca6ea1SDimitry Andric DestOpCode = SPIRV::OpConstantNull; 1389*0fca6ea1SDimitry Andric } else { 1390*0fca6ea1SDimitry Andric DestOpCode = TargetOpcode::COPY; 1391*0fca6ea1SDimitry Andric Reg = OpReg; 1392*0fca6ea1SDimitry Andric } 1393*0fca6ea1SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(DestOpCode)) 1394*0fca6ea1SDimitry Andric .addDef(I.getOperand(0).getReg()) 1395*0fca6ea1SDimitry Andric .addUse(Reg) 1396*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1397*0fca6ea1SDimitry Andric } 1398*0fca6ea1SDimitry Andric return false; 1399*0fca6ea1SDimitry Andric } 1400*0fca6ea1SDimitry Andric 140181ad6265SDimitry Andric bool SPIRVInstructionSelector::selectConstVector(Register ResVReg, 140281ad6265SDimitry Andric const SPIRVType *ResType, 140381ad6265SDimitry Andric MachineInstr &I) const { 140481ad6265SDimitry Andric // TODO: only const case is supported for now. 140581ad6265SDimitry Andric assert(std::all_of( 140681ad6265SDimitry Andric I.operands_begin(), I.operands_end(), [this](const MachineOperand &MO) { 140781ad6265SDimitry Andric if (MO.isDef()) 140881ad6265SDimitry Andric return true; 140981ad6265SDimitry Andric if (!MO.isReg()) 141081ad6265SDimitry Andric return false; 141181ad6265SDimitry Andric SPIRVType *ConstTy = this->MRI->getVRegDef(MO.getReg()); 141281ad6265SDimitry Andric assert(ConstTy && ConstTy->getOpcode() == SPIRV::ASSIGN_TYPE && 141381ad6265SDimitry Andric ConstTy->getOperand(1).isReg()); 141481ad6265SDimitry Andric Register ConstReg = ConstTy->getOperand(1).getReg(); 141581ad6265SDimitry Andric const MachineInstr *Const = this->MRI->getVRegDef(ConstReg); 141681ad6265SDimitry Andric assert(Const); 141781ad6265SDimitry Andric return (Const->getOpcode() == TargetOpcode::G_CONSTANT || 141881ad6265SDimitry Andric Const->getOpcode() == TargetOpcode::G_FCONSTANT); 141981ad6265SDimitry Andric })); 142081ad6265SDimitry Andric 142181ad6265SDimitry Andric auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), 142281ad6265SDimitry Andric TII.get(SPIRV::OpConstantComposite)) 142381ad6265SDimitry Andric .addDef(ResVReg) 142481ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)); 142581ad6265SDimitry Andric for (unsigned i = I.getNumExplicitDefs(); i < I.getNumExplicitOperands(); ++i) 142681ad6265SDimitry Andric MIB.addUse(I.getOperand(i).getReg()); 142781ad6265SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 142881ad6265SDimitry Andric } 142981ad6265SDimitry Andric 1430*0fca6ea1SDimitry Andric static unsigned getArrayComponentCount(MachineRegisterInfo *MRI, 1431*0fca6ea1SDimitry Andric const SPIRVType *ResType) { 1432*0fca6ea1SDimitry Andric Register OpReg = ResType->getOperand(2).getReg(); 1433*0fca6ea1SDimitry Andric SPIRVType *OpDef = MRI->getVRegDef(OpReg); 1434*0fca6ea1SDimitry Andric if (!OpDef) 1435*0fca6ea1SDimitry Andric return 0; 1436*0fca6ea1SDimitry Andric if (OpDef->getOpcode() == SPIRV::ASSIGN_TYPE && 1437*0fca6ea1SDimitry Andric OpDef->getOperand(1).isReg()) { 1438*0fca6ea1SDimitry Andric if (SPIRVType *RefDef = MRI->getVRegDef(OpDef->getOperand(1).getReg())) 1439*0fca6ea1SDimitry Andric OpDef = RefDef; 1440*0fca6ea1SDimitry Andric } 1441*0fca6ea1SDimitry Andric unsigned N = OpDef->getOpcode() == TargetOpcode::G_CONSTANT 1442*0fca6ea1SDimitry Andric ? OpDef->getOperand(1).getCImm()->getValue().getZExtValue() 1443*0fca6ea1SDimitry Andric : 0; 1444*0fca6ea1SDimitry Andric return N; 1445*0fca6ea1SDimitry Andric } 1446*0fca6ea1SDimitry Andric 1447*0fca6ea1SDimitry Andric // Return true if the type represents a constant register 1448*0fca6ea1SDimitry Andric static bool isConstReg(MachineRegisterInfo *MRI, SPIRVType *OpDef, 1449*0fca6ea1SDimitry Andric SmallPtrSet<SPIRVType *, 4> &Visited) { 1450*0fca6ea1SDimitry Andric if (OpDef->getOpcode() == SPIRV::ASSIGN_TYPE && 1451*0fca6ea1SDimitry Andric OpDef->getOperand(1).isReg()) { 1452*0fca6ea1SDimitry Andric if (SPIRVType *RefDef = MRI->getVRegDef(OpDef->getOperand(1).getReg())) 1453*0fca6ea1SDimitry Andric OpDef = RefDef; 1454*0fca6ea1SDimitry Andric } 1455*0fca6ea1SDimitry Andric 1456*0fca6ea1SDimitry Andric if (Visited.contains(OpDef)) 1457*0fca6ea1SDimitry Andric return true; 1458*0fca6ea1SDimitry Andric Visited.insert(OpDef); 1459*0fca6ea1SDimitry Andric 1460*0fca6ea1SDimitry Andric unsigned Opcode = OpDef->getOpcode(); 1461*0fca6ea1SDimitry Andric switch (Opcode) { 1462*0fca6ea1SDimitry Andric case TargetOpcode::G_CONSTANT: 1463*0fca6ea1SDimitry Andric case TargetOpcode::G_FCONSTANT: 1464*0fca6ea1SDimitry Andric return true; 1465*0fca6ea1SDimitry Andric case TargetOpcode::G_INTRINSIC: 1466*0fca6ea1SDimitry Andric case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: 1467*0fca6ea1SDimitry Andric case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: 1468*0fca6ea1SDimitry Andric return cast<GIntrinsic>(*OpDef).getIntrinsicID() == 1469*0fca6ea1SDimitry Andric Intrinsic::spv_const_composite; 1470*0fca6ea1SDimitry Andric case TargetOpcode::G_BUILD_VECTOR: 1471*0fca6ea1SDimitry Andric case TargetOpcode::G_SPLAT_VECTOR: { 1472*0fca6ea1SDimitry Andric for (unsigned i = OpDef->getNumExplicitDefs(); i < OpDef->getNumOperands(); 1473*0fca6ea1SDimitry Andric i++) { 1474*0fca6ea1SDimitry Andric SPIRVType *OpNestedDef = 1475*0fca6ea1SDimitry Andric OpDef->getOperand(i).isReg() 1476*0fca6ea1SDimitry Andric ? MRI->getVRegDef(OpDef->getOperand(i).getReg()) 1477*0fca6ea1SDimitry Andric : nullptr; 1478*0fca6ea1SDimitry Andric if (OpNestedDef && !isConstReg(MRI, OpNestedDef, Visited)) 1479*0fca6ea1SDimitry Andric return false; 1480*0fca6ea1SDimitry Andric } 1481*0fca6ea1SDimitry Andric return true; 1482*0fca6ea1SDimitry Andric } 1483*0fca6ea1SDimitry Andric } 1484*0fca6ea1SDimitry Andric return false; 1485*0fca6ea1SDimitry Andric } 1486*0fca6ea1SDimitry Andric 1487*0fca6ea1SDimitry Andric // Return true if the virtual register represents a constant 1488*0fca6ea1SDimitry Andric static bool isConstReg(MachineRegisterInfo *MRI, Register OpReg) { 1489*0fca6ea1SDimitry Andric SmallPtrSet<SPIRVType *, 4> Visited; 1490*0fca6ea1SDimitry Andric if (SPIRVType *OpDef = MRI->getVRegDef(OpReg)) 1491*0fca6ea1SDimitry Andric return isConstReg(MRI, OpDef, Visited); 1492*0fca6ea1SDimitry Andric return false; 1493*0fca6ea1SDimitry Andric } 1494*0fca6ea1SDimitry Andric 1495*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectSplatVector(Register ResVReg, 1496*0fca6ea1SDimitry Andric const SPIRVType *ResType, 1497*0fca6ea1SDimitry Andric MachineInstr &I) const { 1498*0fca6ea1SDimitry Andric unsigned N = 0; 1499*0fca6ea1SDimitry Andric if (ResType->getOpcode() == SPIRV::OpTypeVector) 1500*0fca6ea1SDimitry Andric N = GR.getScalarOrVectorComponentCount(ResType); 1501*0fca6ea1SDimitry Andric else if (ResType->getOpcode() == SPIRV::OpTypeArray) 1502*0fca6ea1SDimitry Andric N = getArrayComponentCount(MRI, ResType); 1503*0fca6ea1SDimitry Andric else 1504*0fca6ea1SDimitry Andric report_fatal_error("Cannot select G_SPLAT_VECTOR with a non-vector result"); 1505*0fca6ea1SDimitry Andric 1506*0fca6ea1SDimitry Andric unsigned OpIdx = I.getNumExplicitDefs(); 1507*0fca6ea1SDimitry Andric if (!I.getOperand(OpIdx).isReg()) 1508*0fca6ea1SDimitry Andric report_fatal_error("Unexpected argument in G_SPLAT_VECTOR"); 1509*0fca6ea1SDimitry Andric 1510*0fca6ea1SDimitry Andric // check if we may construct a constant vector 1511*0fca6ea1SDimitry Andric Register OpReg = I.getOperand(OpIdx).getReg(); 1512*0fca6ea1SDimitry Andric bool IsConst = isConstReg(MRI, OpReg); 1513*0fca6ea1SDimitry Andric 1514*0fca6ea1SDimitry Andric if (!IsConst && N < 2) 1515*0fca6ea1SDimitry Andric report_fatal_error( 1516*0fca6ea1SDimitry Andric "There must be at least two constituent operands in a vector"); 1517*0fca6ea1SDimitry Andric 1518*0fca6ea1SDimitry Andric auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), 1519*0fca6ea1SDimitry Andric TII.get(IsConst ? SPIRV::OpConstantComposite 1520*0fca6ea1SDimitry Andric : SPIRV::OpCompositeConstruct)) 1521*0fca6ea1SDimitry Andric .addDef(ResVReg) 1522*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)); 1523*0fca6ea1SDimitry Andric for (unsigned i = 0; i < N; ++i) 1524*0fca6ea1SDimitry Andric MIB.addUse(OpReg); 1525*0fca6ea1SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 1526*0fca6ea1SDimitry Andric } 1527*0fca6ea1SDimitry Andric 152881ad6265SDimitry Andric bool SPIRVInstructionSelector::selectCmp(Register ResVReg, 152981ad6265SDimitry Andric const SPIRVType *ResType, 153081ad6265SDimitry Andric unsigned CmpOpc, 153181ad6265SDimitry Andric MachineInstr &I) const { 153281ad6265SDimitry Andric Register Cmp0 = I.getOperand(2).getReg(); 153381ad6265SDimitry Andric Register Cmp1 = I.getOperand(3).getReg(); 153481ad6265SDimitry Andric assert(GR.getSPIRVTypeForVReg(Cmp0)->getOpcode() == 153581ad6265SDimitry Andric GR.getSPIRVTypeForVReg(Cmp1)->getOpcode() && 153681ad6265SDimitry Andric "CMP operands should have the same type"); 153781ad6265SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(CmpOpc)) 153881ad6265SDimitry Andric .addDef(ResVReg) 153981ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 154081ad6265SDimitry Andric .addUse(Cmp0) 154181ad6265SDimitry Andric .addUse(Cmp1) 154281ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 154381ad6265SDimitry Andric } 154481ad6265SDimitry Andric 154581ad6265SDimitry Andric bool SPIRVInstructionSelector::selectICmp(Register ResVReg, 154681ad6265SDimitry Andric const SPIRVType *ResType, 154781ad6265SDimitry Andric MachineInstr &I) const { 154881ad6265SDimitry Andric auto Pred = I.getOperand(1).getPredicate(); 154981ad6265SDimitry Andric unsigned CmpOpc; 155081ad6265SDimitry Andric 155181ad6265SDimitry Andric Register CmpOperand = I.getOperand(2).getReg(); 155281ad6265SDimitry Andric if (GR.isScalarOfType(CmpOperand, SPIRV::OpTypePointer)) 155381ad6265SDimitry Andric CmpOpc = getPtrCmpOpcode(Pred); 155481ad6265SDimitry Andric else if (GR.isScalarOrVectorOfType(CmpOperand, SPIRV::OpTypeBool)) 155581ad6265SDimitry Andric CmpOpc = getBoolCmpOpcode(Pred); 155681ad6265SDimitry Andric else 155781ad6265SDimitry Andric CmpOpc = getICmpOpcode(Pred); 155881ad6265SDimitry Andric return selectCmp(ResVReg, ResType, CmpOpc, I); 155981ad6265SDimitry Andric } 156081ad6265SDimitry Andric 156181ad6265SDimitry Andric void SPIRVInstructionSelector::renderFImm32(MachineInstrBuilder &MIB, 156281ad6265SDimitry Andric const MachineInstr &I, 156381ad6265SDimitry Andric int OpIdx) const { 156481ad6265SDimitry Andric assert(I.getOpcode() == TargetOpcode::G_FCONSTANT && OpIdx == -1 && 156581ad6265SDimitry Andric "Expected G_FCONSTANT"); 156681ad6265SDimitry Andric const ConstantFP *FPImm = I.getOperand(1).getFPImm(); 156781ad6265SDimitry Andric addNumImm(FPImm->getValueAPF().bitcastToAPInt(), MIB); 156881ad6265SDimitry Andric } 156981ad6265SDimitry Andric 157081ad6265SDimitry Andric void SPIRVInstructionSelector::renderImm32(MachineInstrBuilder &MIB, 157181ad6265SDimitry Andric const MachineInstr &I, 157281ad6265SDimitry Andric int OpIdx) const { 157381ad6265SDimitry Andric assert(I.getOpcode() == TargetOpcode::G_CONSTANT && OpIdx == -1 && 157481ad6265SDimitry Andric "Expected G_CONSTANT"); 157581ad6265SDimitry Andric addNumImm(I.getOperand(1).getCImm()->getValue(), MIB); 157681ad6265SDimitry Andric } 157781ad6265SDimitry Andric 157881ad6265SDimitry Andric Register 157981ad6265SDimitry Andric SPIRVInstructionSelector::buildI32Constant(uint32_t Val, MachineInstr &I, 158081ad6265SDimitry Andric const SPIRVType *ResType) const { 1581753f127fSDimitry Andric Type *LLVMTy = IntegerType::get(GR.CurMF->getFunction().getContext(), 32); 158281ad6265SDimitry Andric const SPIRVType *SpvI32Ty = 158381ad6265SDimitry Andric ResType ? ResType : GR.getOrCreateSPIRVIntegerType(32, I, TII); 1584753f127fSDimitry Andric // Find a constant in DT or build a new one. 1585753f127fSDimitry Andric auto ConstInt = ConstantInt::get(LLVMTy, Val); 1586753f127fSDimitry Andric Register NewReg = GR.find(ConstInt, GR.CurMF); 1587753f127fSDimitry Andric if (!NewReg.isValid()) { 158881ad6265SDimitry Andric NewReg = MRI->createGenericVirtualRegister(LLT::scalar(32)); 1589753f127fSDimitry Andric GR.add(ConstInt, GR.CurMF, NewReg); 159081ad6265SDimitry Andric MachineInstr *MI; 159181ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 159281ad6265SDimitry Andric if (Val == 0) { 159381ad6265SDimitry Andric MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantNull)) 159481ad6265SDimitry Andric .addDef(NewReg) 159581ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(SpvI32Ty)); 159681ad6265SDimitry Andric } else { 159781ad6265SDimitry Andric MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantI)) 159881ad6265SDimitry Andric .addDef(NewReg) 159981ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(SpvI32Ty)) 160081ad6265SDimitry Andric .addImm(APInt(32, Val).getZExtValue()); 160181ad6265SDimitry Andric } 160281ad6265SDimitry Andric constrainSelectedInstRegOperands(*MI, TII, TRI, RBI); 1603753f127fSDimitry Andric } 160481ad6265SDimitry Andric return NewReg; 160581ad6265SDimitry Andric } 160681ad6265SDimitry Andric 160781ad6265SDimitry Andric bool SPIRVInstructionSelector::selectFCmp(Register ResVReg, 160881ad6265SDimitry Andric const SPIRVType *ResType, 160981ad6265SDimitry Andric MachineInstr &I) const { 161081ad6265SDimitry Andric unsigned CmpOp = getFCmpOpcode(I.getOperand(1).getPredicate()); 161181ad6265SDimitry Andric return selectCmp(ResVReg, ResType, CmpOp, I); 161281ad6265SDimitry Andric } 161381ad6265SDimitry Andric 161481ad6265SDimitry Andric Register SPIRVInstructionSelector::buildZerosVal(const SPIRVType *ResType, 161581ad6265SDimitry Andric MachineInstr &I) const { 1616*0fca6ea1SDimitry Andric // OpenCL uses nulls for Zero. In HLSL we don't use null constants. 1617*0fca6ea1SDimitry Andric bool ZeroAsNull = STI.isOpenCLEnv(); 1618fcaf7f86SDimitry Andric if (ResType->getOpcode() == SPIRV::OpTypeVector) 1619*0fca6ea1SDimitry Andric return GR.getOrCreateConstVector(0UL, I, ResType, TII, ZeroAsNull); 1620*0fca6ea1SDimitry Andric return GR.getOrCreateConstInt(0, I, ResType, TII, ZeroAsNull); 1621*0fca6ea1SDimitry Andric } 1622*0fca6ea1SDimitry Andric 1623*0fca6ea1SDimitry Andric static APFloat getZeroFP(const Type *LLVMFloatTy) { 1624*0fca6ea1SDimitry Andric if (!LLVMFloatTy) 1625*0fca6ea1SDimitry Andric return APFloat::getZero(APFloat::IEEEsingle()); 1626*0fca6ea1SDimitry Andric switch (LLVMFloatTy->getScalarType()->getTypeID()) { 1627*0fca6ea1SDimitry Andric case Type::HalfTyID: 1628*0fca6ea1SDimitry Andric return APFloat::getZero(APFloat::IEEEhalf()); 1629*0fca6ea1SDimitry Andric default: 1630*0fca6ea1SDimitry Andric case Type::FloatTyID: 1631*0fca6ea1SDimitry Andric return APFloat::getZero(APFloat::IEEEsingle()); 1632*0fca6ea1SDimitry Andric case Type::DoubleTyID: 1633*0fca6ea1SDimitry Andric return APFloat::getZero(APFloat::IEEEdouble()); 1634*0fca6ea1SDimitry Andric } 1635*0fca6ea1SDimitry Andric } 1636*0fca6ea1SDimitry Andric 1637*0fca6ea1SDimitry Andric Register SPIRVInstructionSelector::buildZerosValF(const SPIRVType *ResType, 1638*0fca6ea1SDimitry Andric MachineInstr &I) const { 1639*0fca6ea1SDimitry Andric // OpenCL uses nulls for Zero. In HLSL we don't use null constants. 1640*0fca6ea1SDimitry Andric bool ZeroAsNull = STI.isOpenCLEnv(); 1641*0fca6ea1SDimitry Andric APFloat VZero = getZeroFP(GR.getTypeForSPIRVType(ResType)); 1642*0fca6ea1SDimitry Andric if (ResType->getOpcode() == SPIRV::OpTypeVector) 1643*0fca6ea1SDimitry Andric return GR.getOrCreateConstVector(VZero, I, ResType, TII, ZeroAsNull); 1644*0fca6ea1SDimitry Andric return GR.getOrCreateConstFP(VZero, I, ResType, TII, ZeroAsNull); 164581ad6265SDimitry Andric } 164681ad6265SDimitry Andric 164781ad6265SDimitry Andric Register SPIRVInstructionSelector::buildOnesVal(bool AllOnes, 164881ad6265SDimitry Andric const SPIRVType *ResType, 164981ad6265SDimitry Andric MachineInstr &I) const { 165081ad6265SDimitry Andric unsigned BitWidth = GR.getScalarOrVectorBitWidth(ResType); 165106c3fb27SDimitry Andric APInt One = 165206c3fb27SDimitry Andric AllOnes ? APInt::getAllOnes(BitWidth) : APInt::getOneBitSet(BitWidth, 0); 1653fcaf7f86SDimitry Andric if (ResType->getOpcode() == SPIRV::OpTypeVector) 1654*0fca6ea1SDimitry Andric return GR.getOrCreateConstVector(One.getZExtValue(), I, ResType, TII); 1655fcaf7f86SDimitry Andric return GR.getOrCreateConstInt(One.getZExtValue(), I, ResType, TII); 165681ad6265SDimitry Andric } 165781ad6265SDimitry Andric 165881ad6265SDimitry Andric bool SPIRVInstructionSelector::selectSelect(Register ResVReg, 165981ad6265SDimitry Andric const SPIRVType *ResType, 166081ad6265SDimitry Andric MachineInstr &I, 166181ad6265SDimitry Andric bool IsSigned) const { 166281ad6265SDimitry Andric // To extend a bool, we need to use OpSelect between constants. 166381ad6265SDimitry Andric Register ZeroReg = buildZerosVal(ResType, I); 166481ad6265SDimitry Andric Register OneReg = buildOnesVal(IsSigned, ResType, I); 166581ad6265SDimitry Andric bool IsScalarBool = 166681ad6265SDimitry Andric GR.isScalarOfType(I.getOperand(1).getReg(), SPIRV::OpTypeBool); 166781ad6265SDimitry Andric unsigned Opcode = 166881ad6265SDimitry Andric IsScalarBool ? SPIRV::OpSelectSISCond : SPIRV::OpSelectSIVCond; 166981ad6265SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcode)) 167081ad6265SDimitry Andric .addDef(ResVReg) 167181ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 167281ad6265SDimitry Andric .addUse(I.getOperand(1).getReg()) 167381ad6265SDimitry Andric .addUse(OneReg) 167481ad6265SDimitry Andric .addUse(ZeroReg) 167581ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 167681ad6265SDimitry Andric } 167781ad6265SDimitry Andric 167881ad6265SDimitry Andric bool SPIRVInstructionSelector::selectIToF(Register ResVReg, 167981ad6265SDimitry Andric const SPIRVType *ResType, 168081ad6265SDimitry Andric MachineInstr &I, bool IsSigned, 168181ad6265SDimitry Andric unsigned Opcode) const { 168281ad6265SDimitry Andric Register SrcReg = I.getOperand(1).getReg(); 168381ad6265SDimitry Andric // We can convert bool value directly to float type without OpConvert*ToF, 168481ad6265SDimitry Andric // however the translator generates OpSelect+OpConvert*ToF, so we do the same. 168581ad6265SDimitry Andric if (GR.isScalarOrVectorOfType(I.getOperand(1).getReg(), SPIRV::OpTypeBool)) { 168681ad6265SDimitry Andric unsigned BitWidth = GR.getScalarOrVectorBitWidth(ResType); 168781ad6265SDimitry Andric SPIRVType *TmpType = GR.getOrCreateSPIRVIntegerType(BitWidth, I, TII); 168881ad6265SDimitry Andric if (ResType->getOpcode() == SPIRV::OpTypeVector) { 168981ad6265SDimitry Andric const unsigned NumElts = ResType->getOperand(2).getImm(); 169081ad6265SDimitry Andric TmpType = GR.getOrCreateSPIRVVectorType(TmpType, NumElts, I, TII); 169181ad6265SDimitry Andric } 169281ad6265SDimitry Andric SrcReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 169381ad6265SDimitry Andric selectSelect(SrcReg, TmpType, I, false); 169481ad6265SDimitry Andric } 169581ad6265SDimitry Andric return selectUnOpWithSrc(ResVReg, ResType, I, SrcReg, Opcode); 169681ad6265SDimitry Andric } 169781ad6265SDimitry Andric 169881ad6265SDimitry Andric bool SPIRVInstructionSelector::selectExt(Register ResVReg, 169981ad6265SDimitry Andric const SPIRVType *ResType, 170081ad6265SDimitry Andric MachineInstr &I, bool IsSigned) const { 1701*0fca6ea1SDimitry Andric Register SrcReg = I.getOperand(1).getReg(); 1702*0fca6ea1SDimitry Andric if (GR.isScalarOrVectorOfType(SrcReg, SPIRV::OpTypeBool)) 170381ad6265SDimitry Andric return selectSelect(ResVReg, ResType, I, IsSigned); 1704*0fca6ea1SDimitry Andric 1705*0fca6ea1SDimitry Andric SPIRVType *SrcType = GR.getSPIRVTypeForVReg(SrcReg); 1706*0fca6ea1SDimitry Andric if (SrcType == ResType) 1707*0fca6ea1SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), 1708*0fca6ea1SDimitry Andric TII.get(TargetOpcode::COPY)) 1709*0fca6ea1SDimitry Andric .addDef(ResVReg) 1710*0fca6ea1SDimitry Andric .addUse(SrcReg) 1711*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1712*0fca6ea1SDimitry Andric 171381ad6265SDimitry Andric unsigned Opcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert; 171481ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, Opcode); 171581ad6265SDimitry Andric } 171681ad6265SDimitry Andric 171781ad6265SDimitry Andric bool SPIRVInstructionSelector::selectIntToBool(Register IntReg, 171881ad6265SDimitry Andric Register ResVReg, 1719bdd1243dSDimitry Andric MachineInstr &I, 172081ad6265SDimitry Andric const SPIRVType *IntTy, 1721bdd1243dSDimitry Andric const SPIRVType *BoolTy) const { 172281ad6265SDimitry Andric // To truncate to a bool, we use OpBitwiseAnd 1 and OpINotEqual to zero. 172381ad6265SDimitry Andric Register BitIntReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 172481ad6265SDimitry Andric bool IsVectorTy = IntTy->getOpcode() == SPIRV::OpTypeVector; 172581ad6265SDimitry Andric unsigned Opcode = IsVectorTy ? SPIRV::OpBitwiseAndV : SPIRV::OpBitwiseAndS; 172681ad6265SDimitry Andric Register Zero = buildZerosVal(IntTy, I); 172781ad6265SDimitry Andric Register One = buildOnesVal(false, IntTy, I); 172881ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 172981ad6265SDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode)) 173081ad6265SDimitry Andric .addDef(BitIntReg) 173181ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(IntTy)) 173281ad6265SDimitry Andric .addUse(IntReg) 173381ad6265SDimitry Andric .addUse(One) 173481ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 173581ad6265SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpINotEqual)) 173681ad6265SDimitry Andric .addDef(ResVReg) 173781ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(BoolTy)) 173881ad6265SDimitry Andric .addUse(BitIntReg) 173981ad6265SDimitry Andric .addUse(Zero) 174081ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 174181ad6265SDimitry Andric } 174281ad6265SDimitry Andric 174381ad6265SDimitry Andric bool SPIRVInstructionSelector::selectTrunc(Register ResVReg, 174481ad6265SDimitry Andric const SPIRVType *ResType, 174581ad6265SDimitry Andric MachineInstr &I) const { 174681ad6265SDimitry Andric Register IntReg = I.getOperand(1).getReg(); 174781ad6265SDimitry Andric const SPIRVType *ArgType = GR.getSPIRVTypeForVReg(IntReg); 1748*0fca6ea1SDimitry Andric if (GR.isScalarOrVectorOfType(ResVReg, SPIRV::OpTypeBool)) 1749bdd1243dSDimitry Andric return selectIntToBool(IntReg, ResVReg, I, ArgType, ResType); 1750*0fca6ea1SDimitry Andric if (ArgType == ResType) 1751*0fca6ea1SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), 1752*0fca6ea1SDimitry Andric TII.get(TargetOpcode::COPY)) 1753*0fca6ea1SDimitry Andric .addDef(ResVReg) 1754*0fca6ea1SDimitry Andric .addUse(IntReg) 1755*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 175681ad6265SDimitry Andric bool IsSigned = GR.isScalarOrVectorSigned(ResType); 175781ad6265SDimitry Andric unsigned Opcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert; 175881ad6265SDimitry Andric return selectUnOp(ResVReg, ResType, I, Opcode); 175981ad6265SDimitry Andric } 176081ad6265SDimitry Andric 176181ad6265SDimitry Andric bool SPIRVInstructionSelector::selectConst(Register ResVReg, 176281ad6265SDimitry Andric const SPIRVType *ResType, 176381ad6265SDimitry Andric const APInt &Imm, 176481ad6265SDimitry Andric MachineInstr &I) const { 1765fcaf7f86SDimitry Andric unsigned TyOpcode = ResType->getOpcode(); 176606c3fb27SDimitry Andric assert(TyOpcode != SPIRV::OpTypePointer || Imm.isZero()); 176781ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 1768fcaf7f86SDimitry Andric if ((TyOpcode == SPIRV::OpTypePointer || TyOpcode == SPIRV::OpTypeEvent) && 176906c3fb27SDimitry Andric Imm.isZero()) 177081ad6265SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantNull)) 177181ad6265SDimitry Andric .addDef(ResVReg) 177281ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 177381ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1774fcaf7f86SDimitry Andric if (TyOpcode == SPIRV::OpTypeInt) { 177506c3fb27SDimitry Andric assert(Imm.getBitWidth() <= 64 && "Unsupported integer width!"); 1776fcaf7f86SDimitry Andric Register Reg = GR.getOrCreateConstInt(Imm.getZExtValue(), I, ResType, TII); 1777fcaf7f86SDimitry Andric if (Reg == ResVReg) 1778fcaf7f86SDimitry Andric return true; 1779fcaf7f86SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) 1780fcaf7f86SDimitry Andric .addDef(ResVReg) 1781fcaf7f86SDimitry Andric .addUse(Reg) 1782fcaf7f86SDimitry Andric .constrainAllUses(TII, TRI, RBI); 178381ad6265SDimitry Andric } 178481ad6265SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantI)) 178581ad6265SDimitry Andric .addDef(ResVReg) 178681ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)); 178781ad6265SDimitry Andric // <=32-bit integers should be caught by the sdag pattern. 178881ad6265SDimitry Andric assert(Imm.getBitWidth() > 32); 178981ad6265SDimitry Andric addNumImm(Imm, MIB); 179081ad6265SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 179181ad6265SDimitry Andric } 179281ad6265SDimitry Andric 179381ad6265SDimitry Andric bool SPIRVInstructionSelector::selectOpUndef(Register ResVReg, 179481ad6265SDimitry Andric const SPIRVType *ResType, 179581ad6265SDimitry Andric MachineInstr &I) const { 179681ad6265SDimitry Andric return BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpUndef)) 179781ad6265SDimitry Andric .addDef(ResVReg) 179881ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 179981ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 180081ad6265SDimitry Andric } 180181ad6265SDimitry Andric 180281ad6265SDimitry Andric static bool isImm(const MachineOperand &MO, MachineRegisterInfo *MRI) { 180381ad6265SDimitry Andric assert(MO.isReg()); 180481ad6265SDimitry Andric const SPIRVType *TypeInst = MRI->getVRegDef(MO.getReg()); 1805*0fca6ea1SDimitry Andric if (TypeInst->getOpcode() == SPIRV::ASSIGN_TYPE) { 180681ad6265SDimitry Andric assert(TypeInst->getOperand(1).isReg()); 180781ad6265SDimitry Andric MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg()); 180881ad6265SDimitry Andric return ImmInst->getOpcode() == TargetOpcode::G_CONSTANT; 180981ad6265SDimitry Andric } 1810*0fca6ea1SDimitry Andric return TypeInst->getOpcode() == SPIRV::OpConstantI; 1811*0fca6ea1SDimitry Andric } 181281ad6265SDimitry Andric 181381ad6265SDimitry Andric static int64_t foldImm(const MachineOperand &MO, MachineRegisterInfo *MRI) { 181481ad6265SDimitry Andric const SPIRVType *TypeInst = MRI->getVRegDef(MO.getReg()); 1815*0fca6ea1SDimitry Andric if (TypeInst->getOpcode() == SPIRV::OpConstantI) 1816*0fca6ea1SDimitry Andric return TypeInst->getOperand(2).getImm(); 181781ad6265SDimitry Andric MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg()); 181881ad6265SDimitry Andric assert(ImmInst->getOpcode() == TargetOpcode::G_CONSTANT); 181981ad6265SDimitry Andric return ImmInst->getOperand(1).getCImm()->getZExtValue(); 182081ad6265SDimitry Andric } 182181ad6265SDimitry Andric 182281ad6265SDimitry Andric bool SPIRVInstructionSelector::selectInsertVal(Register ResVReg, 182381ad6265SDimitry Andric const SPIRVType *ResType, 182481ad6265SDimitry Andric MachineInstr &I) const { 182581ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 1826fcaf7f86SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeInsert)) 182781ad6265SDimitry Andric .addDef(ResVReg) 182881ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 182981ad6265SDimitry Andric // object to insert 183081ad6265SDimitry Andric .addUse(I.getOperand(3).getReg()) 183181ad6265SDimitry Andric // composite to insert into 1832fcaf7f86SDimitry Andric .addUse(I.getOperand(2).getReg()); 1833fcaf7f86SDimitry Andric for (unsigned i = 4; i < I.getNumOperands(); i++) 1834fcaf7f86SDimitry Andric MIB.addImm(foldImm(I.getOperand(i), MRI)); 1835fcaf7f86SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 183681ad6265SDimitry Andric } 183781ad6265SDimitry Andric 183881ad6265SDimitry Andric bool SPIRVInstructionSelector::selectExtractVal(Register ResVReg, 183981ad6265SDimitry Andric const SPIRVType *ResType, 184081ad6265SDimitry Andric MachineInstr &I) const { 184181ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 1842fcaf7f86SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeExtract)) 184381ad6265SDimitry Andric .addDef(ResVReg) 184481ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 1845fcaf7f86SDimitry Andric .addUse(I.getOperand(2).getReg()); 1846fcaf7f86SDimitry Andric for (unsigned i = 3; i < I.getNumOperands(); i++) 1847fcaf7f86SDimitry Andric MIB.addImm(foldImm(I.getOperand(i), MRI)); 1848fcaf7f86SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 184981ad6265SDimitry Andric } 185081ad6265SDimitry Andric 185181ad6265SDimitry Andric bool SPIRVInstructionSelector::selectInsertElt(Register ResVReg, 185281ad6265SDimitry Andric const SPIRVType *ResType, 185381ad6265SDimitry Andric MachineInstr &I) const { 185481ad6265SDimitry Andric if (isImm(I.getOperand(4), MRI)) 185581ad6265SDimitry Andric return selectInsertVal(ResVReg, ResType, I); 185681ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 185781ad6265SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpVectorInsertDynamic)) 185881ad6265SDimitry Andric .addDef(ResVReg) 185981ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 186081ad6265SDimitry Andric .addUse(I.getOperand(2).getReg()) 186181ad6265SDimitry Andric .addUse(I.getOperand(3).getReg()) 186281ad6265SDimitry Andric .addUse(I.getOperand(4).getReg()) 186381ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 186481ad6265SDimitry Andric } 186581ad6265SDimitry Andric 186681ad6265SDimitry Andric bool SPIRVInstructionSelector::selectExtractElt(Register ResVReg, 186781ad6265SDimitry Andric const SPIRVType *ResType, 186881ad6265SDimitry Andric MachineInstr &I) const { 186981ad6265SDimitry Andric if (isImm(I.getOperand(3), MRI)) 187081ad6265SDimitry Andric return selectExtractVal(ResVReg, ResType, I); 187181ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 187281ad6265SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpVectorExtractDynamic)) 187381ad6265SDimitry Andric .addDef(ResVReg) 187481ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 187581ad6265SDimitry Andric .addUse(I.getOperand(2).getReg()) 187681ad6265SDimitry Andric .addUse(I.getOperand(3).getReg()) 187781ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 187881ad6265SDimitry Andric } 187981ad6265SDimitry Andric 188081ad6265SDimitry Andric bool SPIRVInstructionSelector::selectGEP(Register ResVReg, 188181ad6265SDimitry Andric const SPIRVType *ResType, 188281ad6265SDimitry Andric MachineInstr &I) const { 18835f757f3fSDimitry Andric const bool IsGEPInBounds = I.getOperand(2).getImm(); 18845f757f3fSDimitry Andric 18855f757f3fSDimitry Andric // OpAccessChain could be used for OpenCL, but the SPIRV-LLVM Translator only 18865f757f3fSDimitry Andric // relies on PtrAccessChain, so we'll try not to deviate. For Vulkan however, 18875f757f3fSDimitry Andric // we have to use Op[InBounds]AccessChain. 18885f757f3fSDimitry Andric const unsigned Opcode = STI.isVulkanEnv() 18895f757f3fSDimitry Andric ? (IsGEPInBounds ? SPIRV::OpInBoundsAccessChain 18905f757f3fSDimitry Andric : SPIRV::OpAccessChain) 18915f757f3fSDimitry Andric : (IsGEPInBounds ? SPIRV::OpInBoundsPtrAccessChain 18925f757f3fSDimitry Andric : SPIRV::OpPtrAccessChain); 18935f757f3fSDimitry Andric 189481ad6265SDimitry Andric auto Res = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcode)) 189581ad6265SDimitry Andric .addDef(ResVReg) 189681ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 189781ad6265SDimitry Andric // Object to get a pointer to. 189881ad6265SDimitry Andric .addUse(I.getOperand(3).getReg()); 189981ad6265SDimitry Andric // Adding indices. 19005f757f3fSDimitry Andric const unsigned StartingIndex = 19015f757f3fSDimitry Andric (Opcode == SPIRV::OpAccessChain || Opcode == SPIRV::OpInBoundsAccessChain) 19025f757f3fSDimitry Andric ? 5 19035f757f3fSDimitry Andric : 4; 19045f757f3fSDimitry Andric for (unsigned i = StartingIndex; i < I.getNumExplicitOperands(); ++i) 190581ad6265SDimitry Andric Res.addUse(I.getOperand(i).getReg()); 190681ad6265SDimitry Andric return Res.constrainAllUses(TII, TRI, RBI); 190781ad6265SDimitry Andric } 190881ad6265SDimitry Andric 1909*0fca6ea1SDimitry Andric // Maybe wrap a value into OpSpecConstantOp 1910*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::wrapIntoSpecConstantOp( 1911*0fca6ea1SDimitry Andric MachineInstr &I, SmallVector<Register> &CompositeArgs) const { 1912*0fca6ea1SDimitry Andric bool Result = true; 1913*0fca6ea1SDimitry Andric unsigned Lim = I.getNumExplicitOperands(); 1914*0fca6ea1SDimitry Andric for (unsigned i = I.getNumExplicitDefs() + 1; i < Lim; ++i) { 1915*0fca6ea1SDimitry Andric Register OpReg = I.getOperand(i).getReg(); 1916*0fca6ea1SDimitry Andric SPIRVType *OpDefine = MRI->getVRegDef(OpReg); 1917*0fca6ea1SDimitry Andric SPIRVType *OpType = GR.getSPIRVTypeForVReg(OpReg); 1918*0fca6ea1SDimitry Andric SmallPtrSet<SPIRVType *, 4> Visited; 1919*0fca6ea1SDimitry Andric if (!OpDefine || !OpType || isConstReg(MRI, OpDefine, Visited) || 1920*0fca6ea1SDimitry Andric OpDefine->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST || 1921*0fca6ea1SDimitry Andric GR.isAggregateType(OpType)) { 1922*0fca6ea1SDimitry Andric // The case of G_ADDRSPACE_CAST inside spv_const_composite() is processed 1923*0fca6ea1SDimitry Andric // by selectAddrSpaceCast() 1924*0fca6ea1SDimitry Andric CompositeArgs.push_back(OpReg); 1925*0fca6ea1SDimitry Andric continue; 1926*0fca6ea1SDimitry Andric } 1927*0fca6ea1SDimitry Andric MachineFunction *MF = I.getMF(); 1928*0fca6ea1SDimitry Andric Register WrapReg = GR.find(OpDefine, MF); 1929*0fca6ea1SDimitry Andric if (WrapReg.isValid()) { 1930*0fca6ea1SDimitry Andric CompositeArgs.push_back(WrapReg); 1931*0fca6ea1SDimitry Andric continue; 1932*0fca6ea1SDimitry Andric } 1933*0fca6ea1SDimitry Andric // Create a new register for the wrapper 1934*0fca6ea1SDimitry Andric WrapReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 1935*0fca6ea1SDimitry Andric GR.add(OpDefine, MF, WrapReg); 1936*0fca6ea1SDimitry Andric CompositeArgs.push_back(WrapReg); 1937*0fca6ea1SDimitry Andric // Decorate the wrapper register and generate a new instruction 1938*0fca6ea1SDimitry Andric MRI->setType(WrapReg, LLT::pointer(0, 32)); 1939*0fca6ea1SDimitry Andric GR.assignSPIRVTypeToVReg(OpType, WrapReg, *MF); 1940*0fca6ea1SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 1941*0fca6ea1SDimitry Andric Result = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSpecConstantOp)) 1942*0fca6ea1SDimitry Andric .addDef(WrapReg) 1943*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(OpType)) 1944*0fca6ea1SDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::Opcode::Bitcast)) 1945*0fca6ea1SDimitry Andric .addUse(OpReg) 1946*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 1947*0fca6ea1SDimitry Andric if (!Result) 1948*0fca6ea1SDimitry Andric break; 1949*0fca6ea1SDimitry Andric } 1950*0fca6ea1SDimitry Andric return Result; 1951*0fca6ea1SDimitry Andric } 1952*0fca6ea1SDimitry Andric 195381ad6265SDimitry Andric bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, 195481ad6265SDimitry Andric const SPIRVType *ResType, 195581ad6265SDimitry Andric MachineInstr &I) const { 195681ad6265SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 1957*0fca6ea1SDimitry Andric Intrinsic::ID IID = cast<GIntrinsic>(I).getIntrinsicID(); 1958*0fca6ea1SDimitry Andric switch (IID) { 195981ad6265SDimitry Andric case Intrinsic::spv_load: 196081ad6265SDimitry Andric return selectLoad(ResVReg, ResType, I); 196181ad6265SDimitry Andric case Intrinsic::spv_store: 196281ad6265SDimitry Andric return selectStore(I); 196381ad6265SDimitry Andric case Intrinsic::spv_extractv: 196481ad6265SDimitry Andric return selectExtractVal(ResVReg, ResType, I); 196581ad6265SDimitry Andric case Intrinsic::spv_insertv: 196681ad6265SDimitry Andric return selectInsertVal(ResVReg, ResType, I); 196781ad6265SDimitry Andric case Intrinsic::spv_extractelt: 196881ad6265SDimitry Andric return selectExtractElt(ResVReg, ResType, I); 196981ad6265SDimitry Andric case Intrinsic::spv_insertelt: 197081ad6265SDimitry Andric return selectInsertElt(ResVReg, ResType, I); 197181ad6265SDimitry Andric case Intrinsic::spv_gep: 197281ad6265SDimitry Andric return selectGEP(ResVReg, ResType, I); 197381ad6265SDimitry Andric case Intrinsic::spv_unref_global: 197481ad6265SDimitry Andric case Intrinsic::spv_init_global: { 197581ad6265SDimitry Andric MachineInstr *MI = MRI->getVRegDef(I.getOperand(1).getReg()); 197681ad6265SDimitry Andric MachineInstr *Init = I.getNumExplicitOperands() > 2 197781ad6265SDimitry Andric ? MRI->getVRegDef(I.getOperand(2).getReg()) 197881ad6265SDimitry Andric : nullptr; 197981ad6265SDimitry Andric assert(MI); 198081ad6265SDimitry Andric return selectGlobalValue(MI->getOperand(0).getReg(), *MI, Init); 198106c3fb27SDimitry Andric } 198206c3fb27SDimitry Andric case Intrinsic::spv_undef: { 198306c3fb27SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpUndef)) 198406c3fb27SDimitry Andric .addDef(ResVReg) 198506c3fb27SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)); 198606c3fb27SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 198706c3fb27SDimitry Andric } 198881ad6265SDimitry Andric case Intrinsic::spv_const_composite: { 198981ad6265SDimitry Andric // If no values are attached, the composite is null constant. 199081ad6265SDimitry Andric bool IsNull = I.getNumExplicitDefs() + 1 == I.getNumExplicitOperands(); 1991*0fca6ea1SDimitry Andric // Select a proper instruction. 1992*0fca6ea1SDimitry Andric unsigned Opcode = SPIRV::OpConstantNull; 1993*0fca6ea1SDimitry Andric SmallVector<Register> CompositeArgs; 1994*0fca6ea1SDimitry Andric if (!IsNull) { 1995*0fca6ea1SDimitry Andric Opcode = SPIRV::OpConstantComposite; 1996*0fca6ea1SDimitry Andric if (!wrapIntoSpecConstantOp(I, CompositeArgs)) 1997*0fca6ea1SDimitry Andric return false; 1998*0fca6ea1SDimitry Andric } 199981ad6265SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode)) 200081ad6265SDimitry Andric .addDef(ResVReg) 200181ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)); 200281ad6265SDimitry Andric // skip type MD node we already used when generated assign.type for this 200381ad6265SDimitry Andric if (!IsNull) { 2004*0fca6ea1SDimitry Andric for (Register OpReg : CompositeArgs) 2005*0fca6ea1SDimitry Andric MIB.addUse(OpReg); 200681ad6265SDimitry Andric } 200781ad6265SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 200806c3fb27SDimitry Andric } 200981ad6265SDimitry Andric case Intrinsic::spv_assign_name: { 201081ad6265SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpName)); 201181ad6265SDimitry Andric MIB.addUse(I.getOperand(I.getNumExplicitDefs() + 1).getReg()); 201281ad6265SDimitry Andric for (unsigned i = I.getNumExplicitDefs() + 2; 201381ad6265SDimitry Andric i < I.getNumExplicitOperands(); ++i) { 201481ad6265SDimitry Andric MIB.addImm(I.getOperand(i).getImm()); 201581ad6265SDimitry Andric } 201681ad6265SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 201706c3fb27SDimitry Andric } 201881ad6265SDimitry Andric case Intrinsic::spv_switch: { 201981ad6265SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSwitch)); 202081ad6265SDimitry Andric for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) { 202181ad6265SDimitry Andric if (I.getOperand(i).isReg()) 202281ad6265SDimitry Andric MIB.addReg(I.getOperand(i).getReg()); 202381ad6265SDimitry Andric else if (I.getOperand(i).isCImm()) 202481ad6265SDimitry Andric addNumImm(I.getOperand(i).getCImm()->getValue(), MIB); 202581ad6265SDimitry Andric else if (I.getOperand(i).isMBB()) 202681ad6265SDimitry Andric MIB.addMBB(I.getOperand(i).getMBB()); 202781ad6265SDimitry Andric else 202881ad6265SDimitry Andric llvm_unreachable("Unexpected OpSwitch operand"); 202981ad6265SDimitry Andric } 203081ad6265SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 203106c3fb27SDimitry Andric } 2032fcaf7f86SDimitry Andric case Intrinsic::spv_cmpxchg: 2033fcaf7f86SDimitry Andric return selectAtomicCmpXchg(ResVReg, ResType, I); 2034bdd1243dSDimitry Andric case Intrinsic::spv_unreachable: 2035bdd1243dSDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpUnreachable)); 2036bdd1243dSDimitry Andric break; 2037bdd1243dSDimitry Andric case Intrinsic::spv_alloca: 2038bdd1243dSDimitry Andric return selectFrameIndex(ResVReg, ResType, I); 2039*0fca6ea1SDimitry Andric case Intrinsic::spv_alloca_array: 2040*0fca6ea1SDimitry Andric return selectAllocaArray(ResVReg, ResType, I); 20415f757f3fSDimitry Andric case Intrinsic::spv_assume: 20425f757f3fSDimitry Andric if (STI.canUseExtension(SPIRV::Extension::SPV_KHR_expect_assume)) 20435f757f3fSDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpAssumeTrueKHR)) 20445f757f3fSDimitry Andric .addUse(I.getOperand(1).getReg()); 20455f757f3fSDimitry Andric break; 20465f757f3fSDimitry Andric case Intrinsic::spv_expect: 20475f757f3fSDimitry Andric if (STI.canUseExtension(SPIRV::Extension::SPV_KHR_expect_assume)) 20485f757f3fSDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExpectKHR)) 20495f757f3fSDimitry Andric .addDef(ResVReg) 20505f757f3fSDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 20515f757f3fSDimitry Andric .addUse(I.getOperand(2).getReg()) 20525f757f3fSDimitry Andric .addUse(I.getOperand(3).getReg()); 20535f757f3fSDimitry Andric break; 2054*0fca6ea1SDimitry Andric case Intrinsic::spv_thread_id: 2055*0fca6ea1SDimitry Andric return selectSpvThreadId(ResVReg, ResType, I); 2056*0fca6ea1SDimitry Andric case Intrinsic::spv_all: 2057*0fca6ea1SDimitry Andric return selectAll(ResVReg, ResType, I); 2058*0fca6ea1SDimitry Andric case Intrinsic::spv_any: 2059*0fca6ea1SDimitry Andric return selectAny(ResVReg, ResType, I); 2060*0fca6ea1SDimitry Andric case Intrinsic::spv_lerp: 2061*0fca6ea1SDimitry Andric return selectFmix(ResVReg, ResType, I); 2062*0fca6ea1SDimitry Andric case Intrinsic::spv_rsqrt: 2063*0fca6ea1SDimitry Andric return selectRsqrt(ResVReg, ResType, I); 2064*0fca6ea1SDimitry Andric case Intrinsic::spv_lifetime_start: 2065*0fca6ea1SDimitry Andric case Intrinsic::spv_lifetime_end: { 2066*0fca6ea1SDimitry Andric unsigned Op = IID == Intrinsic::spv_lifetime_start ? SPIRV::OpLifetimeStart 2067*0fca6ea1SDimitry Andric : SPIRV::OpLifetimeStop; 2068*0fca6ea1SDimitry Andric int64_t Size = I.getOperand(I.getNumExplicitDefs() + 1).getImm(); 2069*0fca6ea1SDimitry Andric Register PtrReg = I.getOperand(I.getNumExplicitDefs() + 2).getReg(); 2070*0fca6ea1SDimitry Andric unsigned PonteeOpType = GR.getPointeeTypeOp(PtrReg); 2071*0fca6ea1SDimitry Andric bool IsNonvoidPtr = PonteeOpType != 0 && PonteeOpType != SPIRV::OpTypeVoid; 2072*0fca6ea1SDimitry Andric if (Size == -1 || IsNonvoidPtr) 2073*0fca6ea1SDimitry Andric Size = 0; 2074*0fca6ea1SDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(Op)).addUse(PtrReg).addImm(Size); 2075*0fca6ea1SDimitry Andric } break; 2076*0fca6ea1SDimitry Andric default: { 2077*0fca6ea1SDimitry Andric std::string DiagMsg; 2078*0fca6ea1SDimitry Andric raw_string_ostream OS(DiagMsg); 2079*0fca6ea1SDimitry Andric I.print(OS); 2080*0fca6ea1SDimitry Andric DiagMsg = "Intrinsic selection not implemented: " + DiagMsg; 2081*0fca6ea1SDimitry Andric report_fatal_error(DiagMsg.c_str(), false); 2082*0fca6ea1SDimitry Andric } 208381ad6265SDimitry Andric } 208481ad6265SDimitry Andric return true; 208581ad6265SDimitry Andric } 208681ad6265SDimitry Andric 2087*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectAllocaArray(Register ResVReg, 2088*0fca6ea1SDimitry Andric const SPIRVType *ResType, 2089*0fca6ea1SDimitry Andric MachineInstr &I) const { 2090*0fca6ea1SDimitry Andric // there was an allocation size parameter to the allocation instruction 2091*0fca6ea1SDimitry Andric // that is not 1 2092*0fca6ea1SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 2093*0fca6ea1SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), 2094*0fca6ea1SDimitry Andric TII.get(SPIRV::OpVariableLengthArrayINTEL)) 2095*0fca6ea1SDimitry Andric .addDef(ResVReg) 2096*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 2097*0fca6ea1SDimitry Andric .addUse(I.getOperand(2).getReg()) 2098*0fca6ea1SDimitry Andric .constrainAllUses(TII, TRI, RBI); 2099*0fca6ea1SDimitry Andric } 2100*0fca6ea1SDimitry Andric 210181ad6265SDimitry Andric bool SPIRVInstructionSelector::selectFrameIndex(Register ResVReg, 210281ad6265SDimitry Andric const SPIRVType *ResType, 210381ad6265SDimitry Andric MachineInstr &I) const { 2104*0fca6ea1SDimitry Andric // Change order of instructions if needed: all OpVariable instructions in a 2105*0fca6ea1SDimitry Andric // function must be the first instructions in the first block 2106*0fca6ea1SDimitry Andric MachineFunction *MF = I.getParent()->getParent(); 2107*0fca6ea1SDimitry Andric MachineBasicBlock *MBB = &MF->front(); 2108*0fca6ea1SDimitry Andric auto It = MBB->SkipPHIsAndLabels(MBB->begin()), E = MBB->end(); 2109*0fca6ea1SDimitry Andric bool IsHeader = false; 2110*0fca6ea1SDimitry Andric unsigned Opcode; 2111*0fca6ea1SDimitry Andric for (; It != E && It != I; ++It) { 2112*0fca6ea1SDimitry Andric Opcode = It->getOpcode(); 2113*0fca6ea1SDimitry Andric if (Opcode == SPIRV::OpFunction || Opcode == SPIRV::OpFunctionParameter) { 2114*0fca6ea1SDimitry Andric IsHeader = true; 2115*0fca6ea1SDimitry Andric } else if (IsHeader && 2116*0fca6ea1SDimitry Andric !(Opcode == SPIRV::ASSIGN_TYPE || Opcode == SPIRV::OpLabel)) { 2117*0fca6ea1SDimitry Andric ++It; 2118*0fca6ea1SDimitry Andric break; 2119*0fca6ea1SDimitry Andric } 2120*0fca6ea1SDimitry Andric } 2121*0fca6ea1SDimitry Andric return BuildMI(*MBB, It, It->getDebugLoc(), TII.get(SPIRV::OpVariable)) 212281ad6265SDimitry Andric .addDef(ResVReg) 212381ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 212481ad6265SDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::StorageClass::Function)) 212581ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 212681ad6265SDimitry Andric } 212781ad6265SDimitry Andric 212881ad6265SDimitry Andric bool SPIRVInstructionSelector::selectBranch(MachineInstr &I) const { 212981ad6265SDimitry Andric // InstructionSelector walks backwards through the instructions. We can use 213081ad6265SDimitry Andric // both a G_BR and a G_BRCOND to create an OpBranchConditional. We hit G_BR 213181ad6265SDimitry Andric // first, so can generate an OpBranchConditional here. If there is no 213281ad6265SDimitry Andric // G_BRCOND, we just use OpBranch for a regular unconditional branch. 213381ad6265SDimitry Andric const MachineInstr *PrevI = I.getPrevNode(); 213481ad6265SDimitry Andric MachineBasicBlock &MBB = *I.getParent(); 213581ad6265SDimitry Andric if (PrevI != nullptr && PrevI->getOpcode() == TargetOpcode::G_BRCOND) { 213681ad6265SDimitry Andric return BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpBranchConditional)) 213781ad6265SDimitry Andric .addUse(PrevI->getOperand(0).getReg()) 213881ad6265SDimitry Andric .addMBB(PrevI->getOperand(1).getMBB()) 213981ad6265SDimitry Andric .addMBB(I.getOperand(0).getMBB()) 214081ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 214181ad6265SDimitry Andric } 214281ad6265SDimitry Andric return BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpBranch)) 214381ad6265SDimitry Andric .addMBB(I.getOperand(0).getMBB()) 214481ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 214581ad6265SDimitry Andric } 214681ad6265SDimitry Andric 214781ad6265SDimitry Andric bool SPIRVInstructionSelector::selectBranchCond(MachineInstr &I) const { 214881ad6265SDimitry Andric // InstructionSelector walks backwards through the instructions. For an 214981ad6265SDimitry Andric // explicit conditional branch with no fallthrough, we use both a G_BR and a 215081ad6265SDimitry Andric // G_BRCOND to create an OpBranchConditional. We should hit G_BR first, and 215181ad6265SDimitry Andric // generate the OpBranchConditional in selectBranch above. 215281ad6265SDimitry Andric // 215381ad6265SDimitry Andric // If an OpBranchConditional has been generated, we simply return, as the work 215481ad6265SDimitry Andric // is alread done. If there is no OpBranchConditional, LLVM must be relying on 215581ad6265SDimitry Andric // implicit fallthrough to the next basic block, so we need to create an 215681ad6265SDimitry Andric // OpBranchConditional with an explicit "false" argument pointing to the next 215781ad6265SDimitry Andric // basic block that LLVM would fall through to. 215881ad6265SDimitry Andric const MachineInstr *NextI = I.getNextNode(); 215981ad6265SDimitry Andric // Check if this has already been successfully selected. 216081ad6265SDimitry Andric if (NextI != nullptr && NextI->getOpcode() == SPIRV::OpBranchConditional) 216181ad6265SDimitry Andric return true; 216281ad6265SDimitry Andric // Must be relying on implicit block fallthrough, so generate an 216381ad6265SDimitry Andric // OpBranchConditional with the "next" basic block as the "false" target. 216481ad6265SDimitry Andric MachineBasicBlock &MBB = *I.getParent(); 216581ad6265SDimitry Andric unsigned NextMBBNum = MBB.getNextNode()->getNumber(); 216681ad6265SDimitry Andric MachineBasicBlock *NextMBB = I.getMF()->getBlockNumbered(NextMBBNum); 216781ad6265SDimitry Andric return BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpBranchConditional)) 216881ad6265SDimitry Andric .addUse(I.getOperand(0).getReg()) 216981ad6265SDimitry Andric .addMBB(I.getOperand(1).getMBB()) 217081ad6265SDimitry Andric .addMBB(NextMBB) 217181ad6265SDimitry Andric .constrainAllUses(TII, TRI, RBI); 217281ad6265SDimitry Andric } 217381ad6265SDimitry Andric 217481ad6265SDimitry Andric bool SPIRVInstructionSelector::selectPhi(Register ResVReg, 217581ad6265SDimitry Andric const SPIRVType *ResType, 217681ad6265SDimitry Andric MachineInstr &I) const { 217781ad6265SDimitry Andric auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpPhi)) 217881ad6265SDimitry Andric .addDef(ResVReg) 217981ad6265SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)); 218081ad6265SDimitry Andric const unsigned NumOps = I.getNumOperands(); 218181ad6265SDimitry Andric for (unsigned i = 1; i < NumOps; i += 2) { 218281ad6265SDimitry Andric MIB.addUse(I.getOperand(i + 0).getReg()); 218381ad6265SDimitry Andric MIB.addMBB(I.getOperand(i + 1).getMBB()); 218481ad6265SDimitry Andric } 218581ad6265SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 218681ad6265SDimitry Andric } 218781ad6265SDimitry Andric 218881ad6265SDimitry Andric bool SPIRVInstructionSelector::selectGlobalValue( 218981ad6265SDimitry Andric Register ResVReg, MachineInstr &I, const MachineInstr *Init) const { 219081ad6265SDimitry Andric // FIXME: don't use MachineIRBuilder here, replace it with BuildMI. 219181ad6265SDimitry Andric MachineIRBuilder MIRBuilder(I); 219281ad6265SDimitry Andric const GlobalValue *GV = I.getOperand(1).getGlobal(); 2193*0fca6ea1SDimitry Andric Type *GVType = toTypedPointer(GR.getDeducedGlobalValueType(GV)); 21945f757f3fSDimitry Andric SPIRVType *PointerBaseType; 21955f757f3fSDimitry Andric if (GVType->isArrayTy()) { 21965f757f3fSDimitry Andric SPIRVType *ArrayElementType = 21975f757f3fSDimitry Andric GR.getOrCreateSPIRVType(GVType->getArrayElementType(), MIRBuilder, 21985f757f3fSDimitry Andric SPIRV::AccessQualifier::ReadWrite, false); 21995f757f3fSDimitry Andric PointerBaseType = GR.getOrCreateSPIRVArrayType( 22005f757f3fSDimitry Andric ArrayElementType, GVType->getArrayNumElements(), I, TII); 22015f757f3fSDimitry Andric } else { 22025f757f3fSDimitry Andric PointerBaseType = GR.getOrCreateSPIRVType( 22035f757f3fSDimitry Andric GVType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false); 22045f757f3fSDimitry Andric } 22055f757f3fSDimitry Andric SPIRVType *ResType = GR.getOrCreateSPIRVPointerType( 22065f757f3fSDimitry Andric PointerBaseType, I, TII, 2207*0fca6ea1SDimitry Andric addressSpaceToStorageClass(GV->getAddressSpace(), STI)); 2208*0fca6ea1SDimitry Andric 2209*0fca6ea1SDimitry Andric std::string GlobalIdent; 2210*0fca6ea1SDimitry Andric if (!GV->hasName()) { 2211*0fca6ea1SDimitry Andric unsigned &ID = UnnamedGlobalIDs[GV]; 2212*0fca6ea1SDimitry Andric if (ID == 0) 2213*0fca6ea1SDimitry Andric ID = UnnamedGlobalIDs.size(); 2214*0fca6ea1SDimitry Andric GlobalIdent = "__unnamed_" + Twine(ID).str(); 2215*0fca6ea1SDimitry Andric } else { 2216*0fca6ea1SDimitry Andric GlobalIdent = GV->getGlobalIdentifier(); 2217*0fca6ea1SDimitry Andric } 2218*0fca6ea1SDimitry Andric 2219*0fca6ea1SDimitry Andric // Behaviour of functions as operands depends on availability of the 2220*0fca6ea1SDimitry Andric // corresponding extension (SPV_INTEL_function_pointers): 2221*0fca6ea1SDimitry Andric // - If there is an extension to operate with functions as operands: 2222*0fca6ea1SDimitry Andric // We create a proper constant operand and evaluate a correct type for a 2223*0fca6ea1SDimitry Andric // function pointer. 2224*0fca6ea1SDimitry Andric // - Without the required extension: 2225fcaf7f86SDimitry Andric // We have functions as operands in tests with blocks of instruction e.g. in 2226fcaf7f86SDimitry Andric // transcoding/global_block.ll. These operands are not used and should be 2227fcaf7f86SDimitry Andric // substituted by zero constants. Their type is expected to be always 2228fcaf7f86SDimitry Andric // OpTypePointer Function %uchar. 2229fcaf7f86SDimitry Andric if (isa<Function>(GV)) { 2230fcaf7f86SDimitry Andric const Constant *ConstVal = GV; 2231fcaf7f86SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 2232fcaf7f86SDimitry Andric Register NewReg = GR.find(ConstVal, GR.CurMF); 2233fcaf7f86SDimitry Andric if (!NewReg.isValid()) { 2234fcaf7f86SDimitry Andric Register NewReg = ResVReg; 2235fcaf7f86SDimitry Andric GR.add(ConstVal, GR.CurMF, NewReg); 2236*0fca6ea1SDimitry Andric const Function *GVFun = 2237*0fca6ea1SDimitry Andric STI.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers) 2238*0fca6ea1SDimitry Andric ? dyn_cast<Function>(GV) 2239*0fca6ea1SDimitry Andric : nullptr; 2240*0fca6ea1SDimitry Andric if (GVFun) { 2241*0fca6ea1SDimitry Andric // References to a function via function pointers generate virtual 2242*0fca6ea1SDimitry Andric // registers without a definition. We will resolve it later, during 2243*0fca6ea1SDimitry Andric // module analysis stage. 2244*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 2245*0fca6ea1SDimitry Andric Register FuncVReg = MRI->createGenericVirtualRegister(LLT::scalar(32)); 2246*0fca6ea1SDimitry Andric MRI->setRegClass(FuncVReg, &SPIRV::IDRegClass); 2247*0fca6ea1SDimitry Andric MachineInstrBuilder MB = 2248*0fca6ea1SDimitry Andric BuildMI(BB, I, I.getDebugLoc(), 2249*0fca6ea1SDimitry Andric TII.get(SPIRV::OpConstantFunctionPointerINTEL)) 2250*0fca6ea1SDimitry Andric .addDef(NewReg) 2251*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 2252*0fca6ea1SDimitry Andric .addUse(FuncVReg); 2253*0fca6ea1SDimitry Andric // mapping the function pointer to the used Function 2254*0fca6ea1SDimitry Andric GR.recordFunctionPointer(&MB.getInstr()->getOperand(2), GVFun); 2255*0fca6ea1SDimitry Andric return MB.constrainAllUses(TII, TRI, RBI); 2256*0fca6ea1SDimitry Andric } 2257fcaf7f86SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantNull)) 2258fcaf7f86SDimitry Andric .addDef(NewReg) 2259fcaf7f86SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 2260fcaf7f86SDimitry Andric .constrainAllUses(TII, TRI, RBI); 2261fcaf7f86SDimitry Andric } 2262fcaf7f86SDimitry Andric assert(NewReg != ResVReg); 2263fcaf7f86SDimitry Andric return BuildMI(BB, I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) 2264fcaf7f86SDimitry Andric .addDef(ResVReg) 2265fcaf7f86SDimitry Andric .addUse(NewReg) 2266fcaf7f86SDimitry Andric .constrainAllUses(TII, TRI, RBI); 2267fcaf7f86SDimitry Andric } 226881ad6265SDimitry Andric auto GlobalVar = cast<GlobalVariable>(GV); 2269fcaf7f86SDimitry Andric assert(GlobalVar->getName() != "llvm.global.annotations"); 227081ad6265SDimitry Andric 227181ad6265SDimitry Andric bool HasInit = GlobalVar->hasInitializer() && 227281ad6265SDimitry Andric !isa<UndefValue>(GlobalVar->getInitializer()); 227381ad6265SDimitry Andric // Skip empty declaration for GVs with initilaizers till we get the decl with 227481ad6265SDimitry Andric // passed initializer. 227581ad6265SDimitry Andric if (HasInit && !Init) 227681ad6265SDimitry Andric return true; 227781ad6265SDimitry Andric 227881ad6265SDimitry Andric unsigned AddrSpace = GV->getAddressSpace(); 2279bdd1243dSDimitry Andric SPIRV::StorageClass::StorageClass Storage = 2280*0fca6ea1SDimitry Andric addressSpaceToStorageClass(AddrSpace, STI); 228181ad6265SDimitry Andric bool HasLnkTy = GV->getLinkage() != GlobalValue::InternalLinkage && 228281ad6265SDimitry Andric Storage != SPIRV::StorageClass::Function; 2283bdd1243dSDimitry Andric SPIRV::LinkageType::LinkageType LnkType = 228481ad6265SDimitry Andric (GV->isDeclaration() || GV->hasAvailableExternallyLinkage()) 228581ad6265SDimitry Andric ? SPIRV::LinkageType::Import 2286*0fca6ea1SDimitry Andric : (GV->getLinkage() == GlobalValue::LinkOnceODRLinkage && 2287*0fca6ea1SDimitry Andric STI.canUseExtension(SPIRV::Extension::SPV_KHR_linkonce_odr) 2288*0fca6ea1SDimitry Andric ? SPIRV::LinkageType::LinkOnceODR 2289*0fca6ea1SDimitry Andric : SPIRV::LinkageType::Export); 229081ad6265SDimitry Andric 229181ad6265SDimitry Andric Register Reg = GR.buildGlobalVariable(ResVReg, ResType, GlobalIdent, GV, 229281ad6265SDimitry Andric Storage, Init, GlobalVar->isConstant(), 229381ad6265SDimitry Andric HasLnkTy, LnkType, MIRBuilder, true); 229481ad6265SDimitry Andric return Reg.isValid(); 229581ad6265SDimitry Andric } 229681ad6265SDimitry Andric 22975f757f3fSDimitry Andric bool SPIRVInstructionSelector::selectLog10(Register ResVReg, 22985f757f3fSDimitry Andric const SPIRVType *ResType, 22995f757f3fSDimitry Andric MachineInstr &I) const { 23005f757f3fSDimitry Andric if (STI.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std)) { 23015f757f3fSDimitry Andric return selectExtInst(ResVReg, ResType, I, CL::log10); 23025f757f3fSDimitry Andric } 23035f757f3fSDimitry Andric 23045f757f3fSDimitry Andric // There is no log10 instruction in the GLSL Extended Instruction set, so it 23055f757f3fSDimitry Andric // is implemented as: 23065f757f3fSDimitry Andric // log10(x) = log2(x) * (1 / log2(10)) 23075f757f3fSDimitry Andric // = log2(x) * 0.30103 23085f757f3fSDimitry Andric 23095f757f3fSDimitry Andric MachineIRBuilder MIRBuilder(I); 23105f757f3fSDimitry Andric MachineBasicBlock &BB = *I.getParent(); 23115f757f3fSDimitry Andric 23125f757f3fSDimitry Andric // Build log2(x). 23135f757f3fSDimitry Andric Register VarReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 23145f757f3fSDimitry Andric bool Result = 23155f757f3fSDimitry Andric BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst)) 23165f757f3fSDimitry Andric .addDef(VarReg) 23175f757f3fSDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 23185f757f3fSDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450)) 23195f757f3fSDimitry Andric .addImm(GL::Log2) 23205f757f3fSDimitry Andric .add(I.getOperand(1)) 23215f757f3fSDimitry Andric .constrainAllUses(TII, TRI, RBI); 23225f757f3fSDimitry Andric 23235f757f3fSDimitry Andric // Build 0.30103. 23245f757f3fSDimitry Andric assert(ResType->getOpcode() == SPIRV::OpTypeVector || 23255f757f3fSDimitry Andric ResType->getOpcode() == SPIRV::OpTypeFloat); 23265f757f3fSDimitry Andric // TODO: Add matrix implementation once supported by the HLSL frontend. 23275f757f3fSDimitry Andric const SPIRVType *SpirvScalarType = 23285f757f3fSDimitry Andric ResType->getOpcode() == SPIRV::OpTypeVector 23295f757f3fSDimitry Andric ? GR.getSPIRVTypeForVReg(ResType->getOperand(1).getReg()) 23305f757f3fSDimitry Andric : ResType; 23315f757f3fSDimitry Andric Register ScaleReg = 23325f757f3fSDimitry Andric GR.buildConstantFP(APFloat(0.30103f), MIRBuilder, SpirvScalarType); 23335f757f3fSDimitry Andric 23345f757f3fSDimitry Andric // Multiply log2(x) by 0.30103 to get log10(x) result. 23355f757f3fSDimitry Andric auto Opcode = ResType->getOpcode() == SPIRV::OpTypeVector 23365f757f3fSDimitry Andric ? SPIRV::OpVectorTimesScalar 23375f757f3fSDimitry Andric : SPIRV::OpFMulS; 23385f757f3fSDimitry Andric Result &= BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode)) 23395f757f3fSDimitry Andric .addDef(ResVReg) 23405f757f3fSDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 23415f757f3fSDimitry Andric .addUse(VarReg) 23425f757f3fSDimitry Andric .addUse(ScaleReg) 23435f757f3fSDimitry Andric .constrainAllUses(TII, TRI, RBI); 23445f757f3fSDimitry Andric 23455f757f3fSDimitry Andric return Result; 23465f757f3fSDimitry Andric } 23475f757f3fSDimitry Andric 2348*0fca6ea1SDimitry Andric bool SPIRVInstructionSelector::selectSpvThreadId(Register ResVReg, 2349*0fca6ea1SDimitry Andric const SPIRVType *ResType, 2350*0fca6ea1SDimitry Andric MachineInstr &I) const { 2351*0fca6ea1SDimitry Andric // DX intrinsic: @llvm.dx.thread.id(i32) 2352*0fca6ea1SDimitry Andric // ID Name Description 2353*0fca6ea1SDimitry Andric // 93 ThreadId reads the thread ID 2354*0fca6ea1SDimitry Andric 2355*0fca6ea1SDimitry Andric MachineIRBuilder MIRBuilder(I); 2356*0fca6ea1SDimitry Andric const SPIRVType *U32Type = GR.getOrCreateSPIRVIntegerType(32, MIRBuilder); 2357*0fca6ea1SDimitry Andric const SPIRVType *Vec3Ty = 2358*0fca6ea1SDimitry Andric GR.getOrCreateSPIRVVectorType(U32Type, 3, MIRBuilder); 2359*0fca6ea1SDimitry Andric const SPIRVType *PtrType = GR.getOrCreateSPIRVPointerType( 2360*0fca6ea1SDimitry Andric Vec3Ty, MIRBuilder, SPIRV::StorageClass::Input); 2361*0fca6ea1SDimitry Andric 2362*0fca6ea1SDimitry Andric // Create new register for GlobalInvocationID builtin variable. 2363*0fca6ea1SDimitry Andric Register NewRegister = 2364*0fca6ea1SDimitry Andric MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass); 2365*0fca6ea1SDimitry Andric MIRBuilder.getMRI()->setType(NewRegister, LLT::pointer(0, 32)); 2366*0fca6ea1SDimitry Andric GR.assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF()); 2367*0fca6ea1SDimitry Andric 2368*0fca6ea1SDimitry Andric // Build GlobalInvocationID global variable with the necessary decorations. 2369*0fca6ea1SDimitry Andric Register Variable = GR.buildGlobalVariable( 2370*0fca6ea1SDimitry Andric NewRegister, PtrType, 2371*0fca6ea1SDimitry Andric getLinkStringForBuiltIn(SPIRV::BuiltIn::GlobalInvocationId), nullptr, 2372*0fca6ea1SDimitry Andric SPIRV::StorageClass::Input, nullptr, true, true, 2373*0fca6ea1SDimitry Andric SPIRV::LinkageType::Import, MIRBuilder, false); 2374*0fca6ea1SDimitry Andric 2375*0fca6ea1SDimitry Andric // Create new register for loading value. 2376*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 2377*0fca6ea1SDimitry Andric Register LoadedRegister = MRI->createVirtualRegister(&SPIRV::IDRegClass); 2378*0fca6ea1SDimitry Andric MIRBuilder.getMRI()->setType(LoadedRegister, LLT::pointer(0, 32)); 2379*0fca6ea1SDimitry Andric GR.assignSPIRVTypeToVReg(Vec3Ty, LoadedRegister, MIRBuilder.getMF()); 2380*0fca6ea1SDimitry Andric 2381*0fca6ea1SDimitry Andric // Load v3uint value from the global variable. 2382*0fca6ea1SDimitry Andric BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad)) 2383*0fca6ea1SDimitry Andric .addDef(LoadedRegister) 2384*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(Vec3Ty)) 2385*0fca6ea1SDimitry Andric .addUse(Variable); 2386*0fca6ea1SDimitry Andric 2387*0fca6ea1SDimitry Andric // Get Thread ID index. Expecting operand is a constant immediate value, 2388*0fca6ea1SDimitry Andric // wrapped in a type assignment. 2389*0fca6ea1SDimitry Andric assert(I.getOperand(2).isReg()); 2390*0fca6ea1SDimitry Andric Register ThreadIdReg = I.getOperand(2).getReg(); 2391*0fca6ea1SDimitry Andric SPIRVType *ConstTy = this->MRI->getVRegDef(ThreadIdReg); 2392*0fca6ea1SDimitry Andric assert(ConstTy && ConstTy->getOpcode() == SPIRV::ASSIGN_TYPE && 2393*0fca6ea1SDimitry Andric ConstTy->getOperand(1).isReg()); 2394*0fca6ea1SDimitry Andric Register ConstReg = ConstTy->getOperand(1).getReg(); 2395*0fca6ea1SDimitry Andric const MachineInstr *Const = this->MRI->getVRegDef(ConstReg); 2396*0fca6ea1SDimitry Andric assert(Const && Const->getOpcode() == TargetOpcode::G_CONSTANT); 2397*0fca6ea1SDimitry Andric const llvm::APInt &Val = Const->getOperand(1).getCImm()->getValue(); 2398*0fca6ea1SDimitry Andric const uint32_t ThreadId = Val.getZExtValue(); 2399*0fca6ea1SDimitry Andric 2400*0fca6ea1SDimitry Andric // Extract the thread ID from the loaded vector value. 2401*0fca6ea1SDimitry Andric MachineBasicBlock &BB = *I.getParent(); 2402*0fca6ea1SDimitry Andric auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeExtract)) 2403*0fca6ea1SDimitry Andric .addDef(ResVReg) 2404*0fca6ea1SDimitry Andric .addUse(GR.getSPIRVTypeID(ResType)) 2405*0fca6ea1SDimitry Andric .addUse(LoadedRegister) 2406*0fca6ea1SDimitry Andric .addImm(ThreadId); 2407*0fca6ea1SDimitry Andric return MIB.constrainAllUses(TII, TRI, RBI); 2408*0fca6ea1SDimitry Andric } 2409*0fca6ea1SDimitry Andric 241081ad6265SDimitry Andric namespace llvm { 241181ad6265SDimitry Andric InstructionSelector * 241281ad6265SDimitry Andric createSPIRVInstructionSelector(const SPIRVTargetMachine &TM, 241381ad6265SDimitry Andric const SPIRVSubtarget &Subtarget, 241481ad6265SDimitry Andric const RegisterBankInfo &RBI) { 241581ad6265SDimitry Andric return new SPIRVInstructionSelector(TM, Subtarget, RBI); 241681ad6265SDimitry Andric } 241781ad6265SDimitry Andric } // namespace llvm 2418