-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[RISCV] Handle recurrences in RISCVVLOptimizer #151285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
lukel97
wants to merge
4
commits into
llvm:main
Choose a base branch
from
lukel97:vloptimizer/dataflow-analysis
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Currently we have a switch statement that checks if a vector instruction may read elements past VL. However it currently doesn't account for instructions in vendor extensions. Handling all possible vendor instructions will result in quite a lot of opcodes being added, so I've created a new TSFlag that we can declare in TableGen, and added it to the existing instruction definitions. I've tried to be conservative as possible here: All SiFive vendor vector instructions should be covered by the flag, as well as all of XRivosVizip, and ri.vextract from XRivosVisni. For now this should be NFC because coincidentally, these instructions aren't handled in getOperandInfo, so RISCVVLOptimizer should currently avoid touching them despite them being liberally handled in getMinimumVLForUser. However in an upcoming patch we'll need to also bail in getMinimumVLForUser, so this prepares for it.
@llvm/pr-subscribers-backend-risc-v Author: Luke Lau (lukel97) Changes
Patch is 25.36 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151285.diff 11 Files Affected:
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index bddea43fbb09c..9d26fc01bf379 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -139,6 +139,9 @@ enum {
// 3 -> SEW * 4
DestEEWShift = ElementsDependOnMaskShift + 1,
DestEEWMask = 3ULL << DestEEWShift,
+
+ ReadsPastVLShift = DestEEWShift + 2,
+ ReadsPastVLMask = 1ULL << ReadsPastVLShift,
};
// Helper functions to read TSFlags.
@@ -195,6 +198,12 @@ static inline bool elementsDependOnMask(uint64_t TSFlags) {
return TSFlags & ElementsDependOnMaskMask;
}
+/// \returns true if the instruction may read elements past VL, e.g.
+/// vslidedown/vrgather
+static inline bool readsPastVL(uint64_t TSFlags) {
+ return TSFlags & ReadsPastVLMask;
+}
+
static inline unsigned getVLOpNum(const MCInstrDesc &Desc) {
const uint64_t TSFlags = Desc.TSFlags;
// This method is only called if we expect to have a VL operand, and all
diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
index d9c6101478064..878a0ec938919 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
@@ -261,6 +261,12 @@ class RVInstCommon<dag outs, dag ins, string opcodestr, string argstr,
// Indicates the EEW of a vector instruction's destination operand.
EEW DestEEW = EEWSEWx1;
let TSFlags{25-24} = DestEEW.Value;
+
+ // Some vector instructions like vslidedown/vrgather will read elements past
+ // VL, and should be marked to make sure RISCVVLOptimizer doesn't reduce its
+ // operands' VLs.
+ bit ReadsPastVL = 0;
+ let TSFlags{26} = ReadsPastVL;
}
class RVInst<dag outs, dag ins, string opcodestr, string argstr,
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoV.td b/llvm/lib/Target/RISCV/RISCVInstrInfoV.td
index 33c713833d8b9..cebab2112d02d 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoV.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoV.td
@@ -1703,8 +1703,9 @@ let Constraints = "@earlyclobber $vd", RVVConstraint = SlideUp in {
defm VSLIDEUP_V : VSLD_IV_X_I<"vslideup", 0b001110, /*slidesUp=*/true>;
defm VSLIDE1UP_V : VSLD1_MV_X<"vslide1up", 0b001110>;
} // Constraints = "@earlyclobber $vd", RVVConstraint = SlideUp
+let ReadsPastVL = 1 in
defm VSLIDEDOWN_V : VSLD_IV_X_I<"vslidedown", 0b001111, /*slidesUp=*/false>;
-let ElementsDependOn = EltDepsVL in
+let ElementsDependOn = EltDepsVL, ReadsPastVL = 1 in
defm VSLIDE1DOWN_V : VSLD1_MV_X<"vslide1down", 0b001111>;
} // Predicates = [HasVInstructions]
@@ -1712,19 +1713,19 @@ let Predicates = [HasVInstructionsAnyF] in {
let Constraints = "@earlyclobber $vd", RVVConstraint = SlideUp in {
defm VFSLIDE1UP_V : VSLD1_FV_F<"vfslide1up", 0b001110>;
} // Constraints = "@earlyclobber $vd", RVVConstraint = SlideUp
-let ElementsDependOn = EltDepsVL in
+let ElementsDependOn = EltDepsVL, ReadsPastVL = 1 in
defm VFSLIDE1DOWN_V : VSLD1_FV_F<"vfslide1down", 0b001111>;
} // Predicates = [HasVInstructionsAnyF]
let Predicates = [HasVInstructions] in {
// Vector Register Gather Instruction
-let Constraints = "@earlyclobber $vd", RVVConstraint = Vrgather in {
+let Constraints = "@earlyclobber $vd", RVVConstraint = Vrgather, ReadsPastVL = 1 in {
defm VRGATHER_V : VGTR_IV_V_X_I<"vrgather", 0b001100>;
def VRGATHEREI16_VV : VALUVV<0b001110, OPIVV, "vrgatherei16.vv">,
SchedBinaryMC<"WriteVRGatherEI16VV",
"ReadVRGatherEI16VV_data",
"ReadVRGatherEI16VV_index">;
-} // Constraints = "@earlyclobber $vd", RVVConstraint = Vrgather
+} // Constraints = "@earlyclobber $vd", RVVConstraint = Vrgather, ReadsPastVL = 1
// Vector Compress Instruction
let Constraints = "@earlyclobber $vd", RVVConstraint = Vcompress, ElementsDependOn = EltDepsVLMask in {
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXRivos.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXRivos.td
index ebcf079f300b3..3a6ce3ce1d469 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXRivos.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXRivos.td
@@ -58,7 +58,7 @@ class CustomRivosXVI<bits<6> funct6, RISCVVFormat opv, dag outs, dag ins,
let Predicates = [HasVendorXRivosVizip], DecoderNamespace = "XRivos",
Constraints = "@earlyclobber $vd", RVVConstraint = Vrgather,
- Inst<6-0> = OPC_CUSTOM_2.Value in {
+ Inst<6-0> = OPC_CUSTOM_2.Value, ReadsPastVL = 1 in {
defm RI_VZIPEVEN_V : VALU_IV_V<"ri.vzipeven", 0b001100>;
defm RI_VZIPODD_V : VALU_IV_V<"ri.vzipodd", 0b011100>;
defm RI_VZIP2A_V : VALU_IV_V<"ri.vzip2a", 0b000100>;
@@ -126,6 +126,7 @@ def RI_VINSERT : CustomRivosVXI<0b010000, OPMVX, (outs VR:$vd_wb),
(ins VR:$vd, GPR:$rs1, uimm5:$imm),
"ri.vinsert.v.x", "$vd, $rs1, $imm">;
+let ReadsPastVL = 1 in
def RI_VEXTRACT : CustomRivosXVI<0b010111, OPMVV, (outs GPR:$rd),
(ins VR:$vs2, uimm5:$imm),
"ri.vextract.x.v", "$rd, $vs2, $imm">;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXSf.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXSf.td
index a47dfe363c21e..b546339ce99e2 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXSf.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXSf.td
@@ -74,6 +74,7 @@ class RVInstVCCustom2<bits<4> funct6_hi4, bits<3> funct3, dag outs, dag ins,
let Uses = [VL, VTYPE];
let RVVConstraint = NoConstraint;
let ElementsDependOn = EltDepsVLMask;
+ let ReadsPastVL = 1;
}
class RVInstVCFCustom2<bits<4> funct6_hi4, bits<3> funct3, dag outs, dag ins,
@@ -98,6 +99,7 @@ class RVInstVCFCustom2<bits<4> funct6_hi4, bits<3> funct3, dag outs, dag ins,
let Uses = [VL, VTYPE];
let RVVConstraint = NoConstraint;
let ElementsDependOn = EltDepsVLMask;
+ let ReadsPastVL = 1;
}
class VCIXInfo<string suffix, VCIXType type, DAGOperand TyRd,
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXSfmm.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXSfmm.td
index 66cb2d53da960..a5ee701386b6d 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXSfmm.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXSfmm.td
@@ -65,6 +65,7 @@ class SFInstTileMemOp<dag outs, dag ins, bits<3> nf, RISCVOpcode opcode,
let Inst{6-0} = opcode.Value;
let Uses = [VTYPE, VL];
+ let ReadsPastVL = 1;
}
let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in
@@ -94,6 +95,7 @@ class SFInstTileMoveOp<bits<6> funct6, dag outs, dag ins, string opcodestr,
let Inst{6-0} = OPC_OP_V.Value;
let Uses = [VTYPE, VL];
+ let ReadsPastVL = 1;
}
let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
@@ -113,6 +115,7 @@ class SFInstMatmulF<dag outs, dag ins, string opcodestr, string argstr>
let Inst{6-0} = OPC_OP_VE.Value;
let Uses = [VTYPE, VL];
+ let ReadsPastVL = 1;
}
let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
@@ -135,6 +138,7 @@ class SFInstMatmulF8<bit a, bit b, dag outs, dag ins,
let Inst{6-0} = OPC_OP_VE.Value;
let Uses = [VTYPE, VL];
+ let ReadsPastVL = 1;
}
@@ -167,6 +171,7 @@ class SFInstMatmulI8<bit funct6_1, bit a, bit b, dag outs, dag ins,
let Inst{6-0} = OPC_OP_VE.Value;
let Uses = [VTYPE, VL];
+ let ReadsPastVL = 1;
}
class I8Encode<bit encoding, string name> {
diff --git a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
index c9464515d2e56..40af9b04c97b6 100644
--- a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
@@ -30,6 +30,27 @@ using namespace llvm;
namespace {
+/// Wrapper around MachineOperand that defaults to immediate 0.
+struct DemandedVL {
+ MachineOperand VL;
+ DemandedVL() : VL(MachineOperand::CreateImm(0)) {}
+ DemandedVL(MachineOperand VL) : VL(VL) {}
+ static DemandedVL vlmax() {
+ return DemandedVL(MachineOperand::CreateImm(RISCV::VLMaxSentinel));
+ }
+ bool operator!=(const DemandedVL &Other) const {
+ return !VL.isIdenticalTo(Other.VL);
+ }
+};
+
+static DemandedVL max(const DemandedVL &LHS, const DemandedVL &RHS) {
+ if (RISCV::isVLKnownLE(LHS.VL, RHS.VL))
+ return RHS;
+ if (RISCV::isVLKnownLE(RHS.VL, LHS.VL))
+ return LHS;
+ return DemandedVL::vlmax();
+}
+
class RISCVVLOptimizer : public MachineFunctionPass {
const MachineRegisterInfo *MRI;
const MachineDominatorTree *MDT;
@@ -51,17 +72,26 @@ class RISCVVLOptimizer : public MachineFunctionPass {
StringRef getPassName() const override { return PASS_NAME; }
private:
- std::optional<MachineOperand>
- getMinimumVLForUser(const MachineOperand &UserOp) const;
- /// Returns the largest common VL MachineOperand that may be used to optimize
- /// MI. Returns std::nullopt if it failed to find a suitable VL.
- std::optional<MachineOperand> checkUsers(const MachineInstr &MI) const;
+ DemandedVL getMinimumVLForUser(const MachineOperand &UserOp) const;
+ /// Returns true if the users of \p MI have compatible EEWs and SEWs.
+ bool checkUsers(const MachineInstr &MI) const;
bool tryReduceVL(MachineInstr &MI) const;
bool isCandidate(const MachineInstr &MI) const;
+ void transfer(const MachineInstr &MI);
+
+ /// Returns all uses of vector virtual registers.
+ auto vector_uses(const MachineInstr &MI) const {
+ auto Pred = [this](const MachineOperand &MO) -> bool {
+ return MO.isReg() && MO.getReg().isVirtual() &&
+ RISCVRegisterInfo::isRVVRegClass(MRI->getRegClass(MO.getReg()));
+ };
+ return make_filter_range(MI.uses(), Pred);
+ }
/// For a given instruction, records what elements of it are demanded by
/// downstream users.
- DenseMap<const MachineInstr *, std::optional<MachineOperand>> DemandedVLs;
+ DenseMap<const MachineInstr *, DemandedVL> DemandedVLs;
+ SetVector<const MachineInstr *> Worklist;
};
/// Represents the EMUL and EEW of a MachineOperand.
@@ -787,6 +817,9 @@ getOperandInfo(const MachineOperand &MO, const MachineRegisterInfo *MRI) {
/// white-list approach simplifies this optimization for instructions that may
/// have more complex semantics with relation to how it uses VL.
static bool isSupportedInstr(const MachineInstr &MI) {
+ if (MI.isPHI() || MI.isFullCopy())
+ return true;
+
const RISCVVPseudosTable::PseudoInfo *RVV =
RISCVVPseudosTable::getPseudoInfo(MI.getOpcode());
@@ -1210,34 +1243,6 @@ static bool isVectorOpUsedAsScalarOp(const MachineOperand &MO) {
}
}
-/// Return true if MI may read elements past VL.
-static bool mayReadPastVL(const MachineInstr &MI) {
- const RISCVVPseudosTable::PseudoInfo *RVV =
- RISCVVPseudosTable::getPseudoInfo(MI.getOpcode());
- if (!RVV)
- return true;
-
- switch (RVV->BaseInstr) {
- // vslidedown instructions may read elements past VL. They are handled
- // according to current tail policy.
- case RISCV::VSLIDEDOWN_VI:
- case RISCV::VSLIDEDOWN_VX:
- case RISCV::VSLIDE1DOWN_VX:
- case RISCV::VFSLIDE1DOWN_VF:
-
- // vrgather instructions may read the source vector at any index < VLMAX,
- // regardless of VL.
- case RISCV::VRGATHER_VI:
- case RISCV::VRGATHER_VV:
- case RISCV::VRGATHER_VX:
- case RISCV::VRGATHEREI16_VV:
- return true;
-
- default:
- return false;
- }
-}
-
bool RISCVVLOptimizer::isCandidate(const MachineInstr &MI) const {
const MCInstrDesc &Desc = MI.getDesc();
if (!RISCVII::hasVLOp(Desc.TSFlags) || !RISCVII::hasSEWOp(Desc.TSFlags))
@@ -1287,20 +1292,24 @@ bool RISCVVLOptimizer::isCandidate(const MachineInstr &MI) const {
return true;
}
-std::optional<MachineOperand>
+DemandedVL
RISCVVLOptimizer::getMinimumVLForUser(const MachineOperand &UserOp) const {
const MachineInstr &UserMI = *UserOp.getParent();
const MCInstrDesc &Desc = UserMI.getDesc();
+ if (UserMI.isPHI() || UserMI.isFullCopy())
+ return DemandedVLs.lookup(&UserMI);
+
if (!RISCVII::hasVLOp(Desc.TSFlags) || !RISCVII::hasSEWOp(Desc.TSFlags)) {
LLVM_DEBUG(dbgs() << " Abort due to lack of VL, assume that"
" use VLMAX\n");
- return std::nullopt;
+ return DemandedVL::vlmax();
}
- if (mayReadPastVL(UserMI)) {
+ if (RISCVII::readsPastVL(
+ TII->get(RISCV::getRVVMCOpcode(UserMI.getOpcode())).TSFlags)) {
LLVM_DEBUG(dbgs() << " Abort because used by unsafe instruction\n");
- return std::nullopt;
+ return DemandedVL::vlmax();
}
unsigned VLOpNum = RISCVII::getVLOpNum(Desc);
@@ -1314,11 +1323,10 @@ RISCVVLOptimizer::getMinimumVLForUser(const MachineOperand &UserOp) const {
if (UserOp.isTied()) {
assert(UserOp.getOperandNo() == UserMI.getNumExplicitDefs() &&
RISCVII::isFirstDefTiedToFirstUse(UserMI.getDesc()));
- auto DemandedVL = DemandedVLs.lookup(&UserMI);
- if (!DemandedVL || !RISCV::isVLKnownLE(*DemandedVL, VLOp)) {
+ if (!RISCV::isVLKnownLE(DemandedVLs.lookup(&UserMI).VL, VLOp)) {
LLVM_DEBUG(dbgs() << " Abort because user is passthru in "
"instruction with demanded tail\n");
- return std::nullopt;
+ return DemandedVL::vlmax();
}
}
@@ -1331,18 +1339,16 @@ RISCVVLOptimizer::getMinimumVLForUser(const MachineOperand &UserOp) const {
// If we know the demanded VL of UserMI, then we can reduce the VL it
// requires.
- if (auto DemandedVL = DemandedVLs.lookup(&UserMI)) {
- assert(isCandidate(UserMI));
- if (RISCV::isVLKnownLE(*DemandedVL, VLOp))
- return DemandedVL;
- }
+ if (RISCV::isVLKnownLE(DemandedVLs.lookup(&UserMI).VL, VLOp))
+ return DemandedVLs.lookup(&UserMI);
return VLOp;
}
-std::optional<MachineOperand>
-RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
- std::optional<MachineOperand> CommonVL;
+bool RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
+ if (MI.isPHI() || MI.isFullCopy())
+ return true;
+
SmallSetVector<MachineOperand *, 8> Worklist;
SmallPtrSet<const MachineInstr *, 4> PHISeen;
for (auto &UserOp : MRI->use_operands(MI.getOperand(0).getReg()))
@@ -1370,23 +1376,9 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
continue;
}
- auto VLOp = getMinimumVLForUser(UserOp);
- if (!VLOp)
- return std::nullopt;
-
- // Use the largest VL among all the users. If we cannot determine this
- // statically, then we cannot optimize the VL.
- if (!CommonVL || RISCV::isVLKnownLE(*CommonVL, *VLOp)) {
- CommonVL = *VLOp;
- LLVM_DEBUG(dbgs() << " User VL is: " << VLOp << "\n");
- } else if (!RISCV::isVLKnownLE(*VLOp, *CommonVL)) {
- LLVM_DEBUG(dbgs() << " Abort because cannot determine a common VL\n");
- return std::nullopt;
- }
-
if (!RISCVII::hasSEWOp(UserMI.getDesc().TSFlags)) {
LLVM_DEBUG(dbgs() << " Abort due to lack of SEW operand\n");
- return std::nullopt;
+ return false;
}
std::optional<OperandInfo> ConsumerInfo = getOperandInfo(UserOp, MRI);
@@ -1396,7 +1388,7 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
LLVM_DEBUG(dbgs() << " Abort due to unknown operand information.\n");
LLVM_DEBUG(dbgs() << " ConsumerInfo is: " << ConsumerInfo << "\n");
LLVM_DEBUG(dbgs() << " ProducerInfo is: " << ProducerInfo << "\n");
- return std::nullopt;
+ return false;
}
// If the operand is used as a scalar operand, then the EEW must be
@@ -1411,11 +1403,11 @@ RISCVVLOptimizer::checkUsers(const MachineInstr &MI) const {
<< " Abort due to incompatible information for EMUL or EEW.\n");
LLVM_DEBUG(dbgs() << " ConsumerInfo is: " << ConsumerInfo << "\n");
LLVM_DEBUG(dbgs() << " ProducerInfo is: " << ProducerInfo << "\n");
- return std::nullopt;
+ return false;
}
}
- return CommonVL;
+ return true;
}
bool RISCVVLOptimizer::tryReduceVL(MachineInstr &MI) const {
@@ -1431,9 +1423,7 @@ bool RISCVVLOptimizer::tryReduceVL(MachineInstr &MI) const {
return false;
}
- auto CommonVL = DemandedVLs.lookup(&MI);
- if (!CommonVL)
- return false;
+ auto *CommonVL = &DemandedVLs.at(&MI).VL;
assert((CommonVL->isImm() || CommonVL->getReg().isVirtual()) &&
"Expected VL to be an Imm or virtual Reg");
@@ -1468,6 +1458,24 @@ bool RISCVVLOptimizer::tryReduceVL(MachineInstr &MI) const {
return true;
}
+static bool isPhysical(const MachineOperand &MO) {
+ return MO.isReg() && MO.getReg().isPhysical();
+}
+
+/// Look through \p MI's operands and propagate what it demands to its uses.
+void RISCVVLOptimizer::transfer(const MachineInstr &MI) {
+ if (!isSupportedInstr(MI) || !checkUsers(MI) || any_of(MI.defs(), isPhysical))
+ DemandedVLs[&MI] = DemandedVL::vlmax();
+
+ for (const MachineOperand &MO : vector_uses(MI)) {
+ const MachineInstr *Def = MRI->getVRegDef(MO.getReg());
+ DemandedVL Prev = DemandedVLs[Def];
+ DemandedVLs[Def] = max(DemandedVLs[Def], getMinimumVLForUser(MO));
+ if (DemandedVLs[Def] != Prev)
+ Worklist.insert(Def);
+ }
+}
+
bool RISCVVLOptimizer::runOnMachineFunction(MachineFunction &MF) {
if (skipFunction(MF.getFunction()))
return false;
@@ -1484,14 +1492,17 @@ bool RISCVVLOptimizer::runOnMachineFunction(MachineFunction &MF) {
assert(DemandedVLs.empty());
// For each instruction that defines a vector, compute what VL its
- // downstream users demand.
+ // upstream uses demand.
for (MachineBasicBlock *MBB : post_order(&MF)) {
assert(MDT->isReachableFromEntry(MBB));
- for (MachineInstr &MI : reverse(*MBB)) {
- if (!isCandidate(MI))
- continue;
- DemandedVLs.insert({&MI, checkUsers(MI)});
- }
+ for (MachineInstr &MI : reverse(*MBB))
+ Worklist.insert(&MI);
+ }
+
+ while (!Worklist.empty()) {
+ const MachineInstr *MI = Worklist.front();
+ Worklist.remove(MI);
+ transfer(*MI);
}
// Then go through and see if we can reduce the VL of any instructions to
diff --git a/llvm/test/CodeGen/RISCV/rvv/reproducer-pr146855.ll b/llvm/test/CodeGen/RISCV/rvv/reproducer-pr146855.ll
index cca00bf58063d..2d64defe8c7b1 100644
--- a/llvm/test/CodeGen/RISCV/rvv/reproducer-pr146855.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/reproducer-pr146855.ll
@@ -6,7 +6,7 @@ target triple = "riscv64-unknown-linux-gnu"
define i32 @_ZN4Mesh12rezone_countESt6vectorIiSaIiEERiS3_(<vscale x 4 x i32> %wide.load, <vscale x 4 x i1> %0, <vscale x 4 x i1> %1, <vscale x 4 x i1> %2, <vscale x 4 x i1> %3) #0 {
; CHECK-LABEL: _ZN4Mesh12rezone_countESt6vectorIiSaIiEERiS3_:
; CHECK: # %bb.0: # %entry
-; CHECK-NEXT: vsetvli a0, zero, e32, m2, ta, ma
+; CHECK-NEXT: vsetivli zero, 0, e32, m2, ta, ma
; CHECK-NEXT: vmv1r.v v8, v0
; CHECK-NEXT: li a0, 0
; CHECK-NEXT: vmv.v.i v10, 0
@@ -14,7 +14,7 @@ define i32 @_ZN4Mesh12rezone_countESt6vectorIiSaIiEERiS3_(<vscale x 4 x i32> %wi
; CHECK-NEXT: vmv.v.i v14, 0
; CHECK-NEXT: .LBB0_1: # %vector.body
; CHECK-NEXT: # =>This Inner Loop Header: Depth=1
-; CHECK-NEXT: vsetvli a1, zero, e32, m2, ta, mu
+; CHECK-NEXT: vsetivli zero, 0, e32, m2, ta, mu
; CHECK-NEXT: vmv1r.v v0, v8
; CHECK-NEXT: slli a0, a0, 2
; CHECK-NEXT: vmv2r.v v16, v10
diff --git a/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll b/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
index cd282c265ae47..ecea4efa4e768 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vl-opt.ll
@@ -198,3 +198,55 @@ define void @fadd_fcmp_select_copy(<vscale x 4 x float> %v, <vscale x 4 x i1> %c
call void @llvm.riscv.vsm(<vscale x 4 x i1> %select, ptr %p, iXLen %vl)
ret void
}
+
+define void @recurrence(<vscale x 4 x i32> %v, ptr %p, iXLen %n, iXLen %vl) {
+; CHECK-LABEL: recurrence:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: vsetvli zero, a2, e32, m2, ta, ma
+; CHECK-NEXT: vmv.v.i v10, 0
+; CHECK-NEXT: .LBB13_1: # %loop
+; CHECK-NEXT: # =>This Inner Loop Header: Depth=1
+; CHECK-NEXT: addi a1, a1, -1
+; CHECK-NEXT: vadd.vv v10, v10, v8
+; CHECK-NEXT: bnez a1, .LBB13_1
+; CHECK-NEXT: # %bb.2: # %exit
+; CHECK-NEXT: vse32.v v10, (a0)
+; CHECK-NEXT: ret
+entry:
+ br label %loop
+loop:
+ %iv = phi iXLen [ 0, %entry ], [ %iv.next, %loop ]
+ %phi = phi <vscale x 4 x i32> [ zeroinitializer, %entry ], [ %x, %loop ]
+ %x = add <vscale x 4 x i32> %phi, %v
+ %iv.next = add iXLen %iv, 1
+ %done = icmp eq iXLen %iv.next, %n
+ br i1 %done, label %exit, label %loop
+exit:
+ call void @llvm.riscv.vse(<vscale x 4 x i32>...
[truncated]
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Stacked on #149704
After #144666 we now support vectorizing loops with induction variables with EVL tail folding. The induction updates don't use VP intrinsics to avoid VL toggles but instead rely on RISCVVLOptimizer. However the RISCVVLOptimizer can't reason about cycles or recurrences today, which means we are left with a VL toggle to VLMAX:
This patch teaches RISCVVLOptimizer to reason about recurrences so we can remove the VLMAX toggle:
With this we remove a significant number of VL toggles and vsetvli instructions across llvm-test-suite and SPEC CPU 2017 with tail folding enabled, since it affects every loop with an induction variable.
This builds upon the work in #124530 where we started computing what VL each instruction demanded, and generalizes it to an optimistic sparse dataflow analysis:
Some implementation details:
isSupportedInstr
) or copies to physical registers (they fail theany_of(MI.defs(), isPhysical)
check)getMinimumVLForUser
andcheckUsers
.getMinimumVLForUser
now returns how many lanes of an operand are read by an instruction, whilstcheckUsers
checks that an instruction and its users have compatible EEW/EMULs.DemandedVL
struct was added so that we have a default constructor of 0 forDenseMap<const MachineInstr *, DemandedVL> DemandedVLs
, so we don't need to check if a key exists when looking things up.There was no measurable compile time impact on llvm-test-suite or SPEC CPU 2017, and the analysis is guaranteed to terminate.
Formally the set of possible states for$$S_1 \le S_2 = \forall x \in \hbox{defs}, S_1[x] \le S_2[x]$$ . The transfer function is monotonic with respect to this ordering (we only ever increase a demanded VL because we take the maximum):
DemandedVLs
forms a semilattice ordered byDemandedVLs is a finite lattice, where the height is determined by the number of distinct VL values in the program (which in practice is usually just one, a call to
@llvm.experimental.get.vector.length
), so either the analysis must reach a fixpoint or reach the maximum, i.e. every instruction demands VLMAX.There are more details in this EuroLLVM talk.
The proof of monotonicity has also been mechanised in Lean