[llvm] [RISCV] Combine two consecutive VSLIDEDOWN_VL nodes into one (PR #225232)
Craig Topper via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 12:56:03 PDT 2026
================
@@ -22299,64 +22299,116 @@ static SDValue performVSlideUpDownCombine(SDNode *N, SelectionDAG &DAG,
if (N->getOperand(1)->isUndef())
return N->getOperand(0);
- SDValue SlideDown = N->getOperand(1);
- SDValue SlideUpOffset = N->getOperand(2);
- SDValue SlideUpMask = N->getOperand(3);
- SDValue SlideUpVL = N->getOperand(4);
+ auto isVLMax = [](SDValue VL) {
+ return (isa<RegisterSDNode>(VL) &&
+ cast<RegisterSDNode>(VL)->getReg() == RISCV::X0) ||
+ isAllOnesConstant(VL);
+ };
// Given this pattern
// ```
// %down = RISCVISD::VSLIDEDOWN_VL undef, %val, %offset, %mask, %vl0
// %up = RISCVISD::VSLIDEUP_VL %val, %down, %offset, %mask, %vl1
// ```
// We can simplify it with `%val`, as it's doing redundant shifting.
- if (Opcode != RISCVISD::VSLIDEUP_VL ||
- SlideDown.getOpcode() != RISCVISD::VSLIDEDOWN_VL)
- return SDValue();
+ if (Opcode == RISCVISD::VSLIDEUP_VL) {
+ SDValue SlideDown = N->getOperand(1);
+ SDValue SlideUpOffset = N->getOperand(2);
+ SDValue SlideUpMask = N->getOperand(3);
+ SDValue SlideUpVL = N->getOperand(4);
- SDValue SlideDownPassthru = SlideDown->getOperand(0);
- SDValue SlideDownVal = SlideDown->getOperand(1);
- SDValue SlideDownOffset = SlideDown->getOperand(2);
- SDValue SlideDownMask = SlideDown->getOperand(3);
- SDValue SlideDownVL = SlideDown->getOperand(4);
- if (!SlideDownPassthru.isUndef() || SlideDownVal != N->getOperand(0) ||
- SlideDownOffset != SlideUpOffset)
- return SDValue();
+ if (SlideDown.getOpcode() != RISCVISD::VSLIDEDOWN_VL)
+ return SDValue();
- auto isVLMax = [](SDValue VL) {
- return (isa<RegisterSDNode>(VL) &&
- cast<RegisterSDNode>(VL)->getReg() == RISCV::X0) ||
- isAllOnesConstant(VL);
- };
+ SDValue SlideDownPassthru = SlideDown->getOperand(0);
+ SDValue SlideDownVal = SlideDown->getOperand(1);
+ SDValue SlideDownOffset = SlideDown->getOperand(2);
+ SDValue SlideDownMask = SlideDown->getOperand(3);
+ SDValue SlideDownVL = SlideDown->getOperand(4);
+ if (!SlideDownPassthru.isUndef() || SlideDownVal != N->getOperand(0) ||
+ SlideDownOffset != SlideUpOffset)
+ return SDValue();
+
+ // Check VLs.
+ // First, we need to worry about the zeros shifted into VSLIDEDOWN_VL. In
+ // this scenario, the worst case would be %vl0 = VLMAX, as %down is
+ // guaranteed to have those zeros. Our goal here is to ensure elements from
+ // %down that are actually inserted into %up are not those zeros. The number
+ // of %down that are actually inserted would be `%vl1 - %offset`, and the
+ // number of non-zero elements from %down would be `VLMAX - %offset`.
+ // Therefore, the invariant would be
+ // `%vl1 - %offset <= VLMAX - %offset` ---> `%vl1 <= VLMAX`
+ // Thus, we will never read those zeros that are shifted in by
+ // VSLIDEDOWN_VL. Second, we don't want slideup to read past the result
+ // produced by slidedown. So the condition for this case would be `%vl0 +
+ // %offset >= %vl1`.
+ if (!isVLMax(SlideDownVL)) {
+ KnownBits DownVLKB = DAG.computeKnownBits(SlideDownVL);
+ KnownBits UpVLKB = DAG.computeKnownBits(SlideUpVL);
+ KnownBits OffsetKB = DAG.computeKnownBits(SlideDownOffset);
+ if (!KnownBits::uge(KnownBits::add(DownVLKB, OffsetKB), UpVLKB)
+ .value_or(false))
+ return SDValue();
+ }
+
+ // Check if they are both all-ones masks.
+ if (SlideDownMask.getOpcode() != RISCVISD::VMSET_VL ||
+ SlideUpMask.getOpcode() != RISCVISD::VMSET_VL)
+ return SDValue();
+
+ return SlideDownVal;
+ }
+
+ // Given this pattern
+ // ```
+ // %down0 = RISCVISD::VSLIDEDOWN_VL undef, %val, %offset0, %mask, %vl0
+ // %down1 = RISCVISD::VSLIDEDOWN_VL undef, %down0, %offset1, %mask, %vl1
+ // ```
+ // we can turn it into
+ // ```
+ // %down1 = RISCVISD::VSLIDEDOWN_VL undef, %val, %offset2, %mask, %vl1
+ // ```
+ // where %offset2 is `%offset0 + %offset1`, if `%vl0 >= %offset1 + %vl1`.
+ // Note that we don't limit %down0 to be one-use as this transformation
+ // could improve IPC even if %down has more than one use.
+ SDValue Down0 = N->getOperand(1);
+ if (Down0.getOpcode() != RISCVISD::VSLIDEDOWN_VL ||
+ !N->getOperand(0).isUndef() || !Down0.getOperand(0).isUndef())
+ return SDValue();
+
+ SDValue Val = Down0.getOperand(1);
+ SDValue Offset0 = Down0.getOperand(2);
+ SDValue Mask0 = Down0.getOperand(3);
+ SDValue VL0 = Down0.getOperand(4);
+ SDValue Offset1 = N->getOperand(2);
+ SDValue Mask1 = N->getOperand(3);
+ SDValue VL1 = N->getOperand(4);
// Check VLs.
- // First, we need to worry about the zeros shifted into VSLIDEDOWN_VL. In this
- // scenario, the worst case would be %vl0 = VLMAX, as %down is guaranteed to
- // have those zeros. Our goal here is to ensure elements from %down that are
- // actually inserted into %up are not those zeros. The number of %down that
- // are actually inserted would be `%vl1 - %offset`, and the number of non-zero
- // elements from %down would be `VLMAX - %offset`. Therefore, the invariant
- // would be
- // `%vl1 - %offset <= VLMAX - %offset` ---> `%vl1 <= VLMAX`
- // Thus, we will never read those zeros that are shifted in by VSLIDEDOWN_VL.
- // Second, we don't want slideup to read past the result produced by
- // slidedown. So the condition for this case would be `%vl0 + %offset >=
- // %vl1`.
- if (!isVLMax(SlideDownVL)) {
- KnownBits DownVLKB = DAG.computeKnownBits(SlideDownVL);
- KnownBits UpVLKB = DAG.computeKnownBits(SlideUpVL);
- KnownBits OffsetKB = DAG.computeKnownBits(SlideDownOffset);
- if (!KnownBits::uge(KnownBits::add(DownVLKB, OffsetKB), UpVLKB)
+ if (!isVLMax(VL0)) {
+ KnownBits VL0KB = DAG.computeKnownBits(VL0);
+ KnownBits VL1KB = DAG.computeKnownBits(VL1);
+ KnownBits Offset1KB = DAG.computeKnownBits(Offset1);
+ if (!KnownBits::uge(VL0KB, KnownBits::add(VL1KB, Offset1KB))
.value_or(false))
return SDValue();
}
// Check if they are both all-ones masks.
- if (SlideDownMask.getOpcode() != RISCVISD::VMSET_VL ||
- SlideUpMask.getOpcode() != RISCVISD::VMSET_VL)
+ if (Mask0.getOpcode() != RISCVISD::VMSET_VL ||
+ Mask1.getOpcode() != RISCVISD::VMSET_VL)
return SDValue();
- return SlideDownVal;
+ SDLoc DL(N);
+ // Even if %offset0 and %offset1 are both constants, and `%offset0 + %offset1`
+ // exceeds uimm5, using an extra register to materialize the new offset + .vx
+ // is probably still more benefitial.
----------------
topperc wrote:
```suggestion
// is probably still more beneficial.
```
https://github.com/llvm/llvm-project/pull/225232
More information about the llvm-commits
mailing list