llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.87k stars 11.93k forks source link

Failure to recognize `llvm.ssub.with.overflow` idiom #102947

Open Kmeakin opened 2 months ago

Kmeakin commented 2 months ago

InstCombine should recognize code of the form x == SMIN ? default : -x and canonicalize it to use llvm.ssub.with.overflow(0, x)

https://godbolt.org/z/MqfvKh74G

#[no_mangle]
#[inline(never)]
pub fn src1(x: i32) -> (i32, bool) {
    x.overflowing_neg()
}

#[no_mangle]
#[inline(never)]
pub fn tgt1(x: i32) -> (i32, bool) {
    0i32.overflowing_sub(x)
}

#[no_mangle]
#[inline(never)]
pub fn src2(x: i32) -> Option<i32> {
    x.checked_neg()
}

#[no_mangle]
#[inline(never)]
pub fn tgt2(x: i32) -> Option<i32> {
    0i32.checked_sub(x)
}

#[no_mangle]
#[inline(never)]
pub fn src3(x: i32) -> Option<i32> {
    x.checked_abs()
}

#[no_mangle]
#[inline(never)]
pub fn tgt3(x: i32) -> Option<i32> {
    if x >= 0 {
        Some(x)
    } else {
        0i32.checked_sub(x)
    }
}

#[no_mangle]
#[inline(never)]
pub fn src4(x: i32) -> (i32, bool) {
    x.overflowing_abs()
}

#[no_mangle]
#[inline(never)]
pub fn tgt4(x: i32) -> (i32, bool) {
    if x >= 0 {
        (x, false)
    } else {
        0i32.overflowing_sub(x)
    }
}
dtcxzyw commented 2 months ago

cc @DianQK

DianQK commented 2 months ago

Could you explain further what transformation you're looking here?

Kmeakin commented 2 months ago

https://alive2.llvm.org/ce/z/gS4aB6

DianQK commented 2 months ago

Ah, thanks! So...

I think this is a backend problem. In these cases, x86_64 either generates the same instructions or the tgt* are even worse: https://godbolt.org/z/aKjWjzKjG.

Perhaps there should be a general matching pattern that recognizes them as the same instruction sequence, allowing different architectures to select the optimal instruction sequence.

llvmbot commented 2 months ago

@llvm/issue-subscribers-backend-aarch64

Author: Karl Meakin (Kmeakin)

InstCombine should recognize code of the form `x == SMIN ? default : -x` and canonicalize it to use `llvm.ssub.with.overflow(0, x)` https://godbolt.org/z/MqfvKh74G ```rust #[no_mangle] #[inline(never)] pub fn src1(x: i32) -> (i32, bool) { x.overflowing_neg() } #[no_mangle] #[inline(never)] pub fn tgt1(x: i32) -> (i32, bool) { 0i32.overflowing_sub(x) } #[no_mangle] #[inline(never)] pub fn src2(x: i32) -> Option<i32> { x.checked_neg() } #[no_mangle] #[inline(never)] pub fn tgt2(x: i32) -> Option<i32> { 0i32.checked_sub(x) } #[no_mangle] #[inline(never)] pub fn src3(x: i32) -> Option<i32> { x.checked_abs() } #[no_mangle] #[inline(never)] pub fn tgt3(x: i32) -> Option<i32> { if x >= 0 { Some(x) } else { 0i32.checked_sub(x) } } #[no_mangle] #[inline(never)] pub fn src4(x: i32) -> (i32, bool) { x.overflowing_abs() } #[no_mangle] #[inline(never)] pub fn tgt4(x: i32) -> (i32, bool) { if x >= 0 { (x, false) } else { 0i32.overflowing_sub(x) } } ```
llvmbot commented 2 months ago

@llvm/issue-subscribers-backend-x86

Author: Karl Meakin (Kmeakin)

InstCombine should recognize code of the form `x == SMIN ? default : -x` and canonicalize it to use `llvm.ssub.with.overflow(0, x)` https://godbolt.org/z/MqfvKh74G ```rust #[no_mangle] #[inline(never)] pub fn src1(x: i32) -> (i32, bool) { x.overflowing_neg() } #[no_mangle] #[inline(never)] pub fn tgt1(x: i32) -> (i32, bool) { 0i32.overflowing_sub(x) } #[no_mangle] #[inline(never)] pub fn src2(x: i32) -> Option<i32> { x.checked_neg() } #[no_mangle] #[inline(never)] pub fn tgt2(x: i32) -> Option<i32> { 0i32.checked_sub(x) } #[no_mangle] #[inline(never)] pub fn src3(x: i32) -> Option<i32> { x.checked_abs() } #[no_mangle] #[inline(never)] pub fn tgt3(x: i32) -> Option<i32> { if x >= 0 { Some(x) } else { 0i32.checked_sub(x) } } #[no_mangle] #[inline(never)] pub fn src4(x: i32) -> (i32, bool) { x.overflowing_abs() } #[no_mangle] #[inline(never)] pub fn tgt4(x: i32) -> (i32, bool) { if x >= 0 { (x, false) } else { 0i32.overflowing_sub(x) } } ```