| //===-- SPIRVPreLegalizer.cpp - prepare IR for legalization -----*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // The pass prepares IR for legalization: it assigns SPIR-V types to registers | |
| // and removes intrinsics which holded these types during IR translation. | |
| // Also it processes constants and registers them in GR to avoid duplication. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "SPIRV.h" | |
| #include "SPIRVSubtarget.h" | |
| #include "SPIRVUtils.h" | |
| #include "llvm/ADT/PostOrderIterator.h" | |
| #include "llvm/Analysis/OptimizationRemarkEmitter.h" | |
| #include "llvm/IR/Attributes.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "llvm/IR/DebugInfoMetadata.h" | |
| #include "llvm/IR/IntrinsicsSPIRV.h" | |
| #include "llvm/Target/TargetIntrinsicInfo.h" | |
| #define DEBUG_TYPE "spirv-prelegalizer" | |
| using namespace llvm; | |
| namespace { | |
| class SPIRVPreLegalizer : public MachineFunctionPass { | |
| public: | |
| static char ID; | |
| SPIRVPreLegalizer() : MachineFunctionPass(ID) { | |
| initializeSPIRVPreLegalizerPass(*PassRegistry::getPassRegistry()); | |
| } | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| }; | |
| } // namespace | |
| static void addConstantsToTrack(MachineFunction &MF, SPIRVGlobalRegistry *GR) { | |
| MachineRegisterInfo &MRI = MF.getRegInfo(); | |
| DenseMap<MachineInstr *, Register> RegsAlreadyAddedToDT; | |
| SmallVector<MachineInstr *, 10> ToErase, ToEraseComposites; | |
| for (MachineBasicBlock &MBB : MF) { | |
| for (MachineInstr &MI : MBB) { | |
| if (!isSpvIntrinsic(MI, Intrinsic::spv_track_constant)) | |
| continue; | |
| ToErase.push_back(&MI); | |
| auto *Const = | |
| cast<Constant>(cast<ConstantAsMetadata>( | |
| MI.getOperand(3).getMetadata()->getOperand(0)) | |
| ->getValue()); | |
| if (auto *GV = dyn_cast<GlobalValue>(Const)) { | |
| Register Reg = GR->find(GV, &MF); | |
| if (!Reg.isValid()) | |
| GR->add(GV, &MF, MI.getOperand(2).getReg()); | |
| else | |
| RegsAlreadyAddedToDT[&MI] = Reg; | |
| } else { | |
| Register Reg = GR->find(Const, &MF); | |
| if (!Reg.isValid()) { | |
| if (auto *ConstVec = dyn_cast<ConstantDataVector>(Const)) { | |
| auto *BuildVec = MRI.getVRegDef(MI.getOperand(2).getReg()); | |
| assert(BuildVec && | |
| BuildVec->getOpcode() == TargetOpcode::G_BUILD_VECTOR); | |
| for (unsigned i = 0; i < ConstVec->getNumElements(); ++i) | |
| GR->add(ConstVec->getElementAsConstant(i), &MF, | |
| BuildVec->getOperand(1 + i).getReg()); | |
| } | |
| GR->add(Const, &MF, MI.getOperand(2).getReg()); | |
| } else { | |
| RegsAlreadyAddedToDT[&MI] = Reg; | |
| // This MI is unused and will be removed. If the MI uses | |
| // const_composite, it will be unused and should be removed too. | |
| assert(MI.getOperand(2).isReg() && "Reg operand is expected"); | |
| MachineInstr *SrcMI = MRI.getVRegDef(MI.getOperand(2).getReg()); | |
| if (SrcMI && isSpvIntrinsic(*SrcMI, Intrinsic::spv_const_composite)) | |
| ToEraseComposites.push_back(SrcMI); | |
| } | |
| } | |
| } | |
| } | |
| for (MachineInstr *MI : ToErase) { | |
| Register Reg = MI->getOperand(2).getReg(); | |
| if (RegsAlreadyAddedToDT.find(MI) != RegsAlreadyAddedToDT.end()) | |
| Reg = RegsAlreadyAddedToDT[MI]; | |
| MRI.replaceRegWith(MI->getOperand(0).getReg(), Reg); | |
| MI->eraseFromParent(); | |
| } | |
| for (MachineInstr *MI : ToEraseComposites) | |
| MI->eraseFromParent(); | |
| } | |
| static void foldConstantsIntoIntrinsics(MachineFunction &MF) { | |
| SmallVector<MachineInstr *, 10> ToErase; | |
| MachineRegisterInfo &MRI = MF.getRegInfo(); | |
| const unsigned AssignNameOperandShift = 2; | |
| for (MachineBasicBlock &MBB : MF) { | |
| for (MachineInstr &MI : MBB) { | |
| if (!isSpvIntrinsic(MI, Intrinsic::spv_assign_name)) | |
| continue; | |
| unsigned NumOp = MI.getNumExplicitDefs() + AssignNameOperandShift; | |
| while (MI.getOperand(NumOp).isReg()) { | |
| MachineOperand &MOp = MI.getOperand(NumOp); | |
| MachineInstr *ConstMI = MRI.getVRegDef(MOp.getReg()); | |
| assert(ConstMI->getOpcode() == TargetOpcode::G_CONSTANT); | |
| MI.removeOperand(NumOp); | |
| MI.addOperand(MachineOperand::CreateImm( | |
| ConstMI->getOperand(1).getCImm()->getZExtValue())); | |
| if (MRI.use_empty(ConstMI->getOperand(0).getReg())) | |
| ToErase.push_back(ConstMI); | |
| } | |
| } | |
| } | |
| for (MachineInstr *MI : ToErase) | |
| MI->eraseFromParent(); | |
| } | |
| static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR, | |
| MachineIRBuilder MIB) { | |
| SmallVector<MachineInstr *, 10> ToErase; | |
| for (MachineBasicBlock &MBB : MF) { | |
| for (MachineInstr &MI : MBB) { | |
| if (!isSpvIntrinsic(MI, Intrinsic::spv_bitcast)) | |
| continue; | |
| assert(MI.getOperand(2).isReg()); | |
| MIB.setInsertPt(*MI.getParent(), MI); | |
| MIB.buildBitcast(MI.getOperand(0).getReg(), MI.getOperand(2).getReg()); | |
| ToErase.push_back(&MI); | |
| } | |
| } | |
| for (MachineInstr *MI : ToErase) | |
| MI->eraseFromParent(); | |
| } | |
| // Translating GV, IRTranslator sometimes generates following IR: | |
| // %1 = G_GLOBAL_VALUE | |
| // %2 = COPY %1 | |
| // %3 = G_ADDRSPACE_CAST %2 | |
| // New registers have no SPIRVType and no register class info. | |
| // | |
| // Set SPIRVType for GV, propagate it from GV to other instructions, | |
| // also set register classes. | |
| static SPIRVType *propagateSPIRVType(MachineInstr *MI, SPIRVGlobalRegistry *GR, | |
| MachineRegisterInfo &MRI, | |
| MachineIRBuilder &MIB) { | |
| SPIRVType *SpirvTy = nullptr; | |
| assert(MI && "Machine instr is expected"); | |
| if (MI->getOperand(0).isReg()) { | |
| Register Reg = MI->getOperand(0).getReg(); | |
| SpirvTy = GR->getSPIRVTypeForVReg(Reg); | |
| if (!SpirvTy) { | |
| switch (MI->getOpcode()) { | |
| case TargetOpcode::G_CONSTANT: { | |
| MIB.setInsertPt(*MI->getParent(), MI); | |
| Type *Ty = MI->getOperand(1).getCImm()->getType(); | |
| SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB); | |
| break; | |
| } | |
| case TargetOpcode::G_GLOBAL_VALUE: { | |
| MIB.setInsertPt(*MI->getParent(), MI); | |
| Type *Ty = MI->getOperand(1).getGlobal()->getType(); | |
| SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB); | |
| break; | |
| } | |
| case TargetOpcode::G_TRUNC: | |
| case TargetOpcode::G_ADDRSPACE_CAST: | |
| case TargetOpcode::G_PTR_ADD: | |
| case TargetOpcode::COPY: { | |
| MachineOperand &Op = MI->getOperand(1); | |
| MachineInstr *Def = Op.isReg() ? MRI.getVRegDef(Op.getReg()) : nullptr; | |
| if (Def) | |
| SpirvTy = propagateSPIRVType(Def, GR, MRI, MIB); | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| if (SpirvTy) | |
| GR->assignSPIRVTypeToVReg(SpirvTy, Reg, MIB.getMF()); | |
| if (!MRI.getRegClassOrNull(Reg)) | |
| MRI.setRegClass(Reg, &SPIRV::IDRegClass); | |
| } | |
| } | |
| return SpirvTy; | |
| } | |
| // Insert ASSIGN_TYPE instuction between Reg and its definition, set NewReg as | |
| // a dst of the definition, assign SPIRVType to both registers. If SpirvTy is | |
| // provided, use it as SPIRVType in ASSIGN_TYPE, otherwise create it from Ty. | |
| // It's used also in SPIRVBuiltins.cpp. | |
| // TODO: maybe move to SPIRVUtils. | |
| namespace llvm { | |
| Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy, | |
| SPIRVGlobalRegistry *GR, MachineIRBuilder &MIB, | |
| MachineRegisterInfo &MRI) { | |
| MachineInstr *Def = MRI.getVRegDef(Reg); | |
| assert((Ty || SpirvTy) && "Either LLVM or SPIRV type is expected."); | |
| MIB.setInsertPt(*Def->getParent(), | |
| (Def->getNextNode() ? Def->getNextNode()->getIterator() | |
| : Def->getParent()->end())); | |
| Register NewReg = MRI.createGenericVirtualRegister(MRI.getType(Reg)); | |
| if (auto *RC = MRI.getRegClassOrNull(Reg)) | |
| MRI.setRegClass(NewReg, RC); | |
| SpirvTy = SpirvTy ? SpirvTy : GR->getOrCreateSPIRVType(Ty, MIB); | |
| GR->assignSPIRVTypeToVReg(SpirvTy, Reg, MIB.getMF()); | |
| // This is to make it convenient for Legalizer to get the SPIRVType | |
| // when processing the actual MI (i.e. not pseudo one). | |
| GR->assignSPIRVTypeToVReg(SpirvTy, NewReg, MIB.getMF()); | |
| // Copy MIFlags from Def to ASSIGN_TYPE instruction. It's required to keep | |
| // the flags after instruction selection. | |
| const uint16_t Flags = Def->getFlags(); | |
| MIB.buildInstr(SPIRV::ASSIGN_TYPE) | |
| .addDef(Reg) | |
| .addUse(NewReg) | |
| .addUse(GR->getSPIRVTypeID(SpirvTy)) | |
| .setMIFlags(Flags); | |
| Def->getOperand(0).setReg(NewReg); | |
| MRI.setRegClass(Reg, &SPIRV::ANYIDRegClass); | |
| return NewReg; | |
| } | |
| } // namespace llvm | |
| static void generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR, | |
| MachineIRBuilder MIB) { | |
| MachineRegisterInfo &MRI = MF.getRegInfo(); | |
| SmallVector<MachineInstr *, 10> ToErase; | |
| for (MachineBasicBlock *MBB : post_order(&MF)) { | |
| if (MBB->empty()) | |
| continue; | |
| bool ReachedBegin = false; | |
| for (auto MII = std::prev(MBB->end()), Begin = MBB->begin(); | |
| !ReachedBegin;) { | |
| MachineInstr &MI = *MII; | |
| if (isSpvIntrinsic(MI, Intrinsic::spv_assign_type)) { | |
| Register Reg = MI.getOperand(1).getReg(); | |
| Type *Ty = getMDOperandAsType(MI.getOperand(2).getMetadata(), 0); | |
| MachineInstr *Def = MRI.getVRegDef(Reg); | |
| assert(Def && "Expecting an instruction that defines the register"); | |
| // G_GLOBAL_VALUE already has type info. | |
| if (Def->getOpcode() != TargetOpcode::G_GLOBAL_VALUE) | |
| insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MF.getRegInfo()); | |
| ToErase.push_back(&MI); | |
| } else if (MI.getOpcode() == TargetOpcode::G_CONSTANT || | |
| MI.getOpcode() == TargetOpcode::G_FCONSTANT || | |
| MI.getOpcode() == TargetOpcode::G_BUILD_VECTOR) { | |
| // %rc = G_CONSTANT ty Val | |
| // ===> | |
| // %cty = OpType* ty | |
| // %rctmp = G_CONSTANT ty Val | |
| // %rc = ASSIGN_TYPE %rctmp, %cty | |
| Register Reg = MI.getOperand(0).getReg(); | |
| if (MRI.hasOneUse(Reg)) { | |
| MachineInstr &UseMI = *MRI.use_instr_begin(Reg); | |
| if (isSpvIntrinsic(UseMI, Intrinsic::spv_assign_type) || | |
| isSpvIntrinsic(UseMI, Intrinsic::spv_assign_name)) | |
| continue; | |
| } | |
| Type *Ty = nullptr; | |
| if (MI.getOpcode() == TargetOpcode::G_CONSTANT) | |
| Ty = MI.getOperand(1).getCImm()->getType(); | |
| else if (MI.getOpcode() == TargetOpcode::G_FCONSTANT) | |
| Ty = MI.getOperand(1).getFPImm()->getType(); | |
| else { | |
| assert(MI.getOpcode() == TargetOpcode::G_BUILD_VECTOR); | |
| Type *ElemTy = nullptr; | |
| MachineInstr *ElemMI = MRI.getVRegDef(MI.getOperand(1).getReg()); | |
| assert(ElemMI); | |
| if (ElemMI->getOpcode() == TargetOpcode::G_CONSTANT) | |
| ElemTy = ElemMI->getOperand(1).getCImm()->getType(); | |
| else if (ElemMI->getOpcode() == TargetOpcode::G_FCONSTANT) | |
| ElemTy = ElemMI->getOperand(1).getFPImm()->getType(); | |
| else | |
| llvm_unreachable("Unexpected opcode"); | |
| unsigned NumElts = | |
| MI.getNumExplicitOperands() - MI.getNumExplicitDefs(); | |
| Ty = VectorType::get(ElemTy, NumElts, false); | |
| } | |
| insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MRI); | |
| } else if (MI.getOpcode() == TargetOpcode::G_TRUNC || | |
| MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE || | |
| MI.getOpcode() == TargetOpcode::COPY || | |
| MI.getOpcode() == TargetOpcode::G_ADDRSPACE_CAST) { | |
| propagateSPIRVType(&MI, GR, MRI, MIB); | |
| } | |
| if (MII == Begin) | |
| ReachedBegin = true; | |
| else | |
| --MII; | |
| } | |
| } | |
| for (MachineInstr *MI : ToErase) | |
| MI->eraseFromParent(); | |
| } | |
| static std::pair<Register, unsigned> | |
| createNewIdReg(Register ValReg, unsigned Opcode, MachineRegisterInfo &MRI, | |
| const SPIRVGlobalRegistry &GR) { | |
| LLT NewT = LLT::scalar(32); | |
| SPIRVType *SpvType = GR.getSPIRVTypeForVReg(ValReg); | |
| assert(SpvType && "VReg is expected to have SPIRV type"); | |
| bool IsFloat = SpvType->getOpcode() == SPIRV::OpTypeFloat; | |
| bool IsVectorFloat = | |
| SpvType->getOpcode() == SPIRV::OpTypeVector && | |
| GR.getSPIRVTypeForVReg(SpvType->getOperand(1).getReg())->getOpcode() == | |
| SPIRV::OpTypeFloat; | |
| IsFloat |= IsVectorFloat; | |
| auto GetIdOp = IsFloat ? SPIRV::GET_fID : SPIRV::GET_ID; | |
| auto DstClass = IsFloat ? &SPIRV::fIDRegClass : &SPIRV::IDRegClass; | |
| if (MRI.getType(ValReg).isPointer()) { | |
| NewT = LLT::pointer(0, 32); | |
| GetIdOp = SPIRV::GET_pID; | |
| DstClass = &SPIRV::pIDRegClass; | |
| } else if (MRI.getType(ValReg).isVector()) { | |
| NewT = LLT::fixed_vector(2, NewT); | |
| GetIdOp = IsFloat ? SPIRV::GET_vfID : SPIRV::GET_vID; | |
| DstClass = IsFloat ? &SPIRV::vfIDRegClass : &SPIRV::vIDRegClass; | |
| } | |
| Register IdReg = MRI.createGenericVirtualRegister(NewT); | |
| MRI.setRegClass(IdReg, DstClass); | |
| return {IdReg, GetIdOp}; | |
| } | |
| static void processInstr(MachineInstr &MI, MachineIRBuilder &MIB, | |
| MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR) { | |
| unsigned Opc = MI.getOpcode(); | |
| assert(MI.getNumDefs() > 0 && MRI.hasOneUse(MI.getOperand(0).getReg())); | |
| MachineInstr &AssignTypeInst = | |
| *(MRI.use_instr_begin(MI.getOperand(0).getReg())); | |
| auto NewReg = createNewIdReg(MI.getOperand(0).getReg(), Opc, MRI, *GR).first; | |
| AssignTypeInst.getOperand(1).setReg(NewReg); | |
| MI.getOperand(0).setReg(NewReg); | |
| MIB.setInsertPt(*MI.getParent(), | |
| (MI.getNextNode() ? MI.getNextNode()->getIterator() | |
| : MI.getParent()->end())); | |
| for (auto &Op : MI.operands()) { | |
| if (!Op.isReg() || Op.isDef()) | |
| continue; | |
| auto IdOpInfo = createNewIdReg(Op.getReg(), Opc, MRI, *GR); | |
| MIB.buildInstr(IdOpInfo.second).addDef(IdOpInfo.first).addUse(Op.getReg()); | |
| Op.setReg(IdOpInfo.first); | |
| } | |
| } | |
| // Defined in SPIRVLegalizerInfo.cpp. | |
| extern bool isTypeFoldingSupported(unsigned Opcode); | |
| static void processInstrsWithTypeFolding(MachineFunction &MF, | |
| SPIRVGlobalRegistry *GR, | |
| MachineIRBuilder MIB) { | |
| MachineRegisterInfo &MRI = MF.getRegInfo(); | |
| for (MachineBasicBlock &MBB : MF) { | |
| for (MachineInstr &MI : MBB) { | |
| if (isTypeFoldingSupported(MI.getOpcode())) | |
| processInstr(MI, MIB, MRI, GR); | |
| } | |
| } | |
| for (MachineBasicBlock &MBB : MF) { | |
| for (MachineInstr &MI : MBB) { | |
| // We need to rewrite dst types for ASSIGN_TYPE instrs to be able | |
| // to perform tblgen'erated selection and we can't do that on Legalizer | |
| // as it operates on gMIR only. | |
| if (MI.getOpcode() != SPIRV::ASSIGN_TYPE) | |
| continue; | |
| Register SrcReg = MI.getOperand(1).getReg(); | |
| unsigned Opcode = MRI.getVRegDef(SrcReg)->getOpcode(); | |
| if (!isTypeFoldingSupported(Opcode)) | |
| continue; | |
| Register DstReg = MI.getOperand(0).getReg(); | |
| if (MRI.getType(DstReg).isVector()) | |
| MRI.setRegClass(DstReg, &SPIRV::IDRegClass); | |
| // Don't need to reset type of register holding constant and used in | |
| // G_ADDRSPACE_CAST, since it braaks legalizer. | |
| if (Opcode == TargetOpcode::G_CONSTANT && MRI.hasOneUse(DstReg)) { | |
| MachineInstr &UseMI = *MRI.use_instr_begin(DstReg); | |
| if (UseMI.getOpcode() == TargetOpcode::G_ADDRSPACE_CAST) | |
| continue; | |
| } | |
| MRI.setType(DstReg, LLT::scalar(32)); | |
| } | |
| } | |
| } | |
| static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR, | |
| MachineIRBuilder MIB) { | |
| // Before IRTranslator pass, calls to spv_switch intrinsic are inserted before | |
| // each switch instruction. IRTranslator lowers switches to G_ICMP + G_BRCOND | |
| // + G_BR triples. A switch with two cases may be transformed to this MIR | |
| // sequence: | |
| // | |
| // intrinsic(@llvm.spv.switch), %CmpReg, %Const0, %Const1 | |
| // %Dst0 = G_ICMP intpred(eq), %CmpReg, %Const0 | |
| // G_BRCOND %Dst0, %bb.2 | |
| // G_BR %bb.5 | |
| // bb.5.entry: | |
| // %Dst1 = G_ICMP intpred(eq), %CmpReg, %Const1 | |
| // G_BRCOND %Dst1, %bb.3 | |
| // G_BR %bb.4 | |
| // bb.2.sw.bb: | |
| // ... | |
| // bb.3.sw.bb1: | |
| // ... | |
| // bb.4.sw.epilog: | |
| // ... | |
| // | |
| // Sometimes (in case of range-compare switches), additional G_SUBs | |
| // instructions are inserted before G_ICMPs. Those need to be additionally | |
| // processed and require type assignment. | |
| // | |
| // This function modifies spv_switch call's operands to include destination | |
| // MBBs (default and for each constant value). | |
| // Note that this function does not remove G_ICMP + G_BRCOND + G_BR sequences, | |
| // but they are marked by ModuleAnalysis as skipped and as a result AsmPrinter | |
| // does not output them. | |
| MachineRegisterInfo &MRI = MF.getRegInfo(); | |
| // Collect all MIs relevant to switches across all MBBs in MF. | |
| std::vector<MachineInstr *> RelevantInsts; | |
| // Temporary set of compare registers. G_SUBs and G_ICMPs relating to | |
| // spv_switch use these registers. | |
| DenseSet<Register> CompareRegs; | |
| for (MachineBasicBlock &MBB : MF) { | |
| for (MachineInstr &MI : MBB) { | |
| // Calls to spv_switch intrinsics representing IR switches. | |
| if (isSpvIntrinsic(MI, Intrinsic::spv_switch)) { | |
| assert(MI.getOperand(1).isReg()); | |
| CompareRegs.insert(MI.getOperand(1).getReg()); | |
| RelevantInsts.push_back(&MI); | |
| } | |
| // G_SUBs coming from range-compare switch lowering. G_SUBs are found | |
| // after spv_switch but before G_ICMP. | |
| if (MI.getOpcode() == TargetOpcode::G_SUB && MI.getOperand(1).isReg() && | |
| CompareRegs.contains(MI.getOperand(1).getReg())) { | |
| assert(MI.getOperand(0).isReg() && MI.getOperand(1).isReg()); | |
| Register Dst = MI.getOperand(0).getReg(); | |
| CompareRegs.insert(Dst); | |
| SPIRVType *Ty = GR->getSPIRVTypeForVReg(MI.getOperand(1).getReg()); | |
| insertAssignInstr(Dst, nullptr, Ty, GR, MIB, MRI); | |
| } | |
| // G_ICMPs relating to switches. | |
| if (MI.getOpcode() == TargetOpcode::G_ICMP && MI.getOperand(2).isReg() && | |
| CompareRegs.contains(MI.getOperand(2).getReg())) { | |
| Register Dst = MI.getOperand(0).getReg(); | |
| // Set type info for destination register of switch's ICMP instruction. | |
| if (GR->getSPIRVTypeForVReg(Dst) == nullptr) { | |
| MIB.setInsertPt(*MI.getParent(), MI); | |
| Type *LLVMTy = IntegerType::get(MF.getFunction().getContext(), 1); | |
| SPIRVType *SpirvTy = GR->getOrCreateSPIRVType(LLVMTy, MIB); | |
| MRI.setRegClass(Dst, &SPIRV::IDRegClass); | |
| GR->assignSPIRVTypeToVReg(SpirvTy, Dst, MIB.getMF()); | |
| } | |
| RelevantInsts.push_back(&MI); | |
| } | |
| } | |
| } | |
| // Update each spv_switch with destination MBBs. | |
| for (auto i = RelevantInsts.begin(); i != RelevantInsts.end(); i++) { | |
| if (!isSpvIntrinsic(**i, Intrinsic::spv_switch)) | |
| continue; | |
| // Currently considered spv_switch. | |
| MachineInstr *Switch = *i; | |
| // Set the first successor as default MBB to support empty switches. | |
| MachineBasicBlock *DefaultMBB = *Switch->getParent()->succ_begin(); | |
| // Container for mapping values to MMBs. | |
| SmallDenseMap<uint64_t, MachineBasicBlock *> ValuesToMBBs; | |
| // Walk all G_ICMPs to collect ValuesToMBBs. Start at currently considered | |
| // spv_switch (i) and break at any spv_switch with the same compare | |
| // register (indicating we are back at the same scope). | |
| Register CompareReg = Switch->getOperand(1).getReg(); | |
| for (auto j = i + 1; j != RelevantInsts.end(); j++) { | |
| if (isSpvIntrinsic(**j, Intrinsic::spv_switch) && | |
| (*j)->getOperand(1).getReg() == CompareReg) | |
| break; | |
| if (!((*j)->getOpcode() == TargetOpcode::G_ICMP && | |
| (*j)->getOperand(2).getReg() == CompareReg)) | |
| continue; | |
| MachineInstr *ICMP = *j; | |
| Register Dst = ICMP->getOperand(0).getReg(); | |
| MachineOperand &PredOp = ICMP->getOperand(1); | |
| const auto CC = static_cast<CmpInst::Predicate>(PredOp.getPredicate()); | |
| assert((CC == CmpInst::ICMP_EQ || CC == CmpInst::ICMP_ULE) && | |
| MRI.hasOneUse(Dst) && MRI.hasOneDef(CompareReg)); | |
| uint64_t Value = getIConstVal(ICMP->getOperand(3).getReg(), &MRI); | |
| MachineInstr *CBr = MRI.use_begin(Dst)->getParent(); | |
| assert(CBr->getOpcode() == SPIRV::G_BRCOND && CBr->getOperand(1).isMBB()); | |
| MachineBasicBlock *MBB = CBr->getOperand(1).getMBB(); | |
| // Map switch case Value to target MBB. | |
| ValuesToMBBs[Value] = MBB; | |
| // The next MI is always G_BR to either the next case or the default. | |
| MachineInstr *NextMI = CBr->getNextNode(); | |
| assert(NextMI->getOpcode() == SPIRV::G_BR && | |
| NextMI->getOperand(0).isMBB()); | |
| MachineBasicBlock *NextMBB = NextMI->getOperand(0).getMBB(); | |
| // Default MBB does not begin with G_ICMP using spv_switch compare | |
| // register. | |
| if (NextMBB->front().getOpcode() != SPIRV::G_ICMP || | |
| (NextMBB->front().getOperand(2).isReg() && | |
| NextMBB->front().getOperand(2).getReg() != CompareReg)) | |
| DefaultMBB = NextMBB; | |
| } | |
| // Modify considered spv_switch operands using collected Values and | |
| // MBBs. | |
| SmallVector<const ConstantInt *, 3> Values; | |
| SmallVector<MachineBasicBlock *, 3> MBBs; | |
| for (unsigned k = 2; k < Switch->getNumExplicitOperands(); k++) { | |
| Register CReg = Switch->getOperand(k).getReg(); | |
| uint64_t Val = getIConstVal(CReg, &MRI); | |
| MachineInstr *ConstInstr = getDefInstrMaybeConstant(CReg, &MRI); | |
| if (!ValuesToMBBs[Val]) | |
| continue; | |
| Values.push_back(ConstInstr->getOperand(1).getCImm()); | |
| MBBs.push_back(ValuesToMBBs[Val]); | |
| } | |
| for (unsigned k = Switch->getNumExplicitOperands() - 1; k > 1; k--) | |
| Switch->removeOperand(k); | |
| Switch->addOperand(MachineOperand::CreateMBB(DefaultMBB)); | |
| for (unsigned k = 0; k < Values.size(); k++) { | |
| Switch->addOperand(MachineOperand::CreateCImm(Values[k])); | |
| Switch->addOperand(MachineOperand::CreateMBB(MBBs[k])); | |
| } | |
| } | |
| } | |
| bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) { | |
| // Initialize the type registry. | |
| const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>(); | |
| SPIRVGlobalRegistry *GR = ST.getSPIRVGlobalRegistry(); | |
| GR->setCurrentFunc(MF); | |
| MachineIRBuilder MIB(MF); | |
| addConstantsToTrack(MF, GR); | |
| foldConstantsIntoIntrinsics(MF); | |
| insertBitcasts(MF, GR, MIB); | |
| generateAssignInstrs(MF, GR, MIB); | |
| processSwitches(MF, GR, MIB); | |
| processInstrsWithTypeFolding(MF, GR, MIB); | |
| return true; | |
| } | |
| INITIALIZE_PASS(SPIRVPreLegalizer, DEBUG_TYPE, "SPIRV pre legalizer", false, | |
| false) | |
| char SPIRVPreLegalizer::ID = 0; | |
| FunctionPass *llvm::createSPIRVPreLegalizerPass() { | |
| return new SPIRVPreLegalizer(); | |
| } |