[llvm] [X86] Fold scalar ABS_MIN_POISON store of a load to PABS (PR #216697)

Shaurya Srivastava via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 17 04:16:02 PDT 2026


https://github.com/Shaurya2k06 created https://github.com/llvm/llvm-project/pull/216697

## Summary
Extend the scalar load→abs→store SIMD fold from #210654 to
`ISD::ABS_MIN_POISON` (`llvm.abs(..., i1 true)`).

That form was left on `neg`+`cmov` while `llvm.abs(..., i1 false)` already
used `PABS`. Vector `PABS` is `ISD::ABS`; `PABS(INT_MIN)` is `INT_MIN`,
which is a valid refinement of poison.

## Test plan
- [x] `llvm/test/CodeGen/X86/scalar-minmax-simd.ll` — `store_abs_*_int_min_poison`
- [x] `llvm/test/CodeGen/X86/abs.ll`

>From e43b7bef63d64163803f20ff777d853670889c8d Mon Sep 17 00:00:00 2001
From: shaurya2k06 <shaurya2k06 at gmail.com>
Date: Mon, 17 Aug 2026 16:34:19 +0530
Subject: [PATCH] [X86] Fold scalar ABS_MIN_POISON store of a load to PABS

llvm.abs(..., i1 true) is ISD::ABS_MIN_POISON and was left on neg+cmov
while the defined-INT_MIN form already used PABS. Treat both abs opcodes
the same: vector PABS is ISD::ABS, and PABS(INT_MIN) is a valid
refinement of poison.

Assisted-by: Cursor
Signed-off-by: shaurya2k06 <shaurya2k06 at gmail.com>
---
 llvm/lib/Target/X86/X86ISelLowering.cpp     |   7 +-
 llvm/test/CodeGen/X86/scalar-minmax-simd.ll | 205 ++++++++++++++++++++
 2 files changed, 210 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 0dc3340a13c73..e89afce282602 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -54946,7 +54946,7 @@ static SDValue combineScalarMinMaxAbsStore(StoreSDNode *St, const SDLoc &DL,
 
   SDValue StoredVal = St->getValue();
   unsigned Opc = StoredVal.getOpcode();
-  bool IsAbs = Opc == ISD::ABS;
+  bool IsAbs = ISD::isAbsOpcode(Opc);
   if ((!IsAbs && !ISD::isMinMaxOpcode(Opc)) || !StoredVal.hasOneUse())
     return SDValue();
 
@@ -54970,7 +54970,10 @@ static SDValue combineScalarMinMaxAbsStore(StoreSDNode *St, const SDLoc &DL,
     else
       return std::nullopt;
 
-    if (!DAG.getTargetLoweringInfo().isOperationLegal(Opc, VecVT))
+    // Vector PABS is ISD::ABS; ABS_MIN_POISON is Expand. PABS(INT_MIN) is
+    // INT_MIN, which is a valid refinement of poison.
+    unsigned LegalOpc = IsAbs ? (unsigned)ISD::ABS : Opc;
+    if (!DAG.getTargetLoweringInfo().isOperationLegal(LegalOpc, VecVT))
       return std::nullopt;
     return VecVT;
   };
diff --git a/llvm/test/CodeGen/X86/scalar-minmax-simd.ll b/llvm/test/CodeGen/X86/scalar-minmax-simd.ll
index f49c6bb34e781..0045a54f09488 100644
--- a/llvm/test/CodeGen/X86/scalar-minmax-simd.ll
+++ b/llvm/test/CodeGen/X86/scalar-minmax-simd.ll
@@ -811,6 +811,66 @@ define void @store_abs_i32(ptr %p, ptr %r) nounwind {
   ret void
 }
 
+; llvm.abs(..., i1 true) is ISD::ABS_MIN_POISON; PABS is still valid.
+define void @store_abs_i32_int_min_poison(ptr %p, ptr %r) nounwind {
+; SSE2-LABEL: store_abs_i32_int_min_poison:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl %eax, %ecx
+; SSE2-NEXT:    negl %ecx
+; SSE2-NEXT:    cmovsl %eax, %ecx
+; SSE2-NEXT:    movl %ecx, (%rsi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_abs_i32_int_min_poison:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; SSE41-NEXT:    pabsd %xmm0, %xmm0
+; SSE41-NEXT:    movd %xmm0, (%rsi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_abs_i32_int_min_poison:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    pabsd %xmm0, %xmm0
+; X86-SSE41-NEXT:    movd %xmm0, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_abs_i32_int_min_poison:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX-NEXT:    vpabsd %xmm0, %xmm0
+; AVX-NEXT:    vmovd %xmm0, (%rsi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_abs_i32_int_min_poison:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vpabsd %xmm0, %xmm0
+; AVX512F-NEXT:    vmovd %xmm0, (%rsi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_abs_i32_int_min_poison:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vpabsd %xmm0, %xmm0
+; AVX512VL-NEXT:    vmovd %xmm0, (%rsi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_abs_i32_int_min_poison:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vpabsd %xmm0, %xmm0
+; AVX512FP16-NEXT:    vmovd %xmm0, (%rsi)
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %a = call i32 @llvm.abs.i32(i32 %x, i1 true)
+  store i32 %a, ptr %r, align 4
+  ret void
+}
+
 ; Negative: register operand / GPR result should stay on neg+cmov.
 define i32 @abs_gpr(i32 %x) nounwind {
 ; SSE2-LABEL: abs_gpr:
@@ -1260,6 +1320,81 @@ define void @store_abs_i64(ptr %p, ptr %r) nounwind {
   ret void
 }
 
+; llvm.abs(..., i1 true) is ISD::ABS_MIN_POISON; PABS is still valid.
+define void @store_abs_i64_int_min_poison(ptr %p, ptr %r) nounwind {
+; SSE2-LABEL: store_abs_i64_int_min_poison:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movq (%rdi), %rax
+; SSE2-NEXT:    movq %rax, %rcx
+; SSE2-NEXT:    negq %rcx
+; SSE2-NEXT:    cmovsq %rax, %rcx
+; SSE2-NEXT:    movq %rcx, (%rsi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_abs_i64_int_min_poison:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movq (%rdi), %rax
+; SSE41-NEXT:    movq %rax, %rcx
+; SSE41-NEXT:    negq %rcx
+; SSE41-NEXT:    cmovsq %rax, %rcx
+; SSE41-NEXT:    movq %rcx, (%rsi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_abs_i64_int_min_poison:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    pushl %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl 4(%ecx), %edx
+; X86-SSE41-NEXT:    movl %edx, %esi
+; X86-SSE41-NEXT:    sarl $31, %esi
+; X86-SSE41-NEXT:    xorl %esi, %edx
+; X86-SSE41-NEXT:    movl (%ecx), %ecx
+; X86-SSE41-NEXT:    xorl %esi, %ecx
+; X86-SSE41-NEXT:    subl %esi, %ecx
+; X86-SSE41-NEXT:    sbbl %esi, %edx
+; X86-SSE41-NEXT:    movl %ecx, (%eax)
+; X86-SSE41-NEXT:    movl %edx, 4(%eax)
+; X86-SSE41-NEXT:    popl %esi
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_abs_i64_int_min_poison:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movq (%rdi), %rax
+; AVX-NEXT:    movq %rax, %rcx
+; AVX-NEXT:    negq %rcx
+; AVX-NEXT:    cmovsq %rax, %rcx
+; AVX-NEXT:    movq %rcx, (%rsi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_abs_i64_int_min_poison:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    vmovq {{.*#+}} xmm0 = mem[0],zero
+; AVX512F-NEXT:    vpabsq %zmm0, %zmm0
+; AVX512F-NEXT:    vmovq %xmm0, (%rsi)
+; AVX512F-NEXT:    vzeroupper
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_abs_i64_int_min_poison:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovq {{.*#+}} xmm0 = mem[0],zero
+; AVX512VL-NEXT:    vpabsq %xmm0, %xmm0
+; AVX512VL-NEXT:    vmovq %xmm0, (%rsi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_abs_i64_int_min_poison:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovq {{.*#+}} xmm0 = mem[0],zero
+; AVX512FP16-NEXT:    vpabsq %zmm0, %zmm0
+; AVX512FP16-NEXT:    vmovq %xmm0, (%rsi)
+; AVX512FP16-NEXT:    vzeroupper
+; AVX512FP16-NEXT:    retq
+  %x = load i64, ptr %p, align 8
+  %a = call i64 @llvm.abs.i64(i64 %x, i1 true)
+  store i64 %a, ptr %r, align 8
+  ret void
+}
+
 ; Negative: register / GPR-result i64 stays on cmp+cmov.
 define i64 @smin_gpr_i64(i64 %x, i64 %y) nounwind {
 ; SSE2-LABEL: smin_gpr_i64:
@@ -1723,3 +1858,73 @@ define void @store_abs_i16(ptr %p, ptr %r) nounwind {
   store i16 %a, ptr %r, align 2
   ret void
 }
+
+; llvm.abs(..., i1 true) is ISD::ABS_MIN_POISON; PABS is still valid.
+define void @store_abs_i16_int_min_poison(ptr %p, ptr %r) nounwind {
+; SSE2-LABEL: store_abs_i16_int_min_poison:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movzwl (%rdi), %eax
+; SSE2-NEXT:    movl %eax, %ecx
+; SSE2-NEXT:    negw %cx
+; SSE2-NEXT:    cmovsw %ax, %cx
+; SSE2-NEXT:    movw %cx, (%rsi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_abs_i16_int_min_poison:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movzwl (%rdi), %eax
+; SSE41-NEXT:    movl %eax, %ecx
+; SSE41-NEXT:    negw %cx
+; SSE41-NEXT:    cmovsw %ax, %cx
+; SSE41-NEXT:    movw %cx, (%rsi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_abs_i16_int_min_poison:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movzwl (%ecx), %ecx
+; X86-SSE41-NEXT:    movl %ecx, %edx
+; X86-SSE41-NEXT:    negw %dx
+; X86-SSE41-NEXT:    cmovsw %cx, %dx
+; X86-SSE41-NEXT:    movw %dx, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_abs_i16_int_min_poison:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movzwl (%rdi), %eax
+; AVX-NEXT:    movl %eax, %ecx
+; AVX-NEXT:    negw %cx
+; AVX-NEXT:    cmovsw %ax, %cx
+; AVX-NEXT:    movw %cx, (%rsi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_abs_i16_int_min_poison:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movzwl (%rdi), %eax
+; AVX512F-NEXT:    movl %eax, %ecx
+; AVX512F-NEXT:    negw %cx
+; AVX512F-NEXT:    cmovsw %ax, %cx
+; AVX512F-NEXT:    movw %cx, (%rsi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_abs_i16_int_min_poison:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movzwl (%rdi), %eax
+; AVX512VL-NEXT:    movl %eax, %ecx
+; AVX512VL-NEXT:    negw %cx
+; AVX512VL-NEXT:    cmovsw %ax, %cx
+; AVX512VL-NEXT:    movw %cx, (%rsi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_abs_i16_int_min_poison:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovw (%rdi), %xmm0
+; AVX512FP16-NEXT:    vpabsw %xmm0, %xmm0
+; AVX512FP16-NEXT:    vpextrw $0, %xmm0, (%rsi)
+; AVX512FP16-NEXT:    retq
+  %x = load i16, ptr %p, align 2
+  %a = call i16 @llvm.abs.i16(i16 %x, i1 true)
+  store i16 %a, ptr %r, align 2
+  ret void
+}



More information about the llvm-commits mailing list