Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3b0709e
[RISCV][VLOPT] Add support for checkUsers when UserMI is an reduction
michaelmaitland Dec 18, 2024
3acf8c4
fixup! add more incompat tests
michaelmaitland Dec 18, 2024
27de222
fixup! do not else after continue/break
michaelmaitland Dec 18, 2024
dac7acf
fixup! rerun test checks
michaelmaitland Dec 18, 2024
3fc5297
fixup! move continue
michaelmaitland Dec 18, 2024
fcf4d81
fixup! VL operand is never x0
michaelmaitland Dec 18, 2024
6a27058
fixup! fix typo
michaelmaitland Dec 18, 2024
f8ce58c
fixup! be less conservative
michaelmaitland Dec 23, 2024
f4dc6b3
fixup! fix tests after rebase:
michaelmaitland Dec 23, 2024
68cd004
fixup! add test case where reduction has VL=0
michaelmaitland Dec 23, 2024
ab12f92
fixup! add instructions to isVectorOpUsedAsScalarOp
michaelmaitland Dec 23, 2024
4656b45
fixup! fix operand number and add special case when no vlop
michaelmaitland Dec 24, 2024
4e463d7
fixup! update test
michaelmaitland Dec 30, 2024
b8c3411
fixup! improve how we assign CommonVL
michaelmaitland Dec 31, 2024
e6f2468
fixup! refactor to address preames comments
michaelmaitland Jan 2, 2025
5232e6f
fixup! fix comment
michaelmaitland Jan 3, 2025
8d50a38
fixup! fix typo
michaelmaitland Jan 3, 2025
61e681c
fixup! respond to review
michaelmaitland Jan 3, 2025
595a709
fixup! move VLOp check
michaelmaitland Jan 5, 2025
fe55f64
fixup! update tests
michaelmaitland Jan 5, 2025
4666a7f
fixup! respond to review
michaelmaitland Jan 5, 2025
f8c54d4
fixup! simplify getMinimumVLForUser
michaelmaitland Jan 7, 2025
85f49a5
fixup! update comment
michaelmaitland Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 110 additions & 50 deletions llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ class RISCVVLOptimizer : public MachineFunctionPass {
StringRef getPassName() const override { return PASS_NAME; }

private:
bool checkUsers(const MachineOperand *&CommonVL, MachineInstr &MI);
std::optional<MachineOperand> getVLForUser(MachineOperand &UserOp);
/// 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(MachineInstr &MI);
bool tryReduceVL(MachineInstr &MI);
bool isCandidate(const MachineInstr &MI) const;
};
Expand Down Expand Up @@ -95,6 +98,8 @@ struct OperandInfo {
OperandInfo(std::pair<unsigned, bool> EMUL, unsigned Log2EEW)
: S(State::Known), EMUL(EMUL), Log2EEW(Log2EEW) {}

OperandInfo(unsigned Log2EEW) : S(State::Known), Log2EEW(Log2EEW) {}

OperandInfo() : S(State::Unknown) {}

bool isUnknown() const { return S == State::Unknown; }
Expand All @@ -107,6 +112,11 @@ struct OperandInfo {
A.EMUL->second == B.EMUL->second;
}

static bool EEWAreEqual(const OperandInfo &A, const OperandInfo &B) {
assert(A.isKnown() && B.isKnown() && "Both operands must be known");
return A.Log2EEW == B.Log2EEW;
}

void print(raw_ostream &OS) const {
if (isUnknown()) {
OS << "Unknown";
Expand Down Expand Up @@ -716,6 +726,23 @@ static OperandInfo getOperandInfo(const MachineOperand &MO,
return OperandInfo(MIVLMul, MILog2SEW);
}

// Vector Reduction Operations
// Vector Single-Width Integer Reduction Instructions
// The Dest and VS1 only read element 0 of the vector register. Return just
// the EEW for these. VS2 has EEW=SEW and EMUL=LMUL.
case RISCV::VREDAND_VS:
case RISCV::VREDMAX_VS:
case RISCV::VREDMAXU_VS:
case RISCV::VREDMIN_VS:
case RISCV::VREDMINU_VS:
case RISCV::VREDOR_VS:
case RISCV::VREDSUM_VS:
case RISCV::VREDXOR_VS: {
if (MO.getOperandNo() == 2)
return OperandInfo(MIVLMul, MILog2SEW);
return OperandInfo(MILog2SEW);
}

default:
return {};
}
Expand Down Expand Up @@ -1028,79 +1055,113 @@ bool RISCVVLOptimizer::isCandidate(const MachineInstr &MI) const {
return true;
}

bool RISCVVLOptimizer::checkUsers(const MachineOperand *&CommonVL,
MachineInstr &MI) {
std::optional<MachineOperand>
RISCVVLOptimizer::getVLForUser(MachineOperand &UserOp) {
const MachineInstr &UserMI = *UserOp.getParent();
const MCInstrDesc &Desc = UserMI.getDesc();

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;
}

// Instructions like reductions may use a vector register as a scalar
// register. In this case, we should treat it like a scalar register which
// does not impact the decision on whether to optimize VL. But if there is
// another user of MI and it may have VL=0, we need to be sure not to reduce
// the VL of MI to zero when the VLOp of UserOp may be non-zero. The most
// we can reduce it to is one.
if (isVectorOpUsedAsScalarOp(UserOp)) {
[[maybe_unused]] Register R = UserOp.getReg();
[[maybe_unused]] const TargetRegisterClass *RC = MRI->getRegClass(R);
assert(RISCV::VRRegClass.hasSubClassEq(RC) &&
"Expect LMUL 1 register class for vector as scalar operands!");
LLVM_DEBUG(dbgs() << " Used this operand as a scalar operand\n");

unsigned VLOpNum = RISCVII::getVLOpNum(Desc);
const MachineOperand &VLOp = UserMI.getOperand(VLOpNum);
if (VLOp.isReg() || (VLOp.isImm() && VLOp.getImm() != 0))
return MachineOperand::CreateImm(1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't know if a register is non-zero.

Which makes me wonder do we need to check the user's VL? I think we can return CreateImm(1) every time even if the user has a VL of 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't know if a register is non-zero.

That is true, but if it is a register, then we need to conservatively treat it that it might, which is why we return 1. If we just returned 1 if it was a non-zero immediate, then I think that is a missed optimization opportunity.

I think what we should really be doing here is returning VLOp in the else branch of this check. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually why can't we always just return VLOp for the scalar operand of a reduction?

If VLOp is non-zero then the source's VL won't get reduced to anything below one.

If VLOp is zero then the user is a no-op so it doesn't matter what the source's VL is?

Copy link
Contributor Author

@michaelmaitland michaelmaitland Jan 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imagine you have the following:
%v = VADD_VV ...
%s = VREDSUM w/ %v as scalar source
%dead = VADD_VV %v, %v w/ VL=0

When scanning the users of %v, we will decide that the correct VL for %v is 0 (or a register which might be zero), and reduce it below the minimum VL=1 required by the reduction. To fix this, we need to treat the CommonVL for the scalar operand case as being VL=1.

If VLOp is zero then the user is a no-op so it doesn't matter what the source's VL is?

I don't think this is true in this case? We need to keep it at 1.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When scanning the users of %v, we will decide that the correct VL for %v is 0 (or a register which might be zero)

IIUC though the only time the VL would be reduced to a register that might be zero is if the VREDSUM also uses the same register for its VL, which would also have to be zero?

Since I thought the RISCV::isVLKnownLE(*CommonVL, *VLOp) check guarantees that we will never reduce the VL below anything we return in getMinimumVLForUser.

We may be able to reduce it further to 1.

Agreed, I think we can return either 1 or VLOp. I just think that we don't need to check the value of VLOp here for correctness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may be able to reduce it further to 1.

I edited my previous comment to say:

We need to keep it at 1.

I think the code as written makes it so we don't have to worry whether your following statement is true, and it comes at very little extra cost to do the check:

IIUC though the only time the VL would be reduced to a register that might be zero is if the VREDSUM also uses the same register for its VL, which would also have to be zero?

I do have MIR tests in this PR where I don't think this assumption holds.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm missing something here. We have two claims in the comments.

we need to be sure not to reduce the VL of MI to zero when the VLOp of UserOp may be non-zero

This implies that we may reduce MI's VL to something smaller than a user's VL.

But meanwhile in checkUsers:

Use the largest VL among all the users. If we cannot determine this statically, then we cannot optimize the VL.

This implies that we never reduce MI's VL to something smaller than a user's VL.

My understanding is that checkUsers is correct here, so we will never reduce MI's VL to zero if a user has a non-zero VL.

And if we remove the isVectorOpUsedAsScalarOp check, the MIR test case is still correct:

diff --git a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
index 0b437e63fa3d..860e56fba839 100644
--- a/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVVLOptimizer.cpp
@@ -1066,26 +1066,6 @@ RISCVVLOptimizer::getMinimumVLForUser(MachineOperand &UserOp) {
     return std::nullopt;
   }
 
-  // Instructions like reductions may use a vector register as a scalar
-  // register. In this case, we should treat it like a scalar register which
-  // does not impact the decision on whether to optimize VL. But if there is
-  // another user of MI and it may have VL=0, we need to be sure not to reduce
-  // the VL of MI to zero when the VLOp of UserOp may be non-zero. The most
-  // we can reduce it to is one.
-  if (isVectorOpUsedAsScalarOp(UserOp)) {
-    [[maybe_unused]] Register R = UserOp.getReg();
-    [[maybe_unused]] const TargetRegisterClass *RC = MRI->getRegClass(R);
-    assert(RISCV::VRRegClass.hasSubClassEq(RC) &&
-           "Expect LMUL 1 register class for vector as scalar operands!");
-    LLVM_DEBUG(dbgs() << "    Used this operand as a scalar operand\n");
-
-    unsigned VLOpNum = RISCVII::getVLOpNum(Desc);
-    const MachineOperand &VLOp = UserMI.getOperand(VLOpNum);
-    return VLOp.isReg() || (VLOp.isImm() && VLOp.getImm() != 0)
-               ? MachineOperand::CreateImm(1)
-               : VLOp;
-  }
-
   unsigned VLOpNum = RISCVII::getVLOpNum(Desc);
   const MachineOperand &VLOp = UserMI.getOperand(VLOpNum);
   // Looking for an immediate or a register VL that isn't X0.
diff --git a/llvm/test/CodeGen/RISCV/rvv/vl-opt-op-info.mir b/llvm/test/CodeGen/RISCV/rvv/vl-opt-op-info.mir
index 9486fe92c041..c3e09d6b1438 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vl-opt-op-info.mir
+++ b/llvm/test/CodeGen/RISCV/rvv/vl-opt-op-info.mir
@@ -1172,13 +1172,13 @@ name: vred_vl0_and_vlreg
 body: |
   bb.0:
     ; CHECK-LABEL: name: vred_vl0_and_vlreg
     ; CHECK: %vl:gprnox0 = COPY $x1
-    ; CHECK-NEXT: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
+    ; CHECK-NEXT: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
     ; CHECK-NEXT: %y:vr = PseudoVREDSUM_VS_M1_E8 $noreg, $noreg, %x, %vl, 3 /* e8 */, 0 /* tu, mu */
     ; CHECK-NEXT: %z:vr = PseudoVADD_VV_M1 $noreg, %x, $noreg, 0, 3 /* e8 */, 0 /* tu, mu */
     %vl:gprnox0 = COPY $x1
     %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0
     %y:vr = PseudoVREDSUM_VS_M1_E8 $noreg, $noreg, %x, %vl, 3 /* e8 */, 0
     %z:vr = PseudoVADD_VV_M1 $noreg, %x, $noreg, 0, 3 /* e8 */, 0
 ...
 ---

checkUsers doesn't reduce the VL because VREDSUM's VL %x is not statically known to be greater than or equal to VADD's VL 0.

Copy link
Contributor Author

@michaelmaitland michaelmaitland Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're in agreement that we don't need the isVectorOpUsedAsScalarOp logic in getMinimumVLForUser for correctness. But we do need it to avoid the missed optimization opportunity in vred_vl0_and_vlreg.

Regarding some of your takeaways in the last comment:

we need to be sure not to reduce the VL of MI to zero when the VLOp of UserOp may be non-zero

This implies that we may reduce MI's VL to something smaller than a user's VL.

I agree. This occurs only in the case where we would return VL=1 in the case when the scalar operand is a register or non-zero immediate. This is because we only read the first lane of the vector register.

Use the largest VL among all the users. If we cannot determine this statically, then we cannot optimize the VL.

This implies that we never reduce MI's VL to something smaller than a user's VL.

I disagree with this implication. largest VL among all the users really refers to the largest getMinimumVLForUser, which may be smaller than a users VL.

So in reality, these two statements are not conflicting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I should have said "smaller than the user's minimum VL", i.e. it won't reduce it to something that's incorrect.

In terms of this check though, if I'm reading it right it's equivalent to this:

    if (VLOp.isImm() && VLOp.getImm() == 0)
      return std::nullopt;
    return MachineOperand::CreateImm(1);

I'm not sure why we need to abort if VLOp is an immediate 0, given it should be correct to return a larger minimum VL of 1 too.

So I think we should drop the check and always return a MachineOperand::CreateImm(1). Or we could also return MachineOperand::CreateImm(0) for that case as a further optimization, but I don't think VLOp = 0 is that common.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Luke, I'm only skimming this subthread, but can I ask that you clearly separate and treat different any potential correctness concern from a missed optimization? I think the code as written was and is correct. Am I missing something? A missed optimization is best handled in a follow up review.

LLVM_DEBUG(dbgs() << " Abort because could not determine VL of vector "
"operand used as scalar operand\n");

return std::nullopt;
}

unsigned VLOpNum = RISCVII::getVLOpNum(Desc);
const MachineOperand &VLOp = UserMI.getOperand(VLOpNum);
// Looking for an immediate or a register VL that isn't X0.
assert((!VLOp.isReg() || VLOp.getReg() != RISCV::X0) &&
"Did not expect X0 VL");
return VLOp;
}

std::optional<MachineOperand> RISCVVLOptimizer::checkUsers(MachineInstr &MI) {
// FIXME: Avoid visiting each user for each time we visit something on the
// worklist, combined with an extra visit from the outer loop. Restructure
// along lines of an instcombine style worklist which integrates the outer
// pass.
bool CanReduceVL = true;
std::optional<MachineOperand> CommonVL;
for (auto &UserOp : MRI->use_operands(MI.getOperand(0).getReg())) {
const MachineInstr &UserMI = *UserOp.getParent();
LLVM_DEBUG(dbgs() << " Checking user: " << UserMI << "\n");

// Instructions like reductions may use a vector register as a scalar
// register. In this case, we should treat it like a scalar register which
// does not impact the decision on whether to optimize VL.
// TODO: Treat it like a scalar register instead of bailing out.
if (isVectorOpUsedAsScalarOp(UserOp)) {
CanReduceVL = false;
break;
}

if (mayReadPastVL(UserMI)) {
LLVM_DEBUG(dbgs() << " Abort because used by unsafe instruction\n");
CanReduceVL = false;
break;
return std::nullopt;
}

// Tied operands might pass through.
if (UserOp.isTied()) {
LLVM_DEBUG(dbgs() << " Abort because user used as tied operand\n");
CanReduceVL = false;
break;
}

const MCInstrDesc &Desc = UserMI.getDesc();
if (!RISCVII::hasVLOp(Desc.TSFlags) || !RISCVII::hasSEWOp(Desc.TSFlags)) {
LLVM_DEBUG(dbgs() << " Abort due to lack of VL or SEW, assume that"
" use VLMAX\n");
CanReduceVL = false;
break;
return std::nullopt;
}

unsigned VLOpNum = RISCVII::getVLOpNum(Desc);
const MachineOperand &VLOp = UserMI.getOperand(VLOpNum);

// Looking for an immediate or a register VL that isn't X0.
assert((!VLOp.isReg() || VLOp.getReg() != RISCV::X0) &&
"Did not expect X0 VL");
auto VLOp = getVLForUser(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;
if (!CommonVL || RISCV::isVLKnownLE(*CommonVL, *VLOp)) {
CommonVL = *VLOp;
LLVM_DEBUG(dbgs() << " User VL is: " << VLOp << "\n");
} else if (!RISCV::isVLKnownLE(VLOp, *CommonVL)) {
} else if (!RISCV::isVLKnownLE(*VLOp, *CommonVL)) {
LLVM_DEBUG(dbgs() << " Abort because cannot determine a common VL\n");
CanReduceVL = false;
break;
return std::nullopt;
}

if (!RISCVII::hasSEWOp(UserMI.getDesc().TSFlags)) {
LLVM_DEBUG(dbgs() << " Abort due to lack of SEW operand\n");
return std::nullopt;
}

// The SEW and LMUL of destination and source registers need to match.
OperandInfo ConsumerInfo = getOperandInfo(UserOp, MRI);
OperandInfo ProducerInfo = getOperandInfo(MI.getOperand(0), MRI);
if (ConsumerInfo.isUnknown() || ProducerInfo.isUnknown() ||
!OperandInfo::EMULAndEEWAreEqual(ConsumerInfo, ProducerInfo)) {
LLVM_DEBUG(dbgs() << " Abort due to incompatible or unknown "
"information for EMUL or EEW.\n");
if (ConsumerInfo.isUnknown() || ProducerInfo.isUnknown()) {
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;
}

// If the operand is used as a scalar operand, then the EEW must be
// compatible. Otherwise, the EMUL *and* EEW must be compatible.
bool IsVectorOpUsedAsScalarOp = isVectorOpUsedAsScalarOp(UserOp);
if ((IsVectorOpUsedAsScalarOp &&
!OperandInfo::EEWAreEqual(ConsumerInfo, ProducerInfo)) ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a possible follow up - in general, I think a larger source LMUL is fine. You could unify the code here by returning the smallest legal LMUL for the given SEW for the scalar source operand, and then using a greater than comparison for EMUL here. Not sure if that improves readability or not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way of phrasing this could be "is vlmax known greater than or equal to"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am happy to address this in a follow up patch.

(!IsVectorOpUsedAsScalarOp &&
!OperandInfo::EMULAndEEWAreEqual(ConsumerInfo, ProducerInfo))) {
LLVM_DEBUG(
dbgs()
<< " Abort due to incompatible information for EMUL or EEW.\n");
LLVM_DEBUG(dbgs() << " ConsumerInfo is: " << ConsumerInfo << "\n");
LLVM_DEBUG(dbgs() << " ProducerInfo is: " << ProducerInfo << "\n");
CanReduceVL = false;
break;
return std::nullopt;
}
}
return CanReduceVL;

return CommonVL;
}

bool RISCVVLOptimizer::tryReduceVL(MachineInstr &OrigMI) {
Expand All @@ -1112,12 +1173,11 @@ bool RISCVVLOptimizer::tryReduceVL(MachineInstr &OrigMI) {
MachineInstr &MI = *Worklist.pop_back_val();
LLVM_DEBUG(dbgs() << "Trying to reduce VL for " << MI << "\n");

const MachineOperand *CommonVL = nullptr;
bool CanReduceVL = true;
if (isVectorRegClass(MI.getOperand(0).getReg(), MRI))
CanReduceVL = checkUsers(CommonVL, MI);
if (!isVectorRegClass(MI.getOperand(0).getReg(), MRI))
continue;

if (!CanReduceVL || !CommonVL)
auto CommonVL = checkUsers(MI);
if (!CommonVL)
continue;

assert((CommonVL->isImm() || CommonVL->getReg().isVirtual()) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ define i32 @reduce_and_16xi32_prefix5(ptr %p) {
; CHECK: # %bb.0:
; CHECK-NEXT: vsetivli zero, 5, e32, m2, ta, ma
; CHECK-NEXT: vle32.v v8, (a0)
; CHECK-NEXT: vsetivli zero, 5, e32, m1, ta, ma
; CHECK-NEXT: vsetivli zero, 1, e32, m1, ta, ma
; CHECK-NEXT: vmv.v.i v10, -1
; CHECK-NEXT: vsetivli zero, 5, e32, m2, ta, ma
; CHECK-NEXT: vredand.vs v8, v8, v10
Expand Down Expand Up @@ -725,7 +725,7 @@ define i32 @reduce_umin_16xi32_prefix5(ptr %p) {
; RV32: # %bb.0:
; RV32-NEXT: vsetivli zero, 5, e32, m2, ta, ma
; RV32-NEXT: vle32.v v8, (a0)
; RV32-NEXT: vsetivli zero, 5, e32, m1, ta, ma
; RV32-NEXT: vsetivli zero, 1, e32, m1, ta, ma
; RV32-NEXT: vmv.v.i v10, -1
; RV32-NEXT: vsetivli zero, 5, e32, m2, ta, ma
; RV32-NEXT: vredminu.vs v8, v8, v10
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/RISCV/rvv/fold-binary-reduce.ll
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ entry:
define i64 @reduce_add2(<4 x i64> %v) {
; CHECK-LABEL: reduce_add2:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: vsetivli zero, 4, e64, m1, ta, ma
; CHECK-NEXT: vsetivli zero, 1, e64, m1, ta, ma
; CHECK-NEXT: vmv.v.i v10, 8
; CHECK-NEXT: vsetivli zero, 4, e64, m2, ta, ma
; CHECK-NEXT: vredsum.vs v8, v8, v10
Expand Down
85 changes: 85 additions & 0 deletions llvm/test/CodeGen/RISCV/rvv/vl-opt-op-info.mir
Original file line number Diff line number Diff line change
Expand Up @@ -1094,3 +1094,88 @@ body: |
%x:vr = PseudoVMAND_MM_B1 $noreg, $noreg, -1, 0
%y:vr = PseudoVIOTA_M_MF2 $noreg, %x, 1, 3 /* e8 */, 0
...
name: vred_vs2
body: |
bb.0:
; CHECK-LABEL: name: vred_vs2
; CHECK: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %y:vr = PseudoVREDAND_VS_M1_E8 $noreg, %x, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
%x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0
%y:vr = PseudoVREDAND_VS_M1_E8 $noreg, %x, $noreg, 1, 3 /* e8 */, 0
...
---
name: vred_vs1
body: |
bb.0:
; CHECK-LABEL: name: vred_vs1
; CHECK: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %y:vr = PseudoVREDAND_VS_M1_E8 $noreg, $noreg, %x, 1, 3 /* e8 */, 0 /* tu, mu */
%x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0
%y:vr = PseudoVREDAND_VS_M1_E8 $noreg, $noreg, %x, 1, 3 /* e8 */, 0
...
---
name: vred_vs1_vs2
body: |
bb.0:
; CHECK-LABEL: name: vred_vs1_vs2
; CHECK: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %y:vr = PseudoVREDAND_VS_M1_E8 $noreg, %x, %x, 1, 3 /* e8 */, 0 /* tu, mu */
%x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0
%y:vr = PseudoVREDAND_VS_M1_E8 $noreg, %x, %x, 1, 3 /* e8 */, 0
...
---
name: vred_vs1_vs2_incompatible_eew
body: |
bb.0:
; CHECK-LABEL: name: vred_vs1_vs2_incompatible_eew
; CHECK: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %y:vr = PseudoVREDAND_VS_M1_E8 $noreg, %x, %x, 1, 4 /* e16 */, 0 /* tu, mu */
%x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0
%y:vr = PseudoVREDAND_VS_M1_E8 $noreg, %x, %x, 1, 4 /* e16 */, 0
...
---
name: vred_vs1_vs2_incompatible_emul
body: |
bb.0:
; CHECK-LABEL: name: vred_vs1_vs2_incompatible_emul
; CHECK: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %y:vr = PseudoVREDAND_VS_MF2_E8 $noreg, %x, %x, 1, 3 /* e8 */, 0 /* tu, mu */
%x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0
%y:vr = PseudoVREDAND_VS_MF2_E8 $noreg, %x, %x, 1, 3 /* e8 */, 0
...
---
name: vred_other_user_is_vl0
body: |
bb.0:
; CHECK-LABEL: name: vred_other_user_is_vl0
; CHECK: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 1, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %y:vr = PseudoVREDSUM_VS_M1_E8 $noreg, $noreg, %x, 1, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %z:vr = PseudoVADD_VV_M1 $noreg, %x, $noreg, 0, 3 /* e8 */, 0 /* tu, mu */
%x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0
%y:vr = PseudoVREDSUM_VS_M1_E8 $noreg, $noreg, %x, 1, 3 /* e8 */, 0
%z:vr = PseudoVADD_VV_M1 $noreg, %x, $noreg, 0, 3 /* e8 */, 0
...
---
name: vred_both_vl0
body: |
bb.0:
; CHECK-LABEL: name: vred_both_vl0
; CHECK: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %y:vr = PseudoVREDSUM_VS_M1_E8 $noreg, $noreg, %x, 0, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %z:vr = PseudoVADD_VV_M1 $noreg, %x, $noreg, 0, 3 /* e8 */, 0 /* tu, mu */
%x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0
%y:vr = PseudoVREDSUM_VS_M1_E8 $noreg, $noreg, %x, 0, 3 /* e8 */, 0
%z:vr = PseudoVADD_VV_M1 $noreg, %x, $noreg, 0, 3 /* e8 */, 0
...
---
name: vred_other_user_is_vl2
body: |
bb.0:
; CHECK-LABEL: name: vred_other_user_is_vl2
; CHECK: %x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, 2, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %y:vr = PseudoVREDSUM_VS_M1_E8 $noreg, $noreg, %x, 1, 3 /* e8 */, 0 /* tu, mu */
; CHECK-NEXT: %z:vr = PseudoVADD_VV_M1 $noreg, %x, $noreg, 2, 3 /* e8 */, 0 /* tu, mu */
%x:vr = PseudoVADD_VV_M1 $noreg, $noreg, $noreg, -1, 3 /* e8 */, 0
%y:vr = PseudoVREDSUM_VS_M1_E8 $noreg, $noreg, %x, 1, 3 /* e8 */, 0
%z:vr = PseudoVADD_VV_M1 $noreg, %x, $noreg, 2, 3 /* e8 */, 0
...
Loading