[llvm] [GlobalISel] Add G_EXTRACT_VECTOR_ELT and G_INSERT_VECTOR_ELT to computeNumSignBits (PR #213480)

Joel Walker via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 14 12:53:39 PDT 2026


https://github.com/Joel-Wwalker updated https://github.com/llvm/llvm-project/pull/213480

>From 8c308c9094a5f14593589496220508f7e7e46028 Mon Sep 17 00:00:00 2001
From: Joel-Wwalker <theagingboy05 at gmail.com>
Date: Sat, 5 Sep 2026 14:39:00 -0400
Subject: [PATCH] [GlobalISel] Add G_INSERT_VECTOR_ELT to computeNumSignBits

computeKnownBitsImpl already handles G_INSERT_VECTOR_ELT, but
computeNumSignBits fell through to the known-bits fallback, which loses
sign information that is not representable as known bits: an insert
into a vector of sign-extended lanes reported a single sign bit.

Port the SelectionDAG case. When the index is a known constant, split
the demand between the inserted value and the remaining lanes;
otherwise demand both. Scalable vectors take the minimum of the whole
vector and the inserted value. Implicitly truncated inserts bail, as in
SelectionDAG; skipping the inserted value there would be unsound when
its lane is demanded.

The extract case from the first version of this change landed
separately in #218283.

Assisted by Claude (Anthropic).
---
 .../CodeGen/GlobalISel/GISelValueTracking.cpp |  37 +++
 .../GlobalISel/knownbits-insert-vector.mir    | 217 ++++++++++++++++++
 2 files changed, 254 insertions(+)

diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index 4cdbeeebc39bb..f200201b5910e 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -2725,6 +2725,43 @@ unsigned GISelValueTracking::computeNumSignBits(Register R,
     }
     break;
   }
