[llvm] [GlobalISel] Add G_INSERT_SUBVECTOR to computeNumSignBits (PR #220976)

via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 6 07:02:33 PDT 2026


https://github.com/pkuyo updated https://github.com/llvm/llvm-project/pull/220976

>From b6a16d76c994a95c53bc6789faecadca6d4ed527 Mon Sep 17 00:00:00 2001
From: Pkuyo <miku_mi at foxmail.com>
Date: Thu, 3 Sep 2026 02:05:38 -0700
Subject: [PATCH 1/3] [GlobalISel] Add computeNumSignBits for
 G_INSERT_SUBVECTOR

---
 .../CodeGen/GlobalISel/GISelValueTracking.cpp | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index 0b85098fc0a04..cdf861ee51c12 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -2736,6 +2736,36 @@ unsigned GISelValueTracking::computeNumSignBits(Register R,
     }
     return computeNumSignBits(SrcReg, DemandedSrcElts, Depth + 1);
   }
+  case TargetOpcode::G_INSERT_SUBVECTOR: {
+    FirstAnswer = TyBits;
+    Register Src = MI.getOperand(1).getReg();
+    Register Sub = MI.getOperand(2).getReg();
+    LLT SrcTy = MRI.getType(Src);
+    uint64_t Idx = MI.getOperand(3).getImm();
+
+    if (SrcTy.isScalableVector()) {
+      FirstAnswer = computeNumSignBits(Sub, Depth + 1);
+      FirstAnswer = std::min(FirstAnswer, computeNumSignBits(Src, Depth + 1));
+      break;
+    }
+    unsigned NumSubElts = MRI.getType(Sub).getNumElements();
+    APInt DemandedSubElts = DemandedElts.extractBits(NumSubElts, Idx);
+    APInt DemandedSrcElts = DemandedElts;
+    DemandedSrcElts.clearBits(Idx, Idx + NumSubElts);
+    // Only query the operands that contribute demanded elements, and take the
+    // minimum over them.
+    if (!!DemandedSubElts) {
+      FirstAnswer = computeNumSignBits(Sub, DemandedSubElts, Depth + 1);
+      // If we don't know any bits, early out.
+      if (FirstAnswer == 1)
+        break;
+    }
+    if (!!DemandedSrcElts) {
+      unsigned SrcAnswer = computeNumSignBits(Src, DemandedSrcElts, Depth + 1);
+      FirstAnswer = std::min(FirstAnswer, SrcAnswer);
+    }
+    break;
+  }
   case TargetOpcode::G_SHUFFLE_VECTOR: {
     // Collect the minimum number of sign bits that are shared by every vector
     // element referenced by the shuffle.

>From 9ad9578fde6b0f45857bc9679d8f59a55d52fe08 Mon Sep 17 00:00:00 2001
From: Pkuyo <miku_mi at foxmail.com>
Date: Thu, 3 Sep 2026 08:13:27 -0700
Subject: [PATCH 2/3] [GlobalISel] Add tests for G_INSERT_SUBVECTOR
 computeNumSignBits

---
 .../GlobalISel/knownbits-insert-subvector.mir | 91 +++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/knownbits-insert-subvector.mir

diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-insert-subvector.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-insert-subvector.mir
new file mode 100644
index 0000000000000..c3a92b9ae03f0
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-insert-subvector.mir
@@ -0,0 +1,91 @@
+# NOTE: Assertions have been autogenerated by utils/update_givaluetracking_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple aarch64 -passes="print<gisel-value-tracking>" %s -filetype=null 2>&1 | FileCheck %s
+
+---
+name: insert_min_of_operands
+body: |
+  bb.0:
+  ; CHECK-LABEL: name: @insert_min_of_operands
+  ; CHECK-NEXT: %v0:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v1:_ KnownBits:???????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %base:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %sub:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %res:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+    %v0:_(<4 x i8>) = COPY $s0
+    %v1:_(<2 x i16>) = COPY $s1
+    %base:_(<4 x i32>) = G_SEXT %v0
+    %sub:_(<2 x i32>) = G_SEXT %v1
+    %res:_(<4 x i32>) = G_INSERT_SUBVECTOR %base, %sub, 2
+...
+
+---
+name: extract_untouched_lanes_of_insert
+body: |
+  bb.0:
+  ; CHECK-LABEL: name: @extract_untouched_lanes_of_insert
+  ; CHECK-NEXT: %v0:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v1:_ KnownBits:???????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %base:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %sub:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %res:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %ext:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+    %v0:_(<4 x i8>) = COPY $s0
+    %v1:_(<2 x i16>) = COPY $s1
+    %base:_(<4 x i32>) = G_SEXT %v0
+    %sub:_(<2 x i32>) = G_SEXT %v1
+    %res:_(<4 x i32>) = G_INSERT_SUBVECTOR %base, %sub, 2
+    %ext:_(<2 x i32>) = G_EXTRACT_SUBVECTOR %res, 0
+...
+
+---
+name: extract_inserted_lanes_of_insert
+body: |
+  bb.0:
+  ; CHECK-LABEL: name: @extract_inserted_lanes_of_insert
+  ; CHECK-NEXT: %v0:_ KnownBits:???????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v1:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %base:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %sub:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %res:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %ext:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+    %v0:_(<4 x i16>) = COPY $d0
+    %v1:_(<2 x i8>) = COPY $h1
+    %base:_(<4 x i32>) = G_SEXT %v0
+    %sub:_(<2 x i32>) = G_SEXT %v1
+    %res:_(<4 x i32>) = G_INSERT_SUBVECTOR %base, %sub, 2
+    %ext:_(<2 x i32>) = G_EXTRACT_SUBVECTOR %res, 2
+...
+
+---
+name: insert_scalable
+body: |
+  bb.0:
+  ; CHECK-LABEL: name: @insert_scalable
+  ; CHECK-NEXT: %v0:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v1:_ KnownBits:???????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %base:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %sub:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %res:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+    %v0:_(<vscale x 4 x i8>) = COPY $z0
+    %v1:_(<vscale x 2 x i16>) = COPY $z1
+    %base:_(<vscale x 4 x i32>) = G_SEXT %v0
+    %sub:_(<vscale x 2 x i32>) = G_SEXT %v1
+    %res:_(<vscale x 4 x i32>) = G_INSERT_SUBVECTOR %base, %sub, 0
+...
+
+---
+name: insert_fixed_into_scalable
+body: |
+  bb.0:
+  ; CHECK-LABEL: name: @insert_fixed_into_scalable
+  ; CHECK-NEXT: %v0:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v1:_ KnownBits:???????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %base:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %sub:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %res:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+    %v0:_(<vscale x 4 x i8>) = COPY $z0
+    %v1:_(<2 x i16>) = COPY $s1
+    %base:_(<vscale x 4 x i32>) = G_SEXT %v0
+    %sub:_(<2 x i32>) = G_SEXT %v1
+    %res:_(<vscale x 4 x i32>) = G_INSERT_SUBVECTOR %base, %sub, 0
+...

>From 09773e79245bb7b8c272028ec23cf786aa3355f6 Mon Sep 17 00:00:00 2001
From: Pkuyo <miku_mi at foxmail.com>
Date: Sun, 6 Sep 2026 06:24:51 -0700
Subject: [PATCH 3/3] [GlobalISel] Move INSERT_SUBVECTOR sign bits logic into
 SignBitsOps

---
 llvm/include/llvm/Support/KnownBits.h         | 12 ++++++
 .../CodeGen/GlobalISel/GISelValueTracking.cpp | 37 ++++++-----------
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 30 +++++---------
 llvm/lib/Support/KnownBits.cpp                | 36 ++++++++++++++++
 .../AArch64/AArch64SelectionDAGTest.cpp       | 41 +++++++++++++++++++
 5 files changed, 111 insertions(+), 45 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index 1353955c8e325..12e060ebb7109 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -15,11 +15,14 @@
 #define LLVM_SUPPORT_KNOWNBITS_H
 
 #include "llvm/ADT/APInt.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/Support/Compiler.h"
 #include <optional>
 
 namespace llvm {
 
+class ElementCount;
+
 // Struct for tracking the known zeros and ones of a value.
 struct KnownBits {
   APInt Zero;
@@ -596,6 +599,15 @@ namespace SignBitsOps {
 LLVM_ABI unsigned rot(unsigned SrcSignBits, unsigned BitWidth,
                       std::optional<APInt> RotAmt, bool IsRotateRight);
 
+/// Compute a lower bound on the number of sign bits in the demanded
+/// elements of an INSERT_SUBVECTOR.
+/// The callbacks use input indices 0 for the source and 1 for the subvector.
+LLVM_ABI unsigned insertSubvector(
+    ElementCount SrcEC, ElementCount SubEC, uint64_t Idx,
+    const APInt &DemandedElts,
+    function_ref<unsigned(unsigned, const APInt &)> ComputeNumSignBits,
+    function_ref<unsigned(unsigned)> ComputeNumSignBitsAllElts);
+
 } // end namespace SignBitsOps
 
 inline KnownBits operator&(KnownBits LHS, const KnownBits &RHS) {
diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index cdf861ee51c12..a8823af3b71c9 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -2737,33 +2737,20 @@ unsigned GISelValueTracking::computeNumSignBits(Register R,
     return computeNumSignBits(SrcReg, DemandedSrcElts, Depth + 1);
   }
   case TargetOpcode::G_INSERT_SUBVECTOR: {
-    FirstAnswer = TyBits;
     Register Src = MI.getOperand(1).getReg();
     Register Sub = MI.getOperand(2).getReg();
-    LLT SrcTy = MRI.getType(Src);
-    uint64_t Idx = MI.getOperand(3).getImm();
-
-    if (SrcTy.isScalableVector()) {
-      FirstAnswer = computeNumSignBits(Sub, Depth + 1);
-      FirstAnswer = std::min(FirstAnswer, computeNumSignBits(Src, Depth + 1));
-      break;
-    }
-    unsigned NumSubElts = MRI.getType(Sub).getNumElements();
-    APInt DemandedSubElts = DemandedElts.extractBits(NumSubElts, Idx);
-    APInt DemandedSrcElts = DemandedElts;
-    DemandedSrcElts.clearBits(Idx, Idx + NumSubElts);
-    // Only query the operands that contribute demanded elements, and take the
-    // minimum over them.
-    if (!!DemandedSubElts) {
-      FirstAnswer = computeNumSignBits(Sub, DemandedSubElts, Depth + 1);
-      // If we don't know any bits, early out.
-      if (FirstAnswer == 1)
-        break;
-    }
-    if (!!DemandedSrcElts) {
-      unsigned SrcAnswer = computeNumSignBits(Src, DemandedSrcElts, Depth + 1);
-      FirstAnswer = std::min(FirstAnswer, SrcAnswer);
-    }
+    // MI operand 0 is the destination, so callback indices need a +1 offset.
+    FirstAnswer = SignBitsOps::insertSubvector(
+        MRI.getType(Src).getElementCount(), MRI.getType(Sub).getElementCount(),
+        MI.getOperand(3).getImm(), DemandedElts,
+        [&](unsigned OpIdx, const APInt &Demanded) {
+          return computeNumSignBits(MI.getOperand(OpIdx + 1).getReg(), Demanded,
+                                    Depth + 1);
+        },
+        [&](unsigned OpIdx) {
+          return computeNumSignBits(MI.getOperand(OpIdx + 1).getReg(),
+                                    Depth + 1);
+        });
     break;
   }
   case TargetOpcode::G_SHUFFLE_VECTOR: {
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 2218e5f59752f..1f4d6240c24fe 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -5418,28 +5418,18 @@ unsigned SelectionDAG::ComputeNumSignBits(SDValue Op, const APInt &DemandedElts,
     return Tmp;
   }
   case ISD::INSERT_SUBVECTOR: {
-    if (VT.isScalableVector())
-      break;
-    // Demand any elements from the subvector and the remainder from the src its
-    // inserted into.
     SDValue Src = Op.getOperand(0);
     SDValue Sub = Op.getOperand(1);
-    uint64_t Idx = Op.getConstantOperandVal(2);
-    unsigned NumSubElts = Sub.getValueType().getVectorNumElements();
-    APInt DemandedSubElts = DemandedElts.extractBits(NumSubElts, Idx);
-    APInt DemandedSrcElts = DemandedElts;
-    DemandedSrcElts.clearBits(Idx, Idx + NumSubElts);
-
-    Tmp = std::numeric_limits<unsigned>::max();
-    if (!!DemandedSubElts) {
-      Tmp = ComputeNumSignBits(Sub, DemandedSubElts, Depth + 1);
-      if (Tmp == 1)
-        return 1; // early-out
-    }
-    if (!!DemandedSrcElts) {
-      Tmp2 = ComputeNumSignBits(Src, DemandedSrcElts, Depth + 1);
-      Tmp = std::min(Tmp, Tmp2);
-    }
+    Tmp = SignBitsOps::insertSubvector(
+        Src.getValueType().getVectorElementCount(),
+        Sub.getValueType().getVectorElementCount(),
+        Op.getConstantOperandVal(2), DemandedElts,
+        [&](unsigned OpIdx, const APInt &Demanded) {
+          return ComputeNumSignBits(Op.getOperand(OpIdx), Demanded, Depth + 1);
+        },
+        [&](unsigned OpIdx) {
+          return ComputeNumSignBits(Op.getOperand(OpIdx), Depth + 1);
+        });
     assert(Tmp <= VTBits && "Failed to determine minimum sign bits");
     return Tmp;
   }
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 70f7a2f4fc887..dfcd4245054a2 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -14,8 +14,10 @@
 #include "llvm/Support/KnownBits.h"
 #include "llvm/ADT/Sequence.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/TypeSize.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cassert>
+#include <limits>
 
 using namespace llvm;
 
@@ -1469,3 +1471,37 @@ unsigned llvm::SignBitsOps::rot(unsigned SrcSignBits, unsigned BitWidth,
     return SrcSignBits - Amt;
   return 1;
 }
+
+unsigned llvm::SignBitsOps::insertSubvector(
+    ElementCount SrcEC, ElementCount SubEC, uint64_t Idx,
+    const APInt &DemandedElts,
+    function_ref<unsigned(unsigned, const APInt &)> ComputeNumSignBits,
+    function_ref<unsigned(unsigned)> ComputeNumSignBitsAllElts) {
+
+  unsigned Result = std::numeric_limits<unsigned>::max();
+  if (SrcEC.isScalable()) {
+    Result = ComputeNumSignBitsAllElts(1);
+    Result = std::min(Result, ComputeNumSignBitsAllElts(0));
+    return Result;
+  }
+
+  unsigned NumSubElts = SubEC.getFixedValue();
+  unsigned Offset = static_cast<unsigned>(Idx);
+  APInt DemandedSubElts = DemandedElts.extractBits(NumSubElts, Offset);
+  APInt DemandedSrcElts = DemandedElts;
+  DemandedSrcElts.clearBits(Offset, Offset + NumSubElts);
+
+  // Only query the operands that contribute demanded elements, and take the
+  // minimum over them.
+  if (!!DemandedSubElts) {
+    Result = ComputeNumSignBits(1, DemandedSubElts);
+    // If we don't know any bits, early out.
+    if (Result == 1)
+      return 1;
+  }
+  if (!!DemandedSrcElts) {
+    unsigned SrcAnswer = ComputeNumSignBits(0, DemandedSrcElts);
+    Result = std::min(Result, SrcAnswer);
+  }
+  return Result;
+}
diff --git a/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp b/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
index be4e927eecb22..c13ff6c0d3231 100644
--- a/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
+++ b/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
@@ -211,6 +211,47 @@ TEST_F(AArch64SelectionDAGTest, ComputeNumSignBitsSVE_EXTRACT_SUBVECTOR) {
   EXPECT_EQ(DAG->ComputeNumSignBits(Op, DemandedElts), 7u);
 }
 
+TEST_F(AArch64SelectionDAGTest, ComputeNumSignBitsSVE_INSERT_SUBVECTOR) {
+  SDLoc Loc;
+  SDValue Src = DAG->getNode(ISD::SIGN_EXTEND, Loc, MVT::nxv4i32,
+                             DAG->getRegister(0, MVT::nxv4i8));
+  SDValue Sub = DAG->getNode(ISD::SIGN_EXTEND, Loc, MVT::nxv2i32,
+                             DAG->getRegister(1, MVT::nxv2i16));
+  SDValue SrcWithFewerSignBits = DAG->getNode(
+      ISD::SIGN_EXTEND, Loc, MVT::nxv4i32, DAG->getRegister(0, MVT::nxv4i16));
+  SDValue SubWithMoreSignBits = DAG->getNode(
+      ISD::SIGN_EXTEND, Loc, MVT::nxv2i32, DAG->getRegister(1, MVT::nxv2i8));
+
+  for (unsigned Idx : {0u, 2u}) {
+    SDValue Index = DAG->getConstant(Idx, Loc, MVT::i64);
+    SDValue Op =
+        DAG->getNode(ISD::INSERT_SUBVECTOR, Loc, MVT::nxv4i32, Src, Sub, Index);
+    EXPECT_EQ(DAG->ComputeNumSignBits(Op), 17u);
+
+    Op = DAG->getNode(ISD::INSERT_SUBVECTOR, Loc, MVT::nxv4i32,
+                      SrcWithFewerSignBits, SubWithMoreSignBits, Index);
+    EXPECT_EQ(DAG->ComputeNumSignBits(Op), 17u);
+  }
+}
+
+TEST_F(AArch64SelectionDAGTest,
+       ComputeNumSignBitsSVE_INSERT_SUBVECTOR_FixedSubvector) {
+  SDLoc Loc;
+  SDValue Src = DAG->getNode(ISD::SIGN_EXTEND, Loc, MVT::nxv4i32,
+                             DAG->getRegister(0, MVT::nxv4i8));
+  SDValue Lane0 = DAG->getNode(ISD::SIGN_EXTEND, Loc, MVT::i32,
+                               DAG->getRegister(1, MVT::i8));
+  SDValue Lane1 = DAG->getNode(ISD::SIGN_EXTEND, Loc, MVT::i32,
+                               DAG->getRegister(2, MVT::i16));
+  SDValue Sub = DAG->getBuildVector(MVT::v2i32, Loc, {Lane0, Lane1});
+
+  for (unsigned Idx : {0u, 2u}) {
+    SDValue Op = DAG->getNode(ISD::INSERT_SUBVECTOR, Loc, MVT::nxv4i32, Src,
+                              Sub, DAG->getConstant(Idx, Loc, MVT::i64));
+    EXPECT_EQ(DAG->ComputeNumSignBits(Op), 17u);
+  }
+}
+
 TEST_F(AArch64SelectionDAGTest, ComputeNumSignBits_VASHR) {
   SDLoc Loc;
   auto VecVT = MVT::v8i8;



More information about the llvm-commits mailing list