| //===- AMDGPULowerVGPREncoding.cpp - lower VGPRs above v255 ---------------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| /// \file | |
| /// Lower VGPRs above first 256 on gfx1250. | |
| /// | |
| /// The pass scans used VGPRs and inserts S_SET_VGPR_MSB instructions to switch | |
| /// VGPR addressing mode. The mode change is effective until the next change. | |
| /// This instruction provides high bits of a VGPR address for four of the | |
| /// operands: vdst, src0, src1, and src2, or other 4 operands depending on the | |
| /// instruction encoding. If bits are set they are added as MSB to the | |
| /// corresponding operand VGPR number. | |
| /// | |
| /// There is no need to replace actual register operands because encoding of the | |
| /// high and low VGPRs is the same. I.e. v0 has the encoding 0x100, so does | |
| /// v256. v1 has the encoding 0x101 and v257 has the same encoding. So high | |
| /// VGPRs will survive until actual encoding and will result in a same actual | |
| /// bit encoding. | |
| /// | |
| /// As a result the pass only inserts S_SET_VGPR_MSB to provide an actual offset | |
| /// to a VGPR address of the subseqent instructions. The InstPrinter will take | |
| /// care of the printing a low VGPR instead of a high one. In prinicple this | |
| /// shall be viable to print actual high VGPR numbers, but that would disagree | |
| /// with a disasm printing and create a situation where asm text is not | |
| /// deterministic. | |
| /// | |
| /// This pass creates a convention where non-fall through basic blocks shall | |
| /// start with all 4 MSBs zero. Otherwise a disassembly would not be readable. | |
| /// An optimization here is possible but deemed not desirable because of the | |
| /// readbility concerns. | |
| /// | |
| /// Consequentially the ABI is set to expect all 4 MSBs to be zero on entry. | |
| /// The pass must run very late in the pipeline to make sure no changes to VGPR | |
| /// operands will be made after it. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "AMDGPULowerVGPREncoding.h" | |
| #include "AMDGPU.h" | |
| #include "GCNSubtarget.h" | |
| #include "SIDefines.h" | |
| #include "SIInstrInfo.h" | |
| #include "llvm/ADT/bit.h" | |
| #include "llvm/CodeGen/MachineBasicBlock.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/MathExtras.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "amdgpu-lower-vgpr-encoding" | |
| namespace { | |
| class AMDGPULowerVGPREncoding { | |
| static constexpr unsigned OpNum = 4; | |
| static constexpr unsigned BitsPerField = 2; | |
| static constexpr unsigned NumFields = 4; | |
| static constexpr unsigned ModeWidth = NumFields * BitsPerField; | |
| static constexpr unsigned ModeMask = (1 << ModeWidth) - 1; | |
| static constexpr unsigned VGPRMSBShift = | |
| llvm::countr_zero_constexpr<unsigned>(AMDGPU::Hwreg::DST_VGPR_MSB); | |
| struct OpMode { | |
| // No MSBs set means they are not required to be of a particular value. | |
| std::optional<unsigned> MSBits; | |
| bool update(const OpMode &New, bool &Rewritten) { | |
| bool Updated = false; | |
| if (New.MSBits) { | |
| if (*New.MSBits != MSBits.value_or(0)) { | |
| Updated = true; | |
| Rewritten |= MSBits.has_value(); | |
| } | |
| MSBits = New.MSBits; | |
| } | |
| return Updated; | |
| } | |
| }; | |
| struct ModeTy { | |
| OpMode Ops[OpNum]; | |
| bool update(const ModeTy &New, bool &Rewritten) { | |
| bool Updated = false; | |
| for (unsigned I : seq(OpNum)) | |
| Updated |= Ops[I].update(New.Ops[I], Rewritten); | |
| return Updated; | |
| } | |
| unsigned encode() const { | |
| // Layout: [src0 msb, src1 msb, src2 msb, dst msb]. | |
| unsigned V = 0; | |
| for (const auto &[I, Op] : enumerate(Ops)) | |
| V |= Op.MSBits.value_or(0) << (I * 2); | |
| return V; | |
| } | |
| void print(raw_ostream &OS) const { | |
| static const char *FieldNames[] = {"src0", "src1", "src2", "dst"}; | |
| OS << '{'; | |
| for (const auto &[I, Op] : enumerate(Ops)) { | |
| if (I) | |
| OS << ", "; | |
| OS << FieldNames[I] << '='; | |
| if (Op.MSBits) | |
| OS << *Op.MSBits; | |
| else | |
| OS << '?'; | |
| } | |
| OS << '}'; | |
| } | |
| // Check if this mode is compatible with required \p NewMode without | |
| // modification. | |
| bool isCompatible(const ModeTy NewMode) const { | |
| for (unsigned I : seq(OpNum)) { | |
| if (!NewMode.Ops[I].MSBits.has_value()) | |
| continue; | |
| if (Ops[I].MSBits.value_or(0) != NewMode.Ops[I].MSBits.value_or(0)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| }; | |
| public: | |
| bool run(MachineFunction &MF); | |
| private: | |
| const SIInstrInfo *TII; | |
| const SIRegisterInfo *TRI; | |
| // Current basic block. | |
| MachineBasicBlock *MBB; | |
| /// Most recent s_set_* instruction. | |
| MachineInstr *MostRecentModeSet; | |
| /// Current mode bits. | |
| ModeTy CurrentMode; | |
| /// Number of current hard clause instructions. | |
| unsigned ClauseLen; | |
| /// Number of hard clause instructions remaining. | |
| unsigned ClauseRemaining; | |
| /// Clause group breaks. | |
| unsigned ClauseBreaks; | |
| /// Last hard clause instruction. | |
| MachineInstr *Clause; | |
| // Remember whether XCNT is known to be zero because of an S_SET_VGPR_MSB | |
| // instruction that we inserted, which implicitly waits for XCNT==0. | |
| bool XCntIsZero; | |
| /// Insert mode change before \p I. \returns true if mode was changed. | |
| bool setMode(ModeTy NewMode, MachineBasicBlock::instr_iterator I); | |
| /// Reset mode to default. | |
| void resetMode(MachineBasicBlock::instr_iterator I) { | |
| ModeTy Mode; | |
| for (OpMode &Op : Mode.Ops) | |
| Op.MSBits = 0; | |
| setMode(Mode, I); | |
| } | |
| /// If \p MO references VGPRs, return the MSBs. Otherwise, return nullopt. | |
| std::optional<unsigned> getMSBs(const MachineOperand &MO) const; | |
| /// Handle single \p MI. \return true if changed. | |
| bool runOnMachineInstr(MachineInstr &MI); | |
| /// Compute the mode for a single \p MI given \p Ops operands | |
| /// bit mapping. Optionally takes second array \p Ops2 for VOPD. | |
| /// If provided and an operand from \p Ops is not a VGPR, then \p Ops2 | |
| /// is checked. | |
| void computeMode(ModeTy &NewMode, const MachineInstr &MI, | |
| const AMDGPU::OpName Ops[OpNum], | |
| const AMDGPU::OpName *Ops2 = nullptr); | |
| /// Check if an instruction \p I is within a clause and returns a suitable | |
| /// iterator to insert mode change. It may also modify the S_CLAUSE | |
| /// instruction to extend it or drop the clause if it cannot be adjusted. | |
| MachineBasicBlock::instr_iterator | |
| handleClause(MachineBasicBlock::instr_iterator I); | |
| /// Check if an instruction \p I is immediately after another program state | |
| /// instruction which it cannot coissue with. If so, insert before that | |
| /// instruction to encourage more coissuing. | |
| MachineBasicBlock::instr_iterator | |
| handleCoissue(MachineBasicBlock::instr_iterator I); | |
| /// S_SET_VGPR_MSB immediately after S_SETREG_IMM32_B32 targeting MODE is | |
| /// silently dropped on GFX1250. When set, the next S_SET_VGPR_MSB insertion | |
| /// must be preceded by S_NOP to avoid the hazard. | |
| bool needNopBeforeSetVGPRMSB(MachineBasicBlock::instr_iterator I); | |
| /// Handle S_SETREG_IMM32_B32 targeting MODE register. On certain hardware, | |
| /// this instruction clobbers VGPR MSB bits[12:19], so we need to restore | |
| /// the current mode. \returns true if the instruction was modified or a | |
| /// new one was inserted. | |
| bool handleSetregMode(MachineInstr &MI); | |
| /// Update bits[12:19] of the imm operand in S_SETREG_IMM32_B32 to contain | |
| /// the VGPR MSB mode value. \returns true if the immediate was changed. | |
| bool updateSetregModeImm(MachineInstr &MI, int64_t ModeValue); | |
| }; | |
| bool AMDGPULowerVGPREncoding::setMode(ModeTy NewMode, | |
| MachineBasicBlock::instr_iterator I) { | |
| LLVM_DEBUG({ | |
| dbgs() << " setMode: NewMode="; | |
| NewMode.print(dbgs()); | |
| dbgs() << " CurrentMode="; | |
| CurrentMode.print(dbgs()); | |
| dbgs() << " MostRecentModeSet=" << (MostRecentModeSet ? "yes" : "null"); | |
| if (I != MBB->instr_end()) | |
| dbgs() << " before: " << *I; | |
| else | |
| dbgs() << " at end\n"; | |
| }); | |
| // Record previous mode into high 8 bits of the immediate. | |
| int64_t OldModeBits = CurrentMode.encode() << ModeWidth; | |
| bool Rewritten = false; | |
| if (!CurrentMode.update(NewMode, Rewritten)) { | |
| LLVM_DEBUG(dbgs() << " -> no change needed\n"); | |
| return false; | |
| } | |
| LLVM_DEBUG(dbgs() << " Rewritten=" << Rewritten << " after update\n"); | |
| if (MostRecentModeSet && !Rewritten) { | |
| // Update MostRecentModeSet with the new mode. | |
| MachineOperand &Op = MostRecentModeSet->getOperand(0); | |
| // Carry old mode bits from the existing instruction. | |
| int64_t OldModeBits = Op.getImm() & (ModeMask << ModeWidth); | |
| Op.setImm(CurrentMode.encode() | OldModeBits); | |
| LLVM_DEBUG(dbgs() << " -> piggybacked onto S_SET_VGPR_MSB: " | |
| << *MostRecentModeSet); | |
| return true; | |
| } | |
| MachineBasicBlock::instr_iterator InsertPt = handleClause(I); | |
| InsertPt = handleCoissue(InsertPt); | |
| // Case 2 match in handleSetregMode: the setreg's imm[12:19] matched | |
| // current MSBs, but the next VALU needs different MSBs, so this | |
| // S_SET_VGPR_MSB would land right after the setreg. Insert S_NOP to | |
| // prevent it from being silently dropped. | |
| if (needNopBeforeSetVGPRMSB(InsertPt)) | |
| BuildMI(*MBB, InsertPt, {}, TII->get(AMDGPU::S_NOP)).addImm(0); | |
| MostRecentModeSet = | |
| BuildMI(*MBB, InsertPt, {}, TII->get(AMDGPU::S_SET_VGPR_MSB)) | |
| .addImm(NewMode.encode() | OldModeBits); | |
| LLVM_DEBUG(dbgs() << " -> inserted new S_SET_VGPR_MSB: " | |
| << *MostRecentModeSet); | |
| // If we inserted S_SET_VGPR_MSB early then XCNT should remain zero from the | |
| // insertion point to the current instruction. Remove any redundant | |
| // S_WAIT_XCNT instructions in that range. | |
| for (MachineInstr &MI : make_early_inc_range(make_range(InsertPt, I))) { | |
| assert(!SIInstrInfo::isVMEM(MI) && !SIInstrInfo::isSMRD(MI)); | |
| if (MI.getOpcode() == AMDGPU::S_WAIT_XCNT) | |
| MI.eraseFromBundle(); | |
| } | |
| XCntIsZero = true; | |
| CurrentMode = NewMode; | |
| return true; | |
| } | |
| std::optional<unsigned> | |
| AMDGPULowerVGPREncoding::getMSBs(const MachineOperand &MO) const { | |
| if (!MO.isReg()) | |
| return std::nullopt; | |
| MCRegister Reg = MO.getReg(); | |
| const TargetRegisterClass *RC = TRI->getPhysRegBaseClass(Reg); | |
| if (!RC || !TRI->isVGPRClass(RC)) | |
| return std::nullopt; | |
| unsigned Idx = TRI->getHWRegIndex(Reg); | |
| return Idx >> 8; | |
| } | |
| void AMDGPULowerVGPREncoding::computeMode(ModeTy &NewMode, | |
| const MachineInstr &MI, | |
| const AMDGPU::OpName Ops[OpNum], | |
| const AMDGPU::OpName *Ops2) { | |
| NewMode = {}; | |
| for (unsigned I = 0; I < OpNum; ++I) { | |
| const MachineOperand *Op = TII->getNamedOperand(MI, Ops[I]); | |
| std::optional<unsigned> MSBits; | |
| if (Op) | |
| MSBits = getMSBs(*Op); | |
| #if !defined(NDEBUG) | |
| if (MSBits.has_value() && Ops2) { | |
| const MachineOperand *Op2 = TII->getNamedOperand(MI, Ops2[I]); | |
| if (Op2) { | |
| std::optional<unsigned> MSBits2; | |
| MSBits2 = getMSBs(*Op2); | |
| if (MSBits2.has_value() && MSBits != MSBits2) | |
| llvm_unreachable("Invalid VOPD pair was created"); | |
| } | |
| } | |
| #endif | |
| if (!MSBits.has_value() && Ops2) { | |
| Op = TII->getNamedOperand(MI, Ops2[I]); | |
| if (Op) | |
| MSBits = getMSBs(*Op); | |
| } | |
| if (!MSBits.has_value()) | |
| continue; | |
| // Skip tied uses of src2 of VOP2, these will be handled along with defs and | |
| // only vdst bit affects these operands. We cannot skip tied uses of VOP3, | |
| // these uses are real even if must match the vdst. | |
| if (Ops[I] == AMDGPU::OpName::src2 && !Op->isDef() && Op->isTied() && | |
| (SIInstrInfo::isVOP2(MI) || | |
| (SIInstrInfo::isVOP3(MI) && | |
| TII->hasVALU32BitEncoding(MI.getOpcode())))) | |
| continue; | |
| NewMode.Ops[I].MSBits = MSBits.value(); | |
| } | |
| } | |
| bool AMDGPULowerVGPREncoding::runOnMachineInstr(MachineInstr &MI) { | |
| auto Ops = AMDGPU::getVGPRLoweringOperandTables(MI.getDesc()); | |
| if (Ops.first) { | |
| ModeTy NewMode; | |
| computeMode(NewMode, MI, Ops.first, Ops.second); | |
| LLVM_DEBUG({ | |
| dbgs() << " runOnMachineInstr: "; | |
| MI.print(dbgs()); | |
| dbgs() << " computed NewMode="; | |
| NewMode.print(dbgs()); | |
| dbgs() << " compatible=" << CurrentMode.isCompatible(NewMode) << '\n'; | |
| }); | |
| if (!CurrentMode.isCompatible(NewMode) && MI.isCommutable() && | |
| TII->commuteInstruction(MI)) { | |
| ModeTy NewModeCommuted; | |
| computeMode(NewModeCommuted, MI, Ops.first, Ops.second); | |
| LLVM_DEBUG({ | |
| dbgs() << " commuted NewMode="; | |
| NewModeCommuted.print(dbgs()); | |
| dbgs() << " compatible=" << CurrentMode.isCompatible(NewModeCommuted) | |
| << '\n'; | |
| }); | |
| if (CurrentMode.isCompatible(NewModeCommuted)) { | |
| // Update CurrentMode with mode bits the commuted instruction relies on. | |
| // This prevents later instructions from piggybacking and corrupting | |
| // those bits (e.g., a nullopt src treated as 0 could be overwritten). | |
| bool Unused = false; | |
| CurrentMode.update(NewModeCommuted, Unused); | |
| // MI was modified by the commute above. | |
| return true; | |
| } | |
| // Commute back. | |
| if (!TII->commuteInstruction(MI)) | |
| llvm_unreachable("Failed to restore commuted instruction."); | |
| } | |
| return setMode(NewMode, MI.getIterator()); | |
| } | |
| assert(!TII->hasVGPRUses(MI) || MI.isMetaInstruction() || MI.isPseudo()); | |
| return false; | |
| } | |
| MachineBasicBlock::instr_iterator | |
| AMDGPULowerVGPREncoding::handleClause(MachineBasicBlock::instr_iterator I) { | |
| if (!ClauseRemaining) | |
| return I; | |
| // A clause cannot start with a special instruction, place it right before | |
| // the clause. | |
| if (ClauseRemaining == ClauseLen) { | |
| I = Clause->getPrevNode()->getIterator(); | |
| assert(I->isBundle()); | |
| return I; | |
| } | |
| // If a clause defines breaks each group cannot start with a mode change. | |
| // just drop the clause. | |
| if (ClauseBreaks) { | |
| Clause->eraseFromBundle(); | |
| ClauseRemaining = 0; | |
| return I; | |
| } | |
| // Otherwise adjust a number of instructions in the clause if it fits. | |
| // If it does not clause will just become shorter. Since the length | |
| // recorded in the clause is one less, increment the length after the | |
| // update. Note that SIMM16[5:0] must be 1-62, not 0 or 63. | |
| if (ClauseLen < 63) | |
| Clause->getOperand(0).setImm(ClauseLen | (ClauseBreaks << 8)); | |
| ++ClauseLen; | |
| return I; | |
| } | |
| MachineBasicBlock::instr_iterator | |
| AMDGPULowerVGPREncoding::handleCoissue(MachineBasicBlock::instr_iterator I) { | |
| // "Program State instructions" are instructions which are used to control | |
| // operation of the GPU rather than performing arithmetic. Such instructions | |
| // have different coissuing rules w.r.t s_set_vgpr_msb. | |
| auto isProgramStateInstr = [this](MachineInstr *MI) { | |
| unsigned Opc = MI->getOpcode(); | |
| return TII->isBarrier(Opc) || TII->isWaitcnt(Opc) || | |
| Opc == AMDGPU::S_DELAY_ALU; | |
| }; | |
| while (I != MBB->begin()) { | |
| auto Prev = std::prev(I); | |
| if (!isProgramStateInstr(&*Prev)) | |
| return I; | |
| I = Prev; | |
| } | |
| return I; | |
| } | |
| /// Returns whether \p MI is a S_SETREG_IMM32_B32(MODE). | |
| static bool isSetregMode(const MachineInstr &MI, const SIInstrInfo &TII) { | |
| if (MI.getOpcode() != AMDGPU::S_SETREG_IMM32_B32) | |
| return false; | |
| const MachineOperand *SIMM16Op = | |
| TII.getNamedOperand(MI, AMDGPU::OpName::simm16); | |
| auto [HwRegId, _Offset, _Size] = | |
| AMDGPU::Hwreg::HwregEncoding::decode(SIMM16Op->getImm()); | |
| return HwRegId == AMDGPU::Hwreg::ID_MODE; | |
| } | |
| bool AMDGPULowerVGPREncoding::needNopBeforeSetVGPRMSB( | |
| MachineBasicBlock::instr_iterator I) { | |
| MachineBasicBlock *CurrentMBB = MBB; | |
| while (true) { | |
| // Walk the block backward until we hit a non-meta instruction or the | |
| // beginning of the block. | |
| while (I != CurrentMBB->instr_begin()) { | |
| I = std::prev(I); | |
| if (isSetregMode(*I, *TII)) | |
| return true; | |
| if (!I->isMetaInstruction()) | |
| return false; | |
| } | |
| // Look for a potential fallthrough predecessor block. When it ends with a | |
| // S_SETREG_IMM32_B32(MODE) we need to insert a S_NOP too. We assume that an | |
| // explicit jump to the current block from the block that would otherwise | |
| // have naturally fallen through to it will remain in the final assembly. | |
| CurrentMBB = CurrentMBB->getPrevNode(); | |
| if (!CurrentMBB || !CurrentMBB->canFallThrough()) | |
| return false; | |
| I = CurrentMBB->instr_end(); | |
| } | |
| return false; | |
| } | |
| /// Convert mode value from S_SET_VGPR_MSB format to MODE register format. | |
| /// S_SET_VGPR_MSB uses: (src0[0-1], src1[2-3], src2[4-5], dst[6-7]) | |
| /// MODE register uses: (dst[0-1], src0[2-3], src1[4-5], src2[6-7]) | |
| /// This is a left rotation by 2 bits on an 8-bit value. | |
| static int64_t convertModeToSetregFormat(int64_t Mode) { | |
| assert(isUInt<8>(Mode) && "Mode expected to be 8-bit"); | |
| return llvm::rotl<uint8_t>(static_cast<uint8_t>(Mode), /*R=*/2); | |
| } | |
| bool AMDGPULowerVGPREncoding::updateSetregModeImm(MachineInstr &MI, | |
| int64_t ModeValue) { | |
| assert(MI.getOpcode() == AMDGPU::S_SETREG_IMM32_B32); | |
| // Convert from S_SET_VGPR_MSB format to MODE register format | |
| int64_t SetregMode = convertModeToSetregFormat(ModeValue); | |
| MachineOperand *ImmOp = TII->getNamedOperand(MI, AMDGPU::OpName::imm); | |
| int64_t OldImm = ImmOp->getImm(); | |
| // Note that Offset is ignored for mode bits here. | |
| int64_t NewImm = (OldImm & ~int64_t(AMDGPU::Hwreg::VGPR_MSB_MASK)) | | |
| (SetregMode << VGPRMSBShift); | |
| ImmOp->setImm(NewImm); | |
| return NewImm != OldImm; | |
| } | |
| bool AMDGPULowerVGPREncoding::handleSetregMode(MachineInstr &MI) { | |
| using namespace AMDGPU::Hwreg; | |
| assert(MI.getOpcode() == AMDGPU::S_SETREG_IMM32_B32 && | |
| "only S_SETREG_IMM32_B32 needs to be handled"); | |
| LLVM_DEBUG(dbgs() << " handleSetregMode: " << MI); | |
| MachineOperand *SIMM16Op = TII->getNamedOperand(MI, AMDGPU::OpName::simm16); | |
| assert(SIMM16Op && "SIMM16Op must be present"); | |
| auto [HwRegId, Offset, Size] = HwregEncoding::decode(SIMM16Op->getImm()); | |
| LLVM_DEBUG(dbgs() << " HwRegId=" << HwRegId << " Offset=" << Offset | |
| << " Size=" << Size << '\n'); | |
| if (HwRegId != ID_MODE) { | |
| LLVM_DEBUG(dbgs() << " -> not ID_MODE, skipping\n"); | |
| return false; | |
| } | |
| // MostRecentModeSet is clobbered by SETREG and not relevant anymore. | |
| MostRecentModeSet = nullptr; | |
| int64_t ModeValue = CurrentMode.encode(); | |
| LLVM_DEBUG({ | |
| dbgs() << " CurrentMode="; | |
| CurrentMode.print(dbgs()); | |
| dbgs() << " encoded=0x" << Twine::utohexstr(ModeValue) | |
| << " VGPRMSBShift=" << VGPRMSBShift << '\n'; | |
| }); | |
| // Case 1: Size <= 12 - the original instruction uses imm32[0:Size-1], so | |
| // imm32[12:19] is unused, or Offset is zero and it is safe to set | |
| // imm32[12:19] to the correct VGPR MSBs. | |
| if (!Offset || Size <= VGPRMSBShift) { | |
| // Set imm32[12:19] to the correct VGPR MSBs. | |
| LLVM_DEBUG(dbgs() << " Case 1: Size(" << Size << ") <= VGPRMSBShift(" | |
| << VGPRMSBShift << "), update mode bits[12:19]\n"); | |
| bool Changed = updateSetregModeImm(MI, ModeValue); | |
| LLVM_DEBUG(dbgs() << " -> " << MI); | |
| return Changed; | |
| } | |
| // Case 2: Size > 12 - the original instruction uses bits beyond 11, so we | |
| // cannot arbitrarily modify imm32[12:19]. Check if it already matches VGPR | |
| // MSBs. Note: imm32[12:19] is in MODE register format, while ModeValue is | |
| // in S_SET_VGPR_MSB format, so we need to convert before comparing. | |
| MachineOperand *ImmOp = TII->getNamedOperand(MI, AMDGPU::OpName::imm); | |
| assert(ImmOp && "ImmOp must be present"); | |
| int64_t ImmBits12To19 = (ImmOp->getImm() & VGPR_MSB_MASK) >> VGPRMSBShift; | |
| int64_t SetregModeValue = convertModeToSetregFormat(ModeValue); | |
| LLVM_DEBUG(dbgs() << " Case 2: Size(" << Size << ") > VGPRMSBShift, " | |
| << "ImmBits12To19=0x" << Twine::utohexstr(ImmBits12To19) | |
| << " SetregModeValue=0x" | |
| << Twine::utohexstr(SetregModeValue) << '\n'); | |
| if (ImmBits12To19 == SetregModeValue) { | |
| LLVM_DEBUG(dbgs() << " -> bits[12:19] already correct\n"); | |
| return false; | |
| } | |
| // imm32[12:19] doesn't match VGPR MSBs - insert s_set_vgpr_msb after | |
| // the original instruction to restore the correct value. Insert S_NOP | |
| // to avoid the GFX1250 hazard where S_SET_VGPR_MSB immediately after | |
| // S_SETREG_IMM32_B32(MODE) is silently dropped. | |
| MachineBasicBlock::iterator InsertPt = std::next(MI.getIterator()); | |
| BuildMI(*MBB, InsertPt, MI.getDebugLoc(), TII->get(AMDGPU::S_NOP)).addImm(0); | |
| MostRecentModeSet = BuildMI(*MBB, InsertPt, MI.getDebugLoc(), | |
| TII->get(AMDGPU::S_SET_VGPR_MSB)) | |
| .addImm(ModeValue | (ModeValue << ModeWidth)); | |
| LLVM_DEBUG(dbgs() << " -> inserted S_SET_VGPR_MSB after setreg: " | |
| << *MostRecentModeSet); | |
| return true; | |
| } | |
| bool AMDGPULowerVGPREncoding::run(MachineFunction &MF) { | |
| const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>(); | |
| if (!ST.has1024AddressableVGPRs()) | |
| return false; | |
| TII = ST.getInstrInfo(); | |
| TRI = ST.getRegisterInfo(); | |
| LLVM_DEBUG(dbgs() << "*** AMDGPULowerVGPREncoding on " << MF.getName() | |
| << " ***\n"); | |
| bool Changed = false; | |
| ClauseLen = ClauseRemaining = 0; | |
| CurrentMode = {}; | |
| for (auto &MBB : MF) { | |
| MostRecentModeSet = nullptr; | |
| XCntIsZero = false; | |
| this->MBB = &MBB; | |
| LLVM_DEBUG(dbgs() << "BB#" << MBB.getNumber() << ' ' << MBB.getName() | |
| << ":\n"); | |
| for (auto &MI : llvm::make_early_inc_range(MBB.instrs())) { | |
| if (MI.isMetaInstruction()) | |
| continue; | |
| if (MI.isTerminator() || MI.isCall()) { | |
| LLVM_DEBUG(dbgs() << " terminator/call: " << MI); | |
| if (MI.getOpcode() == AMDGPU::S_ENDPGM || | |
| MI.getOpcode() == AMDGPU::S_ENDPGM_SAVED) | |
| CurrentMode = {}; | |
| else | |
| resetMode(MI.getIterator()); | |
| continue; | |
| } | |
| if (MI.isInlineAsm()) { | |
| LLVM_DEBUG(dbgs() << " inline asm: " << MI); | |
| if (TII->hasVGPRUses(MI)) | |
| resetMode(MI.getIterator()); | |
| continue; | |
| } | |
| if (MI.getOpcode() == AMDGPU::S_CLAUSE) { | |
| assert(!ClauseRemaining && "Nested clauses are not supported"); | |
| ClauseLen = MI.getOperand(0).getImm(); | |
| ClauseBreaks = (ClauseLen >> 8) & 15; | |
| ClauseLen = ClauseRemaining = (ClauseLen & 63) + 1; | |
| Clause = &MI; | |
| LLVM_DEBUG(dbgs() << " clause: len=" << ClauseLen | |
| << " breaks=" << ClauseBreaks << '\n'); | |
| continue; | |
| } | |
| if (MI.getOpcode() == AMDGPU::S_SETREG_IMM32_B32 && | |
| ST.hasSetregVGPRMSBFixup()) { | |
| Changed |= handleSetregMode(MI); | |
| continue; | |
| } | |
| // If XCNT is known to be zero then any S_WAIT_XCNT instruction is | |
| // redundant and can be removed. | |
| if (MI.getOpcode() == AMDGPU::S_WAIT_XCNT && XCntIsZero) { | |
| MI.eraseFromBundle(); | |
| Changed = true; | |
| continue; | |
| } | |
| Changed |= runOnMachineInstr(MI); | |
| // Any VMEM or SMEM instruction may increment XCNT. | |
| if (SIInstrInfo::isVMEM(MI) || SIInstrInfo::isSMRD(MI)) | |
| XCntIsZero = false; | |
| if (ClauseRemaining) | |
| --ClauseRemaining; | |
| } | |
| // Reset the mode if we are falling through. | |
| LLVM_DEBUG(dbgs() << " end of BB, resetting mode\n"); | |
| resetMode(MBB.instr_end()); | |
| } | |
| return Changed; | |
| } | |
| class AMDGPULowerVGPREncodingLegacy : public MachineFunctionPass { | |
| public: | |
| static char ID; | |
| AMDGPULowerVGPREncodingLegacy() : MachineFunctionPass(ID) {} | |
| bool runOnMachineFunction(MachineFunction &MF) override { | |
| return AMDGPULowerVGPREncoding().run(MF); | |
| } | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| AU.setPreservesCFG(); | |
| MachineFunctionPass::getAnalysisUsage(AU); | |
| } | |
| }; | |
| } // namespace | |
| char AMDGPULowerVGPREncodingLegacy::ID = 0; | |
| char &llvm::AMDGPULowerVGPREncodingLegacyID = AMDGPULowerVGPREncodingLegacy::ID; | |
| INITIALIZE_PASS(AMDGPULowerVGPREncodingLegacy, DEBUG_TYPE, | |
| "AMDGPU Lower VGPR Encoding", false, false) | |
| PreservedAnalyses | |
| AMDGPULowerVGPREncodingPass::run(MachineFunction &MF, | |
| MachineFunctionAnalysisManager &MFAM) { | |
| if (!AMDGPULowerVGPREncoding().run(MF)) | |
| return PreservedAnalyses::all(); | |
| return getMachineFunctionPassPreservedAnalyses().preserveSet<CFGAnalyses>(); | |
| } |