[llvm] [ARM] Special case scmp 0 (PR #207428)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 3 09:43:44 PDT 2026


https://github.com/AZero13 created https://github.com/llvm/llvm-project/pull/207428

We can use cmp instead of subs for scmp 0, and we can do shifts for scmp 0 too.

>From 95f74ef513d7f5cc9ed3a3262bc09e78f0c3b234 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 3 Jul 2026 12:39:47 -0400
Subject: [PATCH 1/2] [ARM] Precommit tests for scmp/ucmp with 0. NFC.

---
 llvm/test/CodeGen/ARM/scmp.ll | 287 +++++++++++++++++++++++++++++++++-
 llvm/test/CodeGen/ARM/ucmp.ll | 198 ++++++++++++++++++++++-
 2 files changed, 483 insertions(+), 2 deletions(-)

diff --git a/llvm/test/CodeGen/ARM/scmp.ll b/llvm/test/CodeGen/ARM/scmp.ll
index 9189aee6aaf43..168a3f90be33c 100644
--- a/llvm/test/CodeGen/ARM/scmp.ll
+++ b/llvm/test/CodeGen/ARM/scmp.ll
@@ -1,6 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
 ; RUN: llc -mtriple=armv7-unknown-eabi %s -o - | FileCheck %s
-
+; RUN: llc -mtriple=thumbv6m-unknown-eabi %s -o - | FileCheck %s --check-prefix=THUMB
 define i8 @scmp_8_8(i8 signext %x, i8 signext %y) nounwind {
 ; CHECK-LABEL: scmp_8_8:
 ; CHECK:       @ %bb.0:
@@ -8,6 +8,26 @@ define i8 @scmp_8_8(i8 signext %x, i8 signext %y) nounwind {
 ; CHECK-NEXT:    movwgt r0, #1
 ; CHECK-NEXT:    mvnlt r0, #0
 ; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: scmp_8_8:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    movs r2, #1
+; THUMB-NEXT:    movs r3, #0
+; THUMB-NEXT:    cmp r0, r1
+; THUMB-NEXT:    mov r0, r2
+; THUMB-NEXT:    bge .LBB0_3
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    ble .LBB0_4
+; THUMB-NEXT:  .LBB0_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
+; THUMB-NEXT:  .LBB0_3:
+; THUMB-NEXT:    mov r0, r3
+; THUMB-NEXT:    bgt .LBB0_2
+; THUMB-NEXT:  .LBB0_4:
+; THUMB-NEXT:    mov r2, r3
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
   %1 = call i8 @llvm.scmp(i8 %x, i8 %y)
   ret i8 %1
 }
@@ -19,6 +39,26 @@ define i8 @scmp_8_16(i16 signext %x, i16 signext %y) nounwind {
 ; CHECK-NEXT:    movwgt r0, #1
 ; CHECK-NEXT:    mvnlt r0, #0
 ; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: scmp_8_16:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    movs r2, #1
+; THUMB-NEXT:    movs r3, #0
+; THUMB-NEXT:    cmp r0, r1
+; THUMB-NEXT:    mov r0, r2
+; THUMB-NEXT:    bge .LBB1_3
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    ble .LBB1_4
+; THUMB-NEXT:  .LBB1_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
+; THUMB-NEXT:  .LBB1_3:
+; THUMB-NEXT:    mov r0, r3
+; THUMB-NEXT:    bgt .LBB1_2
+; THUMB-NEXT:  .LBB1_4:
+; THUMB-NEXT:    mov r2, r3
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
   %1 = call i8 @llvm.scmp(i16 %x, i16 %y)
   ret i8 %1
 }
@@ -30,6 +70,26 @@ define i8 @scmp_8_32(i32 %x, i32 %y) nounwind {
 ; CHECK-NEXT:    movwgt r0, #1
 ; CHECK-NEXT:    mvnlt r0, #0
 ; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: scmp_8_32:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    movs r2, #1
+; THUMB-NEXT:    movs r3, #0
+; THUMB-NEXT:    cmp r0, r1
+; THUMB-NEXT:    mov r0, r2
+; THUMB-NEXT:    bge .LBB2_3
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    ble .LBB2_4
+; THUMB-NEXT:  .LBB2_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
+; THUMB-NEXT:  .LBB2_3:
+; THUMB-NEXT:    mov r0, r3
+; THUMB-NEXT:    bgt .LBB2_2
+; THUMB-NEXT:  .LBB2_4:
+; THUMB-NEXT:    mov r2, r3
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
   %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
   ret i8 %1
 }
@@ -49,6 +109,29 @@ define i8 @scmp_8_64(i64 %x, i64 %y) nounwind {
 ; CHECK-NEXT:    movwlt r12, #1
 ; CHECK-NEXT:    sub r0, r12, lr
 ; CHECK-NEXT:    pop {r11, pc}
+;
+; THUMB-LABEL: scmp_8_64:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    .save {r4, r5, r6, lr}
+; THUMB-NEXT:    push {r4, r5, r6, lr}
+; THUMB-NEXT:    movs r4, #1
+; THUMB-NEXT:    movs r5, #0
+; THUMB-NEXT:    subs r6, r0, r2
+; THUMB-NEXT:    mov r6, r1
+; THUMB-NEXT:    sbcs r6, r3
+; THUMB-NEXT:    mov r6, r4
+; THUMB-NEXT:    blt .LBB3_2
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    mov r6, r5
+; THUMB-NEXT:  .LBB3_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    sbcs r3, r1
+; THUMB-NEXT:    blt .LBB3_4
+; THUMB-NEXT:  @ %bb.3:
+; THUMB-NEXT:    mov r4, r5
+; THUMB-NEXT:  .LBB3_4:
+; THUMB-NEXT:    subs r0, r4, r6
+; THUMB-NEXT:    pop {r4, r5, r6, pc}
   %1 = call i8 @llvm.scmp(i64 %x, i64 %y)
   ret i8 %1
 }
@@ -76,6 +159,50 @@ define i8 @scmp_8_128(i128 %x, i128 %y) nounwind {
 ; CHECK-NEXT:    movwlt r5, #1
 ; CHECK-NEXT:    sub r0, r5, r7
 ; CHECK-NEXT:    pop {r4, r5, r6, r7, r11, pc}
+;
+; THUMB-LABEL: scmp_8_128:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    .save {r4, r5, r6, r7, lr}
+; THUMB-NEXT:    push {r4, r5, r6, r7, lr}
+; THUMB-NEXT:    .pad #20
+; THUMB-NEXT:    sub sp, #20
+; THUMB-NEXT:    str r3, [sp, #16] @ 4-byte Spill
+; THUMB-NEXT:    movs r3, #1
+; THUMB-NEXT:    str r3, [sp] @ 4-byte Spill
+; THUMB-NEXT:    movs r3, #0
+; THUMB-NEXT:    str r3, [sp, #12] @ 4-byte Spill
+; THUMB-NEXT:    ldr r6, [sp, #52]
+; THUMB-NEXT:    add r7, sp, #40
+; THUMB-NEXT:    ldm r7, {r3, r5, r7}
+; THUMB-NEXT:    subs r4, r0, r3
+; THUMB-NEXT:    str r1, [sp, #4] @ 4-byte Spill
+; THUMB-NEXT:    mov r4, r1
+; THUMB-NEXT:    ldr r1, [sp] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r4, r5
+; THUMB-NEXT:    str r2, [sp, #8] @ 4-byte Spill
+; THUMB-NEXT:    mov r4, r2
+; THUMB-NEXT:    sbcs r4, r7
+; THUMB-NEXT:    ldr r4, [sp, #16] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r4, r6
+; THUMB-NEXT:    mov r2, r1
+; THUMB-NEXT:    blt .LBB4_2
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    ldr r2, [sp, #12] @ 4-byte Reload
+; THUMB-NEXT:  .LBB4_2:
+; THUMB-NEXT:    subs r0, r3, r0
+; THUMB-NEXT:    ldr r0, [sp, #4] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r5, r0
+; THUMB-NEXT:    ldr r0, [sp, #8] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r7, r0
+; THUMB-NEXT:    ldr r0, [sp, #16] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r6, r0
+; THUMB-NEXT:    blt .LBB4_4
+; THUMB-NEXT:  @ %bb.3:
+; THUMB-NEXT:    ldr r1, [sp, #12] @ 4-byte Reload
+; THUMB-NEXT:  .LBB4_4:
+; THUMB-NEXT:    subs r0, r1, r2
+; THUMB-NEXT:    add sp, #20
+; THUMB-NEXT:    pop {r4, r5, r6, r7, pc}
   %1 = call i8 @llvm.scmp(i128 %x, i128 %y)
   ret i8 %1
 }
@@ -87,6 +214,26 @@ define i32 @scmp_32_32(i32 %x, i32 %y) nounwind {
 ; CHECK-NEXT:    movwgt r0, #1
 ; CHECK-NEXT:    mvnlt r0, #0
 ; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: scmp_32_32:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    movs r2, #1
+; THUMB-NEXT:    movs r3, #0
+; THUMB-NEXT:    cmp r0, r1
+; THUMB-NEXT:    mov r0, r2
+; THUMB-NEXT:    bge .LBB5_3
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    ble .LBB5_4
+; THUMB-NEXT:  .LBB5_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
+; THUMB-NEXT:  .LBB5_3:
+; THUMB-NEXT:    mov r0, r3
+; THUMB-NEXT:    bgt .LBB5_2
+; THUMB-NEXT:  .LBB5_4:
+; THUMB-NEXT:    mov r2, r3
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
   %1 = call i32 @llvm.scmp(i32 %x, i32 %y)
   ret i32 %1
 }
@@ -98,6 +245,26 @@ define i32 @scmp_neg(i32 %x, i32 %y) nounwind {
 ; CHECK-NEXT:    movwgt r0, #1
 ; CHECK-NEXT:    mvnlt r0, #0
 ; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: scmp_neg:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    movs r2, #1
+; THUMB-NEXT:    movs r3, #0
+; THUMB-NEXT:    cmn r0, r1
+; THUMB-NEXT:    mov r0, r2
+; THUMB-NEXT:    bge .LBB6_3
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    ble .LBB6_4
+; THUMB-NEXT:  .LBB6_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
+; THUMB-NEXT:  .LBB6_3:
+; THUMB-NEXT:    mov r0, r3
+; THUMB-NEXT:    bgt .LBB6_2
+; THUMB-NEXT:  .LBB6_4:
+; THUMB-NEXT:    mov r2, r3
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    bx lr
   %yy = sub nsw i32 0, %y
   %1 = call i32 @llvm.scmp(i32 %x, i32 %yy)
   ret i32 %1
@@ -118,6 +285,29 @@ define i32 @scmp_32_64(i64 %x, i64 %y) nounwind {
 ; CHECK-NEXT:    movwlt r12, #1
 ; CHECK-NEXT:    sub r0, r12, lr
 ; CHECK-NEXT:    pop {r11, pc}
+;
+; THUMB-LABEL: scmp_32_64:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    .save {r4, r5, r6, lr}
+; THUMB-NEXT:    push {r4, r5, r6, lr}
+; THUMB-NEXT:    movs r4, #1
+; THUMB-NEXT:    movs r5, #0
+; THUMB-NEXT:    subs r6, r0, r2
+; THUMB-NEXT:    mov r6, r1
+; THUMB-NEXT:    sbcs r6, r3
+; THUMB-NEXT:    mov r6, r4
+; THUMB-NEXT:    blt .LBB7_2
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    mov r6, r5
+; THUMB-NEXT:  .LBB7_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    sbcs r3, r1
+; THUMB-NEXT:    blt .LBB7_4
+; THUMB-NEXT:  @ %bb.3:
+; THUMB-NEXT:    mov r4, r5
+; THUMB-NEXT:  .LBB7_4:
+; THUMB-NEXT:    subs r0, r4, r6
+; THUMB-NEXT:    pop {r4, r5, r6, pc}
   %1 = call i32 @llvm.scmp(i64 %x, i64 %y)
   ret i32 %1
 }
@@ -138,6 +328,101 @@ define i64 @scmp_64_64(i64 %x, i64 %y) nounwind {
 ; CHECK-NEXT:    sub r0, r12, lr
 ; CHECK-NEXT:    asr r1, r0, #31
 ; CHECK-NEXT:    pop {r11, pc}
+;
+; THUMB-LABEL: scmp_64_64:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    .save {r4, r5, r6, lr}
+; THUMB-NEXT:    push {r4, r5, r6, lr}
+; THUMB-NEXT:    movs r4, #1
+; THUMB-NEXT:    movs r5, #0
+; THUMB-NEXT:    subs r6, r0, r2
+; THUMB-NEXT:    mov r6, r1
+; THUMB-NEXT:    sbcs r6, r3
+; THUMB-NEXT:    mov r6, r4
+; THUMB-NEXT:    blt .LBB8_2
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    mov r6, r5
+; THUMB-NEXT:  .LBB8_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    sbcs r3, r1
+; THUMB-NEXT:    blt .LBB8_4
+; THUMB-NEXT:  @ %bb.3:
+; THUMB-NEXT:    mov r4, r5
+; THUMB-NEXT:  .LBB8_4:
+; THUMB-NEXT:    subs r0, r4, r6
+; THUMB-NEXT:    asrs r1, r0, #31
+; THUMB-NEXT:    pop {r4, r5, r6, pc}
   %1 = call i64 @llvm.scmp(i64 %x, i64 %y)
   ret i64 %1
 }
+
+define i32 @scmp_32_32_zero(i32 %x) nounwind {
+; CHECK-LABEL: scmp_32_32_zero:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    mov r1, #0
+; CHECK-NEXT:    cmp r0, #0
+; CHECK-NEXT:    movwgt r1, #1
+; CHECK-NEXT:    mvnlt r1, #0
+; CHECK-NEXT:    mov r0, r1
+; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: scmp_32_32_zero:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    asrs r1, r0, #31
+; THUMB-NEXT:    rsbs r0, r0, #0
+; THUMB-NEXT:    lsrs r0, r0, #31
+; THUMB-NEXT:    orrs r0, r1
+; THUMB-NEXT:    bx lr
+  %1 = call i32 @llvm.scmp(i32 %x, i32 0)
+  ret i32 %1
+}
+
+define i8 @scmp_8_32_zero(i32 %x) nounwind {
+; CHECK-LABEL: scmp_8_32_zero:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    mov r1, #0
+; CHECK-NEXT:    cmp r0, #0
+; CHECK-NEXT:    movwgt r1, #1
+; CHECK-NEXT:    mvnlt r1, #0
+; CHECK-NEXT:    mov r0, r1
+; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: scmp_8_32_zero:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    asrs r1, r0, #31
+; THUMB-NEXT:    rsbs r0, r0, #0
+; THUMB-NEXT:    lsrs r0, r0, #31
+; THUMB-NEXT:    orrs r0, r1
+; THUMB-NEXT:    bx lr
+  %1 = call i8 @llvm.scmp(i32 %x, i32 0)
+  ret i8 %1
+}
+
+define i64 @scmp_64_64_zero(i64 %x) nounwind {
+; CHECK-LABEL: scmp_64_64_zero:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    rsbs r0, r0, #0
+; CHECK-NEXT:    mov r2, #0
+; CHECK-NEXT:    rscs r0, r1, #0
+; CHECK-NEXT:    movwlt r2, #1
+; CHECK-NEXT:    sub r0, r2, r1, lsr #31
+; CHECK-NEXT:    asr r1, r0, #31
+; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: scmp_64_64_zero:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    movs r2, #0
+; THUMB-NEXT:    rsbs r0, r0, #0
+; THUMB-NEXT:    mov r0, r2
+; THUMB-NEXT:    sbcs r0, r1
+; THUMB-NEXT:    bge .LBB11_2
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    movs r2, #1
+; THUMB-NEXT:  .LBB11_2:
+; THUMB-NEXT:    lsrs r0, r1, #31
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    asrs r1, r0, #31
+; THUMB-NEXT:    bx lr
+  %1 = call i64 @llvm.scmp(i64 %x, i64 0)
+  ret i64 %1
+}
diff --git a/llvm/test/CodeGen/ARM/ucmp.ll b/llvm/test/CodeGen/ARM/ucmp.ll
index bb0201454d1ea..6cafe778e9a31 100644
--- a/llvm/test/CodeGen/ARM/ucmp.ll
+++ b/llvm/test/CodeGen/ARM/ucmp.ll
@@ -1,6 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
 ; RUN: llc -mtriple=armv7-unknown-eabi %s -o - | FileCheck %s
-
+; RUN: llc -mtriple=thumbv6m-unknown-eabi %s -o - | FileCheck %s --check-prefix=THUMB
 define i8 @ucmp_8_8(i8 zeroext %x, i8 zeroext %y) nounwind {
 ; CHECK-LABEL: ucmp_8_8:
 ; CHECK:       @ %bb.0:
@@ -8,6 +8,15 @@ define i8 @ucmp_8_8(i8 zeroext %x, i8 zeroext %y) nounwind {
 ; CHECK-NEXT:    movwhi r0, #1
 ; CHECK-NEXT:    mvnlo r0, #0
 ; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: ucmp_8_8:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    subs r2, r0, r1
+; THUMB-NEXT:    sbcs r2, r2
+; THUMB-NEXT:    cmp r1, r0
+; THUMB-NEXT:    sbcs r1, r1
+; THUMB-NEXT:    subs r0, r2, r1
+; THUMB-NEXT:    bx lr
   %1 = call i8 @llvm.ucmp(i8 %x, i8 %y)
   ret i8 %1
 }
@@ -19,6 +28,15 @@ define i8 @ucmp_8_16(i16 zeroext %x, i16 zeroext %y) nounwind {
 ; CHECK-NEXT:    movwhi r0, #1
 ; CHECK-NEXT:    mvnlo r0, #0
 ; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: ucmp_8_16:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    subs r2, r0, r1
+; THUMB-NEXT:    sbcs r2, r2
+; THUMB-NEXT:    cmp r1, r0
+; THUMB-NEXT:    sbcs r1, r1
+; THUMB-NEXT:    subs r0, r2, r1
+; THUMB-NEXT:    bx lr
   %1 = call i8 @llvm.ucmp(i16 %x, i16 %y)
   ret i8 %1
 }
@@ -30,6 +48,15 @@ define i8 @ucmp_8_32(i32 %x, i32 %y) nounwind {
 ; CHECK-NEXT:    movwhi r0, #1
 ; CHECK-NEXT:    mvnlo r0, #0
 ; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: ucmp_8_32:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    subs r2, r0, r1
+; THUMB-NEXT:    sbcs r2, r2
+; THUMB-NEXT:    cmp r1, r0
+; THUMB-NEXT:    sbcs r1, r1
+; THUMB-NEXT:    subs r0, r2, r1
+; THUMB-NEXT:    bx lr
   %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
   ret i8 %1
 }
@@ -49,6 +76,29 @@ define i8 @ucmp_8_64(i64 %x, i64 %y) nounwind {
 ; CHECK-NEXT:    movwlo r12, #1
 ; CHECK-NEXT:    sub r0, r12, lr
 ; CHECK-NEXT:    pop {r11, pc}
+;
+; THUMB-LABEL: ucmp_8_64:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    .save {r4, r5, r6, lr}
+; THUMB-NEXT:    push {r4, r5, r6, lr}
+; THUMB-NEXT:    movs r4, #1
+; THUMB-NEXT:    movs r5, #0
+; THUMB-NEXT:    subs r6, r0, r2
+; THUMB-NEXT:    mov r6, r1
+; THUMB-NEXT:    sbcs r6, r3
+; THUMB-NEXT:    mov r6, r4
+; THUMB-NEXT:    blo .LBB3_2
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    mov r6, r5
+; THUMB-NEXT:  .LBB3_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    sbcs r3, r1
+; THUMB-NEXT:    blo .LBB3_4
+; THUMB-NEXT:  @ %bb.3:
+; THUMB-NEXT:    mov r4, r5
+; THUMB-NEXT:  .LBB3_4:
+; THUMB-NEXT:    subs r0, r4, r6
+; THUMB-NEXT:    pop {r4, r5, r6, pc}
   %1 = call i8 @llvm.ucmp(i64 %x, i64 %y)
   ret i8 %1
 }
@@ -76,6 +126,50 @@ define i8 @ucmp_8_128(i128 %x, i128 %y) nounwind {
 ; CHECK-NEXT:    movwlo r5, #1
 ; CHECK-NEXT:    sub r0, r5, r7
 ; CHECK-NEXT:    pop {r4, r5, r6, r7, r11, pc}
+;
+; THUMB-LABEL: ucmp_8_128:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    .save {r4, r5, r6, r7, lr}
+; THUMB-NEXT:    push {r4, r5, r6, r7, lr}
+; THUMB-NEXT:    .pad #20
+; THUMB-NEXT:    sub sp, #20
+; THUMB-NEXT:    str r3, [sp, #16] @ 4-byte Spill
+; THUMB-NEXT:    movs r3, #1
+; THUMB-NEXT:    str r3, [sp] @ 4-byte Spill
+; THUMB-NEXT:    movs r3, #0
+; THUMB-NEXT:    str r3, [sp, #12] @ 4-byte Spill
+; THUMB-NEXT:    ldr r6, [sp, #52]
+; THUMB-NEXT:    add r7, sp, #40
+; THUMB-NEXT:    ldm r7, {r3, r5, r7}
+; THUMB-NEXT:    subs r4, r0, r3
+; THUMB-NEXT:    str r1, [sp, #4] @ 4-byte Spill
+; THUMB-NEXT:    mov r4, r1
+; THUMB-NEXT:    ldr r1, [sp] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r4, r5
+; THUMB-NEXT:    str r2, [sp, #8] @ 4-byte Spill
+; THUMB-NEXT:    mov r4, r2
+; THUMB-NEXT:    sbcs r4, r7
+; THUMB-NEXT:    ldr r4, [sp, #16] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r4, r6
+; THUMB-NEXT:    mov r2, r1
+; THUMB-NEXT:    blo .LBB4_2
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    ldr r2, [sp, #12] @ 4-byte Reload
+; THUMB-NEXT:  .LBB4_2:
+; THUMB-NEXT:    subs r0, r3, r0
+; THUMB-NEXT:    ldr r0, [sp, #4] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r5, r0
+; THUMB-NEXT:    ldr r0, [sp, #8] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r7, r0
+; THUMB-NEXT:    ldr r0, [sp, #16] @ 4-byte Reload
+; THUMB-NEXT:    sbcs r6, r0
+; THUMB-NEXT:    blo .LBB4_4
+; THUMB-NEXT:  @ %bb.3:
+; THUMB-NEXT:    ldr r1, [sp, #12] @ 4-byte Reload
+; THUMB-NEXT:  .LBB4_4:
+; THUMB-NEXT:    subs r0, r1, r2
+; THUMB-NEXT:    add sp, #20
+; THUMB-NEXT:    pop {r4, r5, r6, r7, pc}
   %1 = call i8 @llvm.ucmp(i128 %x, i128 %y)
   ret i8 %1
 }
@@ -87,6 +181,15 @@ define i32 @ucmp_32_32(i32 %x, i32 %y) nounwind {
 ; CHECK-NEXT:    movwhi r0, #1
 ; CHECK-NEXT:    mvnlo r0, #0
 ; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: ucmp_32_32:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    subs r2, r0, r1
+; THUMB-NEXT:    sbcs r2, r2
+; THUMB-NEXT:    cmp r1, r0
+; THUMB-NEXT:    sbcs r1, r1
+; THUMB-NEXT:    subs r0, r2, r1
+; THUMB-NEXT:    bx lr
   %1 = call i32 @llvm.ucmp(i32 %x, i32 %y)
   ret i32 %1
 }
@@ -106,6 +209,29 @@ define i32 @ucmp_32_64(i64 %x, i64 %y) nounwind {
 ; CHECK-NEXT:    movwlo r12, #1
 ; CHECK-NEXT:    sub r0, r12, lr
 ; CHECK-NEXT:    pop {r11, pc}
+;
+; THUMB-LABEL: ucmp_32_64:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    .save {r4, r5, r6, lr}
+; THUMB-NEXT:    push {r4, r5, r6, lr}
+; THUMB-NEXT:    movs r4, #1
+; THUMB-NEXT:    movs r5, #0
+; THUMB-NEXT:    subs r6, r0, r2
+; THUMB-NEXT:    mov r6, r1
+; THUMB-NEXT:    sbcs r6, r3
+; THUMB-NEXT:    mov r6, r4
+; THUMB-NEXT:    blo .LBB6_2
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    mov r6, r5
+; THUMB-NEXT:  .LBB6_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    sbcs r3, r1
+; THUMB-NEXT:    blo .LBB6_4
+; THUMB-NEXT:  @ %bb.3:
+; THUMB-NEXT:    mov r4, r5
+; THUMB-NEXT:  .LBB6_4:
+; THUMB-NEXT:    subs r0, r4, r6
+; THUMB-NEXT:    pop {r4, r5, r6, pc}
   %1 = call i32 @llvm.ucmp(i64 %x, i64 %y)
   ret i32 %1
 }
@@ -126,6 +252,76 @@ define i64 @ucmp_64_64(i64 %x, i64 %y) nounwind {
 ; CHECK-NEXT:    sub r0, r12, lr
 ; CHECK-NEXT:    asr r1, r0, #31
 ; CHECK-NEXT:    pop {r11, pc}
+;
+; THUMB-LABEL: ucmp_64_64:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    .save {r4, r5, r6, lr}
+; THUMB-NEXT:    push {r4, r5, r6, lr}
+; THUMB-NEXT:    movs r4, #1
+; THUMB-NEXT:    movs r5, #0
+; THUMB-NEXT:    subs r6, r0, r2
+; THUMB-NEXT:    mov r6, r1
+; THUMB-NEXT:    sbcs r6, r3
+; THUMB-NEXT:    mov r6, r4
+; THUMB-NEXT:    blo .LBB7_2
+; THUMB-NEXT:  @ %bb.1:
+; THUMB-NEXT:    mov r6, r5
+; THUMB-NEXT:  .LBB7_2:
+; THUMB-NEXT:    subs r0, r2, r0
+; THUMB-NEXT:    sbcs r3, r1
+; THUMB-NEXT:    blo .LBB7_4
+; THUMB-NEXT:  @ %bb.3:
+; THUMB-NEXT:    mov r4, r5
+; THUMB-NEXT:  .LBB7_4:
+; THUMB-NEXT:    subs r0, r4, r6
+; THUMB-NEXT:    asrs r1, r0, #31
+; THUMB-NEXT:    pop {r4, r5, r6, pc}
   %1 = call i64 @llvm.ucmp(i64 %x, i64 %y)
   ret i64 %1
 }
+
+define i32 @ucmp_32_32_zero(i32 %x) nounwind {
+; CHECK-LABEL: ucmp_32_32_zero:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    mov r1, #0
+; CHECK-NEXT:    cmp r0, #0
+; CHECK-NEXT:    movwhi r1, #1
+; CHECK-NEXT:    mvnlo r1, #0
+; CHECK-NEXT:    mov r0, r1
+; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: ucmp_32_32_zero:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    movs r1, #0
+; THUMB-NEXT:    cmp r1, r0
+; THUMB-NEXT:    sbcs r1, r1
+; THUMB-NEXT:    subs r0, r0, #0
+; THUMB-NEXT:    sbcs r0, r0
+; THUMB-NEXT:    subs r0, r0, r1
+; THUMB-NEXT:    bx lr
+  %1 = call i32 @llvm.ucmp(i32 %x, i32 0)
+  ret i32 %1
+}
+
+define i8 @ucmp_8_32_zero(i32 %x) nounwind {
+; CHECK-LABEL: ucmp_8_32_zero:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    mov r1, #0
+; CHECK-NEXT:    cmp r0, #0
+; CHECK-NEXT:    movwhi r1, #1
+; CHECK-NEXT:    mvnlo r1, #0
+; CHECK-NEXT:    mov r0, r1
+; CHECK-NEXT:    bx lr
+;
+; THUMB-LABEL: ucmp_8_32_zero:
+; THUMB:       @ %bb.0:
+; THUMB-NEXT:    movs r1, #0
+; THUMB-NEXT:    cmp r1, r0
+; THUMB-NEXT:    sbcs r1, r1
+; THUMB-NEXT:    subs r0, r0, #0
+; THUMB-NEXT:    sbcs r0, r0
+; THUMB-NEXT:    subs r0, r0, r1
+; THUMB-NEXT:    bx lr
+  %1 = call i8 @llvm.ucmp(i32 %x, i32 0)
+  ret i8 %1
+}

>From c7c63f82b9f3a0ea201a7715c0f1d3688533cc77 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 3 Jul 2026 11:27:06 -0400
Subject: [PATCH 2/2] [ARM] Special case scmp 0

We can use cmp instead of subs for scmp 0, and we can do shifts for scmp 0 too.
---
 llvm/lib/Target/ARM/ARMISelLowering.cpp | 84 ++++++++++++-------------
 llvm/test/CodeGen/ARM/scmp.ll           | 12 ++--
 llvm/test/CodeGen/ARM/ucmp.ll           | 12 ++--
 3 files changed, 49 insertions(+), 59 deletions(-)

diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 577e97e736c25..b155bb2742110 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -592,11 +592,10 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM_,
     setOperationAction(ISD::BSWAP, VT, Expand);
   }
 
-  if (!Subtarget->isThumb1Only() && !Subtarget->hasV8_1MMainlineOps())
+  if (!Subtarget->hasV8_1MMainlineOps()) {
     setOperationAction(ISD::SCMP, MVT::i32, Custom);
-
-  if (!Subtarget->hasV8_1MMainlineOps())
     setOperationAction(ISD::UCMP, MVT::i32, Custom);
+  }
 
   if (!Subtarget->isThumb1Only())
     setOperationAction(ISD::ABS, MVT::i32, Custom);
@@ -10388,10 +10387,25 @@ SDValue ARMTargetLowering::LowerCMP(SDValue Op, SelectionDAG &DAG) const {
     // Final subtraction: Sbc1Result - Sbc2Result (no flags needed)
     SDValue Result =
         DAG.getNode(ISD::SUB, dl, MVT::i32, Sbc1Result, Sbc2Result);
-    if (Op.getValueType() != MVT::i32)
-      Result = DAG.getSExtOrTrunc(Result, dl, Op.getValueType());
+    return DAG.getSExtOrTrunc(Result, dl, Op.getValueType());
+  }
 
-    return Result;
+  // Optimize scmp with 0 using (x ashr 31) | (-x shr 31)
+  if (Subtarget->isThumb1Only()) {
+    if (!isNullConstant(RHS))
+      return SDValue();
+
+    // Freeze?
+    LHS = DAG.getFreeze(LHS);
+    EVT VT = LHS.getValueType();
+    unsigned BitWidth = VT.getScalarSizeInBits();
+    SDValue ShiftAmount = DAG.getShiftAmountConstant(BitWidth - 1, VT, dl);
+    SDValue SignShift = DAG.getNode(ISD::SRA, dl, VT, LHS, ShiftAmount);
+    SDValue NegX =
+        DAG.getNode(ISD::SUB, dl, VT, DAG.getConstant(0, dl, VT), LHS);
+    SDValue LshrNeg = DAG.getNode(ISD::SRL, dl, VT, NegX, ShiftAmount);
+    SDValue Res = DAG.getNode(ISD::OR, dl, VT, SignShift, LshrNeg);
+    return DAG.getSExtOrTrunc(Res, dl, Op.getValueType());
   }
 
   // For the ARM assembly pattern:
@@ -10404,42 +10418,29 @@ SDValue ARMTargetLowering::LowerCMP(SDValue Op, SelectionDAG &DAG) const {
   // Optimization: if RHS is a subtraction against 0, use ADDC instead of SUBC
   unsigned Opcode = ARMISD::SUBC;
 
-  // Check if RHS is a subtraction against 0: (0 - X)
-  if (RHS.getOpcode() == ISD::SUB) {
-    SDValue SubLHS = RHS.getOperand(0);
-    SDValue SubRHS = RHS.getOperand(1);
-
-    // Check if it's 0 - X
-    if (isNullConstant(SubLHS)) {
-      bool CanUseAdd = false;
-      if (IsSigned) {
-        // For SCMP: only if X is known to never be INT_MIN (to avoid overflow)
-        if (RHS->getFlags().hasNoSignedWrap() || !DAG.computeKnownBits(SubRHS)
-                                                      .getSignedMinValue()
-                                                      .isMinSignedValue()) {
-          CanUseAdd = true;
-        }
-      } else {
-        // For UCMP: only if X is known to never be zero
-        if (DAG.isKnownNeverZero(SubRHS)) {
-          CanUseAdd = true;
-        }
-      }
-
-      if (CanUseAdd) {
-        Opcode = ARMISD::ADDC;
-        RHS = SubRHS; // Replace RHS with X, so we do LHS + X instead of
-                      // LHS - (0 - X)
-      }
-    }
+  ISD::CondCode CC = IsSigned ? ISD::SETGT : ISD::SETUGT;
+  if (isCMN(RHS, CC, DAG)) {
+    Opcode = ARMISD::ADDC;
+    RHS = RHS.getOperand(1); // Replace RHS with X, so we do LHS + X instead of
+                             // LHS - (0 - X)
   }
 
-  // Generate the operation with flags
-  SDValue OpWithFlags =
-      DAG.getNode(Opcode, dl, DAG.getVTList(MVT::i32, FlagsVT), LHS, RHS);
+  // isCMN should not return true for 0
+  bool isCmp = isNullConstant(RHS);
+  assert((Opcode != ARMISD::ADDC || !isCmp) && "isCMN should not return true for 0");
 
-  SDValue OpResult = OpWithFlags.getValue(0);
-  SDValue Flags = OpWithFlags.getValue(1);
+  // Generate the operation with flags
+  SDValue OpResult;
+  SDValue Flags;
+  if (isCmp) {
+    OpResult = LHS;
+    Flags = DAG.getNode(ARMISD::CMP, dl, FlagsVT, LHS, RHS);
+  } else {
+    SDValue OpWithFlags =
+        DAG.getNode(Opcode, dl, DAG.getVTList(MVT::i32, FlagsVT), LHS, RHS);
+    OpResult = OpWithFlags.getValue(0);
+    Flags = OpWithFlags.getValue(1);
+  }
 
   // Constants for conditional moves
   SDValue One = DAG.getConstant(1, dl, MVT::i32);
@@ -10459,10 +10460,7 @@ SDValue ARMTargetLowering::LowerCMP(SDValue Op, SelectionDAG &DAG) const {
   SDValue Result2 = DAG.getNode(ARMISD::CMOV, dl, MVT::i32, Result1, MinusOne,
                                 LTCondValue, Flags);
 
-  if (Op.getValueType() != MVT::i32)
-    Result2 = DAG.getSExtOrTrunc(Result2, dl, Op.getValueType());
-
-  return Result2;
+  return DAG.getSExtOrTrunc(Result2, dl, Op.getValueType());
 }
 
 SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
diff --git a/llvm/test/CodeGen/ARM/scmp.ll b/llvm/test/CodeGen/ARM/scmp.ll
index 168a3f90be33c..da748d3966fd5 100644
--- a/llvm/test/CodeGen/ARM/scmp.ll
+++ b/llvm/test/CodeGen/ARM/scmp.ll
@@ -359,11 +359,9 @@ define i64 @scmp_64_64(i64 %x, i64 %y) nounwind {
 define i32 @scmp_32_32_zero(i32 %x) nounwind {
 ; CHECK-LABEL: scmp_32_32_zero:
 ; CHECK:       @ %bb.0:
-; CHECK-NEXT:    mov r1, #0
 ; CHECK-NEXT:    cmp r0, #0
-; CHECK-NEXT:    movwgt r1, #1
-; CHECK-NEXT:    mvnlt r1, #0
-; CHECK-NEXT:    mov r0, r1
+; CHECK-NEXT:    movwgt r0, #1
+; CHECK-NEXT:    mvnlt r0, #0
 ; CHECK-NEXT:    bx lr
 ;
 ; THUMB-LABEL: scmp_32_32_zero:
@@ -380,11 +378,9 @@ define i32 @scmp_32_32_zero(i32 %x) nounwind {
 define i8 @scmp_8_32_zero(i32 %x) nounwind {
 ; CHECK-LABEL: scmp_8_32_zero:
 ; CHECK:       @ %bb.0:
-; CHECK-NEXT:    mov r1, #0
 ; CHECK-NEXT:    cmp r0, #0
-; CHECK-NEXT:    movwgt r1, #1
-; CHECK-NEXT:    mvnlt r1, #0
-; CHECK-NEXT:    mov r0, r1
+; CHECK-NEXT:    movwgt r0, #1
+; CHECK-NEXT:    mvnlt r0, #0
 ; CHECK-NEXT:    bx lr
 ;
 ; THUMB-LABEL: scmp_8_32_zero:
diff --git a/llvm/test/CodeGen/ARM/ucmp.ll b/llvm/test/CodeGen/ARM/ucmp.ll
index 6cafe778e9a31..1ff22c2c5ca18 100644
--- a/llvm/test/CodeGen/ARM/ucmp.ll
+++ b/llvm/test/CodeGen/ARM/ucmp.ll
@@ -283,11 +283,9 @@ define i64 @ucmp_64_64(i64 %x, i64 %y) nounwind {
 define i32 @ucmp_32_32_zero(i32 %x) nounwind {
 ; CHECK-LABEL: ucmp_32_32_zero:
 ; CHECK:       @ %bb.0:
-; CHECK-NEXT:    mov r1, #0
 ; CHECK-NEXT:    cmp r0, #0
-; CHECK-NEXT:    movwhi r1, #1
-; CHECK-NEXT:    mvnlo r1, #0
-; CHECK-NEXT:    mov r0, r1
+; CHECK-NEXT:    movwhi r0, #1
+; CHECK-NEXT:    mvnlo r0, #0
 ; CHECK-NEXT:    bx lr
 ;
 ; THUMB-LABEL: ucmp_32_32_zero:
@@ -306,11 +304,9 @@ define i32 @ucmp_32_32_zero(i32 %x) nounwind {
 define i8 @ucmp_8_32_zero(i32 %x) nounwind {
 ; CHECK-LABEL: ucmp_8_32_zero:
 ; CHECK:       @ %bb.0:
-; CHECK-NEXT:    mov r1, #0
 ; CHECK-NEXT:    cmp r0, #0
-; CHECK-NEXT:    movwhi r1, #1
-; CHECK-NEXT:    mvnlo r1, #0
-; CHECK-NEXT:    mov r0, r1
+; CHECK-NEXT:    movwhi r0, #1
+; CHECK-NEXT:    mvnlo r0, #0
 ; CHECK-NEXT:    bx lr
 ;
 ; THUMB-LABEL: ucmp_8_32_zero:



More information about the llvm-commits mailing list