+  case TargetOpcode::G_INSERT_VECTOR_ELT: {
+    GInsertVectorElement &Insert = cast<GInsertVectorElement>(MI);
+    Register InVec = Insert.getVectorReg();
+    Register InVal = Insert.getElementReg();
+    LLT VecVT = MRI.getType(InVec);
+
+    // If we know the element index, split the demand between the inserted
+    // value and the source vector, otherwise assume we need both. Scalable
+    // vectors carry no per-lane demand, so they always take the minimum of the
+    // whole vector and the inserted value.
+    bool DemandedVal = true;
+    APInt DemandedVecElts = DemandedElts;
+    if (!VecVT.isScalableVector()) {
+      unsigned NumElts = VecVT.getNumElements();
+      auto ConstEltNo = getIConstantVRegVal(Insert.getIndexReg(), MRI);
+      if (ConstEltNo && ConstEltNo->ult(NumElts)) {
+        unsigned EltIdx = ConstEltNo->getZExtValue();
+        DemandedVal = !!DemandedElts[EltIdx];
+        DemandedVecElts.clearBit(EltIdx);
+      }
+    }
+
+    unsigned Tmp = TyBits;
+    if (DemandedVal) {
+      // TODO: Handle implicit truncation of inserted elements.
+      if (MRI.getType(InVal).getSizeInBits() != TyBits)
+        break;
+      unsigned ValSignBits = computeNumSignBits(InVal, APInt(1, 1), Depth + 1);
+      Tmp = std::min(Tmp, ValSignBits);
+    }
+    if (!!DemandedVecElts) {
+      unsigned VecSignBits =
+          computeNumSignBits(InVec, DemandedVecElts, Depth + 1);
+      Tmp = std::min(Tmp, VecSignBits);
+    }
+    return Tmp;
+  }
   case TargetOpcode::G_EXTRACT_VECTOR_ELT: {
     GExtractVectorElement &Extract = cast<GExtractVectorElement>(MI);
     Register InVec = Extract.getVectorReg();
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-insert-vector.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-insert-vector.mir
index b80e3eba35bca..8411e1eb6e9d4 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-insert-vector.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-insert-vector.mir
@@ -97,3 +97,220 @@ body: |
     %4:_(i8) = G_CONSTANT i8 6
     %5:_(<2 x i8>) = G_INSERT_VECTOR_ELT %3, %4, %idx
 ...
+---
+# computeNumSignBits: known index splits demand between the vector and the
+# inserted value; the result is the minimum of the two.
+name: signbits_const_idx
+body: |
+  bb.0:
+    liveins: $w0, $w1
+  ; CHECK-LABEL: name: @signbits_const_idx
+  ; CHECK-NEXT: %w0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %w1:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %a:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %s:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %val:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %idx0:_ KnownBits:0000000000000000000000000000000000000000000000000000000000000000 SignBits:64 IsKnownNeverZero:0
+  ; CHECK-NEXT: %r:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+    %w0:_(i32) = COPY $w0
+    %w1:_(i32) = COPY $w1
+    %a:_(i8) = G_TRUNC %w0
+    %s:_(i32) = G_SEXT %a
+    %v:_(<2 x i32>) = G_BUILD_VECTOR %s, %s
+    %val:_(i32) = G_SEXT_INREG %w1, 16
+    %idx0:_(i64) = G_CONSTANT i64 0
+    %r:_(<2 x i32>) = G_INSERT_VECTOR_ELT %v, %val, %idx0
+    $d0 = COPY %r(<2 x i32>)
+...
+---
+# computeNumSignBits: the vector can be the limiting operand.
+name: signbits_const_idx_vec_limits
+body: |
+  bb.0:
+    liveins: $w0, $w1
+  ; CHECK-LABEL: name: @signbits_const_idx_vec_limits
+  ; CHECK-NEXT: %w0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %w1:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %a:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %val:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %s:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %idx0:_ KnownBits:0000000000000000000000000000000000000000000000000000000000000000 SignBits:64 IsKnownNeverZero:0
+  ; CHECK-NEXT: %r:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+    %w0:_(i32) = COPY $w0
+    %w1:_(i32) = COPY $w1
+    %a:_(i8) = G_TRUNC %w1
+    %val:_(i32) = G_SEXT %a
+    %s:_(i32) = G_SEXT_INREG %w0, 16
+    %v:_(<2 x i32>) = G_BUILD_VECTOR %s, %s
+    %idx0:_(i64) = G_CONSTANT i64 0
+    %r:_(<2 x i32>) = G_INSERT_VECTOR_ELT %v, %val, %idx0
+    $d0 = COPY %r(<2 x i32>)
+...
+---
+# computeNumSignBits: unknown index demands the value and every lane.
+name: signbits_unknown_idx
+body: |
+  bb.0:
+    liveins: $w0, $w1, $x2
+  ; CHECK-LABEL: name: @signbits_unknown_idx
+  ; CHECK-NEXT: %w0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %w1:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %idx:_ KnownBits:???????????????????????????????????????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %a:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %s:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %val:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %r:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+    %w0:_(i32) = COPY $w0
+    %w1:_(i32) = COPY $w1
+    %idx:_(i64) = COPY $x2
+    %a:_(i8) = G_TRUNC %w0
+    %s:_(i32) = G_SEXT %a
+    %v:_(<2 x i32>) = G_BUILD_VECTOR %s, %s
+    %val:_(i32) = G_SEXT_INREG %w1, 16
+    %r:_(<2 x i32>) = G_INSERT_VECTOR_ELT %v, %val, %idx
+    $d0 = COPY %r(<2 x i32>)
+...
+---
+# computeNumSignBits: an out-of-bounds constant index is treated as unknown.
+name: signbits_out_of_bounds_idx
+body: |
+  bb.0:
+    liveins: $w0, $w1
+  ; CHECK-LABEL: name: @signbits_out_of_bounds_idx
+  ; CHECK-NEXT: %w0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %w1:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %a:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %s:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %val:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %idx:_ KnownBits:0000000000000000000000000000000000000000000000000000000000000101 SignBits:61 IsKnownNeverZero:1
+  ; CHECK-NEXT: %r:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+    %w0:_(i32) = COPY $w0
+    %w1:_(i32) = COPY $w1
+    %a:_(i8) = G_TRUNC %w0
+    %s:_(i32) = G_SEXT %a
+    %v:_(<2 x i32>) = G_BUILD_VECTOR %s, %s
+    %val:_(i32) = G_SEXT_INREG %w1, 16
+    %idx:_(i64) = G_CONSTANT i64 5
+    %r:_(<2 x i32>) = G_INSERT_VECTOR_ELT %v, %val, %idx
+    $d0 = COPY %r(<2 x i32>)
+...
+---
+# computeNumSignBits: only the demanded lanes count. Extracting lane 1 does not
+# see the value inserted into lane 0; extracting lane 0 sees only the value.
+name: signbits_demanded_lanes
+body: |
+  bb.0:
+    liveins: $w0, $w1
+  ; CHECK-LABEL: name: @signbits_demanded_lanes
+  ; CHECK-NEXT: %w0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %w1:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %a:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %s:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %idx0:_ KnownBits:0000000000000000000000000000000000000000000000000000000000000000 SignBits:64 IsKnownNeverZero:0
+  ; CHECK-NEXT: %idx1:_ KnownBits:0000000000000000000000000000000000000000000000000000000000000001 SignBits:63 IsKnownNeverZero:1
+  ; CHECK-NEXT: %r:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %e0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %e1:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+    %w0:_(i32) = COPY $w0
+    %w1:_(i32) = COPY $w1
+    %a:_(i8) = G_TRUNC %w0
+    %s:_(i32) = G_SEXT %a
+    %v:_(<2 x i32>) = G_BUILD_VECTOR %s, %s
+    %idx0:_(i64) = G_CONSTANT i64 0
+    %idx1:_(i64) = G_CONSTANT i64 1
+    %r:_(<2 x i32>) = G_INSERT_VECTOR_ELT %v, %w1, %idx0
+    %e0:_(i32) = G_EXTRACT_VECTOR_ELT %r, %idx0
+    %e1:_(i32) = G_EXTRACT_VECTOR_ELT %r, %idx1
+    $w0 = COPY %e0(i32)
+    $w1 = COPY %e1(i32)
+...
+---
+# computeNumSignBits: scalable vectors have no per-lane demand, so the result is
+# the minimum over the whole vector and the inserted value.
+name: signbits_scalable
+body: |
+  bb.0:
+    liveins: $w0, $w1, $z0
+  ; CHECK-LABEL: name: @signbits_scalable
+  ; CHECK-NEXT: %w0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %w1:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %z0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %a:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %s:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %val:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %idx0:_ KnownBits:0000000000000000000000000000000000000000000000000000000000000000 SignBits:64 IsKnownNeverZero:0
+  ; CHECK-NEXT: %r:_ KnownBits:???????????????????????????????? SignBits:17 IsKnownNeverZero:0
+  ; CHECK-NEXT: %ru:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+    %w0:_(i32) = COPY $w0
+    %w1:_(i32) = COPY $w1
+    %z0:_(<vscale x 4 x i32>) = COPY $z0
+    %a:_(i8) = G_TRUNC %w0
+    %s:_(i32) = G_SEXT %a
+    %v:_(<vscale x 4 x i32>) = G_SPLAT_VECTOR %s
+    %val:_(i32) = G_SEXT_INREG %w1, 16
+    %idx0:_(i64) = G_CONSTANT i64 0
+    %r:_(<vscale x 4 x i32>) = G_INSERT_VECTOR_ELT %v, %val, %idx0
+    %ru:_(<vscale x 4 x i32>) = G_INSERT_VECTOR_ELT %z0, %val, %idx0
+    $z0 = COPY %r(<vscale x 4 x i32>)
+    $z1 = COPY %ru(<vscale x 4 x i32>)
+...
+---
+# computeNumSignBits: an inserted value wider than the element is implicitly
+# truncated. Not handled yet, so only the known-bits fallback applies.
+name: signbits_implicit_trunc
+body: |
+  bb.0:
+    liveins: $w0, $w1
+  ; CHECK-LABEL: name: @signbits_implicit_trunc
+  ; CHECK-NEXT: %w0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %w1:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %a:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %s:_ KnownBits:???????????????? SignBits:9 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v:_ KnownBits:???????????????? SignBits:9 IsKnownNeverZero:0
+  ; CHECK-NEXT: %b:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %val:_ KnownBits:???????????????????????????????? SignBits:25 IsKnownNeverZero:0
+  ; CHECK-NEXT: %idx0:_ KnownBits:0000000000000000000000000000000000000000000000000000000000000000 SignBits:64 IsKnownNeverZero:0
+  ; CHECK-NEXT: %r:_ KnownBits:???????????????? SignBits:1 IsKnownNeverZero:0
+    %w0:_(i32) = COPY $w0
+    %w1:_(i32) = COPY $w1
+    %a:_(i8) = G_TRUNC %w0
+    %s:_(i16) = G_SEXT %a
+    %v:_(<4 x i16>) = G_BUILD_VECTOR %s, %s, %s, %s
+    %b:_(i8) = G_TRUNC %w1
+    %val:_(i32) = G_SEXT %b
+    %idx0:_(i64) = G_CONSTANT i64 0
+    %r:_(<4 x i16>) = G_INSERT_VECTOR_ELT %v, %val, %idx0
+    $d0 = COPY %r(<4 x i16>)
+...
+---
+# computeNumSignBits: implicitly truncated insert of an unknown value. The
+# inserted lane has a single sign bit, so the result cannot come from the
+# vector alone, and an extract of that lane must not report more either.
+name: signbits_implicit_trunc_unknown_val
+body: |
+  bb.0:
+    liveins: $w0, $w1
+  ; CHECK-LABEL: name: @signbits_implicit_trunc_unknown_val
+  ; CHECK-NEXT: %w0:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %w1:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %a:_ KnownBits:???????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %s:_ KnownBits:???????????????? SignBits:9 IsKnownNeverZero:0
+  ; CHECK-NEXT: %v:_ KnownBits:???????????????? SignBits:9 IsKnownNeverZero:0
+  ; CHECK-NEXT: %idx0:_ KnownBits:0000000000000000000000000000000000000000000000000000000000000000 SignBits:64 IsKnownNeverZero:0
+  ; CHECK-NEXT: %r:_ KnownBits:???????????????? SignBits:1 IsKnownNeverZero:0
+  ; CHECK-NEXT: %e0:_ KnownBits:???????????????? SignBits:1 IsKnownNeverZero:0
+    %w0:_(i32) = COPY $w0
+    %w1:_(i32) = COPY $w1
+    %a:_(i8) = G_TRUNC %w0
+    %s:_(i16) = G_SEXT %a
+    %v:_(<4 x i16>) = G_BUILD_VECTOR %s, %s, %s, %s
+    %idx0:_(i64) = G_CONSTANT i64 0
+    %r:_(<4 x i16>) = G_INSERT_VECTOR_ELT %v, %w1, %idx0
+    %e0:_(i16) = G_EXTRACT_VECTOR_ELT %r, %idx0
+...



More information about the llvm-commits mailing list