10098f2aeSIlia Diachkov //===-- SPIRVPreLegalizer.cpp - prepare IR for legalization -----*- C++ -*-===// 20098f2aeSIlia Diachkov // 30098f2aeSIlia Diachkov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40098f2aeSIlia Diachkov // See https://llvm.org/LICENSE.txt for license information. 50098f2aeSIlia Diachkov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60098f2aeSIlia Diachkov // 70098f2aeSIlia Diachkov //===----------------------------------------------------------------------===// 80098f2aeSIlia Diachkov // 90098f2aeSIlia Diachkov // The pass prepares IR for legalization: it assigns SPIR-V types to registers 100098f2aeSIlia Diachkov // and removes intrinsics which holded these types during IR translation. 110098f2aeSIlia Diachkov // Also it processes constants and registers them in GR to avoid duplication. 120098f2aeSIlia Diachkov // 130098f2aeSIlia Diachkov //===----------------------------------------------------------------------===// 140098f2aeSIlia Diachkov 150098f2aeSIlia Diachkov #include "SPIRV.h" 160098f2aeSIlia Diachkov #include "SPIRVSubtarget.h" 170098f2aeSIlia Diachkov #include "SPIRVUtils.h" 180098f2aeSIlia Diachkov #include "llvm/ADT/PostOrderIterator.h" 190098f2aeSIlia Diachkov #include "llvm/Analysis/OptimizationRemarkEmitter.h" 20*eddeb36cSFarzon Lotfi #include "llvm/CodeGen/GlobalISel/CSEInfo.h" 21*eddeb36cSFarzon Lotfi #include "llvm/CodeGen/GlobalISel/GISelKnownBits.h" 220098f2aeSIlia Diachkov #include "llvm/IR/Attributes.h" 230098f2aeSIlia Diachkov #include "llvm/IR/Constants.h" 240098f2aeSIlia Diachkov #include "llvm/IR/DebugInfoMetadata.h" 250098f2aeSIlia Diachkov #include "llvm/IR/IntrinsicsSPIRV.h" 260098f2aeSIlia Diachkov #include "llvm/Target/TargetIntrinsicInfo.h" 270098f2aeSIlia Diachkov 280098f2aeSIlia Diachkov #define DEBUG_TYPE "spirv-prelegalizer" 290098f2aeSIlia Diachkov 300098f2aeSIlia Diachkov using namespace llvm; 310098f2aeSIlia Diachkov 320098f2aeSIlia Diachkov namespace { 330098f2aeSIlia Diachkov class SPIRVPreLegalizer : public MachineFunctionPass { 340098f2aeSIlia Diachkov public: 350098f2aeSIlia Diachkov static char ID; 360098f2aeSIlia Diachkov SPIRVPreLegalizer() : MachineFunctionPass(ID) { 370098f2aeSIlia Diachkov initializeSPIRVPreLegalizerPass(*PassRegistry::getPassRegistry()); 380098f2aeSIlia Diachkov } 390098f2aeSIlia Diachkov bool runOnMachineFunction(MachineFunction &MF) override; 40*eddeb36cSFarzon Lotfi void getAnalysisUsage(AnalysisUsage &AU) const override; 410098f2aeSIlia Diachkov }; 420098f2aeSIlia Diachkov } // namespace 430098f2aeSIlia Diachkov 44*eddeb36cSFarzon Lotfi void SPIRVPreLegalizer::getAnalysisUsage(AnalysisUsage &AU) const { 45*eddeb36cSFarzon Lotfi AU.addPreserved<GISelKnownBitsAnalysis>(); 46*eddeb36cSFarzon Lotfi MachineFunctionPass::getAnalysisUsage(AU); 47*eddeb36cSFarzon Lotfi } 48*eddeb36cSFarzon Lotfi 491ed1ec9aSVyacheslav Levytskyy static void 501ed1ec9aSVyacheslav Levytskyy addConstantsToTrack(MachineFunction &MF, SPIRVGlobalRegistry *GR, 517c917e82SVyacheslav Levytskyy const SPIRVSubtarget &STI, 529c29217aSVyacheslav Levytskyy DenseMap<MachineInstr *, Type *> &TargetExtConstTypes, 539c29217aSVyacheslav Levytskyy SmallSet<Register, 4> &TrackedConstRegs) { 54b8e1544bSIlia Diachkov MachineRegisterInfo &MRI = MF.getRegInfo(); 55b8e1544bSIlia Diachkov DenseMap<MachineInstr *, Register> RegsAlreadyAddedToDT; 56b8e1544bSIlia Diachkov SmallVector<MachineInstr *, 10> ToErase, ToEraseComposites; 57b8e1544bSIlia Diachkov for (MachineBasicBlock &MBB : MF) { 58b8e1544bSIlia Diachkov for (MachineInstr &MI : MBB) { 59b8e1544bSIlia Diachkov if (!isSpvIntrinsic(MI, Intrinsic::spv_track_constant)) 60b8e1544bSIlia Diachkov continue; 61b8e1544bSIlia Diachkov ToErase.push_back(&MI); 621ed1ec9aSVyacheslav Levytskyy Register SrcReg = MI.getOperand(2).getReg(); 63b8e1544bSIlia Diachkov auto *Const = 64b8e1544bSIlia Diachkov cast<Constant>(cast<ConstantAsMetadata>( 65b8e1544bSIlia Diachkov MI.getOperand(3).getMetadata()->getOperand(0)) 66b8e1544bSIlia Diachkov ->getValue()); 67b8e1544bSIlia Diachkov if (auto *GV = dyn_cast<GlobalValue>(Const)) { 68b8e1544bSIlia Diachkov Register Reg = GR->find(GV, &MF); 6983c1d003SVyacheslav Levytskyy if (!Reg.isValid()) { 701ed1ec9aSVyacheslav Levytskyy GR->add(GV, &MF, SrcReg); 7183c1d003SVyacheslav Levytskyy GR->addGlobalObject(GV, &MF, SrcReg); 7283c1d003SVyacheslav Levytskyy } else 73b8e1544bSIlia Diachkov RegsAlreadyAddedToDT[&MI] = Reg; 74b8e1544bSIlia Diachkov } else { 75b8e1544bSIlia Diachkov Register Reg = GR->find(Const, &MF); 76b8e1544bSIlia Diachkov if (!Reg.isValid()) { 77b8e1544bSIlia Diachkov if (auto *ConstVec = dyn_cast<ConstantDataVector>(Const)) { 781ed1ec9aSVyacheslav Levytskyy auto *BuildVec = MRI.getVRegDef(SrcReg); 79b8e1544bSIlia Diachkov assert(BuildVec && 80b8e1544bSIlia Diachkov BuildVec->getOpcode() == TargetOpcode::G_BUILD_VECTOR); 81f7680835SVyacheslav Levytskyy for (unsigned i = 0; i < ConstVec->getNumElements(); ++i) { 82f7680835SVyacheslav Levytskyy // Ensure that OpConstantComposite reuses a constant when it's 83f7680835SVyacheslav Levytskyy // already created and available in the same machine function. 84f7680835SVyacheslav Levytskyy Constant *ElemConst = ConstVec->getElementAsConstant(i); 85f7680835SVyacheslav Levytskyy Register ElemReg = GR->find(ElemConst, &MF); 86f7680835SVyacheslav Levytskyy if (!ElemReg.isValid()) 87f7680835SVyacheslav Levytskyy GR->add(ElemConst, &MF, BuildVec->getOperand(1 + i).getReg()); 88f7680835SVyacheslav Levytskyy else 89f7680835SVyacheslav Levytskyy BuildVec->getOperand(1 + i).setReg(ElemReg); 90f7680835SVyacheslav Levytskyy } 91b8e1544bSIlia Diachkov } 921ed1ec9aSVyacheslav Levytskyy GR->add(Const, &MF, SrcReg); 939c29217aSVyacheslav Levytskyy TrackedConstRegs.insert(SrcReg); 941ed1ec9aSVyacheslav Levytskyy if (Const->getType()->isTargetExtTy()) { 951ed1ec9aSVyacheslav Levytskyy // remember association so that we can restore it when assign types 961ed1ec9aSVyacheslav Levytskyy MachineInstr *SrcMI = MRI.getVRegDef(SrcReg); 977c917e82SVyacheslav Levytskyy if (SrcMI && (SrcMI->getOpcode() == TargetOpcode::G_CONSTANT || 987c917e82SVyacheslav Levytskyy SrcMI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF)) 991ed1ec9aSVyacheslav Levytskyy TargetExtConstTypes[SrcMI] = Const->getType(); 1007c917e82SVyacheslav Levytskyy if (Const->isNullValue()) { 1017c917e82SVyacheslav Levytskyy MachineIRBuilder MIB(MF); 1027c917e82SVyacheslav Levytskyy SPIRVType *ExtType = 1037c917e82SVyacheslav Levytskyy GR->getOrCreateSPIRVType(Const->getType(), MIB); 1047c917e82SVyacheslav Levytskyy SrcMI->setDesc(STI.getInstrInfo()->get(SPIRV::OpConstantNull)); 1057c917e82SVyacheslav Levytskyy SrcMI->addOperand(MachineOperand::CreateReg( 1067c917e82SVyacheslav Levytskyy GR->getSPIRVTypeID(ExtType), false)); 1077c917e82SVyacheslav Levytskyy } 1081ed1ec9aSVyacheslav Levytskyy } 109b8e1544bSIlia Diachkov } else { 110b8e1544bSIlia Diachkov RegsAlreadyAddedToDT[&MI] = Reg; 111b8e1544bSIlia Diachkov // This MI is unused and will be removed. If the MI uses 112b8e1544bSIlia Diachkov // const_composite, it will be unused and should be removed too. 113b8e1544bSIlia Diachkov assert(MI.getOperand(2).isReg() && "Reg operand is expected"); 114b8e1544bSIlia Diachkov MachineInstr *SrcMI = MRI.getVRegDef(MI.getOperand(2).getReg()); 115b8e1544bSIlia Diachkov if (SrcMI && isSpvIntrinsic(*SrcMI, Intrinsic::spv_const_composite)) 116b8e1544bSIlia Diachkov ToEraseComposites.push_back(SrcMI); 117b8e1544bSIlia Diachkov } 118b8e1544bSIlia Diachkov } 119b8e1544bSIlia Diachkov } 120b8e1544bSIlia Diachkov } 121b8e1544bSIlia Diachkov for (MachineInstr *MI : ToErase) { 122b8e1544bSIlia Diachkov Register Reg = MI->getOperand(2).getReg(); 123e01c0636SKazu Hirata if (RegsAlreadyAddedToDT.contains(MI)) 124b8e1544bSIlia Diachkov Reg = RegsAlreadyAddedToDT[MI]; 12574c66710SIlia Diachkov auto *RC = MRI.getRegClassOrNull(MI->getOperand(0).getReg()); 12674c66710SIlia Diachkov if (!MRI.getRegClassOrNull(Reg) && RC) 12774c66710SIlia Diachkov MRI.setRegClass(Reg, RC); 128b8e1544bSIlia Diachkov MRI.replaceRegWith(MI->getOperand(0).getReg(), Reg); 129b8e1544bSIlia Diachkov MI->eraseFromParent(); 130b8e1544bSIlia Diachkov } 131b8e1544bSIlia Diachkov for (MachineInstr *MI : ToEraseComposites) 132b8e1544bSIlia Diachkov MI->eraseFromParent(); 1330098f2aeSIlia Diachkov } 1340098f2aeSIlia Diachkov 1359c29217aSVyacheslav Levytskyy static void 1369c29217aSVyacheslav Levytskyy foldConstantsIntoIntrinsics(MachineFunction &MF, 1379c29217aSVyacheslav Levytskyy const SmallSet<Register, 4> &TrackedConstRegs) { 1380098f2aeSIlia Diachkov SmallVector<MachineInstr *, 10> ToErase; 1390098f2aeSIlia Diachkov MachineRegisterInfo &MRI = MF.getRegInfo(); 1400098f2aeSIlia Diachkov const unsigned AssignNameOperandShift = 2; 1410098f2aeSIlia Diachkov for (MachineBasicBlock &MBB : MF) { 1420098f2aeSIlia Diachkov for (MachineInstr &MI : MBB) { 1430098f2aeSIlia Diachkov if (!isSpvIntrinsic(MI, Intrinsic::spv_assign_name)) 1440098f2aeSIlia Diachkov continue; 1450098f2aeSIlia Diachkov unsigned NumOp = MI.getNumExplicitDefs() + AssignNameOperandShift; 1460098f2aeSIlia Diachkov while (MI.getOperand(NumOp).isReg()) { 1470098f2aeSIlia Diachkov MachineOperand &MOp = MI.getOperand(NumOp); 1480098f2aeSIlia Diachkov MachineInstr *ConstMI = MRI.getVRegDef(MOp.getReg()); 1490098f2aeSIlia Diachkov assert(ConstMI->getOpcode() == TargetOpcode::G_CONSTANT); 1500098f2aeSIlia Diachkov MI.removeOperand(NumOp); 1510098f2aeSIlia Diachkov MI.addOperand(MachineOperand::CreateImm( 1520098f2aeSIlia Diachkov ConstMI->getOperand(1).getCImm()->getZExtValue())); 1539c29217aSVyacheslav Levytskyy Register DefReg = ConstMI->getOperand(0).getReg(); 1549c29217aSVyacheslav Levytskyy if (MRI.use_empty(DefReg) && !TrackedConstRegs.contains(DefReg)) 1550098f2aeSIlia Diachkov ToErase.push_back(ConstMI); 1560098f2aeSIlia Diachkov } 1570098f2aeSIlia Diachkov } 1580098f2aeSIlia Diachkov } 1590098f2aeSIlia Diachkov for (MachineInstr *MI : ToErase) 1600098f2aeSIlia Diachkov MI->eraseFromParent(); 1610098f2aeSIlia Diachkov } 1620098f2aeSIlia Diachkov 163dbd00a59SVyacheslav Levytskyy static MachineInstr *findAssignTypeInstr(Register Reg, 164dbd00a59SVyacheslav Levytskyy MachineRegisterInfo *MRI) { 165dbd00a59SVyacheslav Levytskyy for (MachineRegisterInfo::use_instr_iterator I = MRI->use_instr_begin(Reg), 166dbd00a59SVyacheslav Levytskyy IE = MRI->use_instr_end(); 167dbd00a59SVyacheslav Levytskyy I != IE; ++I) { 168dbd00a59SVyacheslav Levytskyy MachineInstr *UseMI = &*I; 169dbd00a59SVyacheslav Levytskyy if ((isSpvIntrinsic(*UseMI, Intrinsic::spv_assign_ptr_type) || 170dbd00a59SVyacheslav Levytskyy isSpvIntrinsic(*UseMI, Intrinsic::spv_assign_type)) && 171dbd00a59SVyacheslav Levytskyy UseMI->getOperand(1).getReg() == Reg) 172dbd00a59SVyacheslav Levytskyy return UseMI; 173dbd00a59SVyacheslav Levytskyy } 174dbd00a59SVyacheslav Levytskyy return nullptr; 175dbd00a59SVyacheslav Levytskyy } 176dbd00a59SVyacheslav Levytskyy 177c616f24bSVyacheslav Levytskyy static void buildOpBitcast(SPIRVGlobalRegistry *GR, MachineIRBuilder &MIB, 178c616f24bSVyacheslav Levytskyy Register ResVReg, Register OpReg) { 179c616f24bSVyacheslav Levytskyy SPIRVType *ResType = GR->getSPIRVTypeForVReg(ResVReg); 180c616f24bSVyacheslav Levytskyy SPIRVType *OpType = GR->getSPIRVTypeForVReg(OpReg); 181c616f24bSVyacheslav Levytskyy assert(ResType && OpType && "Operand types are expected"); 182c616f24bSVyacheslav Levytskyy if (!GR->isBitcastCompatible(ResType, OpType)) 183c616f24bSVyacheslav Levytskyy report_fatal_error("incompatible result and operand types in a bitcast"); 184c616f24bSVyacheslav Levytskyy MachineRegisterInfo *MRI = MIB.getMRI(); 185c616f24bSVyacheslav Levytskyy if (!MRI->getRegClassOrNull(ResVReg)) 186c616f24bSVyacheslav Levytskyy MRI->setRegClass(ResVReg, GR->getRegClass(ResType)); 18793cda6d6SVyacheslav Levytskyy if (ResType == OpType) 18893cda6d6SVyacheslav Levytskyy MIB.buildInstr(TargetOpcode::COPY).addDef(ResVReg).addUse(OpReg); 18993cda6d6SVyacheslav Levytskyy else 190c616f24bSVyacheslav Levytskyy MIB.buildInstr(SPIRV::OpBitcast) 191c616f24bSVyacheslav Levytskyy .addDef(ResVReg) 192c616f24bSVyacheslav Levytskyy .addUse(GR->getSPIRVTypeID(ResType)) 193c616f24bSVyacheslav Levytskyy .addUse(OpReg); 194c616f24bSVyacheslav Levytskyy } 195c616f24bSVyacheslav Levytskyy 196c616f24bSVyacheslav Levytskyy // We do instruction selections early instead of calling MIB.buildBitcast() 197c616f24bSVyacheslav Levytskyy // generating the general op code G_BITCAST. When MachineVerifier validates 198c616f24bSVyacheslav Levytskyy // G_BITCAST we see a check of a kind: if Source Type is equal to Destination 199c616f24bSVyacheslav Levytskyy // Type then report error "bitcast must change the type". This doesn't take into 200c616f24bSVyacheslav Levytskyy // account the notion of a typed pointer that is important for SPIR-V where a 201c616f24bSVyacheslav Levytskyy // user may and should use bitcast between pointers with different pointee types 202c616f24bSVyacheslav Levytskyy // (https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpBitcast). 203c616f24bSVyacheslav Levytskyy // It's important for correct lowering in SPIR-V, because interpretation of the 204c616f24bSVyacheslav Levytskyy // data type is not left to instructions that utilize the pointer, but encoded 205c616f24bSVyacheslav Levytskyy // by the pointer declaration, and the SPIRV target can and must handle the 206c616f24bSVyacheslav Levytskyy // declaration and use of pointers that specify the type of data they point to. 207c616f24bSVyacheslav Levytskyy // It's not feasible to improve validation of G_BITCAST using just information 208c616f24bSVyacheslav Levytskyy // provided by low level types of source and destination. Therefore we don't 209c616f24bSVyacheslav Levytskyy // produce G_BITCAST as the general op code with semantics different from 210c616f24bSVyacheslav Levytskyy // OpBitcast, but rather lower to OpBitcast immediately. As for now, the only 211c616f24bSVyacheslav Levytskyy // difference would be that CombinerHelper couldn't transform known patterns 212c616f24bSVyacheslav Levytskyy // around G_BUILD_VECTOR. See discussion 213c616f24bSVyacheslav Levytskyy // in https://github.com/llvm/llvm-project/pull/110270 for even more context. 214c616f24bSVyacheslav Levytskyy static void selectOpBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR, 215c616f24bSVyacheslav Levytskyy MachineIRBuilder MIB) { 216c616f24bSVyacheslav Levytskyy SmallVector<MachineInstr *, 16> ToErase; 217c616f24bSVyacheslav Levytskyy for (MachineBasicBlock &MBB : MF) { 218c616f24bSVyacheslav Levytskyy for (MachineInstr &MI : MBB) { 219c616f24bSVyacheslav Levytskyy if (MI.getOpcode() != TargetOpcode::G_BITCAST) 220c616f24bSVyacheslav Levytskyy continue; 221c616f24bSVyacheslav Levytskyy MIB.setInsertPt(*MI.getParent(), MI); 222c616f24bSVyacheslav Levytskyy buildOpBitcast(GR, MIB, MI.getOperand(0).getReg(), 223c616f24bSVyacheslav Levytskyy MI.getOperand(1).getReg()); 224c616f24bSVyacheslav Levytskyy ToErase.push_back(&MI); 225c616f24bSVyacheslav Levytskyy } 226c616f24bSVyacheslav Levytskyy } 227c616f24bSVyacheslav Levytskyy for (MachineInstr *MI : ToErase) 228c616f24bSVyacheslav Levytskyy MI->eraseFromParent(); 229c616f24bSVyacheslav Levytskyy } 230c616f24bSVyacheslav Levytskyy 2310098f2aeSIlia Diachkov static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR, 2320098f2aeSIlia Diachkov MachineIRBuilder MIB) { 2334a602d92SVyacheslav Levytskyy // Get access to information about available extensions 2344a602d92SVyacheslav Levytskyy const SPIRVSubtarget *ST = 2354a602d92SVyacheslav Levytskyy static_cast<const SPIRVSubtarget *>(&MIB.getMF().getSubtarget()); 2360098f2aeSIlia Diachkov SmallVector<MachineInstr *, 10> ToErase; 2370098f2aeSIlia Diachkov for (MachineBasicBlock &MBB : MF) { 2380098f2aeSIlia Diachkov for (MachineInstr &MI : MBB) { 239b4cfb50cSMichal Paszkowski if (!isSpvIntrinsic(MI, Intrinsic::spv_bitcast) && 240b4cfb50cSMichal Paszkowski !isSpvIntrinsic(MI, Intrinsic::spv_ptrcast)) 2410098f2aeSIlia Diachkov continue; 2420098f2aeSIlia Diachkov assert(MI.getOperand(2).isReg()); 2430098f2aeSIlia Diachkov MIB.setInsertPt(*MI.getParent(), MI); 2440098f2aeSIlia Diachkov ToErase.push_back(&MI); 245b4cfb50cSMichal Paszkowski if (isSpvIntrinsic(MI, Intrinsic::spv_bitcast)) { 246b4cfb50cSMichal Paszkowski MIB.buildBitcast(MI.getOperand(0).getReg(), MI.getOperand(2).getReg()); 247b4cfb50cSMichal Paszkowski continue; 248b4cfb50cSMichal Paszkowski } 249b4cfb50cSMichal Paszkowski Register Def = MI.getOperand(0).getReg(); 250b4cfb50cSMichal Paszkowski Register Source = MI.getOperand(2).getReg(); 2519a737109SVyacheslav Levytskyy Type *ElemTy = getMDOperandAsType(MI.getOperand(3).getMetadata(), 0); 2529a737109SVyacheslav Levytskyy SPIRVType *BaseTy = GR->getOrCreateSPIRVType(ElemTy, MIB); 253b4cfb50cSMichal Paszkowski SPIRVType *AssignedPtrType = GR->getOrCreateSPIRVPointerType( 254b4cfb50cSMichal Paszkowski BaseTy, MI, *MF.getSubtarget<SPIRVSubtarget>().getInstrInfo(), 2554a602d92SVyacheslav Levytskyy addressSpaceToStorageClass(MI.getOperand(4).getImm(), *ST)); 256b4cfb50cSMichal Paszkowski 257dbd00a59SVyacheslav Levytskyy // If the ptrcast would be redundant, replace all uses with the source 258b4cfb50cSMichal Paszkowski // register. 25967d3ef74SVyacheslav Levytskyy MachineRegisterInfo *MRI = MIB.getMRI(); 260b4cfb50cSMichal Paszkowski if (GR->getSPIRVTypeForVReg(Source) == AssignedPtrType) { 261dbd00a59SVyacheslav Levytskyy // Erase Def's assign type instruction if we are going to replace Def. 26267d3ef74SVyacheslav Levytskyy if (MachineInstr *AssignMI = findAssignTypeInstr(Def, MRI)) 263dbd00a59SVyacheslav Levytskyy ToErase.push_back(AssignMI); 26467d3ef74SVyacheslav Levytskyy MRI->replaceRegWith(Def, Source); 265b4cfb50cSMichal Paszkowski } else { 266b4cfb50cSMichal Paszkowski GR->assignSPIRVTypeToVReg(AssignedPtrType, Def, MF); 267b4cfb50cSMichal Paszkowski MIB.buildBitcast(Def, Source); 268b4cfb50cSMichal Paszkowski } 2690098f2aeSIlia Diachkov } 2700098f2aeSIlia Diachkov } 2710098f2aeSIlia Diachkov for (MachineInstr *MI : ToErase) 2720098f2aeSIlia Diachkov MI->eraseFromParent(); 2730098f2aeSIlia Diachkov } 2740098f2aeSIlia Diachkov 2750098f2aeSIlia Diachkov // Translating GV, IRTranslator sometimes generates following IR: 2760098f2aeSIlia Diachkov // %1 = G_GLOBAL_VALUE 2770098f2aeSIlia Diachkov // %2 = COPY %1 2780098f2aeSIlia Diachkov // %3 = G_ADDRSPACE_CAST %2 27942d801d4SVyacheslav Levytskyy // 28042d801d4SVyacheslav Levytskyy // or 28142d801d4SVyacheslav Levytskyy // 28242d801d4SVyacheslav Levytskyy // %1 = G_ZEXT %2 28342d801d4SVyacheslav Levytskyy // G_MEMCPY ... %2 ... 28442d801d4SVyacheslav Levytskyy // 2850098f2aeSIlia Diachkov // New registers have no SPIRVType and no register class info. 2860098f2aeSIlia Diachkov // 2870098f2aeSIlia Diachkov // Set SPIRVType for GV, propagate it from GV to other instructions, 2880098f2aeSIlia Diachkov // also set register classes. 2890098f2aeSIlia Diachkov static SPIRVType *propagateSPIRVType(MachineInstr *MI, SPIRVGlobalRegistry *GR, 2900098f2aeSIlia Diachkov MachineRegisterInfo &MRI, 2910098f2aeSIlia Diachkov MachineIRBuilder &MIB) { 292f9c98068SVyacheslav Levytskyy SPIRVType *SpvType = nullptr; 2930098f2aeSIlia Diachkov assert(MI && "Machine instr is expected"); 2940098f2aeSIlia Diachkov if (MI->getOperand(0).isReg()) { 2950098f2aeSIlia Diachkov Register Reg = MI->getOperand(0).getReg(); 296f9c98068SVyacheslav Levytskyy SpvType = GR->getSPIRVTypeForVReg(Reg); 297f9c98068SVyacheslav Levytskyy if (!SpvType) { 2980098f2aeSIlia Diachkov switch (MI->getOpcode()) { 299978de2d6SVyacheslav Levytskyy case TargetOpcode::G_FCONSTANT: 3000098f2aeSIlia Diachkov case TargetOpcode::G_CONSTANT: { 3010098f2aeSIlia Diachkov MIB.setInsertPt(*MI->getParent(), MI); 3020098f2aeSIlia Diachkov Type *Ty = MI->getOperand(1).getCImm()->getType(); 303f9c98068SVyacheslav Levytskyy SpvType = GR->getOrCreateSPIRVType(Ty, MIB); 3040098f2aeSIlia Diachkov break; 3050098f2aeSIlia Diachkov } 3060098f2aeSIlia Diachkov case TargetOpcode::G_GLOBAL_VALUE: { 3070098f2aeSIlia Diachkov MIB.setInsertPt(*MI->getParent(), MI); 308b7ac8fddSVyacheslav Levytskyy const GlobalValue *Global = MI->getOperand(1).getGlobal(); 309dbd00a59SVyacheslav Levytskyy Type *ElementTy = toTypedPointer(GR->getDeducedGlobalValueType(Global)); 310dbd00a59SVyacheslav Levytskyy auto *Ty = TypedPointerType::get(ElementTy, 311f0eb9083SNathan Gauër Global->getType()->getAddressSpace()); 312f9c98068SVyacheslav Levytskyy SpvType = GR->getOrCreateSPIRVType(Ty, MIB); 3130098f2aeSIlia Diachkov break; 3140098f2aeSIlia Diachkov } 315214e6b40SVyacheslav Levytskyy case TargetOpcode::G_ANYEXT: 316214e6b40SVyacheslav Levytskyy case TargetOpcode::G_SEXT: 31742d801d4SVyacheslav Levytskyy case TargetOpcode::G_ZEXT: { 31842d801d4SVyacheslav Levytskyy if (MI->getOperand(1).isReg()) { 31942d801d4SVyacheslav Levytskyy if (MachineInstr *DefInstr = 32042d801d4SVyacheslav Levytskyy MRI.getVRegDef(MI->getOperand(1).getReg())) { 32142d801d4SVyacheslav Levytskyy if (SPIRVType *Def = propagateSPIRVType(DefInstr, GR, MRI, MIB)) { 32242d801d4SVyacheslav Levytskyy unsigned CurrentBW = GR->getScalarOrVectorBitWidth(Def); 32342d801d4SVyacheslav Levytskyy unsigned ExpectedBW = 32442d801d4SVyacheslav Levytskyy std::max(MRI.getType(Reg).getScalarSizeInBits(), CurrentBW); 32542d801d4SVyacheslav Levytskyy unsigned NumElements = GR->getScalarOrVectorComponentCount(Def); 326f9c98068SVyacheslav Levytskyy SpvType = GR->getOrCreateSPIRVIntegerType(ExpectedBW, MIB); 32742d801d4SVyacheslav Levytskyy if (NumElements > 1) 328f9c98068SVyacheslav Levytskyy SpvType = 329f9c98068SVyacheslav Levytskyy GR->getOrCreateSPIRVVectorType(SpvType, NumElements, MIB); 33042d801d4SVyacheslav Levytskyy } 33142d801d4SVyacheslav Levytskyy } 33242d801d4SVyacheslav Levytskyy } 33342d801d4SVyacheslav Levytskyy break; 33442d801d4SVyacheslav Levytskyy } 335486ea1ecSVyacheslav Levytskyy case TargetOpcode::G_PTRTOINT: 336f9c98068SVyacheslav Levytskyy SpvType = GR->getOrCreateSPIRVIntegerType( 337486ea1ecSVyacheslav Levytskyy MRI.getType(Reg).getScalarSizeInBits(), MIB); 338486ea1ecSVyacheslav Levytskyy break; 3390098f2aeSIlia Diachkov case TargetOpcode::G_TRUNC: 3400098f2aeSIlia Diachkov case TargetOpcode::G_ADDRSPACE_CAST: 341b8e1544bSIlia Diachkov case TargetOpcode::G_PTR_ADD: 3420098f2aeSIlia Diachkov case TargetOpcode::COPY: { 3430098f2aeSIlia Diachkov MachineOperand &Op = MI->getOperand(1); 3440098f2aeSIlia Diachkov MachineInstr *Def = Op.isReg() ? MRI.getVRegDef(Op.getReg()) : nullptr; 3450098f2aeSIlia Diachkov if (Def) 346f9c98068SVyacheslav Levytskyy SpvType = propagateSPIRVType(Def, GR, MRI, MIB); 3470098f2aeSIlia Diachkov break; 3480098f2aeSIlia Diachkov } 3490098f2aeSIlia Diachkov default: 3500098f2aeSIlia Diachkov break; 3510098f2aeSIlia Diachkov } 3523e79c7feSVyacheslav Levytskyy if (SpvType) { 3533e79c7feSVyacheslav Levytskyy // check if the address space needs correction 3543e79c7feSVyacheslav Levytskyy LLT RegType = MRI.getType(Reg); 3553e79c7feSVyacheslav Levytskyy if (SpvType->getOpcode() == SPIRV::OpTypePointer && 3563e79c7feSVyacheslav Levytskyy RegType.isPointer() && 3573e79c7feSVyacheslav Levytskyy storageClassToAddressSpace(GR->getPointerStorageClass(SpvType)) != 3583e79c7feSVyacheslav Levytskyy RegType.getAddressSpace()) { 3593e79c7feSVyacheslav Levytskyy const SPIRVSubtarget &ST = 3603e79c7feSVyacheslav Levytskyy MI->getParent()->getParent()->getSubtarget<SPIRVSubtarget>(); 3613e79c7feSVyacheslav Levytskyy SpvType = GR->getOrCreateSPIRVPointerType( 3623e79c7feSVyacheslav Levytskyy GR->getPointeeType(SpvType), *MI, *ST.getInstrInfo(), 3633e79c7feSVyacheslav Levytskyy addressSpaceToStorageClass(RegType.getAddressSpace(), ST)); 3643e79c7feSVyacheslav Levytskyy } 365f9c98068SVyacheslav Levytskyy GR->assignSPIRVTypeToVReg(SpvType, Reg, MIB.getMF()); 3663e79c7feSVyacheslav Levytskyy } 3670098f2aeSIlia Diachkov if (!MRI.getRegClassOrNull(Reg)) 36867d3ef74SVyacheslav Levytskyy MRI.setRegClass(Reg, SpvType ? GR->getRegClass(SpvType) 36967d3ef74SVyacheslav Levytskyy : &SPIRV::iIDRegClass); 3700098f2aeSIlia Diachkov } 3710098f2aeSIlia Diachkov } 372f9c98068SVyacheslav Levytskyy return SpvType; 3730098f2aeSIlia Diachkov } 3740098f2aeSIlia Diachkov 375163d036dSVyacheslav Levytskyy // To support current approach and limitations wrt. bit width here we widen a 376163d036dSVyacheslav Levytskyy // scalar register with a bit width greater than 1 to valid sizes and cap it to 377163d036dSVyacheslav Levytskyy // 64 width. 378163d036dSVyacheslav Levytskyy static void widenScalarLLTNextPow2(Register Reg, MachineRegisterInfo &MRI) { 379163d036dSVyacheslav Levytskyy LLT RegType = MRI.getType(Reg); 380163d036dSVyacheslav Levytskyy if (!RegType.isScalar()) 381163d036dSVyacheslav Levytskyy return; 382163d036dSVyacheslav Levytskyy unsigned Sz = RegType.getScalarSizeInBits(); 383163d036dSVyacheslav Levytskyy if (Sz == 1) 384163d036dSVyacheslav Levytskyy return; 385163d036dSVyacheslav Levytskyy unsigned NewSz = std::min(std::max(1u << Log2_32_Ceil(Sz), 8u), 64u); 386163d036dSVyacheslav Levytskyy if (NewSz != Sz) 387163d036dSVyacheslav Levytskyy MRI.setType(Reg, LLT::scalar(NewSz)); 388163d036dSVyacheslav Levytskyy } 389163d036dSVyacheslav Levytskyy 390540d2551SVyacheslav Levytskyy static std::pair<Register, unsigned> 391b512df66SVyacheslav Levytskyy createNewIdReg(SPIRVType *SpvType, Register SrcReg, MachineRegisterInfo &MRI, 392540d2551SVyacheslav Levytskyy const SPIRVGlobalRegistry &GR) { 393b512df66SVyacheslav Levytskyy if (!SpvType) 394b512df66SVyacheslav Levytskyy SpvType = GR.getSPIRVTypeForVReg(SrcReg); 39567d3ef74SVyacheslav Levytskyy const TargetRegisterClass *RC = GR.getRegClass(SpvType); 39667d3ef74SVyacheslav Levytskyy Register Reg = MRI.createGenericVirtualRegister(GR.getRegType(SpvType)); 39767d3ef74SVyacheslav Levytskyy MRI.setRegClass(Reg, RC); 39867d3ef74SVyacheslav Levytskyy unsigned GetIdOp = SPIRV::GET_ID; 39967d3ef74SVyacheslav Levytskyy if (RC == &SPIRV::fIDRegClass) 40067d3ef74SVyacheslav Levytskyy GetIdOp = SPIRV::GET_fID; 40167d3ef74SVyacheslav Levytskyy else if (RC == &SPIRV::pIDRegClass) 40267d3ef74SVyacheslav Levytskyy GetIdOp = SPIRV::GET_pID; 40367d3ef74SVyacheslav Levytskyy else if (RC == &SPIRV::vfIDRegClass) 40467d3ef74SVyacheslav Levytskyy GetIdOp = SPIRV::GET_vfID; 40567d3ef74SVyacheslav Levytskyy else if (RC == &SPIRV::vpIDRegClass) 40667d3ef74SVyacheslav Levytskyy GetIdOp = SPIRV::GET_vpID; 40767d3ef74SVyacheslav Levytskyy else if (RC == &SPIRV::vIDRegClass) 40867d3ef74SVyacheslav Levytskyy GetIdOp = SPIRV::GET_vID; 40967d3ef74SVyacheslav Levytskyy return {Reg, GetIdOp}; 410540d2551SVyacheslav Levytskyy } 411540d2551SVyacheslav Levytskyy 4128bc8b842SVyacheslav Levytskyy static void setInsertPtAfterDef(MachineIRBuilder &MIB, MachineInstr *Def) { 4138bc8b842SVyacheslav Levytskyy MachineBasicBlock &MBB = *Def->getParent(); 4148bc8b842SVyacheslav Levytskyy MachineBasicBlock::iterator DefIt = 4158bc8b842SVyacheslav Levytskyy Def->getNextNode() ? Def->getNextNode()->getIterator() : MBB.end(); 4168bc8b842SVyacheslav Levytskyy // Skip all the PHI and debug instructions. 4178bc8b842SVyacheslav Levytskyy while (DefIt != MBB.end() && 4188bc8b842SVyacheslav Levytskyy (DefIt->isPHI() || DefIt->isDebugOrPseudoInstr())) 4198bc8b842SVyacheslav Levytskyy DefIt = std::next(DefIt); 4208bc8b842SVyacheslav Levytskyy MIB.setInsertPt(MBB, DefIt); 4218bc8b842SVyacheslav Levytskyy } 4228bc8b842SVyacheslav Levytskyy 4230098f2aeSIlia Diachkov // Insert ASSIGN_TYPE instuction between Reg and its definition, set NewReg as 424f9c98068SVyacheslav Levytskyy // a dst of the definition, assign SPIRVType to both registers. If SpvType is 4250098f2aeSIlia Diachkov // provided, use it as SPIRVType in ASSIGN_TYPE, otherwise create it from Ty. 426f61eb416SIlia Diachkov // It's used also in SPIRVBuiltins.cpp. 4270098f2aeSIlia Diachkov // TODO: maybe move to SPIRVUtils. 428f61eb416SIlia Diachkov namespace llvm { 429f9c98068SVyacheslav Levytskyy Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpvType, 430f61eb416SIlia Diachkov SPIRVGlobalRegistry *GR, MachineIRBuilder &MIB, 4310098f2aeSIlia Diachkov MachineRegisterInfo &MRI) { 432f9c98068SVyacheslav Levytskyy assert((Ty || SpvType) && "Either LLVM or SPIRV type is expected."); 4338bc8b842SVyacheslav Levytskyy MachineInstr *Def = MRI.getVRegDef(Reg); 4348bc8b842SVyacheslav Levytskyy setInsertPtAfterDef(MIB, Def); 435f9c98068SVyacheslav Levytskyy SpvType = SpvType ? SpvType : GR->getOrCreateSPIRVType(Ty, MIB); 4360098f2aeSIlia Diachkov Register NewReg = MRI.createGenericVirtualRegister(MRI.getType(Reg)); 43774c66710SIlia Diachkov if (auto *RC = MRI.getRegClassOrNull(Reg)) { 4380098f2aeSIlia Diachkov MRI.setRegClass(NewReg, RC); 43974c66710SIlia Diachkov } else { 44067d3ef74SVyacheslav Levytskyy auto RegClass = GR->getRegClass(SpvType); 441f9c98068SVyacheslav Levytskyy MRI.setRegClass(NewReg, RegClass); 442f9c98068SVyacheslav Levytskyy MRI.setRegClass(Reg, RegClass); 44374c66710SIlia Diachkov } 444f9c98068SVyacheslav Levytskyy GR->assignSPIRVTypeToVReg(SpvType, Reg, MIB.getMF()); 4450098f2aeSIlia Diachkov // This is to make it convenient for Legalizer to get the SPIRVType 4460098f2aeSIlia Diachkov // when processing the actual MI (i.e. not pseudo one). 447f9c98068SVyacheslav Levytskyy GR->assignSPIRVTypeToVReg(SpvType, NewReg, MIB.getMF()); 448df871307SIlia Diachkov // Copy MIFlags from Def to ASSIGN_TYPE instruction. It's required to keep 449df871307SIlia Diachkov // the flags after instruction selection. 45009515f2cSDávid Bolvanský const uint32_t Flags = Def->getFlags(); 4510098f2aeSIlia Diachkov MIB.buildInstr(SPIRV::ASSIGN_TYPE) 4520098f2aeSIlia Diachkov .addDef(Reg) 4530098f2aeSIlia Diachkov .addUse(NewReg) 454f9c98068SVyacheslav Levytskyy .addUse(GR->getSPIRVTypeID(SpvType)) 455df871307SIlia Diachkov .setMIFlags(Flags); 456a059b299SVyacheslav Levytskyy for (unsigned I = 0, E = Def->getNumDefs(); I != E; ++I) { 457a059b299SVyacheslav Levytskyy MachineOperand &MO = Def->getOperand(I); 458a059b299SVyacheslav Levytskyy if (MO.getReg() == Reg) { 459a059b299SVyacheslav Levytskyy MO.setReg(NewReg); 460a059b299SVyacheslav Levytskyy break; 461a059b299SVyacheslav Levytskyy } 462a059b299SVyacheslav Levytskyy } 4630098f2aeSIlia Diachkov return NewReg; 4640098f2aeSIlia Diachkov } 465540d2551SVyacheslav Levytskyy 466540d2551SVyacheslav Levytskyy void processInstr(MachineInstr &MI, MachineIRBuilder &MIB, 467540d2551SVyacheslav Levytskyy MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR) { 46802a334deSNathan Gauër MIB.setInsertPt(*MI.getParent(), MI.getIterator()); 469540d2551SVyacheslav Levytskyy for (auto &Op : MI.operands()) { 470540d2551SVyacheslav Levytskyy if (!Op.isReg() || Op.isDef()) 471540d2551SVyacheslav Levytskyy continue; 47242633cf2SVyacheslav Levytskyy Register OpReg = Op.getReg(); 47342633cf2SVyacheslav Levytskyy SPIRVType *SpvType = GR->getSPIRVTypeForVReg(OpReg); 47442633cf2SVyacheslav Levytskyy auto IdOpInfo = createNewIdReg(SpvType, OpReg, MRI, *GR); 47542633cf2SVyacheslav Levytskyy MIB.buildInstr(IdOpInfo.second).addDef(IdOpInfo.first).addUse(OpReg); 47642633cf2SVyacheslav Levytskyy const TargetRegisterClass *RC = GR->getRegClass(SpvType); 47742633cf2SVyacheslav Levytskyy if (RC != MRI.getRegClassOrNull(OpReg)) 47842633cf2SVyacheslav Levytskyy MRI.setRegClass(OpReg, RC); 479540d2551SVyacheslav Levytskyy Op.setReg(IdOpInfo.first); 480540d2551SVyacheslav Levytskyy } 481540d2551SVyacheslav Levytskyy } 482f61eb416SIlia Diachkov } // namespace llvm 4830098f2aeSIlia Diachkov 4841ed1ec9aSVyacheslav Levytskyy static void 4851ed1ec9aSVyacheslav Levytskyy generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR, 4861ed1ec9aSVyacheslav Levytskyy MachineIRBuilder MIB, 4871ed1ec9aSVyacheslav Levytskyy DenseMap<MachineInstr *, Type *> &TargetExtConstTypes) { 4884a602d92SVyacheslav Levytskyy // Get access to information about available extensions 4894a602d92SVyacheslav Levytskyy const SPIRVSubtarget *ST = 4904a602d92SVyacheslav Levytskyy static_cast<const SPIRVSubtarget *>(&MIB.getMF().getSubtarget()); 4914a602d92SVyacheslav Levytskyy 4920098f2aeSIlia Diachkov MachineRegisterInfo &MRI = MF.getRegInfo(); 4930098f2aeSIlia Diachkov SmallVector<MachineInstr *, 10> ToErase; 4940d9172ecSVyacheslav Levytskyy DenseMap<MachineInstr *, Register> RegsAlreadyAddedToDT; 4950098f2aeSIlia Diachkov 49667d3ef74SVyacheslav Levytskyy bool IsExtendedInts = 49767d3ef74SVyacheslav Levytskyy ST->canUseExtension( 49867d3ef74SVyacheslav Levytskyy SPIRV::Extension::SPV_INTEL_arbitrary_precision_integers) || 49967d3ef74SVyacheslav Levytskyy ST->canUseExtension(SPIRV::Extension::SPV_KHR_bit_instructions); 50067d3ef74SVyacheslav Levytskyy 5010098f2aeSIlia Diachkov for (MachineBasicBlock *MBB : post_order(&MF)) { 5020098f2aeSIlia Diachkov if (MBB->empty()) 5030098f2aeSIlia Diachkov continue; 5040098f2aeSIlia Diachkov 5050098f2aeSIlia Diachkov bool ReachedBegin = false; 5060098f2aeSIlia Diachkov for (auto MII = std::prev(MBB->end()), Begin = MBB->begin(); 5070098f2aeSIlia Diachkov !ReachedBegin;) { 5080098f2aeSIlia Diachkov MachineInstr &MI = *MII; 5097c917e82SVyacheslav Levytskyy unsigned MIOp = MI.getOpcode(); 5100098f2aeSIlia Diachkov 51167d3ef74SVyacheslav Levytskyy if (!IsExtendedInts) { 512163d036dSVyacheslav Levytskyy // validate bit width of scalar registers 513163d036dSVyacheslav Levytskyy for (const auto &MOP : MI.operands()) 514163d036dSVyacheslav Levytskyy if (MOP.isReg()) 515163d036dSVyacheslav Levytskyy widenScalarLLTNextPow2(MOP.getReg(), MRI); 51667d3ef74SVyacheslav Levytskyy } 517163d036dSVyacheslav Levytskyy 518ec7baca1SMichal Paszkowski if (isSpvIntrinsic(MI, Intrinsic::spv_assign_ptr_type)) { 519ec7baca1SMichal Paszkowski Register Reg = MI.getOperand(1).getReg(); 520ec7baca1SMichal Paszkowski MIB.setInsertPt(*MI.getParent(), MI.getIterator()); 5219a737109SVyacheslav Levytskyy Type *ElementTy = getMDOperandAsType(MI.getOperand(2).getMetadata(), 0); 5229a737109SVyacheslav Levytskyy SPIRVType *BaseTy = GR->getOrCreateSPIRVType(ElementTy, MIB); 523ec7baca1SMichal Paszkowski SPIRVType *AssignedPtrType = GR->getOrCreateSPIRVPointerType( 5242616c279SMichal Paszkowski BaseTy, MI, *MF.getSubtarget<SPIRVSubtarget>().getInstrInfo(), 5254a602d92SVyacheslav Levytskyy addressSpaceToStorageClass(MI.getOperand(3).getImm(), *ST)); 526ec7baca1SMichal Paszkowski MachineInstr *Def = MRI.getVRegDef(Reg); 527ec7baca1SMichal Paszkowski assert(Def && "Expecting an instruction that defines the register"); 52843222bd3SMichal Paszkowski // G_GLOBAL_VALUE already has type info. 529505cd125SVyacheslav Levytskyy if (Def->getOpcode() != TargetOpcode::G_GLOBAL_VALUE && 530505cd125SVyacheslav Levytskyy Def->getOpcode() != SPIRV::ASSIGN_TYPE) 531ec7baca1SMichal Paszkowski insertAssignInstr(Reg, nullptr, AssignedPtrType, GR, MIB, 532ec7baca1SMichal Paszkowski MF.getRegInfo()); 533ec7baca1SMichal Paszkowski ToErase.push_back(&MI); 534ec7baca1SMichal Paszkowski } else if (isSpvIntrinsic(MI, Intrinsic::spv_assign_type)) { 5350098f2aeSIlia Diachkov Register Reg = MI.getOperand(1).getReg(); 5360098f2aeSIlia Diachkov Type *Ty = getMDOperandAsType(MI.getOperand(2).getMetadata(), 0); 5370098f2aeSIlia Diachkov MachineInstr *Def = MRI.getVRegDef(Reg); 5380098f2aeSIlia Diachkov assert(Def && "Expecting an instruction that defines the register"); 5390098f2aeSIlia Diachkov // G_GLOBAL_VALUE already has type info. 540505cd125SVyacheslav Levytskyy if (Def->getOpcode() != TargetOpcode::G_GLOBAL_VALUE && 541505cd125SVyacheslav Levytskyy Def->getOpcode() != SPIRV::ASSIGN_TYPE) 5420098f2aeSIlia Diachkov insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MF.getRegInfo()); 5430098f2aeSIlia Diachkov ToErase.push_back(&MI); 544a059b299SVyacheslav Levytskyy } else if (MIOp == TargetOpcode::FAKE_USE && MI.getNumOperands() > 0) { 545a059b299SVyacheslav Levytskyy MachineInstr *MdMI = MI.getPrevNode(); 546a059b299SVyacheslav Levytskyy if (MdMI && isSpvIntrinsic(*MdMI, Intrinsic::spv_value_md)) { 547a059b299SVyacheslav Levytskyy // It's an internal service info from before IRTranslator passes. 548a059b299SVyacheslav Levytskyy MachineInstr *Def = getVRegDef(MRI, MI.getOperand(0).getReg()); 549a059b299SVyacheslav Levytskyy for (unsigned I = 1, E = MI.getNumOperands(); I != E && Def; ++I) 550a059b299SVyacheslav Levytskyy if (getVRegDef(MRI, MI.getOperand(I).getReg()) != Def) 551a059b299SVyacheslav Levytskyy Def = nullptr; 552a059b299SVyacheslav Levytskyy if (Def) { 553a059b299SVyacheslav Levytskyy const MDNode *MD = MdMI->getOperand(1).getMetadata(); 554a059b299SVyacheslav Levytskyy StringRef ValueName = 555a059b299SVyacheslav Levytskyy cast<MDString>(MD->getOperand(1))->getString(); 556a059b299SVyacheslav Levytskyy const MDNode *TypeMD = cast<MDNode>(MD->getOperand(0)); 557a059b299SVyacheslav Levytskyy Type *ValueTy = getMDOperandAsType(TypeMD, 0); 558a059b299SVyacheslav Levytskyy GR->addValueAttrs(Def, std::make_pair(ValueTy, ValueName.str())); 559a059b299SVyacheslav Levytskyy } 560a059b299SVyacheslav Levytskyy ToErase.push_back(MdMI); 561a059b299SVyacheslav Levytskyy } 562a059b299SVyacheslav Levytskyy ToErase.push_back(&MI); 5637c917e82SVyacheslav Levytskyy } else if (MIOp == TargetOpcode::G_CONSTANT || 5647c917e82SVyacheslav Levytskyy MIOp == TargetOpcode::G_FCONSTANT || 5657c917e82SVyacheslav Levytskyy MIOp == TargetOpcode::G_BUILD_VECTOR) { 5660098f2aeSIlia Diachkov // %rc = G_CONSTANT ty Val 5670098f2aeSIlia Diachkov // ===> 5680098f2aeSIlia Diachkov // %cty = OpType* ty 5690098f2aeSIlia Diachkov // %rctmp = G_CONSTANT ty Val 5700098f2aeSIlia Diachkov // %rc = ASSIGN_TYPE %rctmp, %cty 5710098f2aeSIlia Diachkov Register Reg = MI.getOperand(0).getReg(); 5720d9172ecSVyacheslav Levytskyy bool NeedAssignType = true; 5730098f2aeSIlia Diachkov if (MRI.hasOneUse(Reg)) { 5740098f2aeSIlia Diachkov MachineInstr &UseMI = *MRI.use_instr_begin(Reg); 5750098f2aeSIlia Diachkov if (isSpvIntrinsic(UseMI, Intrinsic::spv_assign_type) || 5760098f2aeSIlia Diachkov isSpvIntrinsic(UseMI, Intrinsic::spv_assign_name)) 5770098f2aeSIlia Diachkov continue; 57867d3ef74SVyacheslav Levytskyy if (UseMI.getOpcode() == SPIRV::ASSIGN_TYPE) 57967d3ef74SVyacheslav Levytskyy NeedAssignType = false; 5800098f2aeSIlia Diachkov } 5810098f2aeSIlia Diachkov Type *Ty = nullptr; 5827c917e82SVyacheslav Levytskyy if (MIOp == TargetOpcode::G_CONSTANT) { 5831ed1ec9aSVyacheslav Levytskyy auto TargetExtIt = TargetExtConstTypes.find(&MI); 5841ed1ec9aSVyacheslav Levytskyy Ty = TargetExtIt == TargetExtConstTypes.end() 5851ed1ec9aSVyacheslav Levytskyy ? MI.getOperand(1).getCImm()->getType() 5861ed1ec9aSVyacheslav Levytskyy : TargetExtIt->second; 5870d9172ecSVyacheslav Levytskyy const ConstantInt *OpCI = MI.getOperand(1).getCImm(); 5883e79c7feSVyacheslav Levytskyy // TODO: we may wish to analyze here if OpCI is zero and LLT RegType = 5893e79c7feSVyacheslav Levytskyy // MRI.getType(Reg); RegType.isPointer() is true, so that we observe 5903e79c7feSVyacheslav Levytskyy // at this point not i64/i32 constant but null pointer in the 5913e79c7feSVyacheslav Levytskyy // corresponding address space of RegType.getAddressSpace(). This may 5923e79c7feSVyacheslav Levytskyy // help to successfully validate the case when a OpConstantComposite's 5933e79c7feSVyacheslav Levytskyy // constituent has type that does not match Result Type of 5943e79c7feSVyacheslav Levytskyy // OpConstantComposite (see, for example, 5953e79c7feSVyacheslav Levytskyy // pointers/PtrCast-null-in-OpSpecConstantOp.ll). 5960d9172ecSVyacheslav Levytskyy Register PrimaryReg = GR->find(OpCI, &MF); 5970d9172ecSVyacheslav Levytskyy if (!PrimaryReg.isValid()) { 5980d9172ecSVyacheslav Levytskyy GR->add(OpCI, &MF, Reg); 5990d9172ecSVyacheslav Levytskyy } else if (PrimaryReg != Reg && 6000d9172ecSVyacheslav Levytskyy MRI.getType(Reg) == MRI.getType(PrimaryReg)) { 6010d9172ecSVyacheslav Levytskyy auto *RCReg = MRI.getRegClassOrNull(Reg); 6020d9172ecSVyacheslav Levytskyy auto *RCPrimary = MRI.getRegClassOrNull(PrimaryReg); 6030d9172ecSVyacheslav Levytskyy if (!RCReg || RCPrimary == RCReg) { 6040d9172ecSVyacheslav Levytskyy RegsAlreadyAddedToDT[&MI] = PrimaryReg; 6050d9172ecSVyacheslav Levytskyy ToErase.push_back(&MI); 6060d9172ecSVyacheslav Levytskyy NeedAssignType = false; 6070d9172ecSVyacheslav Levytskyy } 6080d9172ecSVyacheslav Levytskyy } 6097c917e82SVyacheslav Levytskyy } else if (MIOp == TargetOpcode::G_FCONSTANT) { 6100098f2aeSIlia Diachkov Ty = MI.getOperand(1).getFPImm()->getType(); 6111ed1ec9aSVyacheslav Levytskyy } else { 6127c917e82SVyacheslav Levytskyy assert(MIOp == TargetOpcode::G_BUILD_VECTOR); 6130098f2aeSIlia Diachkov Type *ElemTy = nullptr; 6140098f2aeSIlia Diachkov MachineInstr *ElemMI = MRI.getVRegDef(MI.getOperand(1).getReg()); 6150098f2aeSIlia Diachkov assert(ElemMI); 6160098f2aeSIlia Diachkov 6172fc7a727SVyacheslav Levytskyy if (ElemMI->getOpcode() == TargetOpcode::G_CONSTANT) { 6180098f2aeSIlia Diachkov ElemTy = ElemMI->getOperand(1).getCImm()->getType(); 6192fc7a727SVyacheslav Levytskyy } else if (ElemMI->getOpcode() == TargetOpcode::G_FCONSTANT) { 6200098f2aeSIlia Diachkov ElemTy = ElemMI->getOperand(1).getFPImm()->getType(); 6212fc7a727SVyacheslav Levytskyy } else { 6222fc7a727SVyacheslav Levytskyy // There may be a case when we already know Reg's type. 6232fc7a727SVyacheslav Levytskyy MachineInstr *NextMI = MI.getNextNode(); 6242fc7a727SVyacheslav Levytskyy if (!NextMI || NextMI->getOpcode() != SPIRV::ASSIGN_TYPE || 6252fc7a727SVyacheslav Levytskyy NextMI->getOperand(1).getReg() != Reg) 6260098f2aeSIlia Diachkov llvm_unreachable("Unexpected opcode"); 6272fc7a727SVyacheslav Levytskyy } 6282fc7a727SVyacheslav Levytskyy if (ElemTy) 6292fc7a727SVyacheslav Levytskyy Ty = VectorType::get( 6302fc7a727SVyacheslav Levytskyy ElemTy, MI.getNumExplicitOperands() - MI.getNumExplicitDefs(), 6312fc7a727SVyacheslav Levytskyy false); 6322fc7a727SVyacheslav Levytskyy else 6332fc7a727SVyacheslav Levytskyy NeedAssignType = false; 6340098f2aeSIlia Diachkov } 6350d9172ecSVyacheslav Levytskyy if (NeedAssignType) 6360098f2aeSIlia Diachkov insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MRI); 6377c917e82SVyacheslav Levytskyy } else if (MIOp == TargetOpcode::G_GLOBAL_VALUE) { 6380098f2aeSIlia Diachkov propagateSPIRVType(&MI, GR, MRI, MIB); 6390098f2aeSIlia Diachkov } 6400098f2aeSIlia Diachkov 6410098f2aeSIlia Diachkov if (MII == Begin) 6420098f2aeSIlia Diachkov ReachedBegin = true; 6430098f2aeSIlia Diachkov else 6440098f2aeSIlia Diachkov --MII; 6450098f2aeSIlia Diachkov } 6460098f2aeSIlia Diachkov } 6470d9172ecSVyacheslav Levytskyy for (MachineInstr *MI : ToErase) { 6480d9172ecSVyacheslav Levytskyy auto It = RegsAlreadyAddedToDT.find(MI); 6490d9172ecSVyacheslav Levytskyy if (RegsAlreadyAddedToDT.contains(MI)) 6500d9172ecSVyacheslav Levytskyy MRI.replaceRegWith(MI->getOperand(0).getReg(), It->second); 6510098f2aeSIlia Diachkov MI->eraseFromParent(); 6520d9172ecSVyacheslav Levytskyy } 653214e6b40SVyacheslav Levytskyy 654214e6b40SVyacheslav Levytskyy // Address the case when IRTranslator introduces instructions with new 655214e6b40SVyacheslav Levytskyy // registers without SPIRVType associated. 656214e6b40SVyacheslav Levytskyy for (MachineBasicBlock &MBB : MF) { 657214e6b40SVyacheslav Levytskyy for (MachineInstr &MI : MBB) { 658214e6b40SVyacheslav Levytskyy switch (MI.getOpcode()) { 659214e6b40SVyacheslav Levytskyy case TargetOpcode::G_TRUNC: 660214e6b40SVyacheslav Levytskyy case TargetOpcode::G_ANYEXT: 661214e6b40SVyacheslav Levytskyy case TargetOpcode::G_SEXT: 662214e6b40SVyacheslav Levytskyy case TargetOpcode::G_ZEXT: 663214e6b40SVyacheslav Levytskyy case TargetOpcode::G_PTRTOINT: 664214e6b40SVyacheslav Levytskyy case TargetOpcode::COPY: 665214e6b40SVyacheslav Levytskyy case TargetOpcode::G_ADDRSPACE_CAST: 666214e6b40SVyacheslav Levytskyy propagateSPIRVType(&MI, GR, MRI, MIB); 667214e6b40SVyacheslav Levytskyy break; 668214e6b40SVyacheslav Levytskyy } 669214e6b40SVyacheslav Levytskyy } 670214e6b40SVyacheslav Levytskyy } 6710098f2aeSIlia Diachkov } 6720098f2aeSIlia Diachkov 6730098f2aeSIlia Diachkov // Defined in SPIRVLegalizerInfo.cpp. 6740098f2aeSIlia Diachkov extern bool isTypeFoldingSupported(unsigned Opcode); 6750098f2aeSIlia Diachkov 6760098f2aeSIlia Diachkov static void processInstrsWithTypeFolding(MachineFunction &MF, 6770098f2aeSIlia Diachkov SPIRVGlobalRegistry *GR, 6780098f2aeSIlia Diachkov MachineIRBuilder MIB) { 6790098f2aeSIlia Diachkov MachineRegisterInfo &MRI = MF.getRegInfo(); 680978de2d6SVyacheslav Levytskyy for (MachineBasicBlock &MBB : MF) 681978de2d6SVyacheslav Levytskyy for (MachineInstr &MI : MBB) 6820098f2aeSIlia Diachkov if (isTypeFoldingSupported(MI.getOpcode())) 6830098f2aeSIlia Diachkov processInstr(MI, MIB, MRI, GR); 6840098f2aeSIlia Diachkov } 6850098f2aeSIlia Diachkov 68667d3ef74SVyacheslav Levytskyy static Register 68767d3ef74SVyacheslav Levytskyy collectInlineAsmInstrOperands(MachineInstr *MI, 68867d3ef74SVyacheslav Levytskyy SmallVector<unsigned, 4> *Ops = nullptr) { 68967d3ef74SVyacheslav Levytskyy Register DefReg; 69067d3ef74SVyacheslav Levytskyy unsigned StartOp = InlineAsm::MIOp_FirstOperand, 69167d3ef74SVyacheslav Levytskyy AsmDescOp = InlineAsm::MIOp_FirstOperand; 69267d3ef74SVyacheslav Levytskyy for (unsigned Idx = StartOp, MISz = MI->getNumOperands(); Idx != MISz; 69367d3ef74SVyacheslav Levytskyy ++Idx) { 69467d3ef74SVyacheslav Levytskyy const MachineOperand &MO = MI->getOperand(Idx); 69567d3ef74SVyacheslav Levytskyy if (MO.isMetadata()) 69667d3ef74SVyacheslav Levytskyy continue; 69767d3ef74SVyacheslav Levytskyy if (Idx == AsmDescOp && MO.isImm()) { 69867d3ef74SVyacheslav Levytskyy // compute the index of the next operand descriptor 69967d3ef74SVyacheslav Levytskyy const InlineAsm::Flag F(MO.getImm()); 70067d3ef74SVyacheslav Levytskyy AsmDescOp += 1 + F.getNumOperandRegisters(); 70167d3ef74SVyacheslav Levytskyy continue; 70267d3ef74SVyacheslav Levytskyy } 70367d3ef74SVyacheslav Levytskyy if (MO.isReg() && MO.isDef()) { 70467d3ef74SVyacheslav Levytskyy if (!Ops) 70567d3ef74SVyacheslav Levytskyy return MO.getReg(); 70667d3ef74SVyacheslav Levytskyy else 70767d3ef74SVyacheslav Levytskyy DefReg = MO.getReg(); 70867d3ef74SVyacheslav Levytskyy } else if (Ops) { 70967d3ef74SVyacheslav Levytskyy Ops->push_back(Idx); 71067d3ef74SVyacheslav Levytskyy } 71167d3ef74SVyacheslav Levytskyy } 71267d3ef74SVyacheslav Levytskyy return DefReg; 71367d3ef74SVyacheslav Levytskyy } 71467d3ef74SVyacheslav Levytskyy 715214e6b40SVyacheslav Levytskyy static void 716214e6b40SVyacheslav Levytskyy insertInlineAsmProcess(MachineFunction &MF, SPIRVGlobalRegistry *GR, 717214e6b40SVyacheslav Levytskyy const SPIRVSubtarget &ST, MachineIRBuilder MIRBuilder, 718214e6b40SVyacheslav Levytskyy const SmallVector<MachineInstr *> &ToProcess) { 719214e6b40SVyacheslav Levytskyy MachineRegisterInfo &MRI = MF.getRegInfo(); 720214e6b40SVyacheslav Levytskyy Register AsmTargetReg; 721214e6b40SVyacheslav Levytskyy for (unsigned i = 0, Sz = ToProcess.size(); i + 1 < Sz; i += 2) { 722214e6b40SVyacheslav Levytskyy MachineInstr *I1 = ToProcess[i], *I2 = ToProcess[i + 1]; 723214e6b40SVyacheslav Levytskyy assert(isSpvIntrinsic(*I1, Intrinsic::spv_inline_asm) && I2->isInlineAsm()); 72467d3ef74SVyacheslav Levytskyy MIRBuilder.setInsertPt(*I2->getParent(), *I2); 725214e6b40SVyacheslav Levytskyy 726214e6b40SVyacheslav Levytskyy if (!AsmTargetReg.isValid()) { 727214e6b40SVyacheslav Levytskyy // define vendor specific assembly target or dialect 728214e6b40SVyacheslav Levytskyy AsmTargetReg = MRI.createGenericVirtualRegister(LLT::scalar(32)); 729f9c98068SVyacheslav Levytskyy MRI.setRegClass(AsmTargetReg, &SPIRV::iIDRegClass); 730214e6b40SVyacheslav Levytskyy auto AsmTargetMIB = 731214e6b40SVyacheslav Levytskyy MIRBuilder.buildInstr(SPIRV::OpAsmTargetINTEL).addDef(AsmTargetReg); 732214e6b40SVyacheslav Levytskyy addStringImm(ST.getTargetTripleAsStr(), AsmTargetMIB); 733214e6b40SVyacheslav Levytskyy GR->add(AsmTargetMIB.getInstr(), &MF, AsmTargetReg); 734214e6b40SVyacheslav Levytskyy } 735214e6b40SVyacheslav Levytskyy 736214e6b40SVyacheslav Levytskyy // create types 737214e6b40SVyacheslav Levytskyy const MDNode *IAMD = I1->getOperand(1).getMetadata(); 738214e6b40SVyacheslav Levytskyy FunctionType *FTy = cast<FunctionType>(getMDOperandAsType(IAMD, 0)); 739214e6b40SVyacheslav Levytskyy SmallVector<SPIRVType *, 4> ArgTypes; 740214e6b40SVyacheslav Levytskyy for (const auto &ArgTy : FTy->params()) 741214e6b40SVyacheslav Levytskyy ArgTypes.push_back(GR->getOrCreateSPIRVType(ArgTy, MIRBuilder)); 742214e6b40SVyacheslav Levytskyy SPIRVType *RetType = 743214e6b40SVyacheslav Levytskyy GR->getOrCreateSPIRVType(FTy->getReturnType(), MIRBuilder); 744214e6b40SVyacheslav Levytskyy SPIRVType *FuncType = GR->getOrCreateOpTypeFunctionWithArgs( 745214e6b40SVyacheslav Levytskyy FTy, RetType, ArgTypes, MIRBuilder); 746214e6b40SVyacheslav Levytskyy 747214e6b40SVyacheslav Levytskyy // define vendor specific assembly instructions string 748214e6b40SVyacheslav Levytskyy Register AsmReg = MRI.createGenericVirtualRegister(LLT::scalar(32)); 749f9c98068SVyacheslav Levytskyy MRI.setRegClass(AsmReg, &SPIRV::iIDRegClass); 750214e6b40SVyacheslav Levytskyy auto AsmMIB = MIRBuilder.buildInstr(SPIRV::OpAsmINTEL) 751214e6b40SVyacheslav Levytskyy .addDef(AsmReg) 752214e6b40SVyacheslav Levytskyy .addUse(GR->getSPIRVTypeID(RetType)) 753214e6b40SVyacheslav Levytskyy .addUse(GR->getSPIRVTypeID(FuncType)) 754214e6b40SVyacheslav Levytskyy .addUse(AsmTargetReg); 755214e6b40SVyacheslav Levytskyy // inline asm string: 756214e6b40SVyacheslav Levytskyy addStringImm(I2->getOperand(InlineAsm::MIOp_AsmString).getSymbolName(), 757214e6b40SVyacheslav Levytskyy AsmMIB); 758214e6b40SVyacheslav Levytskyy // inline asm constraint string: 759214e6b40SVyacheslav Levytskyy addStringImm(cast<MDString>(I1->getOperand(2).getMetadata()->getOperand(0)) 760214e6b40SVyacheslav Levytskyy ->getString(), 761214e6b40SVyacheslav Levytskyy AsmMIB); 762214e6b40SVyacheslav Levytskyy GR->add(AsmMIB.getInstr(), &MF, AsmReg); 763214e6b40SVyacheslav Levytskyy 764214e6b40SVyacheslav Levytskyy // calls the inline assembly instruction 765214e6b40SVyacheslav Levytskyy unsigned ExtraInfo = I2->getOperand(InlineAsm::MIOp_ExtraInfo).getImm(); 766214e6b40SVyacheslav Levytskyy if (ExtraInfo & InlineAsm::Extra_HasSideEffects) 767214e6b40SVyacheslav Levytskyy MIRBuilder.buildInstr(SPIRV::OpDecorate) 768214e6b40SVyacheslav Levytskyy .addUse(AsmReg) 769214e6b40SVyacheslav Levytskyy .addImm(static_cast<uint32_t>(SPIRV::Decoration::SideEffectsINTEL)); 77067d3ef74SVyacheslav Levytskyy 77167d3ef74SVyacheslav Levytskyy Register DefReg = collectInlineAsmInstrOperands(I2); 772214e6b40SVyacheslav Levytskyy if (!DefReg.isValid()) { 773214e6b40SVyacheslav Levytskyy DefReg = MRI.createGenericVirtualRegister(LLT::scalar(32)); 774f9c98068SVyacheslav Levytskyy MRI.setRegClass(DefReg, &SPIRV::iIDRegClass); 775214e6b40SVyacheslav Levytskyy SPIRVType *VoidType = GR->getOrCreateSPIRVType( 776214e6b40SVyacheslav Levytskyy Type::getVoidTy(MF.getFunction().getContext()), MIRBuilder); 777214e6b40SVyacheslav Levytskyy GR->assignSPIRVTypeToVReg(VoidType, DefReg, MF); 778214e6b40SVyacheslav Levytskyy } 77967d3ef74SVyacheslav Levytskyy 780214e6b40SVyacheslav Levytskyy auto AsmCall = MIRBuilder.buildInstr(SPIRV::OpAsmCallINTEL) 781214e6b40SVyacheslav Levytskyy .addDef(DefReg) 782214e6b40SVyacheslav Levytskyy .addUse(GR->getSPIRVTypeID(RetType)) 783214e6b40SVyacheslav Levytskyy .addUse(AsmReg); 78467d3ef74SVyacheslav Levytskyy for (unsigned IntrIdx = 3; IntrIdx < I1->getNumOperands(); ++IntrIdx) 785214e6b40SVyacheslav Levytskyy AsmCall.addUse(I1->getOperand(IntrIdx).getReg()); 786214e6b40SVyacheslav Levytskyy } 787214e6b40SVyacheslav Levytskyy for (MachineInstr *MI : ToProcess) 788214e6b40SVyacheslav Levytskyy MI->eraseFromParent(); 789214e6b40SVyacheslav Levytskyy } 790214e6b40SVyacheslav Levytskyy 791214e6b40SVyacheslav Levytskyy static void insertInlineAsm(MachineFunction &MF, SPIRVGlobalRegistry *GR, 792214e6b40SVyacheslav Levytskyy const SPIRVSubtarget &ST, 793214e6b40SVyacheslav Levytskyy MachineIRBuilder MIRBuilder) { 794214e6b40SVyacheslav Levytskyy SmallVector<MachineInstr *> ToProcess; 795214e6b40SVyacheslav Levytskyy for (MachineBasicBlock &MBB : MF) { 796214e6b40SVyacheslav Levytskyy for (MachineInstr &MI : MBB) { 797214e6b40SVyacheslav Levytskyy if (isSpvIntrinsic(MI, Intrinsic::spv_inline_asm) || 798214e6b40SVyacheslav Levytskyy MI.getOpcode() == TargetOpcode::INLINEASM) 799214e6b40SVyacheslav Levytskyy ToProcess.push_back(&MI); 800214e6b40SVyacheslav Levytskyy } 801214e6b40SVyacheslav Levytskyy } 802214e6b40SVyacheslav Levytskyy if (ToProcess.size() == 0) 803214e6b40SVyacheslav Levytskyy return; 804214e6b40SVyacheslav Levytskyy 805214e6b40SVyacheslav Levytskyy if (!ST.canUseExtension(SPIRV::Extension::SPV_INTEL_inline_assembly)) 806214e6b40SVyacheslav Levytskyy report_fatal_error("Inline assembly instructions require the " 807214e6b40SVyacheslav Levytskyy "following SPIR-V extension: SPV_INTEL_inline_assembly", 808214e6b40SVyacheslav Levytskyy false); 809214e6b40SVyacheslav Levytskyy 810214e6b40SVyacheslav Levytskyy insertInlineAsmProcess(MF, GR, ST, MIRBuilder, ToProcess); 811214e6b40SVyacheslav Levytskyy } 812214e6b40SVyacheslav Levytskyy 813be9b4dabSVyacheslav Levytskyy static void insertSpirvDecorations(MachineFunction &MF, MachineIRBuilder MIB) { 814be9b4dabSVyacheslav Levytskyy SmallVector<MachineInstr *, 10> ToErase; 815be9b4dabSVyacheslav Levytskyy for (MachineBasicBlock &MBB : MF) { 816be9b4dabSVyacheslav Levytskyy for (MachineInstr &MI : MBB) { 817be9b4dabSVyacheslav Levytskyy if (!isSpvIntrinsic(MI, Intrinsic::spv_assign_decoration)) 818be9b4dabSVyacheslav Levytskyy continue; 819e41df5cbSNathan Gauër MIB.setInsertPt(*MI.getParent(), MI.getNextNode()); 820be9b4dabSVyacheslav Levytskyy buildOpSpirvDecorations(MI.getOperand(1).getReg(), MIB, 821be9b4dabSVyacheslav Levytskyy MI.getOperand(2).getMetadata()); 822be9b4dabSVyacheslav Levytskyy ToErase.push_back(&MI); 823be9b4dabSVyacheslav Levytskyy } 824be9b4dabSVyacheslav Levytskyy } 825be9b4dabSVyacheslav Levytskyy for (MachineInstr *MI : ToErase) 826be9b4dabSVyacheslav Levytskyy MI->eraseFromParent(); 827be9b4dabSVyacheslav Levytskyy } 828be9b4dabSVyacheslav Levytskyy 8291ed65febSNathan Gauër // LLVM allows the switches to use registers as cases, while SPIR-V required 8301ed65febSNathan Gauër // those to be immediate values. This function replaces such operands with the 8311ed65febSNathan Gauër // equivalent immediate constant. 8321ed65febSNathan Gauër static void processSwitchesConstants(MachineFunction &MF, 8331ed65febSNathan Gauër SPIRVGlobalRegistry *GR, 8340098f2aeSIlia Diachkov MachineIRBuilder MIB) { 83523b058cbSVyacheslav Levytskyy MachineRegisterInfo &MRI = MF.getRegInfo(); 8361ed65febSNathan Gauër for (MachineBasicBlock &MBB : MF) { 8370098f2aeSIlia Diachkov for (MachineInstr &MI : MBB) { 83823b058cbSVyacheslav Levytskyy if (!isSpvIntrinsic(MI, Intrinsic::spv_switch)) 83923b058cbSVyacheslav Levytskyy continue; 8401ed65febSNathan Gauër 8411ed65febSNathan Gauër SmallVector<MachineOperand, 8> NewOperands; 8421ed65febSNathan Gauër NewOperands.push_back(MI.getOperand(0)); // Opcode 8431ed65febSNathan Gauër NewOperands.push_back(MI.getOperand(1)); // Condition 8441ed65febSNathan Gauër NewOperands.push_back(MI.getOperand(2)); // Default 8451ed65febSNathan Gauër for (unsigned i = 3; i < MI.getNumOperands(); i += 2) { 84623b058cbSVyacheslav Levytskyy Register Reg = MI.getOperand(i).getReg(); 84723b058cbSVyacheslav Levytskyy MachineInstr *ConstInstr = getDefInstrMaybeConstant(Reg, &MRI); 8481ed65febSNathan Gauër NewOperands.push_back( 8491ed65febSNathan Gauër MachineOperand::CreateCImm(ConstInstr->getOperand(1).getCImm())); 8501ed65febSNathan Gauër 8511ed65febSNathan Gauër NewOperands.push_back(MI.getOperand(i + 1)); 852e461bdf6SMichal Paszkowski } 8531ed65febSNathan Gauër 8541ed65febSNathan Gauër assert(MI.getNumOperands() == NewOperands.size()); 8551ed65febSNathan Gauër while (MI.getNumOperands() > 0) 8561ed65febSNathan Gauër MI.removeOperand(0); 8571ed65febSNathan Gauër for (auto &MO : NewOperands) 8581ed65febSNathan Gauër MI.addOperand(MO); 859e461bdf6SMichal Paszkowski } 8608bfb2b6dSMichal Paszkowski } 8610098f2aeSIlia Diachkov } 862e461bdf6SMichal Paszkowski 8631ed65febSNathan Gauër // Some instructions are used during CodeGen but should never be emitted. 8641ed65febSNathan Gauër // Cleaning up those. 8651ed65febSNathan Gauër static void cleanupHelperInstructions(MachineFunction &MF) { 8661ed65febSNathan Gauër SmallVector<MachineInstr *, 8> ToEraseMI; 8671ed65febSNathan Gauër for (MachineBasicBlock &MBB : MF) { 8681ed65febSNathan Gauër for (MachineInstr &MI : MBB) { 8691ed65febSNathan Gauër if (isSpvIntrinsic(MI, Intrinsic::spv_track_constant) || 8701ed65febSNathan Gauër MI.getOpcode() == TargetOpcode::G_BRINDIRECT) 8711ed65febSNathan Gauër ToEraseMI.push_back(&MI); 8721ed65febSNathan Gauër } 8731ed65febSNathan Gauër } 8741ed65febSNathan Gauër 8751ed65febSNathan Gauër for (MachineInstr *MI : ToEraseMI) 8761ed65febSNathan Gauër MI->eraseFromParent(); 8771ed65febSNathan Gauër } 8781ed65febSNathan Gauër 8791ed65febSNathan Gauër // Find all usages of G_BLOCK_ADDR in our intrinsics and replace those 8801ed65febSNathan Gauër // operands/registers by the actual MBB it references. 8811ed65febSNathan Gauër static void processBlockAddr(MachineFunction &MF, SPIRVGlobalRegistry *GR, 8821ed65febSNathan Gauër MachineIRBuilder MIB) { 8831ed65febSNathan Gauër // Gather the reverse-mapping BB -> MBB. 8841ed65febSNathan Gauër DenseMap<const BasicBlock *, MachineBasicBlock *> BB2MBB; 8851ed65febSNathan Gauër for (MachineBasicBlock &MBB : MF) 8861ed65febSNathan Gauër BB2MBB[MBB.getBasicBlock()] = &MBB; 8871ed65febSNathan Gauër 8881ed65febSNathan Gauër // Gather instructions requiring patching. For now, only those can use 8891ed65febSNathan Gauër // G_BLOCK_ADDR. 8901ed65febSNathan Gauër SmallVector<MachineInstr *, 8> InstructionsToPatch; 8911ed65febSNathan Gauër for (MachineBasicBlock &MBB : MF) { 8921ed65febSNathan Gauër for (MachineInstr &MI : MBB) { 8931ed65febSNathan Gauër if (isSpvIntrinsic(MI, Intrinsic::spv_switch) || 8941ed65febSNathan Gauër isSpvIntrinsic(MI, Intrinsic::spv_loop_merge) || 8951ed65febSNathan Gauër isSpvIntrinsic(MI, Intrinsic::spv_selection_merge)) 8961ed65febSNathan Gauër InstructionsToPatch.push_back(&MI); 8971ed65febSNathan Gauër } 8981ed65febSNathan Gauër } 8991ed65febSNathan Gauër 9001ed65febSNathan Gauër // For each instruction to fix, we replace all the G_BLOCK_ADDR operands by 9011ed65febSNathan Gauër // the actual MBB it references. Once those references have been updated, we 9021ed65febSNathan Gauër // can cleanup remaining G_BLOCK_ADDR references. 903ebdadcfeSVyacheslav Levytskyy SmallPtrSet<MachineBasicBlock *, 8> ClearAddressTaken; 9041ed65febSNathan Gauër SmallPtrSet<MachineInstr *, 8> ToEraseMI; 9051ed65febSNathan Gauër MachineRegisterInfo &MRI = MF.getRegInfo(); 9061ed65febSNathan Gauër for (MachineInstr *MI : InstructionsToPatch) { 90723b058cbSVyacheslav Levytskyy SmallVector<MachineOperand, 8> NewOps; 9081ed65febSNathan Gauër for (unsigned i = 0; i < MI->getNumOperands(); ++i) { 9091ed65febSNathan Gauër // The operand is not a register, keep as-is. 9101ed65febSNathan Gauër if (!MI->getOperand(i).isReg()) { 9111ed65febSNathan Gauër NewOps.push_back(MI->getOperand(i)); 9121ed65febSNathan Gauër continue; 9131ed65febSNathan Gauër } 9141ed65febSNathan Gauër 9151ed65febSNathan Gauër Register Reg = MI->getOperand(i).getReg(); 9161ed65febSNathan Gauër MachineInstr *BuildMBB = MRI.getVRegDef(Reg); 9171ed65febSNathan Gauër // The register is not the result of G_BLOCK_ADDR, keep as-is. 9181ed65febSNathan Gauër if (!BuildMBB || BuildMBB->getOpcode() != TargetOpcode::G_BLOCK_ADDR) { 9191ed65febSNathan Gauër NewOps.push_back(MI->getOperand(i)); 9201ed65febSNathan Gauër continue; 9211ed65febSNathan Gauër } 9221ed65febSNathan Gauër 9231ed65febSNathan Gauër assert(BuildMBB && BuildMBB->getOpcode() == TargetOpcode::G_BLOCK_ADDR && 9241ed65febSNathan Gauër BuildMBB->getOperand(1).isBlockAddress() && 9251ed65febSNathan Gauër BuildMBB->getOperand(1).getBlockAddress()); 9261ed65febSNathan Gauër BasicBlock *BB = 9271ed65febSNathan Gauër BuildMBB->getOperand(1).getBlockAddress()->getBasicBlock(); 9281ed65febSNathan Gauër auto It = BB2MBB.find(BB); 92923b058cbSVyacheslav Levytskyy if (It == BB2MBB.end()) 9301ed65febSNathan Gauër report_fatal_error("cannot find a machine basic block by a basic block " 9311ed65febSNathan Gauër "in a switch statement"); 9321ed65febSNathan Gauër MachineBasicBlock *ReferencedBlock = It->second; 9331ed65febSNathan Gauër NewOps.push_back(MachineOperand::CreateMBB(ReferencedBlock)); 9341ed65febSNathan Gauër 9351ed65febSNathan Gauër ClearAddressTaken.insert(ReferencedBlock); 9361ed65febSNathan Gauër ToEraseMI.insert(BuildMBB); 9370098f2aeSIlia Diachkov } 9381ed65febSNathan Gauër 9391ed65febSNathan Gauër // Replace the operands. 9401ed65febSNathan Gauër assert(MI->getNumOperands() == NewOps.size()); 9411ed65febSNathan Gauër while (MI->getNumOperands() > 0) 9421ed65febSNathan Gauër MI->removeOperand(0); 94323b058cbSVyacheslav Levytskyy for (auto &MO : NewOps) 9441ed65febSNathan Gauër MI->addOperand(MO); 9451ed65febSNathan Gauër 9461ed65febSNathan Gauër if (MachineInstr *Next = MI->getNextNode()) { 94723b058cbSVyacheslav Levytskyy if (isSpvIntrinsic(*Next, Intrinsic::spv_track_constant)) { 94823b058cbSVyacheslav Levytskyy ToEraseMI.insert(Next); 9491ed65febSNathan Gauër Next = MI->getNextNode(); 95023b058cbSVyacheslav Levytskyy } 95123b058cbSVyacheslav Levytskyy if (Next && Next->getOpcode() == TargetOpcode::G_BRINDIRECT) 95223b058cbSVyacheslav Levytskyy ToEraseMI.insert(Next); 9538bfb2b6dSMichal Paszkowski } 9548bfb2b6dSMichal Paszkowski } 955e3e06135SVyacheslav Levytskyy 9561ed65febSNathan Gauër // BlockAddress operands were used to keep information between passes, 9571ed65febSNathan Gauër // let's undo the "address taken" status to reflect that Succ doesn't 9581ed65febSNathan Gauër // actually correspond to an IR-level basic block. 9591ed65febSNathan Gauër for (MachineBasicBlock *Succ : ClearAddressTaken) 9601ed65febSNathan Gauër Succ->setAddressTakenIRBlock(nullptr); 9611ed65febSNathan Gauër 962e3e06135SVyacheslav Levytskyy // If we just delete G_BLOCK_ADDR instructions with BlockAddress operands, 963e3e06135SVyacheslav Levytskyy // this leaves their BasicBlock counterparts in a "address taken" status. This 964e3e06135SVyacheslav Levytskyy // would make AsmPrinter to generate a series of unneeded labels of a "Address 965e3e06135SVyacheslav Levytskyy // of block that was removed by CodeGen" kind. Let's first ensure that we 966e3e06135SVyacheslav Levytskyy // don't have a dangling BlockAddress constants by zapping the BlockAddress 967e3e06135SVyacheslav Levytskyy // nodes, and only after that proceed with erasing G_BLOCK_ADDR instructions. 968e3e06135SVyacheslav Levytskyy Constant *Replacement = 969e3e06135SVyacheslav Levytskyy ConstantInt::get(Type::getInt32Ty(MF.getFunction().getContext()), 1); 970e3e06135SVyacheslav Levytskyy for (MachineInstr *BlockAddrI : ToEraseMI) { 971e3e06135SVyacheslav Levytskyy if (BlockAddrI->getOpcode() == TargetOpcode::G_BLOCK_ADDR) { 972e3e06135SVyacheslav Levytskyy BlockAddress *BA = const_cast<BlockAddress *>( 973e3e06135SVyacheslav Levytskyy BlockAddrI->getOperand(1).getBlockAddress()); 974e3e06135SVyacheslav Levytskyy BA->replaceAllUsesWith( 975e3e06135SVyacheslav Levytskyy ConstantExpr::getIntToPtr(Replacement, BA->getType())); 976e3e06135SVyacheslav Levytskyy BA->destroyConstant(); 977e3e06135SVyacheslav Levytskyy } 97823b058cbSVyacheslav Levytskyy BlockAddrI->eraseFromParent(); 9790098f2aeSIlia Diachkov } 980e3e06135SVyacheslav Levytskyy } 9810098f2aeSIlia Diachkov 982a9ffc92fSNathan Gauër static bool isImplicitFallthrough(MachineBasicBlock &MBB) { 983a9ffc92fSNathan Gauër if (MBB.empty()) 984a9ffc92fSNathan Gauër return true; 985a9ffc92fSNathan Gauër 986a9ffc92fSNathan Gauër // Branching SPIR-V intrinsics are not detected by this generic method. 987a9ffc92fSNathan Gauër // Thus, we can only trust negative result. 988a9ffc92fSNathan Gauër if (!MBB.canFallThrough()) 989a9ffc92fSNathan Gauër return false; 990a9ffc92fSNathan Gauër 991a9ffc92fSNathan Gauër // Otherwise, we must manually check if we have a SPIR-V intrinsic which 992a9ffc92fSNathan Gauër // prevent an implicit fallthrough. 993a9ffc92fSNathan Gauër for (MachineBasicBlock::reverse_iterator It = MBB.rbegin(), E = MBB.rend(); 994a9ffc92fSNathan Gauër It != E; ++It) { 995a9ffc92fSNathan Gauër if (isSpvIntrinsic(*It, Intrinsic::spv_switch)) 996a9ffc92fSNathan Gauër return false; 997a9ffc92fSNathan Gauër } 998a9ffc92fSNathan Gauër return true; 999a9ffc92fSNathan Gauër } 1000a9ffc92fSNathan Gauër 1001a9ffc92fSNathan Gauër static void removeImplicitFallthroughs(MachineFunction &MF, 1002a9ffc92fSNathan Gauër MachineIRBuilder MIB) { 1003a9ffc92fSNathan Gauër // It is valid for MachineBasicBlocks to not finish with a branch instruction. 1004a9ffc92fSNathan Gauër // In such cases, they will simply fallthrough their immediate successor. 1005a9ffc92fSNathan Gauër for (MachineBasicBlock &MBB : MF) { 1006a9ffc92fSNathan Gauër if (!isImplicitFallthrough(MBB)) 1007a9ffc92fSNathan Gauër continue; 1008a9ffc92fSNathan Gauër 1009a9ffc92fSNathan Gauër assert(std::distance(MBB.successors().begin(), MBB.successors().end()) == 1010a9ffc92fSNathan Gauër 1); 1011a9ffc92fSNathan Gauër MIB.setInsertPt(MBB, MBB.end()); 1012a9ffc92fSNathan Gauër MIB.buildBr(**MBB.successors().begin()); 1013a9ffc92fSNathan Gauër } 1014a9ffc92fSNathan Gauër } 1015a9ffc92fSNathan Gauër 10160098f2aeSIlia Diachkov bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) { 10170098f2aeSIlia Diachkov // Initialize the type registry. 10180098f2aeSIlia Diachkov const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>(); 10190098f2aeSIlia Diachkov SPIRVGlobalRegistry *GR = ST.getSPIRVGlobalRegistry(); 10200098f2aeSIlia Diachkov GR->setCurrentFunc(MF); 10210098f2aeSIlia Diachkov MachineIRBuilder MIB(MF); 10221ed1ec9aSVyacheslav Levytskyy // a registry of target extension constants 10231ed1ec9aSVyacheslav Levytskyy DenseMap<MachineInstr *, Type *> TargetExtConstTypes; 10249c29217aSVyacheslav Levytskyy // to keep record of tracked constants 10259c29217aSVyacheslav Levytskyy SmallSet<Register, 4> TrackedConstRegs; 10269c29217aSVyacheslav Levytskyy addConstantsToTrack(MF, GR, ST, TargetExtConstTypes, TrackedConstRegs); 10279c29217aSVyacheslav Levytskyy foldConstantsIntoIntrinsics(MF, TrackedConstRegs); 10280098f2aeSIlia Diachkov insertBitcasts(MF, GR, MIB); 10291ed1ec9aSVyacheslav Levytskyy generateAssignInstrs(MF, GR, MIB, TargetExtConstTypes); 10301ed65febSNathan Gauër 10311ed65febSNathan Gauër processSwitchesConstants(MF, GR, MIB); 10321ed65febSNathan Gauër processBlockAddr(MF, GR, MIB); 10331ed65febSNathan Gauër cleanupHelperInstructions(MF); 10341ed65febSNathan Gauër 1035df871307SIlia Diachkov processInstrsWithTypeFolding(MF, GR, MIB); 1036a9ffc92fSNathan Gauër removeImplicitFallthroughs(MF, MIB); 1037be9b4dabSVyacheslav Levytskyy insertSpirvDecorations(MF, MIB); 1038214e6b40SVyacheslav Levytskyy insertInlineAsm(MF, GR, ST, MIB); 1039c616f24bSVyacheslav Levytskyy selectOpBitcasts(MF, GR, MIB); 10400098f2aeSIlia Diachkov 10410098f2aeSIlia Diachkov return true; 10420098f2aeSIlia Diachkov } 10430098f2aeSIlia Diachkov 10440098f2aeSIlia Diachkov INITIALIZE_PASS(SPIRVPreLegalizer, DEBUG_TYPE, "SPIRV pre legalizer", false, 10450098f2aeSIlia Diachkov false) 10460098f2aeSIlia Diachkov 10470098f2aeSIlia Diachkov char SPIRVPreLegalizer::ID = 0; 10480098f2aeSIlia Diachkov 10490098f2aeSIlia Diachkov FunctionPass *llvm::createSPIRVPreLegalizerPass() { 10500098f2aeSIlia Diachkov return new SPIRVPreLegalizer(); 10510098f2aeSIlia Diachkov } 1052