blob: 3fa9baea6de1413eebf4e6c67e8e29f48ec2973a [file] [log] [blame] [edit]
//===- RISCVVSETVLIInfoAnalysis.cpp - VSETVLI Info Analysis ---------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements an analysis of the vtype/vl information that is needed
// by RISCVInsertVSETVLI pass and others.
//
//===----------------------------------------------------------------------===//
#include "RISCVVSETVLIInfoAnalysis.h"
#include "RISCVSubtarget.h"
#include "llvm/CodeGen/LiveIntervals.h"
namespace llvm {
namespace RISCV {
/// Given a virtual register \p Reg, return the corresponding VNInfo for it.
/// This will return nullptr if the virtual register is an implicit_def or
/// if LiveIntervals is not available.
static VNInfo *getVNInfoFromReg(Register Reg, const MachineInstr &MI,
const LiveIntervals *LIS) {
assert(Reg.isVirtual());
if (!LIS)
return nullptr;
auto &LI = LIS->getInterval(Reg);
SlotIndex SI = LIS->getSlotIndexes()->getInstructionIndex(MI);
return LI.getVNInfoBefore(SI);
}
static unsigned getVLOpNum(const MachineInstr &MI) {
return RISCVII::getVLOpNum(MI.getDesc());
}
static unsigned getSEWOpNum(const MachineInstr &MI) {
return RISCVII::getSEWOpNum(MI.getDesc());
}
static unsigned getVecPolicyOpNum(const MachineInstr &MI) {
return RISCVII::getVecPolicyOpNum(MI.getDesc());
}
/// Get the EEW for a load or store instruction. Return std::nullopt if MI is
/// not a load or store which ignores SEW.
static std::optional<unsigned> getEEWForLoadStore(const MachineInstr &MI) {
switch (RISCV::getRVVMCOpcode(MI.getOpcode())) {
default:
return std::nullopt;
case RISCV::VLE8_V:
case RISCV::VLSE8_V:
case RISCV::VSE8_V:
case RISCV::VSSE8_V:
return 8;
case RISCV::VLE16_V:
case RISCV::VLSE16_V:
case RISCV::VSE16_V:
case RISCV::VSSE16_V:
return 16;
case RISCV::VLE32_V:
case RISCV::VLSE32_V:
case RISCV::VSE32_V:
case RISCV::VSSE32_V:
return 32;
case RISCV::VLE64_V:
case RISCV::VLSE64_V:
case RISCV::VSE64_V:
case RISCV::VSSE64_V:
return 64;
}
}
/// Return true if this is an operation on mask registers. Note that
/// this includes both arithmetic/logical ops and load/store (vlm/vsm).
static bool isMaskRegOp(const MachineInstr &MI) {
if (!RISCVII::hasSEWOp(MI.getDesc().TSFlags))
return false;
const unsigned Log2SEW = MI.getOperand(getSEWOpNum(MI)).getImm();
// A Log2SEW of 0 is an operation on mask registers only.
return Log2SEW == 0;
}
/// Return true if the inactive elements in the result are entirely undefined.
/// Note that this is different from "agnostic" as defined by the vector
/// specification. Agnostic requires each lane to either be undisturbed, or
/// take the value -1; no other value is allowed.
static bool hasUndefinedPassthru(const MachineInstr &MI) {
unsigned UseOpIdx;
if (!MI.isRegTiedToUseOperand(0, &UseOpIdx))
// If there is no passthrough operand, then the pass through
// lanes are undefined.
return true;
// All undefined passthrus should be $noreg: see
// RISCVDAGToDAGISel::doPeepholeNoRegPassThru
const MachineOperand &UseMO = MI.getOperand(UseOpIdx);
return !UseMO.getReg().isValid() || UseMO.isUndef();
}
static bool isLMUL1OrSmaller(RISCVVType::VLMUL LMUL) {
auto [LMul, Fractional] = RISCVVType::decodeVLMUL(LMUL);
return Fractional || LMul == 1;
}
/// Return true if moving from CurVType to NewVType is
/// indistinguishable from the perspective of an instruction (or set
/// of instructions) which use only the Used subfields and properties.
bool areCompatibleVTYPEs(uint64_t CurVType, uint64_t NewVType,
const DemandedFields &Used) {
switch (Used.SEW) {
case DemandedFields::SEWNone:
break;
case DemandedFields::SEWEqual:
if (RISCVVType::getSEW(CurVType) != RISCVVType::getSEW(NewVType))
return false;
break;
case DemandedFields::SEWGreaterThanOrEqual:
if (RISCVVType::getSEW(NewVType) < RISCVVType::getSEW(CurVType))
return false;
break;
case DemandedFields::SEWGreaterThanOrEqualAndLessThan64:
if (RISCVVType::getSEW(NewVType) < RISCVVType::getSEW(CurVType) ||
RISCVVType::getSEW(NewVType) >= 64)
return false;
break;
}
switch (Used.LMUL) {
case DemandedFields::LMULNone:
break;
case DemandedFields::LMULEqual:
if (RISCVVType::getVLMUL(CurVType) != RISCVVType::getVLMUL(NewVType))
return false;
break;
case DemandedFields::LMULLessThanOrEqualToM1:
if (!isLMUL1OrSmaller(RISCVVType::getVLMUL(NewVType)))
return false;
break;
}
if (Used.SEWLMULRatio) {
auto Ratio1 = RISCVVType::getSEWLMULRatio(RISCVVType::getSEW(CurVType),
RISCVVType::getVLMUL(CurVType));
auto Ratio2 = RISCVVType::getSEWLMULRatio(RISCVVType::getSEW(NewVType),
RISCVVType::getVLMUL(NewVType));
if (Ratio1 != Ratio2)
return false;
}
if (Used.TailPolicy && RISCVVType::isTailAgnostic(CurVType) !=
RISCVVType::isTailAgnostic(NewVType))
return false;
if (Used.MaskPolicy && RISCVVType::isMaskAgnostic(CurVType) !=
RISCVVType::isMaskAgnostic(NewVType))
return false;
if (Used.TWiden && (RISCVVType::hasXSfmmWiden(CurVType) !=
RISCVVType::hasXSfmmWiden(NewVType) ||
(RISCVVType::hasXSfmmWiden(CurVType) &&
RISCVVType::getXSfmmWiden(CurVType) !=
RISCVVType::getXSfmmWiden(NewVType))))
return false;
if (Used.AltFmt &&
RISCVVType::isAltFmt(CurVType) != RISCVVType::isAltFmt(NewVType))
return false;
return true;
}
/// Return the fields and properties demanded by the provided instruction.
DemandedFields getDemanded(const MachineInstr &MI, const RISCVSubtarget *ST) {
// This function works in coalesceVSETVLI too. We can still use the value of a
// SEW, VL, or Policy operand even though it might not be the exact value in
// the VL or VTYPE, since we only care about what the instruction originally
// demanded.
// Most instructions don't use any of these subfeilds.
DemandedFields Res;
// Start conservative if registers are used
if (MI.isCall() || MI.isInlineAsm() ||
MI.readsRegister(RISCV::VL, /*TRI=*/nullptr))
Res.demandVL();
if (MI.isCall() || MI.isInlineAsm() ||
MI.readsRegister(RISCV::VTYPE, /*TRI=*/nullptr))
Res.demandVTYPE();
// Start conservative on the unlowered form too
uint64_t TSFlags = MI.getDesc().TSFlags;
if (RISCVII::hasSEWOp(TSFlags)) {
Res.demandVTYPE();
if (RISCVII::hasVLOp(TSFlags))
if (const MachineOperand &VLOp = MI.getOperand(getVLOpNum(MI));
!VLOp.isReg() || !VLOp.isUndef())
Res.demandVL();
// Behavior is independent of mask policy.
if (!RISCVII::usesMaskPolicy(TSFlags))
Res.MaskPolicy = false;
}
// Loads and stores with implicit EEW do not demand SEW or LMUL directly.
// They instead demand the ratio of the two which is used in computing
// EMUL, but which allows us the flexibility to change SEW and LMUL
// provided we don't change the ratio.
// Note: We assume that the instructions initial SEW is the EEW encoded
// in the opcode. This is asserted when constructing the VSETVLIInfo.
if (RISCV::getEEWForLoadStore(MI)) {
Res.SEW = DemandedFields::SEWNone;
Res.LMUL = DemandedFields::LMULNone;
}
// Store instructions don't use the policy fields.
if (RISCVII::hasSEWOp(TSFlags) && MI.getNumExplicitDefs() == 0) {
Res.TailPolicy = false;
Res.MaskPolicy = false;
}
// If this is a mask reg operation, it only cares about VLMAX.
// TODO: Possible extensions to this logic
// * Probably ok if available VLMax is larger than demanded
// * The policy bits can probably be ignored..
if (isMaskRegOp(MI)) {
Res.SEW = DemandedFields::SEWNone;
Res.LMUL = DemandedFields::LMULNone;
}
// For vmv.s.x and vfmv.s.f, there are only two behaviors, VL = 0 and VL > 0.
if (RISCVInstrInfo::isScalarInsertInstr(MI)) {
Res.LMUL = DemandedFields::LMULNone;
Res.SEWLMULRatio = false;
Res.VLAny = false;
// For vmv.s.x and vfmv.s.f, if the passthru is *undefined*, we don't
// need to preserve any other bits and are thus compatible with any larger,
// etype and can disregard policy bits. Warning: It's tempting to try doing
// this for any tail agnostic operation, but we can't as TA requires
// tail lanes to either be the original value or -1. We are writing
// unknown bits to the lanes here.
if (RISCV::hasUndefinedPassthru(MI)) {
if (RISCVInstrInfo::isFloatScalarMoveOrScalarSplatInstr(MI) &&
!ST->hasVInstructionsF64())
Res.SEW = DemandedFields::SEWGreaterThanOrEqualAndLessThan64;
else
Res.SEW = DemandedFields::SEWGreaterThanOrEqual;
Res.TailPolicy = false;
}
}
// vmv.x.s, and vfmv.f.s are unconditional and ignore everything except SEW.
if (RISCVInstrInfo::isScalarExtractInstr(MI)) {
assert(!RISCVII::hasVLOp(TSFlags));
Res.LMUL = DemandedFields::LMULNone;
Res.SEWLMULRatio = false;
Res.TailPolicy = false;
Res.MaskPolicy = false;
}
if (RISCVII::hasVLOp(MI.getDesc().TSFlags)) {
const MachineOperand &VLOp = MI.getOperand(getVLOpNum(MI));
// A slidedown/slideup with an *undefined* passthru can freely clobber
// elements not copied from the source vector (e.g. masked off, tail, or
// slideup's prefix). Notes:
// * We can't modify SEW here since the slide amount is in units of SEW.
// * VL=1 is special only because we have existing support for zero vs
// non-zero VL. We could generalize this if we had a VL > C predicate.
// * The LMUL1 restriction is for machines whose latency may depend on LMUL.
// * As above, this is only legal for tail "undefined" not "agnostic".
// * We avoid increasing vl if the subtarget has +vl-dependent-latency
if (RISCVInstrInfo::isVSlideInstr(MI) && VLOp.isImm() &&
VLOp.getImm() == 1 && RISCV::hasUndefinedPassthru(MI) &&
!ST->hasVLDependentLatency()) {
Res.VLAny = false;
Res.VLZeroness = true;
Res.LMUL = DemandedFields::LMULLessThanOrEqualToM1;
Res.TailPolicy = false;
}
// A tail undefined vmv.v.i/x or vfmv.v.f with VL=1 can be treated in the
// same semantically as vmv.s.x. This is particularly useful since we don't
// have an immediate form of vmv.s.x, and thus frequently use vmv.v.i in
// it's place. Since a splat is non-constant time in LMUL, we do need to be
// careful to not increase the number of active vector registers (unlike for
// vmv.s.x.)
if (RISCVInstrInfo::isScalarSplatInstr(MI) && VLOp.isImm() &&
VLOp.getImm() == 1 && RISCV::hasUndefinedPassthru(MI) &&
!ST->hasVLDependentLatency()) {
Res.LMUL = DemandedFields::LMULLessThanOrEqualToM1;
Res.SEWLMULRatio = false;
Res.VLAny = false;
if (RISCVInstrInfo::isFloatScalarMoveOrScalarSplatInstr(MI) &&
!ST->hasVInstructionsF64())
Res.SEW = DemandedFields::SEWGreaterThanOrEqualAndLessThan64;
else
Res.SEW = DemandedFields::SEWGreaterThanOrEqual;
Res.TailPolicy = false;
}
}
// In §32.16.6, whole vector register moves have a dependency on SEW. At the
// MIR level though we don't encode the element type, and it gives the same
// result whatever the SEW may be.
//
// However it does need valid SEW, i.e. vill must be cleared. The entry to a
// function, calls and inline assembly may all set it, so make sure we clear
// it for whole register copies. Do this by leaving VILL demanded.
if (RISCV::isVectorCopy(ST->getRegisterInfo(), MI)) {
Res.LMUL = DemandedFields::LMULNone;
Res.SEW = DemandedFields::SEWNone;
Res.SEWLMULRatio = false;
Res.TailPolicy = false;
Res.MaskPolicy = false;
}
if (RISCVInstrInfo::isVExtractInstr(MI)) {
assert(!RISCVII::hasVLOp(TSFlags));
// TODO: LMUL can be any larger value (without cost)
Res.TailPolicy = false;
}
Res.AltFmt = RISCVII::getAltFmtType(MI.getDesc().TSFlags) !=
RISCVII::AltFmtType::DontCare;
Res.TWiden = RISCVII::hasTWidenOp(MI.getDesc().TSFlags) ||
RISCVInstrInfo::isXSfmmVectorConfigInstr(MI);
return Res;
}
bool VSETVLIInfo::hasCompatibleVTYPE(const DemandedFields &Used,
const VSETVLIInfo &Require) const {
return areCompatibleVTYPEs(Require.encodeVTYPE(), encodeVTYPE(), Used);
}
// If the AVL is defined by a vsetvli's output vl with the same VLMAX, we can
// replace the AVL operand with the AVL of the defining vsetvli. E.g.
//
// %vl = PseudoVSETVLI %avl:gpr, SEW=32, LMUL=M1
// $x0 = PseudoVSETVLI %vl:gpr, SEW=32, LMUL=M1
// ->
// %vl = PseudoVSETVLI %avl:gpr, SEW=32, LMUL=M1
// $x0 = PseudoVSETVLI %avl:gpr, SEW=32, LMUL=M1
void RISCVVSETVLIInfoAnalysis::forwardVSETVLIAVL(VSETVLIInfo &Info) const {
if (!Info.hasAVLReg())
return;
const MachineInstr *DefMI = Info.getAVLDefMI(LIS);
if (!DefMI || !RISCVInstrInfo::isVectorConfigInstr(*DefMI))
return;
VSETVLIInfo DefInstrInfo = getInfoForVSETVLI(*DefMI);
if (!DefInstrInfo.hasSameVLMAX(Info))
return;
Info.setAVL(DefInstrInfo);
}
// Return a VSETVLIInfo representing the changes made by this VSETVLI or
// VSETIVLI instruction.
VSETVLIInfo
RISCVVSETVLIInfoAnalysis::getInfoForVSETVLI(const MachineInstr &MI) const {
VSETVLIInfo NewInfo;
if (MI.getOpcode() == RISCV::PseudoVSETIVLI) {
NewInfo.setAVLImm(MI.getOperand(1).getImm());
} else if (RISCVInstrInfo::isXSfmmVectorConfigTNInstr(MI)) {
assert(MI.getOpcode() == RISCV::PseudoSF_VSETTNT ||
MI.getOpcode() == RISCV::PseudoSF_VSETTNTX0);
switch (MI.getOpcode()) {
case RISCV::PseudoSF_VSETTNTX0:
NewInfo.setAVLVLMAX();
break;
case RISCV::PseudoSF_VSETTNT:
Register ATNReg = MI.getOperand(1).getReg();
NewInfo.setAVLRegDef(getVNInfoFromReg(ATNReg, MI, LIS), ATNReg);
break;
}
} else {
assert(MI.getOpcode() == RISCV::PseudoVSETVLI ||
MI.getOpcode() == RISCV::PseudoVSETVLIX0);
if (MI.getOpcode() == RISCV::PseudoVSETVLIX0)
NewInfo.setAVLVLMAX();
else if (MI.getOperand(1).isUndef())
// Otherwise use an AVL of 1 to avoid depending on previous vl.
NewInfo.setAVLImm(1);
else {
Register AVLReg = MI.getOperand(1).getReg();
VNInfo *VNI = getVNInfoFromReg(AVLReg, MI, LIS);
NewInfo.setAVLRegDef(VNI, AVLReg);
}
}
NewInfo.setVTYPE(MI.getOperand(2).getImm());
forwardVSETVLIAVL(NewInfo);
return NewInfo;
}
static unsigned computeVLMAX(unsigned VLEN, unsigned SEW,
RISCVVType::VLMUL VLMul) {
auto [LMul, Fractional] = RISCVVType::decodeVLMUL(VLMul);
if (Fractional)
VLEN = VLEN / LMul;
else
VLEN = VLEN * LMul;
return VLEN / SEW;
}
VSETVLIInfo
RISCVVSETVLIInfoAnalysis::computeInfoForInstr(const MachineInstr &MI) const {
VSETVLIInfo InstrInfo;
const uint64_t TSFlags = MI.getDesc().TSFlags;
bool TailAgnostic = true;
bool MaskAgnostic = true;
if (!RISCV::hasUndefinedPassthru(MI)) {
// Start with undisturbed.
TailAgnostic = false;
MaskAgnostic = false;
// If there is a policy operand, use it.
if (RISCVII::hasVecPolicyOp(TSFlags)) {
const MachineOperand &Op = MI.getOperand(getVecPolicyOpNum(MI));
uint64_t Policy = Op.getImm();
assert(Policy <=
(RISCVVType::TAIL_AGNOSTIC | RISCVVType::MASK_AGNOSTIC) &&
"Invalid Policy Value");
TailAgnostic = Policy & RISCVVType::TAIL_AGNOSTIC;
MaskAgnostic = Policy & RISCVVType::MASK_AGNOSTIC;
}
if (!RISCVII::usesMaskPolicy(TSFlags))
MaskAgnostic = true;
}
RISCVVType::VLMUL VLMul = RISCVII::getLMul(TSFlags);
bool AltFmt = RISCVII::getAltFmtType(TSFlags) == RISCVII::AltFmtType::AltFmt;
InstrInfo.setAltFmt(AltFmt);
unsigned Log2SEW = MI.getOperand(getSEWOpNum(MI)).getImm();
// A Log2SEW of 0 is an operation on mask registers only.
unsigned SEW = Log2SEW ? 1 << Log2SEW : 8;
assert(RISCVVType::isValidSEW(SEW) && "Unexpected SEW");
if (RISCVII::hasTWidenOp(TSFlags)) {
const MachineOperand &TWidenOp =
MI.getOperand(MI.getNumExplicitOperands() - 1);
unsigned TWiden = TWidenOp.getImm();
InstrInfo.setAVLVLMAX();
if (RISCVII::hasVLOp(TSFlags)) {
const MachineOperand &TNOp =
MI.getOperand(RISCVII::getTNOpNum(MI.getDesc()));
if (TNOp.getReg().isVirtual())
InstrInfo.setAVLRegDef(getVNInfoFromReg(TNOp.getReg(), MI, LIS),
TNOp.getReg());
}
InstrInfo.setVTYPE(VLMul, SEW, TailAgnostic, MaskAgnostic, AltFmt, TWiden);
return InstrInfo;
}
if (RISCVII::hasVLOp(TSFlags)) {
const MachineOperand &VLOp = MI.getOperand(getVLOpNum(MI));
if (VLOp.isImm()) {
int64_t Imm = VLOp.getImm();
// Convert the VLMax sentintel to X0 register.
if (Imm == RISCV::VLMaxSentinel) {
// If we know the exact VLEN, see if we can use the constant encoding
// for the VLMAX instead. This reduces register pressure slightly.
const unsigned VLMAX = computeVLMAX(ST->getRealMaxVLen(), SEW, VLMul);
if (ST->getRealMinVLen() == ST->getRealMaxVLen() && VLMAX <= 31)
InstrInfo.setAVLImm(VLMAX);
else
InstrInfo.setAVLVLMAX();
} else
InstrInfo.setAVLImm(Imm);
} else if (VLOp.isUndef()) {
// Otherwise use an AVL of 1 to avoid depending on previous vl.
InstrInfo.setAVLImm(1);
} else {
VNInfo *VNI = getVNInfoFromReg(VLOp.getReg(), MI, LIS);
InstrInfo.setAVLRegDef(VNI, VLOp.getReg());
}
} else {
assert(RISCVInstrInfo::isScalarExtractInstr(MI) ||
RISCVInstrInfo::isVExtractInstr(MI));
// Pick a random value for state tracking purposes, will be ignored via
// the demanded fields mechanism
InstrInfo.setAVLImm(1);
}
#ifndef NDEBUG
if (std::optional<unsigned> EEW = RISCV::getEEWForLoadStore(MI)) {
assert(SEW == EEW && "Initial SEW doesn't match expected EEW");
}
#endif
// TODO: Propagate the twiden from previous vtype for potential reuse.
InstrInfo.setVTYPE(VLMul, SEW, TailAgnostic, MaskAgnostic, AltFmt,
/*TWiden*/ 0);
forwardVSETVLIAVL(InstrInfo);
return InstrInfo;
}
} // namespace RISCV
} // namespace llvm