[llvm] [AArch64][NEON] Fold insert(zero, extract(X, 0), 0) -> X when X is known to zero lanes 1-N (PR #213940)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 4 07:26:55 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-aarch64

Author: CarolineConcatto

<details>
<summary>Changes</summary>

Add the integer NEON vector-reduction ISD nodes to isLanes1toNKnownZero, allowing redundant zero initialization around insertelement operations to be eliminated.

This is valid because these instructions place their result in lane zero and clear the remaining lanes.

---
Full diff: https://github.com/llvm/llvm-project/pull/213940.diff


2 Files Affected:

- (modified) llvm/lib/Target/AArch64/AArch64ISelLowering.cpp (+6) 
- (added) llvm/test/CodeGen/AArch64/neon-implicit-zero-filling.ll (+212) 


``````````diff
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 908b16ced437b..a9e1b10dd141a 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -28987,6 +28987,12 @@ static bool isLanes1toNKnownZero(SDValue Op) {
   case AArch64ISD::UADDV_PRED:
   case AArch64ISD::UMAXV_PRED:
   case AArch64ISD::UMINV_PRED:
+  case AArch64ISD::UADDV:
+  case AArch64ISD::SADDV:
+  case AArch64ISD::SMAXV:
+  case AArch64ISD::SMINV:
+  case AArch64ISD::UMAXV:
+  case AArch64ISD::UMINV:
     return true;
   }
 }
