[llvm] [RISCV] Move performCombineVMergeAndVOps into RISCVFoldMasks (PR #71764)
Luke Lau via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 28 02:48:14 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);
----------------
lukel97 wrote:
I gave this a try by building up a `SmallVector<MachineOperand>` and then creating a new `MachineInstr` with `MaskedMCID`, but then ran into the issue that it doesn't preserve any of the flags or tied operands. The only way I can think of preserving these is by operating on the operands in place, or by cloning the instruction entirely. But then we still need to insert an operand for the unmasked vmv.v.v case. Any ideas?
https://github.com/llvm/llvm-project/pull/71764
More information about the llvm-commits
mailing list