llvm / llvm-project

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

[x86-64 BMI2] Missed canonicalization on ~blsmsk #103501

Open Validark opened 4 weeks ago

Validark commented 4 weeks ago

This code:

export fn foo(a: u64) u64 {
    return ~blsmsk(a);
}

fn blsmsk(a: u64) u64 {
    return (a ^ (a - 1));
}

Gives this emit:

foo:
        mov     rax, rdi
        neg     rax
        xor     rax, rdi
        ret

With inline assembly, we can force the compiler to do the thing we want:

export fn bar(a: u64) u64 {
    return ~blsmsk_asm(a);
}

inline fn blsmsk_asm(src: u64) u64 {
    return asm ("blsmsk %[src], %[ret]"
        : [ret] "=r" (-> u64),
        : [src] "r" (src),
    );
}
bar:
        blsmsk  rax, rdi
        not     rax
        ret

In my real code, I was doing a bitwise AND on ~blsmsk(a). Meaning the not can be folded into an ANDN instruction.

export fn foo(a: u64, b: u64) u64 {
    return ~blsmsk(a) & b;
}
foo:
        mov     rax, rdi
        neg     rax
        xor     rax, rdi
        and     rax, rsi
        ret

Again, with inline assembly, we can get:

bar:
        blsmsk  rax, rdi
        andn    rax, rax, rsi
        ret

Godbolt link

llvmbot commented 4 weeks ago

@llvm/issue-subscribers-backend-x86

Author: Niles Salter (Validark)

This code: ```zig export fn foo(a: u64) u64 { return ~blsmsk(a); } fn blsmsk(a: u64) u64 { return (a ^ (a - 1)); } ``` Gives this emit: ```asm foo: mov rax, rdi neg rax xor rax, rdi ret ``` With inline assembly, we can force the compiler to do the thing we want: ```zig export fn bar(a: u64) u64 { return ~blsmsk_asm(a); } inline fn blsmsk_asm(src: u64) u64 { return asm ("blsmsk %[src], %[ret]" : [ret] "=r" (-> u64), : [src] "r" (src), ); } ``` ```asm bar: blsmsk rax, rdi not rax ret ``` In my real code, I was doing a bitwise AND on `~blsmsk(a)`. Meaning the `not` can be folded into an `ANDN` instruction. ```zig export fn foo(a: u64, b: u64) u64 { return ~blsmsk(a) & b; } ``` ```asm foo: mov rax, rdi neg rax xor rax, rdi and rax, rsi ret ``` Again, with inline assembly, we can get: ```asm bar: blsmsk rax, rdi andn rax, rax, rsi ret ``` [Godbolt link](https://zig.godbolt.org/z/GMcf56rj7)