181ad6265SDimitry Andric //===-- SPIRVPreLegalizer.cpp - prepare IR for legalization -----*- 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 // The pass prepares IR for legalization: it assigns SPIR-V types to registers 1081ad6265SDimitry Andric // and removes intrinsics which holded these types during IR translation. 1181ad6265SDimitry Andric // Also it processes constants and registers them in GR to avoid duplication. 1281ad6265SDimitry Andric // 1381ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1481ad6265SDimitry Andric 1581ad6265SDimitry Andric #include "SPIRV.h" 1681ad6265SDimitry Andric #include "SPIRVSubtarget.h" 1781ad6265SDimitry Andric #include "SPIRVUtils.h" 1881ad6265SDimitry Andric #include "llvm/ADT/PostOrderIterator.h" 1981ad6265SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h" 2081ad6265SDimitry Andric #include "llvm/IR/Attributes.h" 2181ad6265SDimitry Andric #include "llvm/IR/Constants.h" 2281ad6265SDimitry Andric #include "llvm/IR/DebugInfoMetadata.h" 2381ad6265SDimitry Andric #include "llvm/IR/IntrinsicsSPIRV.h" 2481ad6265SDimitry Andric #include "llvm/Target/TargetIntrinsicInfo.h" 2581ad6265SDimitry Andric 2681ad6265SDimitry Andric #define DEBUG_TYPE "spirv-prelegalizer" 2781ad6265SDimitry Andric 2881ad6265SDimitry Andric using namespace llvm; 2981ad6265SDimitry Andric 3081ad6265SDimitry Andric namespace { 3181ad6265SDimitry Andric class SPIRVPreLegalizer : public MachineFunctionPass { 3281ad6265SDimitry Andric public: 3381ad6265SDimitry Andric static char ID; 3481ad6265SDimitry Andric SPIRVPreLegalizer() : MachineFunctionPass(ID) { 3581ad6265SDimitry Andric initializeSPIRVPreLegalizerPass(*PassRegistry::getPassRegistry()); 3681ad6265SDimitry Andric } 3781ad6265SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 3881ad6265SDimitry Andric }; 3981ad6265SDimitry Andric } // namespace 4081ad6265SDimitry Andric 41fcaf7f86SDimitry Andric static void addConstantsToTrack(MachineFunction &MF, SPIRVGlobalRegistry *GR) { 42fcaf7f86SDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 43fcaf7f86SDimitry Andric DenseMap<MachineInstr *, Register> RegsAlreadyAddedToDT; 44fcaf7f86SDimitry Andric SmallVector<MachineInstr *, 10> ToErase, ToEraseComposites; 45fcaf7f86SDimitry Andric for (MachineBasicBlock &MBB : MF) { 46fcaf7f86SDimitry Andric for (MachineInstr &MI : MBB) { 47fcaf7f86SDimitry Andric if (!isSpvIntrinsic(MI, Intrinsic::spv_track_constant)) 48fcaf7f86SDimitry Andric continue; 49fcaf7f86SDimitry Andric ToErase.push_back(&MI); 50fcaf7f86SDimitry Andric auto *Const = 51fcaf7f86SDimitry Andric cast<Constant>(cast<ConstantAsMetadata>( 52fcaf7f86SDimitry Andric MI.getOperand(3).getMetadata()->getOperand(0)) 53fcaf7f86SDimitry Andric ->getValue()); 54fcaf7f86SDimitry Andric if (auto *GV = dyn_cast<GlobalValue>(Const)) { 55fcaf7f86SDimitry Andric Register Reg = GR->find(GV, &MF); 56fcaf7f86SDimitry Andric if (!Reg.isValid()) 57fcaf7f86SDimitry Andric GR->add(GV, &MF, MI.getOperand(2).getReg()); 58fcaf7f86SDimitry Andric else 59fcaf7f86SDimitry Andric RegsAlreadyAddedToDT[&MI] = Reg; 60fcaf7f86SDimitry Andric } else { 61fcaf7f86SDimitry Andric Register Reg = GR->find(Const, &MF); 62fcaf7f86SDimitry Andric if (!Reg.isValid()) { 63fcaf7f86SDimitry Andric if (auto *ConstVec = dyn_cast<ConstantDataVector>(Const)) { 64fcaf7f86SDimitry Andric auto *BuildVec = MRI.getVRegDef(MI.getOperand(2).getReg()); 65fcaf7f86SDimitry Andric assert(BuildVec && 66fcaf7f86SDimitry Andric BuildVec->getOpcode() == TargetOpcode::G_BUILD_VECTOR); 67fcaf7f86SDimitry Andric for (unsigned i = 0; i < ConstVec->getNumElements(); ++i) 68fcaf7f86SDimitry Andric GR->add(ConstVec->getElementAsConstant(i), &MF, 69fcaf7f86SDimitry Andric BuildVec->getOperand(1 + i).getReg()); 70fcaf7f86SDimitry Andric } 71fcaf7f86SDimitry Andric GR->add(Const, &MF, MI.getOperand(2).getReg()); 72fcaf7f86SDimitry Andric } else { 73fcaf7f86SDimitry Andric RegsAlreadyAddedToDT[&MI] = Reg; 74fcaf7f86SDimitry Andric // This MI is unused and will be removed. If the MI uses 75fcaf7f86SDimitry Andric // const_composite, it will be unused and should be removed too. 76fcaf7f86SDimitry Andric assert(MI.getOperand(2).isReg() && "Reg operand is expected"); 77fcaf7f86SDimitry Andric MachineInstr *SrcMI = MRI.getVRegDef(MI.getOperand(2).getReg()); 78fcaf7f86SDimitry Andric if (SrcMI && isSpvIntrinsic(*SrcMI, Intrinsic::spv_const_composite)) 79fcaf7f86SDimitry Andric ToEraseComposites.push_back(SrcMI); 80fcaf7f86SDimitry Andric } 81fcaf7f86SDimitry Andric } 82fcaf7f86SDimitry Andric } 83fcaf7f86SDimitry Andric } 84fcaf7f86SDimitry Andric for (MachineInstr *MI : ToErase) { 85fcaf7f86SDimitry Andric Register Reg = MI->getOperand(2).getReg(); 86*cb14a3feSDimitry Andric if (RegsAlreadyAddedToDT.contains(MI)) 87fcaf7f86SDimitry Andric Reg = RegsAlreadyAddedToDT[MI]; 8806c3fb27SDimitry Andric auto *RC = MRI.getRegClassOrNull(MI->getOperand(0).getReg()); 8906c3fb27SDimitry Andric if (!MRI.getRegClassOrNull(Reg) && RC) 9006c3fb27SDimitry Andric MRI.setRegClass(Reg, RC); 91fcaf7f86SDimitry Andric MRI.replaceRegWith(MI->getOperand(0).getReg(), Reg); 92fcaf7f86SDimitry Andric MI->eraseFromParent(); 93fcaf7f86SDimitry Andric } 94fcaf7f86SDimitry Andric for (MachineInstr *MI : ToEraseComposites) 95fcaf7f86SDimitry Andric MI->eraseFromParent(); 9681ad6265SDimitry Andric } 9781ad6265SDimitry Andric 9881ad6265SDimitry Andric static void foldConstantsIntoIntrinsics(MachineFunction &MF) { 9981ad6265SDimitry Andric SmallVector<MachineInstr *, 10> ToErase; 10081ad6265SDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 10181ad6265SDimitry Andric const unsigned AssignNameOperandShift = 2; 10281ad6265SDimitry Andric for (MachineBasicBlock &MBB : MF) { 10381ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 10481ad6265SDimitry Andric if (!isSpvIntrinsic(MI, Intrinsic::spv_assign_name)) 10581ad6265SDimitry Andric continue; 10681ad6265SDimitry Andric unsigned NumOp = MI.getNumExplicitDefs() + AssignNameOperandShift; 10781ad6265SDimitry Andric while (MI.getOperand(NumOp).isReg()) { 10881ad6265SDimitry Andric MachineOperand &MOp = MI.getOperand(NumOp); 10981ad6265SDimitry Andric MachineInstr *ConstMI = MRI.getVRegDef(MOp.getReg()); 11081ad6265SDimitry Andric assert(ConstMI->getOpcode() == TargetOpcode::G_CONSTANT); 11181ad6265SDimitry Andric MI.removeOperand(NumOp); 11281ad6265SDimitry Andric MI.addOperand(MachineOperand::CreateImm( 11381ad6265SDimitry Andric ConstMI->getOperand(1).getCImm()->getZExtValue())); 11481ad6265SDimitry Andric if (MRI.use_empty(ConstMI->getOperand(0).getReg())) 11581ad6265SDimitry Andric ToErase.push_back(ConstMI); 11681ad6265SDimitry Andric } 11781ad6265SDimitry Andric } 11881ad6265SDimitry Andric } 11981ad6265SDimitry Andric for (MachineInstr *MI : ToErase) 12081ad6265SDimitry Andric MI->eraseFromParent(); 12181ad6265SDimitry Andric } 12281ad6265SDimitry Andric 12381ad6265SDimitry Andric static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR, 12481ad6265SDimitry Andric MachineIRBuilder MIB) { 12581ad6265SDimitry Andric SmallVector<MachineInstr *, 10> ToErase; 12681ad6265SDimitry Andric for (MachineBasicBlock &MBB : MF) { 12781ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 12881ad6265SDimitry Andric if (!isSpvIntrinsic(MI, Intrinsic::spv_bitcast)) 12981ad6265SDimitry Andric continue; 13081ad6265SDimitry Andric assert(MI.getOperand(2).isReg()); 13181ad6265SDimitry Andric MIB.setInsertPt(*MI.getParent(), MI); 13281ad6265SDimitry Andric MIB.buildBitcast(MI.getOperand(0).getReg(), MI.getOperand(2).getReg()); 13381ad6265SDimitry Andric ToErase.push_back(&MI); 13481ad6265SDimitry Andric } 13581ad6265SDimitry Andric } 13681ad6265SDimitry Andric for (MachineInstr *MI : ToErase) 13781ad6265SDimitry Andric MI->eraseFromParent(); 13881ad6265SDimitry Andric } 13981ad6265SDimitry Andric 14081ad6265SDimitry Andric // Translating GV, IRTranslator sometimes generates following IR: 14181ad6265SDimitry Andric // %1 = G_GLOBAL_VALUE 14281ad6265SDimitry Andric // %2 = COPY %1 14381ad6265SDimitry Andric // %3 = G_ADDRSPACE_CAST %2 14481ad6265SDimitry Andric // New registers have no SPIRVType and no register class info. 14581ad6265SDimitry Andric // 14681ad6265SDimitry Andric // Set SPIRVType for GV, propagate it from GV to other instructions, 14781ad6265SDimitry Andric // also set register classes. 14881ad6265SDimitry Andric static SPIRVType *propagateSPIRVType(MachineInstr *MI, SPIRVGlobalRegistry *GR, 14981ad6265SDimitry Andric MachineRegisterInfo &MRI, 15081ad6265SDimitry Andric MachineIRBuilder &MIB) { 15181ad6265SDimitry Andric SPIRVType *SpirvTy = nullptr; 15281ad6265SDimitry Andric assert(MI && "Machine instr is expected"); 15381ad6265SDimitry Andric if (MI->getOperand(0).isReg()) { 15481ad6265SDimitry Andric Register Reg = MI->getOperand(0).getReg(); 15581ad6265SDimitry Andric SpirvTy = GR->getSPIRVTypeForVReg(Reg); 15681ad6265SDimitry Andric if (!SpirvTy) { 15781ad6265SDimitry Andric switch (MI->getOpcode()) { 15881ad6265SDimitry Andric case TargetOpcode::G_CONSTANT: { 15981ad6265SDimitry Andric MIB.setInsertPt(*MI->getParent(), MI); 16081ad6265SDimitry Andric Type *Ty = MI->getOperand(1).getCImm()->getType(); 16181ad6265SDimitry Andric SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB); 16281ad6265SDimitry Andric break; 16381ad6265SDimitry Andric } 16481ad6265SDimitry Andric case TargetOpcode::G_GLOBAL_VALUE: { 16581ad6265SDimitry Andric MIB.setInsertPt(*MI->getParent(), MI); 16681ad6265SDimitry Andric Type *Ty = MI->getOperand(1).getGlobal()->getType(); 16781ad6265SDimitry Andric SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB); 16881ad6265SDimitry Andric break; 16981ad6265SDimitry Andric } 17081ad6265SDimitry Andric case TargetOpcode::G_TRUNC: 17181ad6265SDimitry Andric case TargetOpcode::G_ADDRSPACE_CAST: 172fcaf7f86SDimitry Andric case TargetOpcode::G_PTR_ADD: 17381ad6265SDimitry Andric case TargetOpcode::COPY: { 17481ad6265SDimitry Andric MachineOperand &Op = MI->getOperand(1); 17581ad6265SDimitry Andric MachineInstr *Def = Op.isReg() ? MRI.getVRegDef(Op.getReg()) : nullptr; 17681ad6265SDimitry Andric if (Def) 17781ad6265SDimitry Andric SpirvTy = propagateSPIRVType(Def, GR, MRI, MIB); 17881ad6265SDimitry Andric break; 17981ad6265SDimitry Andric } 18081ad6265SDimitry Andric default: 18181ad6265SDimitry Andric break; 18281ad6265SDimitry Andric } 18381ad6265SDimitry Andric if (SpirvTy) 18481ad6265SDimitry Andric GR->assignSPIRVTypeToVReg(SpirvTy, Reg, MIB.getMF()); 18581ad6265SDimitry Andric if (!MRI.getRegClassOrNull(Reg)) 18681ad6265SDimitry Andric MRI.setRegClass(Reg, &SPIRV::IDRegClass); 18781ad6265SDimitry Andric } 18881ad6265SDimitry Andric } 18981ad6265SDimitry Andric return SpirvTy; 19081ad6265SDimitry Andric } 19181ad6265SDimitry Andric 19281ad6265SDimitry Andric // Insert ASSIGN_TYPE instuction between Reg and its definition, set NewReg as 19381ad6265SDimitry Andric // a dst of the definition, assign SPIRVType to both registers. If SpirvTy is 19481ad6265SDimitry Andric // provided, use it as SPIRVType in ASSIGN_TYPE, otherwise create it from Ty. 195bdd1243dSDimitry Andric // It's used also in SPIRVBuiltins.cpp. 19681ad6265SDimitry Andric // TODO: maybe move to SPIRVUtils. 197bdd1243dSDimitry Andric namespace llvm { 198bdd1243dSDimitry Andric Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy, 199bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, MachineIRBuilder &MIB, 20081ad6265SDimitry Andric MachineRegisterInfo &MRI) { 20181ad6265SDimitry Andric MachineInstr *Def = MRI.getVRegDef(Reg); 20281ad6265SDimitry Andric assert((Ty || SpirvTy) && "Either LLVM or SPIRV type is expected."); 20381ad6265SDimitry Andric MIB.setInsertPt(*Def->getParent(), 20481ad6265SDimitry Andric (Def->getNextNode() ? Def->getNextNode()->getIterator() 20581ad6265SDimitry Andric : Def->getParent()->end())); 20681ad6265SDimitry Andric Register NewReg = MRI.createGenericVirtualRegister(MRI.getType(Reg)); 20706c3fb27SDimitry Andric if (auto *RC = MRI.getRegClassOrNull(Reg)) { 20881ad6265SDimitry Andric MRI.setRegClass(NewReg, RC); 20906c3fb27SDimitry Andric } else { 21006c3fb27SDimitry Andric MRI.setRegClass(NewReg, &SPIRV::IDRegClass); 21106c3fb27SDimitry Andric MRI.setRegClass(Reg, &SPIRV::IDRegClass); 21206c3fb27SDimitry Andric } 21381ad6265SDimitry Andric SpirvTy = SpirvTy ? SpirvTy : GR->getOrCreateSPIRVType(Ty, MIB); 21481ad6265SDimitry Andric GR->assignSPIRVTypeToVReg(SpirvTy, Reg, MIB.getMF()); 21581ad6265SDimitry Andric // This is to make it convenient for Legalizer to get the SPIRVType 21681ad6265SDimitry Andric // when processing the actual MI (i.e. not pseudo one). 21781ad6265SDimitry Andric GR->assignSPIRVTypeToVReg(SpirvTy, NewReg, MIB.getMF()); 218bdd1243dSDimitry Andric // Copy MIFlags from Def to ASSIGN_TYPE instruction. It's required to keep 219bdd1243dSDimitry Andric // the flags after instruction selection. 22006c3fb27SDimitry Andric const uint32_t Flags = Def->getFlags(); 22181ad6265SDimitry Andric MIB.buildInstr(SPIRV::ASSIGN_TYPE) 22281ad6265SDimitry Andric .addDef(Reg) 22381ad6265SDimitry Andric .addUse(NewReg) 224bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(SpirvTy)) 225bdd1243dSDimitry Andric .setMIFlags(Flags); 22681ad6265SDimitry Andric Def->getOperand(0).setReg(NewReg); 22781ad6265SDimitry Andric return NewReg; 22881ad6265SDimitry Andric } 229bdd1243dSDimitry Andric } // namespace llvm 23081ad6265SDimitry Andric 23181ad6265SDimitry Andric static void generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR, 23281ad6265SDimitry Andric MachineIRBuilder MIB) { 23381ad6265SDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 23481ad6265SDimitry Andric SmallVector<MachineInstr *, 10> ToErase; 23581ad6265SDimitry Andric 23681ad6265SDimitry Andric for (MachineBasicBlock *MBB : post_order(&MF)) { 23781ad6265SDimitry Andric if (MBB->empty()) 23881ad6265SDimitry Andric continue; 23981ad6265SDimitry Andric 24081ad6265SDimitry Andric bool ReachedBegin = false; 24181ad6265SDimitry Andric for (auto MII = std::prev(MBB->end()), Begin = MBB->begin(); 24281ad6265SDimitry Andric !ReachedBegin;) { 24381ad6265SDimitry Andric MachineInstr &MI = *MII; 24481ad6265SDimitry Andric 2455f757f3fSDimitry Andric if (isSpvIntrinsic(MI, Intrinsic::spv_assign_ptr_type)) { 2465f757f3fSDimitry Andric Register Reg = MI.getOperand(1).getReg(); 2475f757f3fSDimitry Andric MIB.setInsertPt(*MI.getParent(), MI.getIterator()); 2485f757f3fSDimitry Andric SPIRVType *BaseTy = GR->getOrCreateSPIRVType( 2495f757f3fSDimitry Andric getMDOperandAsType(MI.getOperand(2).getMetadata(), 0), MIB); 2505f757f3fSDimitry Andric SPIRVType *AssignedPtrType = GR->getOrCreateSPIRVPointerType( 2515f757f3fSDimitry Andric BaseTy, MI, *MF.getSubtarget<SPIRVSubtarget>().getInstrInfo(), 2525f757f3fSDimitry Andric addressSpaceToStorageClass(MI.getOperand(3).getImm())); 2535f757f3fSDimitry Andric MachineInstr *Def = MRI.getVRegDef(Reg); 2545f757f3fSDimitry Andric assert(Def && "Expecting an instruction that defines the register"); 2555f757f3fSDimitry Andric insertAssignInstr(Reg, nullptr, AssignedPtrType, GR, MIB, 2565f757f3fSDimitry Andric MF.getRegInfo()); 2575f757f3fSDimitry Andric ToErase.push_back(&MI); 2585f757f3fSDimitry Andric } else if (isSpvIntrinsic(MI, Intrinsic::spv_assign_type)) { 25981ad6265SDimitry Andric Register Reg = MI.getOperand(1).getReg(); 26081ad6265SDimitry Andric Type *Ty = getMDOperandAsType(MI.getOperand(2).getMetadata(), 0); 26181ad6265SDimitry Andric MachineInstr *Def = MRI.getVRegDef(Reg); 26281ad6265SDimitry Andric assert(Def && "Expecting an instruction that defines the register"); 26381ad6265SDimitry Andric // G_GLOBAL_VALUE already has type info. 26481ad6265SDimitry Andric if (Def->getOpcode() != TargetOpcode::G_GLOBAL_VALUE) 26581ad6265SDimitry Andric insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MF.getRegInfo()); 26681ad6265SDimitry Andric ToErase.push_back(&MI); 26781ad6265SDimitry Andric } else if (MI.getOpcode() == TargetOpcode::G_CONSTANT || 26881ad6265SDimitry Andric MI.getOpcode() == TargetOpcode::G_FCONSTANT || 26981ad6265SDimitry Andric MI.getOpcode() == TargetOpcode::G_BUILD_VECTOR) { 27081ad6265SDimitry Andric // %rc = G_CONSTANT ty Val 27181ad6265SDimitry Andric // ===> 27281ad6265SDimitry Andric // %cty = OpType* ty 27381ad6265SDimitry Andric // %rctmp = G_CONSTANT ty Val 27481ad6265SDimitry Andric // %rc = ASSIGN_TYPE %rctmp, %cty 27581ad6265SDimitry Andric Register Reg = MI.getOperand(0).getReg(); 27681ad6265SDimitry Andric if (MRI.hasOneUse(Reg)) { 27781ad6265SDimitry Andric MachineInstr &UseMI = *MRI.use_instr_begin(Reg); 27881ad6265SDimitry Andric if (isSpvIntrinsic(UseMI, Intrinsic::spv_assign_type) || 27981ad6265SDimitry Andric isSpvIntrinsic(UseMI, Intrinsic::spv_assign_name)) 28081ad6265SDimitry Andric continue; 28181ad6265SDimitry Andric } 28281ad6265SDimitry Andric Type *Ty = nullptr; 28381ad6265SDimitry Andric if (MI.getOpcode() == TargetOpcode::G_CONSTANT) 28481ad6265SDimitry Andric Ty = MI.getOperand(1).getCImm()->getType(); 28581ad6265SDimitry Andric else if (MI.getOpcode() == TargetOpcode::G_FCONSTANT) 28681ad6265SDimitry Andric Ty = MI.getOperand(1).getFPImm()->getType(); 28781ad6265SDimitry Andric else { 28881ad6265SDimitry Andric assert(MI.getOpcode() == TargetOpcode::G_BUILD_VECTOR); 28981ad6265SDimitry Andric Type *ElemTy = nullptr; 29081ad6265SDimitry Andric MachineInstr *ElemMI = MRI.getVRegDef(MI.getOperand(1).getReg()); 29181ad6265SDimitry Andric assert(ElemMI); 29281ad6265SDimitry Andric 29381ad6265SDimitry Andric if (ElemMI->getOpcode() == TargetOpcode::G_CONSTANT) 29481ad6265SDimitry Andric ElemTy = ElemMI->getOperand(1).getCImm()->getType(); 29581ad6265SDimitry Andric else if (ElemMI->getOpcode() == TargetOpcode::G_FCONSTANT) 29681ad6265SDimitry Andric ElemTy = ElemMI->getOperand(1).getFPImm()->getType(); 29781ad6265SDimitry Andric else 29881ad6265SDimitry Andric llvm_unreachable("Unexpected opcode"); 29981ad6265SDimitry Andric unsigned NumElts = 30081ad6265SDimitry Andric MI.getNumExplicitOperands() - MI.getNumExplicitDefs(); 30181ad6265SDimitry Andric Ty = VectorType::get(ElemTy, NumElts, false); 30281ad6265SDimitry Andric } 30381ad6265SDimitry Andric insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MRI); 30481ad6265SDimitry Andric } else if (MI.getOpcode() == TargetOpcode::G_TRUNC || 30581ad6265SDimitry Andric MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE || 30681ad6265SDimitry Andric MI.getOpcode() == TargetOpcode::COPY || 30781ad6265SDimitry Andric MI.getOpcode() == TargetOpcode::G_ADDRSPACE_CAST) { 30881ad6265SDimitry Andric propagateSPIRVType(&MI, GR, MRI, MIB); 30981ad6265SDimitry Andric } 31081ad6265SDimitry Andric 31181ad6265SDimitry Andric if (MII == Begin) 31281ad6265SDimitry Andric ReachedBegin = true; 31381ad6265SDimitry Andric else 31481ad6265SDimitry Andric --MII; 31581ad6265SDimitry Andric } 31681ad6265SDimitry Andric } 31781ad6265SDimitry Andric for (MachineInstr *MI : ToErase) 31881ad6265SDimitry Andric MI->eraseFromParent(); 31981ad6265SDimitry Andric } 32081ad6265SDimitry Andric 32181ad6265SDimitry Andric static std::pair<Register, unsigned> 32281ad6265SDimitry Andric createNewIdReg(Register ValReg, unsigned Opcode, MachineRegisterInfo &MRI, 32381ad6265SDimitry Andric const SPIRVGlobalRegistry &GR) { 32481ad6265SDimitry Andric LLT NewT = LLT::scalar(32); 32581ad6265SDimitry Andric SPIRVType *SpvType = GR.getSPIRVTypeForVReg(ValReg); 32681ad6265SDimitry Andric assert(SpvType && "VReg is expected to have SPIRV type"); 32781ad6265SDimitry Andric bool IsFloat = SpvType->getOpcode() == SPIRV::OpTypeFloat; 32881ad6265SDimitry Andric bool IsVectorFloat = 32981ad6265SDimitry Andric SpvType->getOpcode() == SPIRV::OpTypeVector && 33081ad6265SDimitry Andric GR.getSPIRVTypeForVReg(SpvType->getOperand(1).getReg())->getOpcode() == 33181ad6265SDimitry Andric SPIRV::OpTypeFloat; 33281ad6265SDimitry Andric IsFloat |= IsVectorFloat; 33381ad6265SDimitry Andric auto GetIdOp = IsFloat ? SPIRV::GET_fID : SPIRV::GET_ID; 33481ad6265SDimitry Andric auto DstClass = IsFloat ? &SPIRV::fIDRegClass : &SPIRV::IDRegClass; 33581ad6265SDimitry Andric if (MRI.getType(ValReg).isPointer()) { 33681ad6265SDimitry Andric NewT = LLT::pointer(0, 32); 33781ad6265SDimitry Andric GetIdOp = SPIRV::GET_pID; 33881ad6265SDimitry Andric DstClass = &SPIRV::pIDRegClass; 33981ad6265SDimitry Andric } else if (MRI.getType(ValReg).isVector()) { 34081ad6265SDimitry Andric NewT = LLT::fixed_vector(2, NewT); 34181ad6265SDimitry Andric GetIdOp = IsFloat ? SPIRV::GET_vfID : SPIRV::GET_vID; 34281ad6265SDimitry Andric DstClass = IsFloat ? &SPIRV::vfIDRegClass : &SPIRV::vIDRegClass; 34381ad6265SDimitry Andric } 34481ad6265SDimitry Andric Register IdReg = MRI.createGenericVirtualRegister(NewT); 34581ad6265SDimitry Andric MRI.setRegClass(IdReg, DstClass); 34681ad6265SDimitry Andric return {IdReg, GetIdOp}; 34781ad6265SDimitry Andric } 34881ad6265SDimitry Andric 34981ad6265SDimitry Andric static void processInstr(MachineInstr &MI, MachineIRBuilder &MIB, 35081ad6265SDimitry Andric MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR) { 35181ad6265SDimitry Andric unsigned Opc = MI.getOpcode(); 35281ad6265SDimitry Andric assert(MI.getNumDefs() > 0 && MRI.hasOneUse(MI.getOperand(0).getReg())); 35381ad6265SDimitry Andric MachineInstr &AssignTypeInst = 35481ad6265SDimitry Andric *(MRI.use_instr_begin(MI.getOperand(0).getReg())); 35581ad6265SDimitry Andric auto NewReg = createNewIdReg(MI.getOperand(0).getReg(), Opc, MRI, *GR).first; 35681ad6265SDimitry Andric AssignTypeInst.getOperand(1).setReg(NewReg); 35781ad6265SDimitry Andric MI.getOperand(0).setReg(NewReg); 35881ad6265SDimitry Andric MIB.setInsertPt(*MI.getParent(), 35981ad6265SDimitry Andric (MI.getNextNode() ? MI.getNextNode()->getIterator() 36081ad6265SDimitry Andric : MI.getParent()->end())); 36181ad6265SDimitry Andric for (auto &Op : MI.operands()) { 36281ad6265SDimitry Andric if (!Op.isReg() || Op.isDef()) 36381ad6265SDimitry Andric continue; 36481ad6265SDimitry Andric auto IdOpInfo = createNewIdReg(Op.getReg(), Opc, MRI, *GR); 36581ad6265SDimitry Andric MIB.buildInstr(IdOpInfo.second).addDef(IdOpInfo.first).addUse(Op.getReg()); 36681ad6265SDimitry Andric Op.setReg(IdOpInfo.first); 36781ad6265SDimitry Andric } 36881ad6265SDimitry Andric } 36981ad6265SDimitry Andric 37081ad6265SDimitry Andric // Defined in SPIRVLegalizerInfo.cpp. 37181ad6265SDimitry Andric extern bool isTypeFoldingSupported(unsigned Opcode); 37281ad6265SDimitry Andric 37381ad6265SDimitry Andric static void processInstrsWithTypeFolding(MachineFunction &MF, 37481ad6265SDimitry Andric SPIRVGlobalRegistry *GR, 37581ad6265SDimitry Andric MachineIRBuilder MIB) { 37681ad6265SDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 37781ad6265SDimitry Andric for (MachineBasicBlock &MBB : MF) { 37881ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 37981ad6265SDimitry Andric if (isTypeFoldingSupported(MI.getOpcode())) 38081ad6265SDimitry Andric processInstr(MI, MIB, MRI, GR); 38181ad6265SDimitry Andric } 38281ad6265SDimitry Andric } 383fcaf7f86SDimitry Andric for (MachineBasicBlock &MBB : MF) { 384fcaf7f86SDimitry Andric for (MachineInstr &MI : MBB) { 385fcaf7f86SDimitry Andric // We need to rewrite dst types for ASSIGN_TYPE instrs to be able 386fcaf7f86SDimitry Andric // to perform tblgen'erated selection and we can't do that on Legalizer 387fcaf7f86SDimitry Andric // as it operates on gMIR only. 388fcaf7f86SDimitry Andric if (MI.getOpcode() != SPIRV::ASSIGN_TYPE) 389fcaf7f86SDimitry Andric continue; 390fcaf7f86SDimitry Andric Register SrcReg = MI.getOperand(1).getReg(); 391bdd1243dSDimitry Andric unsigned Opcode = MRI.getVRegDef(SrcReg)->getOpcode(); 392bdd1243dSDimitry Andric if (!isTypeFoldingSupported(Opcode)) 393fcaf7f86SDimitry Andric continue; 394fcaf7f86SDimitry Andric Register DstReg = MI.getOperand(0).getReg(); 395fcaf7f86SDimitry Andric if (MRI.getType(DstReg).isVector()) 396fcaf7f86SDimitry Andric MRI.setRegClass(DstReg, &SPIRV::IDRegClass); 397bdd1243dSDimitry Andric // Don't need to reset type of register holding constant and used in 398bdd1243dSDimitry Andric // G_ADDRSPACE_CAST, since it braaks legalizer. 399bdd1243dSDimitry Andric if (Opcode == TargetOpcode::G_CONSTANT && MRI.hasOneUse(DstReg)) { 400bdd1243dSDimitry Andric MachineInstr &UseMI = *MRI.use_instr_begin(DstReg); 401bdd1243dSDimitry Andric if (UseMI.getOpcode() == TargetOpcode::G_ADDRSPACE_CAST) 402bdd1243dSDimitry Andric continue; 403bdd1243dSDimitry Andric } 404fcaf7f86SDimitry Andric MRI.setType(DstReg, LLT::scalar(32)); 405fcaf7f86SDimitry Andric } 406fcaf7f86SDimitry Andric } 40781ad6265SDimitry Andric } 40881ad6265SDimitry Andric 40981ad6265SDimitry Andric static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR, 41081ad6265SDimitry Andric MachineIRBuilder MIB) { 411bdd1243dSDimitry Andric // Before IRTranslator pass, calls to spv_switch intrinsic are inserted before 412bdd1243dSDimitry Andric // each switch instruction. IRTranslator lowers switches to G_ICMP + G_BRCOND 413bdd1243dSDimitry Andric // + G_BR triples. A switch with two cases may be transformed to this MIR 414bdd1243dSDimitry Andric // sequence: 415bdd1243dSDimitry Andric // 41681ad6265SDimitry Andric // intrinsic(@llvm.spv.switch), %CmpReg, %Const0, %Const1 41781ad6265SDimitry Andric // %Dst0 = G_ICMP intpred(eq), %CmpReg, %Const0 41881ad6265SDimitry Andric // G_BRCOND %Dst0, %bb.2 41981ad6265SDimitry Andric // G_BR %bb.5 42081ad6265SDimitry Andric // bb.5.entry: 42181ad6265SDimitry Andric // %Dst1 = G_ICMP intpred(eq), %CmpReg, %Const1 42281ad6265SDimitry Andric // G_BRCOND %Dst1, %bb.3 42381ad6265SDimitry Andric // G_BR %bb.4 42481ad6265SDimitry Andric // bb.2.sw.bb: 42581ad6265SDimitry Andric // ... 42681ad6265SDimitry Andric // bb.3.sw.bb1: 42781ad6265SDimitry Andric // ... 42881ad6265SDimitry Andric // bb.4.sw.epilog: 42981ad6265SDimitry Andric // ... 430bdd1243dSDimitry Andric // 431bdd1243dSDimitry Andric // Sometimes (in case of range-compare switches), additional G_SUBs 432bdd1243dSDimitry Andric // instructions are inserted before G_ICMPs. Those need to be additionally 43306c3fb27SDimitry Andric // processed. 434bdd1243dSDimitry Andric // 435bdd1243dSDimitry Andric // This function modifies spv_switch call's operands to include destination 436bdd1243dSDimitry Andric // MBBs (default and for each constant value). 43706c3fb27SDimitry Andric // 43806c3fb27SDimitry Andric // At the end, the function removes redundant [G_SUB] + G_ICMP + G_BRCOND + 43906c3fb27SDimitry Andric // G_BR sequences. 440bdd1243dSDimitry Andric 441bdd1243dSDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 442bdd1243dSDimitry Andric 44306c3fb27SDimitry Andric // Collect spv_switches and G_ICMPs across all MBBs in MF. 444bdd1243dSDimitry Andric std::vector<MachineInstr *> RelevantInsts; 445bdd1243dSDimitry Andric 44606c3fb27SDimitry Andric // Collect redundant MIs from [G_SUB] + G_ICMP + G_BRCOND + G_BR sequences. 44706c3fb27SDimitry Andric // After updating spv_switches, the instructions can be removed. 44806c3fb27SDimitry Andric std::vector<MachineInstr *> PostUpdateArtifacts; 44906c3fb27SDimitry Andric 450bdd1243dSDimitry Andric // Temporary set of compare registers. G_SUBs and G_ICMPs relating to 451bdd1243dSDimitry Andric // spv_switch use these registers. 452bdd1243dSDimitry Andric DenseSet<Register> CompareRegs; 45381ad6265SDimitry Andric for (MachineBasicBlock &MBB : MF) { 45481ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 455bdd1243dSDimitry Andric // Calls to spv_switch intrinsics representing IR switches. 45681ad6265SDimitry Andric if (isSpvIntrinsic(MI, Intrinsic::spv_switch)) { 45781ad6265SDimitry Andric assert(MI.getOperand(1).isReg()); 458bdd1243dSDimitry Andric CompareRegs.insert(MI.getOperand(1).getReg()); 459bdd1243dSDimitry Andric RelevantInsts.push_back(&MI); 46081ad6265SDimitry Andric } 461bdd1243dSDimitry Andric 462bdd1243dSDimitry Andric // G_SUBs coming from range-compare switch lowering. G_SUBs are found 463bdd1243dSDimitry Andric // after spv_switch but before G_ICMP. 464bdd1243dSDimitry Andric if (MI.getOpcode() == TargetOpcode::G_SUB && MI.getOperand(1).isReg() && 465bdd1243dSDimitry Andric CompareRegs.contains(MI.getOperand(1).getReg())) { 466bdd1243dSDimitry Andric assert(MI.getOperand(0).isReg() && MI.getOperand(1).isReg()); 467bdd1243dSDimitry Andric Register Dst = MI.getOperand(0).getReg(); 468bdd1243dSDimitry Andric CompareRegs.insert(Dst); 46906c3fb27SDimitry Andric PostUpdateArtifacts.push_back(&MI); 470bdd1243dSDimitry Andric } 471bdd1243dSDimitry Andric 472bdd1243dSDimitry Andric // G_ICMPs relating to switches. 47381ad6265SDimitry Andric if (MI.getOpcode() == TargetOpcode::G_ICMP && MI.getOperand(2).isReg() && 474bdd1243dSDimitry Andric CompareRegs.contains(MI.getOperand(2).getReg())) { 47581ad6265SDimitry Andric Register Dst = MI.getOperand(0).getReg(); 476bdd1243dSDimitry Andric RelevantInsts.push_back(&MI); 47706c3fb27SDimitry Andric PostUpdateArtifacts.push_back(&MI); 47806c3fb27SDimitry Andric MachineInstr *CBr = MRI.use_begin(Dst)->getParent(); 47906c3fb27SDimitry Andric assert(CBr->getOpcode() == SPIRV::G_BRCOND); 48006c3fb27SDimitry Andric PostUpdateArtifacts.push_back(CBr); 48106c3fb27SDimitry Andric MachineInstr *Br = CBr->getNextNode(); 48206c3fb27SDimitry Andric assert(Br->getOpcode() == SPIRV::G_BR); 48306c3fb27SDimitry Andric PostUpdateArtifacts.push_back(Br); 484bdd1243dSDimitry Andric } 485bdd1243dSDimitry Andric } 486bdd1243dSDimitry Andric } 487bdd1243dSDimitry Andric 488bdd1243dSDimitry Andric // Update each spv_switch with destination MBBs. 489bdd1243dSDimitry Andric for (auto i = RelevantInsts.begin(); i != RelevantInsts.end(); i++) { 490bdd1243dSDimitry Andric if (!isSpvIntrinsic(**i, Intrinsic::spv_switch)) 491bdd1243dSDimitry Andric continue; 492bdd1243dSDimitry Andric 493bdd1243dSDimitry Andric // Currently considered spv_switch. 494bdd1243dSDimitry Andric MachineInstr *Switch = *i; 495bdd1243dSDimitry Andric // Set the first successor as default MBB to support empty switches. 496bdd1243dSDimitry Andric MachineBasicBlock *DefaultMBB = *Switch->getParent()->succ_begin(); 497bdd1243dSDimitry Andric // Container for mapping values to MMBs. 498bdd1243dSDimitry Andric SmallDenseMap<uint64_t, MachineBasicBlock *> ValuesToMBBs; 499bdd1243dSDimitry Andric 500bdd1243dSDimitry Andric // Walk all G_ICMPs to collect ValuesToMBBs. Start at currently considered 501bdd1243dSDimitry Andric // spv_switch (i) and break at any spv_switch with the same compare 502bdd1243dSDimitry Andric // register (indicating we are back at the same scope). 503bdd1243dSDimitry Andric Register CompareReg = Switch->getOperand(1).getReg(); 504bdd1243dSDimitry Andric for (auto j = i + 1; j != RelevantInsts.end(); j++) { 505bdd1243dSDimitry Andric if (isSpvIntrinsic(**j, Intrinsic::spv_switch) && 506bdd1243dSDimitry Andric (*j)->getOperand(1).getReg() == CompareReg) 507bdd1243dSDimitry Andric break; 508bdd1243dSDimitry Andric 509bdd1243dSDimitry Andric if (!((*j)->getOpcode() == TargetOpcode::G_ICMP && 510bdd1243dSDimitry Andric (*j)->getOperand(2).getReg() == CompareReg)) 511bdd1243dSDimitry Andric continue; 512bdd1243dSDimitry Andric 513bdd1243dSDimitry Andric MachineInstr *ICMP = *j; 514bdd1243dSDimitry Andric Register Dst = ICMP->getOperand(0).getReg(); 515bdd1243dSDimitry Andric MachineOperand &PredOp = ICMP->getOperand(1); 51681ad6265SDimitry Andric const auto CC = static_cast<CmpInst::Predicate>(PredOp.getPredicate()); 517bdd1243dSDimitry Andric assert((CC == CmpInst::ICMP_EQ || CC == CmpInst::ICMP_ULE) && 518bdd1243dSDimitry Andric MRI.hasOneUse(Dst) && MRI.hasOneDef(CompareReg)); 519bdd1243dSDimitry Andric uint64_t Value = getIConstVal(ICMP->getOperand(3).getReg(), &MRI); 52081ad6265SDimitry Andric MachineInstr *CBr = MRI.use_begin(Dst)->getParent(); 521bdd1243dSDimitry Andric assert(CBr->getOpcode() == SPIRV::G_BRCOND && CBr->getOperand(1).isMBB()); 522bdd1243dSDimitry Andric MachineBasicBlock *MBB = CBr->getOperand(1).getMBB(); 523bdd1243dSDimitry Andric 524bdd1243dSDimitry Andric // Map switch case Value to target MBB. 525bdd1243dSDimitry Andric ValuesToMBBs[Value] = MBB; 526bdd1243dSDimitry Andric 52706c3fb27SDimitry Andric // Add target MBB as successor to the switch's MBB. 52806c3fb27SDimitry Andric Switch->getParent()->addSuccessor(MBB); 52906c3fb27SDimitry Andric 530bdd1243dSDimitry Andric // The next MI is always G_BR to either the next case or the default. 53181ad6265SDimitry Andric MachineInstr *NextMI = CBr->getNextNode(); 53281ad6265SDimitry Andric assert(NextMI->getOpcode() == SPIRV::G_BR && 53381ad6265SDimitry Andric NextMI->getOperand(0).isMBB()); 53481ad6265SDimitry Andric MachineBasicBlock *NextMBB = NextMI->getOperand(0).getMBB(); 535bdd1243dSDimitry Andric // Default MBB does not begin with G_ICMP using spv_switch compare 536bdd1243dSDimitry Andric // register. 53781ad6265SDimitry Andric if (NextMBB->front().getOpcode() != SPIRV::G_ICMP || 53881ad6265SDimitry Andric (NextMBB->front().getOperand(2).isReg() && 53906c3fb27SDimitry Andric NextMBB->front().getOperand(2).getReg() != CompareReg)) { 54006c3fb27SDimitry Andric // Set default MBB and add it as successor to the switch's MBB. 541bdd1243dSDimitry Andric DefaultMBB = NextMBB; 54206c3fb27SDimitry Andric Switch->getParent()->addSuccessor(DefaultMBB); 54306c3fb27SDimitry Andric } 54481ad6265SDimitry Andric } 545bdd1243dSDimitry Andric 546bdd1243dSDimitry Andric // Modify considered spv_switch operands using collected Values and 547bdd1243dSDimitry Andric // MBBs. 548bdd1243dSDimitry Andric SmallVector<const ConstantInt *, 3> Values; 54981ad6265SDimitry Andric SmallVector<MachineBasicBlock *, 3> MBBs; 550bdd1243dSDimitry Andric for (unsigned k = 2; k < Switch->getNumExplicitOperands(); k++) { 551bdd1243dSDimitry Andric Register CReg = Switch->getOperand(k).getReg(); 55281ad6265SDimitry Andric uint64_t Val = getIConstVal(CReg, &MRI); 55381ad6265SDimitry Andric MachineInstr *ConstInstr = getDefInstrMaybeConstant(CReg, &MRI); 554bdd1243dSDimitry Andric if (!ValuesToMBBs[Val]) 555bdd1243dSDimitry Andric continue; 556bdd1243dSDimitry Andric 557bdd1243dSDimitry Andric Values.push_back(ConstInstr->getOperand(1).getCImm()); 558bdd1243dSDimitry Andric MBBs.push_back(ValuesToMBBs[Val]); 55981ad6265SDimitry Andric } 560bdd1243dSDimitry Andric 561bdd1243dSDimitry Andric for (unsigned k = Switch->getNumExplicitOperands() - 1; k > 1; k--) 562bdd1243dSDimitry Andric Switch->removeOperand(k); 563bdd1243dSDimitry Andric 564bdd1243dSDimitry Andric Switch->addOperand(MachineOperand::CreateMBB(DefaultMBB)); 565bdd1243dSDimitry Andric for (unsigned k = 0; k < Values.size(); k++) { 566bdd1243dSDimitry Andric Switch->addOperand(MachineOperand::CreateCImm(Values[k])); 567bdd1243dSDimitry Andric Switch->addOperand(MachineOperand::CreateMBB(MBBs[k])); 56881ad6265SDimitry Andric } 56981ad6265SDimitry Andric } 57006c3fb27SDimitry Andric 57106c3fb27SDimitry Andric for (MachineInstr *MI : PostUpdateArtifacts) { 57206c3fb27SDimitry Andric MachineBasicBlock *ParentMBB = MI->getParent(); 57306c3fb27SDimitry Andric MI->eraseFromParent(); 57406c3fb27SDimitry Andric // If G_ICMP + G_BRCOND + G_BR were the only MIs in MBB, erase this MBB. It 57506c3fb27SDimitry Andric // can be safely assumed, there are no breaks or phis directing into this 57606c3fb27SDimitry Andric // MBB. However, we need to remove this MBB from the CFG graph. MBBs must be 57706c3fb27SDimitry Andric // erased top-down. 57806c3fb27SDimitry Andric if (ParentMBB->empty()) { 57906c3fb27SDimitry Andric while (!ParentMBB->pred_empty()) 58006c3fb27SDimitry Andric (*ParentMBB->pred_begin())->removeSuccessor(ParentMBB); 58106c3fb27SDimitry Andric 58206c3fb27SDimitry Andric while (!ParentMBB->succ_empty()) 58306c3fb27SDimitry Andric ParentMBB->removeSuccessor(ParentMBB->succ_begin()); 58406c3fb27SDimitry Andric 58506c3fb27SDimitry Andric ParentMBB->eraseFromParent(); 58606c3fb27SDimitry Andric } 58706c3fb27SDimitry Andric } 58881ad6265SDimitry Andric } 58981ad6265SDimitry Andric 59081ad6265SDimitry Andric bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) { 59181ad6265SDimitry Andric // Initialize the type registry. 59281ad6265SDimitry Andric const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>(); 59381ad6265SDimitry Andric SPIRVGlobalRegistry *GR = ST.getSPIRVGlobalRegistry(); 59481ad6265SDimitry Andric GR->setCurrentFunc(MF); 59581ad6265SDimitry Andric MachineIRBuilder MIB(MF); 596fcaf7f86SDimitry Andric addConstantsToTrack(MF, GR); 59781ad6265SDimitry Andric foldConstantsIntoIntrinsics(MF); 59881ad6265SDimitry Andric insertBitcasts(MF, GR, MIB); 59981ad6265SDimitry Andric generateAssignInstrs(MF, GR, MIB); 60081ad6265SDimitry Andric processSwitches(MF, GR, MIB); 601bdd1243dSDimitry Andric processInstrsWithTypeFolding(MF, GR, MIB); 60281ad6265SDimitry Andric 60381ad6265SDimitry Andric return true; 60481ad6265SDimitry Andric } 60581ad6265SDimitry Andric 60681ad6265SDimitry Andric INITIALIZE_PASS(SPIRVPreLegalizer, DEBUG_TYPE, "SPIRV pre legalizer", false, 60781ad6265SDimitry Andric false) 60881ad6265SDimitry Andric 60981ad6265SDimitry Andric char SPIRVPreLegalizer::ID = 0; 61081ad6265SDimitry Andric 61181ad6265SDimitry Andric FunctionPass *llvm::createSPIRVPreLegalizerPass() { 61281ad6265SDimitry Andric return new SPIRVPreLegalizer(); 61381ad6265SDimitry Andric } 614