[llvm] [DAG] Define computeConstantRange for VSCALE folding (PR #176027)

Neil Phan via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 3 08:07:25 PST 2026


https://github.com/neil-phan updated https://github.com/llvm/llvm-project/pull/176027

>From b62d1615d38bf57c4ca0e0633bbdfedf2a398321 Mon Sep 17 00:00:00 2001
From: neil-phan <hello at neil.place>
Date: Wed, 14 Jan 2026 15:13:58 -0500
Subject: [PATCH 1/5] [SelectionDAG] Define computeConstantRange for VSCALE
 folding

---
 llvm/include/llvm/CodeGen/SelectionDAG.h      | 10 +++
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 36 ++++++++
 .../CodeGen/AArch64/vscale-constant-range.ll  | 86 +++++++++++++++++++
 3 files changed, 132 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/vscale-constant-range.ll

diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
index 604319095e74f..821dcdaefad62 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -2126,6 +2126,16 @@ class SelectionDAG {
   LLVM_ABI KnownBits computeKnownBits(SDValue Op, const APInt &DemandedElts,
                                       unsigned Depth = 0) const;
 
+  /// Determine the possible constant range of an integer or vector of integers.
+  LLVM_ABI ConstantRange computeConstantRange(SDValue Op,
+                                              const APInt &DemandedElts,
+                                              unsigned Depth = 0) const;
+
+  /// Combine constant ranges from computeConstantRange() and
+  /// computeKnownBits().
+  LLVM_ABI ConstantRange computeConstantRangeIncludingKnownBits(
+      SDValue Op, const APInt &DemandedElts, unsigned Depth = 0) const;
+
   /// Used to represent the possible overflow behavior of an operation.
   /// Never: the operation cannot overflow.
   /// Always: the operation will always overflow.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index c8a4dc6e67908..b0ea8e9eed7a8 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -4563,6 +4563,42 @@ SelectionDAG::computeOverflowForSignedMul(SDValue N0, SDValue N1) const {
   return OFK_Sometime;
 }
 
+ConstantRange SelectionDAG::computeConstantRange(SDValue Op,
+                                                 const APInt &DemandedElts,
+                                                 unsigned Depth) const {
+  EVT VT = Op.getValueType();
+  unsigned BitWidth = VT.getScalarSizeInBits();
+
+  if (Depth >= MaxRecursionDepth)
+    return ConstantRange::getFull(BitWidth);
+
+  if (ConstantSDNode *C = isConstOrConstSplat(Op, DemandedElts))
+    return ConstantRange(C->getAPIntValue());
+
+  unsigned Opcode = Op.getOpcode();
+  switch (Opcode) {
+  case ISD::VSCALE: {
+    const Function &F = getMachineFunction().getFunction();
+    const APInt &Multiplier = Op.getConstantOperandAPInt(0);
+    return getVScaleRange(&F, BitWidth).multiply(Multiplier);
+  }
+  default:
+    break;
+  }
+
+  return ConstantRange::getFull(BitWidth);
+}
+
+ConstantRange SelectionDAG::computeConstantRangeIncludingKnownBits(
+    SDValue Op, const APInt &DemandedElts, unsigned Depth) const {
+  ConstantRange CR = computeConstantRange(Op, DemandedElts, Depth);
+  if (!CR.isFullSet())
+    return CR;
+
+  KnownBits Known = computeKnownBits(Op, DemandedElts, Depth);
+  return ConstantRange::fromKnownBits(Known, false);
+}
+
 bool SelectionDAG::isKnownToBeAPowerOfTwo(SDValue Val, unsigned Depth) const {
   if (Depth >= MaxRecursionDepth)
     return false; // Limit search depth.
diff --git a/llvm/test/CodeGen/AArch64/vscale-constant-range.ll b/llvm/test/CodeGen/AArch64/vscale-constant-range.ll
new file mode 100644
index 0000000000000..c5227066a7a69
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/vscale-constant-range.ll
@@ -0,0 +1,86 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=aarch64 -mattr=+sve -verify-machineinstrs < %s | FileCheck %s
+
+declare i64 @llvm.vscale.i64()
+
+define i64 @vscale_bounded_range_sub(i64 %x) vscale_range(1, 16) {
+; CHECK-LABEL: vscale_bounded_range_sub:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    rdvl x8, #1
+; CHECK-NEXT:    lsr x8, x8, #4
+; CHECK-NEXT:    sub x0, x8, #1
+; CHECK-NEXT:    ret
+  %vscale = call i64 @llvm.vscale.i64()
+  %sub = sub i64 %vscale, 1
+  ret i64 %sub
+}
+
+define i64 @vscale_exact_value() vscale_range(2, 2) {
+; CHECK-LABEL: vscale_exact_value:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w0, #2 // =0x2
+; CHECK-NEXT:    ret
+  %vscale = call i64 @llvm.vscale.i64()
+  ret i64 %vscale
+}
+
+define i64 @vscale_mul_bounded(i64 %x) vscale_range(1, 4) {
+; CHECK-LABEL: vscale_mul_bounded:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cntw x0
+; CHECK-NEXT:    ret
+  %vscale = call i64 @llvm.vscale.i64()
+  %mul = mul i64 %vscale, 4
+  ret i64 %mul
+}
+
+define i64 @vscale_and_mask(i64 %x) vscale_range(1, 4) {
+; CHECK-LABEL: vscale_and_mask:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    rdvl x8, #1
+; CHECK-NEXT:    lsr x0, x8, #4
+; CHECK-NEXT:    ret
+  %vscale = call i64 @llvm.vscale.i64()
+  %masked = and i64 %vscale, 15
+  ret i64 %masked
+}
+
+define i1 @vscale_compare_bounded_true() vscale_range(1, 4) {
+; CHECK-LABEL: vscale_compare_bounded_true:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    rdvl x8, #1
+; CHECK-NEXT:    lsr x8, x8, #4
+; CHECK-NEXT:    cmp x8, #100
+; CHECK-NEXT:    cset w0, lo
+; CHECK-NEXT:    ret
+  %vscale = call i64 @llvm.vscale.i64()
+  %cmp = icmp ult i64 %vscale, 100
+  ret i1 %cmp
+}
+
+define i1 @vscale_or_const_range(i64 %x) vscale_range(1, 4) {
+; CHECK-LABEL: vscale_or_const_range:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    rdvl x8, #1
+; CHECK-NEXT:    lsr x8, x8, #4
+; CHECK-NEXT:    orr x8, x8, #0x8
+; CHECK-NEXT:    cmp x8, #4
+; CHECK-NEXT:    cset w0, hi
+; CHECK-NEXT:    ret
+  %vscale = call i64 @llvm.vscale.i64()
+  %or = or i64 %vscale, 8
+  %cmp = icmp ugt i64 %or, 4
+  ret i1 %cmp
+}
+
+define i1 @vscale_shl_known_bits(i64 %x) vscale_range(1, 4) {
+; CHECK-LABEL: vscale_shl_known_bits:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w0, #1 // =0x1
+; CHECK-NEXT:    ret
+  %vscale = call i64 @llvm.vscale.i64()
+  %shl = shl i64 %vscale, 2
+  %and = and i64 %shl, 3
+  %cmp = icmp eq i64 %and, 0
+  ret i1 %cmp
+}

>From fc05477562d7cf2f938e7e105283139b26f1c9cf Mon Sep 17 00:00:00 2001
From: neil-phan <hello at neil.place>
Date: Thu, 15 Jan 2026 08:30:29 -0500
Subject: [PATCH 2/5] Add isSigned arg and intersection

---
 llvm/include/llvm/CodeGen/SelectionDAG.h       |  3 ++-
 llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 13 +++++++------
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
index 821dcdaefad62..a669e51a4678f 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -2134,7 +2134,8 @@ class SelectionDAG {
   /// Combine constant ranges from computeConstantRange() and
   /// computeKnownBits().
   LLVM_ABI ConstantRange computeConstantRangeIncludingKnownBits(
-      SDValue Op, const APInt &DemandedElts, unsigned Depth = 0) const;
+      SDValue Op, const APInt &DemandedElts, bool ForSigned,
+      unsigned Depth = 0) const;
 
   /// Used to represent the possible overflow behavior of an operation.
   /// Never: the operation cannot overflow.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index b0ea8e9eed7a8..130a9dc5e9776 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -4590,13 +4590,14 @@ ConstantRange SelectionDAG::computeConstantRange(SDValue Op,
 }
 
 ConstantRange SelectionDAG::computeConstantRangeIncludingKnownBits(
-    SDValue Op, const APInt &DemandedElts, unsigned Depth) const {
-  ConstantRange CR = computeConstantRange(Op, DemandedElts, Depth);
-  if (!CR.isFullSet())
-    return CR;
-
+    SDValue Op, const APInt &DemandedElts, bool ForSigned,
+    unsigned Depth) const {
   KnownBits Known = computeKnownBits(Op, DemandedElts, Depth);
-  return ConstantRange::fromKnownBits(Known, false);
+  ConstantRange CR1 = ConstantRange::fromKnownBits(Known, ForSigned);
+  ConstantRange CR2 = computeConstantRange(Op, DemandedElts, Depth);
+  ConstantRange::PreferredRangeType RangeType =
+      ForSigned ? ConstantRange::Signed : ConstantRange::Unsigned;
+  return CR1.intersectWith(CR2, RangeType);
 }
 
 bool SelectionDAG::isKnownToBeAPowerOfTwo(SDValue Val, unsigned Depth) const {

>From 6a1d556a5ef34b485da83d74bb5f927d359e38fa Mon Sep 17 00:00:00 2001
From: neil-phan <hello at neil.place>
Date: Thu, 15 Jan 2026 08:50:31 -0500
Subject: [PATCH 3/5] Clean up test cases

---
 .../CodeGen/AArch64/vscale-constant-range.ll  | 40 -------------------
 1 file changed, 40 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/vscale-constant-range.ll b/llvm/test/CodeGen/AArch64/vscale-constant-range.ll
index c5227066a7a69..88a911a901ccb 100644
--- a/llvm/test/CodeGen/AArch64/vscale-constant-range.ll
+++ b/llvm/test/CodeGen/AArch64/vscale-constant-range.ll
@@ -3,18 +3,6 @@
 
 declare i64 @llvm.vscale.i64()
 
-define i64 @vscale_bounded_range_sub(i64 %x) vscale_range(1, 16) {
-; CHECK-LABEL: vscale_bounded_range_sub:
-; CHECK:       // %bb.0:
-; CHECK-NEXT:    rdvl x8, #1
-; CHECK-NEXT:    lsr x8, x8, #4
-; CHECK-NEXT:    sub x0, x8, #1
-; CHECK-NEXT:    ret
-  %vscale = call i64 @llvm.vscale.i64()
-  %sub = sub i64 %vscale, 1
-  ret i64 %sub
-}
-
 define i64 @vscale_exact_value() vscale_range(2, 2) {
 ; CHECK-LABEL: vscale_exact_value:
 ; CHECK:       // %bb.0:
@@ -45,34 +33,6 @@ define i64 @vscale_and_mask(i64 %x) vscale_range(1, 4) {
   ret i64 %masked
 }
 
-define i1 @vscale_compare_bounded_true() vscale_range(1, 4) {
-; CHECK-LABEL: vscale_compare_bounded_true:
-; CHECK:       // %bb.0:
-; CHECK-NEXT:    rdvl x8, #1
-; CHECK-NEXT:    lsr x8, x8, #4
-; CHECK-NEXT:    cmp x8, #100
-; CHECK-NEXT:    cset w0, lo
-; CHECK-NEXT:    ret
-  %vscale = call i64 @llvm.vscale.i64()
-  %cmp = icmp ult i64 %vscale, 100
-  ret i1 %cmp
-}
-
-define i1 @vscale_or_const_range(i64 %x) vscale_range(1, 4) {
-; CHECK-LABEL: vscale_or_const_range:
-; CHECK:       // %bb.0:
-; CHECK-NEXT:    rdvl x8, #1
-; CHECK-NEXT:    lsr x8, x8, #4
-; CHECK-NEXT:    orr x8, x8, #0x8
-; CHECK-NEXT:    cmp x8, #4
-; CHECK-NEXT:    cset w0, hi
-; CHECK-NEXT:    ret
-  %vscale = call i64 @llvm.vscale.i64()
-  %or = or i64 %vscale, 8
-  %cmp = icmp ugt i64 %or, 4
-  ret i1 %cmp
-}
-
 define i1 @vscale_shl_known_bits(i64 %x) vscale_range(1, 4) {
 ; CHECK-LABEL: vscale_shl_known_bits:
 ; CHECK:       // %bb.0:

>From bffbf596b039083742230717fdbbbf7092128c2f Mon Sep 17 00:00:00 2001
From: neil-phan <hello at neil.place>
Date: Thu, 15 Jan 2026 12:26:38 -0500
Subject: [PATCH 4/5] Replace overflow mul/sub with constant range impl

---
 llvm/include/llvm/CodeGen/SelectionDAG.h      |  3 +++
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 25 +++++++++++++------
 2 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
index a669e51a4678f..1db58c5ae4f97 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -2131,6 +2131,9 @@ class SelectionDAG {
                                               const APInt &DemandedElts,
                                               unsigned Depth = 0) const;
 
+  LLVM_ABI ConstantRange computeConstantRangeIncludingKnownBits(
+      SDValue Op, bool ForSigned, unsigned Depth = 0) const;
+
   /// Combine constant ranges from computeConstantRange() and
   /// computeKnownBits().
   LLVM_ABI ConstantRange computeConstantRangeIncludingKnownBits(
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 130a9dc5e9776..34dd0b637fd90 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -4513,10 +4513,8 @@ SelectionDAG::computeOverflowForUnsignedSub(SDValue N0, SDValue N1) const {
   if (isNullConstant(N1))
     return OFK_Never;
 
-  KnownBits N0Known = computeKnownBits(N0);
-  KnownBits N1Known = computeKnownBits(N1);
-  ConstantRange N0Range = ConstantRange::fromKnownBits(N0Known, false);
-  ConstantRange N1Range = ConstantRange::fromKnownBits(N1Known, false);
+  ConstantRange N0Range = computeConstantRangeIncludingKnownBits(N0, false);
+  ConstantRange N1Range = computeConstantRangeIncludingKnownBits(N1, false);
   return mapOverflowResult(N0Range.unsignedSubMayOverflow(N1Range));
 }
 
@@ -4526,10 +4524,8 @@ SelectionDAG::computeOverflowForUnsignedMul(SDValue N0, SDValue N1) const {
   if (isNullConstant(N1) || isOneConstant(N1))
     return OFK_Never;
 
-  KnownBits N0Known = computeKnownBits(N0);
-  KnownBits N1Known = computeKnownBits(N1);
-  ConstantRange N0Range = ConstantRange::fromKnownBits(N0Known, false);
-  ConstantRange N1Range = ConstantRange::fromKnownBits(N1Known, false);
+  ConstantRange N0Range = computeConstantRangeIncludingKnownBits(N0, false);
+  ConstantRange N1Range = computeConstantRangeIncludingKnownBits(N1, false);
   return mapOverflowResult(N0Range.unsignedMulMayOverflow(N1Range));
 }
 
@@ -4589,6 +4585,19 @@ ConstantRange SelectionDAG::computeConstantRange(SDValue Op,
   return ConstantRange::getFull(BitWidth);
 }
 
+ConstantRange
+SelectionDAG::computeConstantRangeIncludingKnownBits(SDValue Op, bool ForSigned,
+                                                     unsigned Depth) const {
+  EVT VT = Op.getValueType();
+
+  APInt DemandedElts = VT.isFixedLengthVector()
+                           ? APInt::getAllOnes(VT.getVectorNumElements())
+                           : APInt(1, 1);
+
+  return computeConstantRangeIncludingKnownBits(Op, DemandedElts, ForSigned,
+                                                Depth);
+}
+
 ConstantRange SelectionDAG::computeConstantRangeIncludingKnownBits(
     SDValue Op, const APInt &DemandedElts, bool ForSigned,
     unsigned Depth) const {

>From b26b5004338c701cab219e3da05d11aed4654885 Mon Sep 17 00:00:00 2001
From: Neil Phan <hello at neil.place>
Date: Tue, 3 Feb 2026 11:07:13 -0500
Subject: [PATCH 5/5] Add styling for ForSigned param

Co-authored-by: Simon Pilgrim <git at redking.me.uk>
---
 llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 34dd0b637fd90..8faa8fd82d6d2 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -4514,7 +4514,8 @@ SelectionDAG::computeOverflowForUnsignedSub(SDValue N0, SDValue N1) const {
     return OFK_Never;
 
   ConstantRange N0Range = computeConstantRangeIncludingKnownBits(N0, false);
-  ConstantRange N1Range = computeConstantRangeIncludingKnownBits(N1, false);
+  ConstantRange N0Range = computeConstantRangeIncludingKnownBits(N1, /*ForSigned=*/false);
+  ConstantRange N1Range = computeConstantRangeIncludingKnownBits(N1, /*ForSigned=*/false);
   return mapOverflowResult(N0Range.unsignedSubMayOverflow(N1Range));
 }
 



More information about the llvm-commits mailing list