diff --git a/llvm/test/CodeGen/AArch64/neon-implicit-zero-filling.ll b/llvm/test/CodeGen/AArch64/neon-implicit-zero-filling.ll
new file mode 100644
index 0000000000000..989e89e8c2e0d
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/neon-implicit-zero-filling.ll
@@ -0,0 +1,212 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc < %s | FileCheck %s
+
+target triple = "aarch64-unknown-linux-gnu"
+
+;; The following tests are the llvm-ir of this function
+;; In the example it is vector_reduce_add, but could be any other
+;; vector_reduce operation.
+;vec_sz_out uaddvq_sz_zero_fill(vec_sz_in a) {
+;  return (vec_sz_out){vaddvq_u8(a)};
+;}
+
+define <16 x i8> @uaddv_v16i8_zero_fill(<16 x i8> %a) {
+; CHECK-LABEL: uaddv_v16i8_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    addv b0, v0.16b
+; CHECK-NEXT:    ret
+  %t1 = call i8 @llvm.aarch64.neon.uaddv.i8.v16i8(<16 x i8> %a)
+  %t2 = insertelement <16 x i8> zeroinitializer, i8 %t1, i64 0
+  ret <16 x i8> %t2
+}
+
+define <8 x i16> @uaddv_v8i16_zero_fill(<8 x i16> %a) {
+; CHECK-LABEL: uaddv_v8i16_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    addv h0, v0.8h
+; CHECK-NEXT:    ret
+  %t1 = call i16 @llvm.aarch64.neon.uaddv.i16.v8i16(<8 x i16> %a)
+  %t2 = insertelement <8 x i16> zeroinitializer, i16 %t1, i64 0
+  ret <8 x i16> %t2
+}
+
+define <4 x i32> @uaddv_v4i32_zero_fill(<4 x i32> %a) {
+; CHECK-LABEL: uaddv_v4i32_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    addv s0, v0.4s
+; CHECK-NEXT:    ret
+  %t1 = call i32 @llvm.aarch64.neon.uaddv.i32.v4i32(<4 x i32> %a)
+  %t2 = insertelement <4 x i32> zeroinitializer, i32 %t1, i64 0
+  ret <4 x i32> %t2
+}
+
+define <16 x i8> @smaxv_v16i8_zero_fill(<16 x i8> %a) {
+; CHECK-LABEL: smaxv_v16i8_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    smaxv b0, v0.16b
+; CHECK-NEXT:    ret
+  %t1 = call i8 @llvm.aarch64.neon.smaxv.i8.v16i8(<16 x i8> %a)
+  %t2 = insertelement <16 x i8> zeroinitializer, i8 %t1, i64 0
+  ret <16 x i8> %t2
+}
+
+define <8 x i16> @smaxv_v8i16_zero_fill(<8 x i16> %a) {
+; CHECK-LABEL: smaxv_v8i16_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    smaxv h0, v0.8h
+; CHECK-NEXT:    ret
+  %t1 = call i16 @llvm.aarch64.neon.smaxv.i16.v8i16(<8 x i16> %a)
+  %t2 = insertelement <8 x i16> zeroinitializer, i16 %t1, i64 0
+  ret <8 x i16> %t2
+}
+
+define <4 x i32> @smaxv_v4i32_zero_fill(<4 x i32> %a) {
+; CHECK-LABEL: smaxv_v4i32_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    smaxv s0, v0.4s
+; CHECK-NEXT:    ret
+  %t1 = call i32 @llvm.aarch64.neon.smaxv.i32.v4i32(<4 x i32> %a)
+  %t2 = insertelement <4 x i32> zeroinitializer, i32 %t1, i64 0
+  ret <4 x i32> %t2
+}
+
+define <16 x i8> @sminv_v16i8_zero_fill(<16 x i8> %a) {
+; CHECK-LABEL: sminv_v16i8_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sminv b0, v0.16b
+; CHECK-NEXT:    ret
+  %t1 = call i8 @llvm.aarch64.neon.sminv.i8.v16i8(<16 x i8> %a)
+  %t2 = insertelement <16 x i8> zeroinitializer, i8 %t1, i64 0
+  ret <16 x i8> %t2
+}
+
+define <8 x i16> @sminv_v8i16_zero_fill(<8 x i16> %a) {
+; CHECK-LABEL: sminv_v8i16_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sminv h0, v0.8h
+; CHECK-NEXT:    ret
+  %t1 = call i16 @llvm.aarch64.neon.sminv.i16.v8i16(<8 x i16> %a)
+  %t2 = insertelement <8 x i16> zeroinitializer, i16 %t1, i64 0
+  ret <8 x i16> %t2
+}
+
+define <4 x i32> @sminv_v4i32_zero_fill(<4 x i32> %a) {
+; CHECK-LABEL: sminv_v4i32_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sminv s0, v0.4s
+; CHECK-NEXT:    ret
+  %t1 = call i32 @llvm.aarch64.neon.sminv.i32.v4i32(<4 x i32> %a)
+  %t2 = insertelement <4 x i32> zeroinitializer, i32 %t1, i64 0
+  ret <4 x i32> %t2
+}
+
+define <16 x i8> @umaxv_v16i8_zero_fill(<16 x i8> %a) {
+; CHECK-LABEL: umaxv_v16i8_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    umaxv b0, v0.16b
+; CHECK-NEXT:    ret
+  %t1 = call i8 @llvm.aarch64.neon.umaxv.i8.v16i8(<16 x i8> %a)
+  %t2 = insertelement <16 x i8> zeroinitializer, i8 %t1, i64 0
+  ret <16 x i8> %t2
+}
+
+define <8 x i16> @umaxv_v8i16_zero_fill(<8 x i16> %a) {
+; CHECK-LABEL: umaxv_v8i16_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    umaxv h0, v0.8h
+; CHECK-NEXT:    ret
+  %t1 = call i16 @llvm.aarch64.neon.umaxv.i16.v8i16(<8 x i16> %a)
+  %t2 = insertelement <8 x i16> zeroinitializer, i16 %t1, i64 0
+  ret <8 x i16> %t2
+}
+
+define <4 x i32> @umaxv_v4i32_zero_fill(<4 x i32> %a) {
+; CHECK-LABEL: umaxv_v4i32_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    umaxv s0, v0.4s
+; CHECK-NEXT:    ret
+  %t1 = call i32 @llvm.aarch64.neon.umaxv.i32.v4i32(<4 x i32> %a)
+  %t2 = insertelement <4 x i32> zeroinitializer, i32 %t1, i64 0
+  ret <4 x i32> %t2
+}
+
+define <16 x i8> @uminv_v16i8_zero_fill(<16 x i8> %a) {
+; CHECK-LABEL: uminv_v16i8_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    uminv b0, v0.16b
+; CHECK-NEXT:    ret
+  %t1 = call i8 @llvm.aarch64.neon.uminv.i8.v16i8(<16 x i8> %a)
+  %t2 = insertelement <16 x i8> zeroinitializer, i8 %t1, i64 0
+  ret <16 x i8> %t2
+}
+
+define <8 x i16> @uminv_v8i16_zero_fill(<8 x i16> %a) {
+; CHECK-LABEL: uminv_v8i16_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    uminv h0, v0.8h
+; CHECK-NEXT:    ret
+  %t1 = call i16 @llvm.aarch64.neon.uminv.i16.v8i16(<8 x i16> %a)
+  %t2 = insertelement <8 x i16> zeroinitializer, i16 %t1, i64 0
+  ret <8 x i16> %t2
+}
+
+; Ensure we rely on the reduction's implicit zero filling.
+define <4 x i32> @uminv_v4i32_zero_fill(<4 x i32> %a) {
+; CHECK-LABEL: uminv_v4i32_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    uminv s0, v0.4s
+; CHECK-NEXT:    ret
+  %t1 = call i32 @llvm.aarch64.neon.uminv.i32.v4i32(<4 x i32> %a)
+  %t2 = insertelement <4 x i32> zeroinitializer, i32 %t1, i64 0
+  ret <4 x i32> %t2
+}
+
+define <16 x i8> @saddv_v16i8_zero_fill(<16 x i8> %a) {
+; CHECK-LABEL: saddv_v16i8_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    addv b0, v0.16b
+; CHECK-NEXT:    ret
+  %t1 = call i8 @llvm.aarch64.neon.saddv.i8.v16i8(<16 x i8> %a)
+  %t2 = insertelement <16 x i8> zeroinitializer, i8 %t1, i64 0
+  ret <16 x i8> %t2
+}
+
+define <4 x float> @faddv_v4f32_zero_fill(<4 x float> %a) {
+; CHECK-LABEL: faddv_v4f32_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    faddp v0.4s, v0.4s, v0.4s
+; CHECK-NEXT:    movi v1.2d, #0000000000000000
+; CHECK-NEXT:    faddp s0, v0.2s
+; CHECK-NEXT:    mov v1.s[0], v0.s[0]
+; CHECK-NEXT:    mov v0.16b, v1.16b
+; CHECK-NEXT:    ret
+  %t1 = call float @llvm.aarch64.neon.faddv.f32.v4f32(<4 x float> %a)
+  %t2 = insertelement <4 x float> zeroinitializer, float %t1, i64 0
+  ret <4 x float> %t2
+}
+
+define <4 x float> @fminv_v4f32_zero_fill(<4 x float> %a) {
+; CHECK-LABEL: fminv_v4f32_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    movi v1.2d, #0000000000000000
+; CHECK-NEXT:    fminv s0, v0.4s
+; CHECK-NEXT:    mov v1.s[0], v0.s[0]
+; CHECK-NEXT:    mov v0.16b, v1.16b
+; CHECK-NEXT:    ret
+  %t1 = call float @llvm.aarch64.neon.fminv.f32.v4f32(<4 x float> %a)
+  %t2 = insertelement <4 x float> zeroinitializer, float %t1, i64 0
+  ret <4 x float> %t2
+}
+
+define <4 x float> @fmaxv_v4f32_zero_fill(<4 x float> %a) {
+; CHECK-LABEL: fmaxv_v4f32_zero_fill:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    movi v1.2d, #0000000000000000
+; CHECK-NEXT:    fmaxv s0, v0.4s
+; CHECK-NEXT:    mov v1.s[0], v0.s[0]
+; CHECK-NEXT:    mov v0.16b, v1.16b
+; CHECK-NEXT:    ret
+  %t1 = call float @llvm.aarch64.neon.fmaxv.f32.v4f32(<4 x float> %a)
+  %t2 = insertelement <4 x float> zeroinitializer, float %t1, i64 0
+  ret <4 x float> %t2
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/213940


More information about the llvm-commits mailing list