[llvm] [RISCV] Move performCombineVMergeAndVOps into RISCVFoldMasks (PR #71764)
Philip Reames via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 27 09:14:16 PST 2023
================
@@ -87,6 +97,258 @@ bool RISCVFoldMasks::isAllOnesMask(MachineInstr *MaskDef) {
}
}
+static unsigned getVMSetForLMul(RISCVII::VLMUL LMUL) {
+ switch (LMUL) {
+ case RISCVII::LMUL_F8:
+ return RISCV::PseudoVMSET_M_B1;
+ case RISCVII::LMUL_F4:
+ return RISCV::PseudoVMSET_M_B2;
+ case RISCVII::LMUL_F2:
+ return RISCV::PseudoVMSET_M_B4;
+ case RISCVII::LMUL_1:
+ return RISCV::PseudoVMSET_M_B8;
+ case RISCVII::LMUL_2:
+ return RISCV::PseudoVMSET_M_B16;
+ case RISCVII::LMUL_4:
+ return RISCV::PseudoVMSET_M_B32;
+ case RISCVII::LMUL_8:
+ return RISCV::PseudoVMSET_M_B64;
+ case RISCVII::LMUL_RESERVED:
+ llvm_unreachable("Unexpected LMUL");
+ }
+ llvm_unreachable("Unknown VLMUL enum");
+}
+
+// Returns true if LHS is the same register as RHS, or if LHS is undefined.
+bool RISCVFoldMasks::isOpSameAs(const MachineOperand &LHS,
+ const MachineOperand &RHS) {
+ if (LHS.getReg() == RISCV::NoRegister)
+ return true;
+ if (RHS.getReg() == RISCV::NoRegister)
+ return false;
+ return TRI->lookThruCopyLike(LHS.getReg(), MRI) ==
+ TRI->lookThruCopyLike(RHS.getReg(), MRI);
+}
+
+// Try to fold away VMERGE_VVM instructions. We handle these cases:
+// -Masked TU VMERGE_VVM combined with an unmasked TA instruction instruction
+// folds to a masked TU instruction. VMERGE_VVM must have have merge operand
+// same as false operand.
+// -Masked TA VMERGE_VVM combined with an unmasked TA instruction fold to a
+// masked TA instruction.
+// -Unmasked TU VMERGE_VVM combined with a masked MU TA instruction folds to
+// masked TU instruction. Both instructions must have the same merge operand.
+// VMERGE_VVM must have have merge operand same as false operand.
+// Note: The VMERGE_VVM forms above (TA, and TU) refer to the policy implied,
+// not the pseudo name. That is, a TA VMERGE_VVM can be either the _TU pseudo
+// form with an IMPLICIT_DEF passthrough operand or the unsuffixed (TA) pseudo
+// form.
+bool RISCVFoldMasks::foldVMergeIntoOps(MachineInstr &MI,
+ MachineInstr *MaskDef) {
+ MachineOperand *True;
+ MachineOperand *Merge;
+ MachineOperand *False;
+
+ const unsigned BaseOpc = RISCV::getRVVMCOpcode(MI.getOpcode());
+ // A vmv.v.v is equivalent to a vmerge with an all-ones mask.
+ if (BaseOpc == RISCV::VMV_V_V) {
+ Merge = &MI.getOperand(1);
+ False = &MI.getOperand(1);
+ True = &MI.getOperand(2);
+ } else if (BaseOpc == RISCV::VMERGE_VVM) {
+ Merge = &MI.getOperand(1);
+ False = &MI.getOperand(2);
+ True = &MI.getOperand(3);
+ } else
+ return false;
+
+ MachineInstr &TrueMI = *MRI->getVRegDef(True->getReg());
+
+ // We require that either merge and false are the same, or that merge
+ // is undefined.
+ if (!isOpSameAs(*Merge, *False))
+ return false;
+
+ // N must be the only user of True.
+ if (!MRI->hasOneUse(True->getReg()))
+ return false;
+
+ unsigned TrueOpc = TrueMI.getOpcode();
+ const MCInstrDesc &TrueMCID = TrueMI.getDesc();
+ bool HasTiedDest = RISCVII::isFirstDefTiedToFirstUse(TrueMCID);
+
+ bool IsMasked = false;
+ const RISCV::RISCVMaskedPseudoInfo *Info =
+ RISCV::lookupMaskedIntrinsicByUnmasked(TrueOpc);
+ if (!Info && HasTiedDest) {
+ Info = RISCV::getMaskedPseudoInfo(TrueOpc);
+ IsMasked = true;
+ }
+
+ if (!Info)
+ return false;
+
+ // When Mask is not a true mask, this transformation is illegal for some
+ // operations whose results are affected by mask, like viota.m.
+ if (Info->MaskAffectsResult && BaseOpc == RISCV::VMERGE_VVM &&
+ !isAllOnesMask(MaskDef))
+ return false;
+
+ MachineOperand &TrueMergeOp = TrueMI.getOperand(1);
+ if (HasTiedDest && TrueMergeOp.getReg() != RISCV::NoRegister) {
+ // The vmerge instruction must be TU.
+ // FIXME: This could be relaxed, but we need to handle the policy for the
+ // resulting op correctly.
+ if (Merge->getReg() == RISCV::NoRegister)
+ return false;
+ // Both the vmerge instruction and the True instruction must have the same
+ // merge operand.
+ if (!isOpSameAs(TrueMergeOp, *False))
+ return false;
+ }
+
+ if (IsMasked) {
+ assert(HasTiedDest && "Expected tied dest");
+ // The vmerge instruction must be TU.
+ if (Merge->getReg() == RISCV::NoRegister)
+ return false;
+ // The vmerge instruction must have an all 1s mask since we're going to keep
+ // the mask from the True instruction.
+ // FIXME: Support mask agnostic True instruction which would have an
+ // undef merge operand.
+ if (BaseOpc == RISCV::VMERGE_VVM && !isAllOnesMask(MaskDef))
+ return false;
+ }
+
+ // Skip if True has side effect.
+ // TODO: Support vleff and vlsegff.
+ if (TII->get(TrueOpc).hasUnmodeledSideEffects())
+ return false;
+
+ // The vector policy operand may be present for masked intrinsics
+ const MachineOperand &TrueVL =
+ TrueMI.getOperand(RISCVII::getVLOpNum(TrueMCID));
+
+ auto GetMinVL =
+ [](const MachineOperand &LHS,
+ const MachineOperand &RHS) -> std::optional<MachineOperand> {
+ if (LHS.isReg() && RHS.isReg() && LHS.getReg().isVirtual() &&
+ LHS.getReg() == RHS.getReg())
+ return LHS;
+ if (LHS.isImm() && LHS.getImm() == RISCV::VLMaxSentinel)
+ return RHS;
+ if (RHS.isImm() && RHS.getImm() == RISCV::VLMaxSentinel)
+ return LHS;
+ if (!LHS.isImm() || !RHS.isImm())
+ return std::nullopt;
+ return LHS.getImm() <= RHS.getImm() ? LHS : RHS;
+ };
+
+ // Because MI and True must have the same merge operand (or True's operand is
+ // implicit_def), the "effective" body is the minimum of their VLs.
+ const MachineOperand VL = MI.getOperand(RISCVII::getVLOpNum(MI.getDesc()));
+ auto MinVL = GetMinVL(TrueVL, VL);
+ if (!MinVL)
+ return false;
+ bool VLChanged = !MinVL->isIdenticalTo(VL);
+
+ // If we end up changing the VL or mask of True, then we need to make sure it
+ // doesn't raise any observable fp exceptions, since changing the active
+ // elements will affect how fflags is set.
+ if (VLChanged || !IsMasked)
+ if (TrueMCID.mayRaiseFPException() &&
+ !TrueMI.getFlag(MachineInstr::MIFlag::NoFPExcept))
+ return false;
+
+ unsigned MaskedOpc = Info->MaskedPseudo;
+ const MCInstrDesc &MaskedMCID = TII->get(MaskedOpc);
+#ifndef NDEBUG
+ assert(RISCVII::hasVecPolicyOp(MaskedMCID.TSFlags) &&
+ "Expected instructions with mask have policy operand.");
+ assert(MaskedMCID.getOperandConstraint(MaskedMCID.getNumDefs(),
+ MCOI::TIED_TO) == 0 &&
+ "Expected instructions with mask have a tied dest.");
+#endif
+
+ // Sink True down to MI so that it can access MI's operands.
+ assert(!TrueMI.hasImplicitDef());
+ bool SawStore = false;
+ for (MachineBasicBlock::instr_iterator II = TrueMI.getIterator();
+ II != MI.getIterator(); II++) {
+ if (II->mayStore()) {
+ SawStore = true;
+ break;
+ }
+ }
+ if (!TrueMI.isSafeToMove(nullptr, SawStore))
+ return false;
+ TrueMI.moveBefore(&MI);
+
+ // Set the merge to the false operand of the merge.
+ TrueMI.getOperand(1).setReg(False->getReg());
+
+ // If we're converting it to a masked pseudo, reuse MI's mask.
+ if (!IsMasked) {
+ if (BaseOpc == RISCV::VMV_V_V) {
+ // If MI is a vmv.v.v, it won't have a mask operand. So insert an all-ones
+ // mask just before True.
+ unsigned VMSetOpc =
+ getVMSetForLMul(RISCVII::getLMul(MI.getDesc().TSFlags));
+ Register Dest = MRI->createVirtualRegister(&RISCV::VRRegClass);
+ BuildMI(*MI.getParent(), TrueMI, MI.getDebugLoc(), TII->get(VMSetOpc),
+ Dest)
+ .add(VL)
+ .add(TrueMI.getOperand(RISCVII::getSEWOpNum(TrueMCID)));
+ BuildMI(*MI.getParent(), TrueMI, MI.getDebugLoc(), TII->get(RISCV::COPY),
+ RISCV::V0)
+ .addReg(Dest);
+ }
+
+ TrueMI.setDesc(MaskedMCID);
+
+ // TODO: Increment MaskOpIdx by number of explicit defs in tablegen?
+ unsigned MaskOpIdx = Info->MaskOpIdx + TrueMI.getNumExplicitDefs();
+ TrueMI.insert(&TrueMI.getOperand(MaskOpIdx),
+ MachineOperand::CreateReg(RISCV::V0, false));
+ }
+
+ // Update the AVL.
+ if (MinVL->isReg())
+ TrueMI.getOperand(RISCVII::getVLOpNum(MaskedMCID))
+ .ChangeToRegister(MinVL->getReg(), false);
+ else
+ TrueMI.getOperand(RISCVII::getVLOpNum(MaskedMCID))
+ .ChangeToImmediate(MinVL->getImm());
+
+ // Use a tumu policy, relaxing it to tail agnostic provided that the merge
+ // operand is undefined.
+ //
+ // However, if the VL became smaller than what the vmerge had originally, then
+ // elements past VL that were previously in the vmerge's body will have moved
+ // to the tail. In that case we always need to use tail undisturbed to
+ // preserve them.
+ uint64_t Policy = (Merge->getReg() == RISCV::NoRegister && !VLChanged)
+ ? RISCVII::TAIL_AGNOSTIC
+ : RISCVII::TAIL_UNDISTURBED_MASK_UNDISTURBED;
+ TrueMI.getOperand(RISCVII::getVecPolicyOpNum(MaskedMCID)).setImm(Policy);
+
+ const TargetRegisterClass *V0RC =
+ TII->getRegClass(MaskedMCID, 0, TRI, *MI.getMF());
+
+ // The destination and passthru can no longer be in V0.
+ MRI->constrainRegClass(TrueMI.getOperand(0).getReg(), V0RC);
+ Register PassthruReg = TrueMI.getOperand(1).getReg();
+ if (PassthruReg != RISCV::NoRegister)
+ MRI->constrainRegClass(PassthruReg, V0RC);
+
+ MRI->replaceRegWith(MI.getOperand(0).getReg(), TrueMI.getOperand(0).getReg());
+ MI.eraseFromParent();
+ if (IsMasked)
----------------
preames wrote:
How do you know there's not another user of the same mask elsewhere?
https://github.com/llvm/llvm-project/pull/71764
More information about the llvm-commits
mailing list