Skip to content

x86 bt instruction emission can be improved #147216

Open
@TDecking

Description

@TDecking

The following four functions are examples where the bt instruction can be used during code generation:

LLVM
define noundef zeroext i1 @bit_test_0(i64 noundef %num, i64 noundef %shift) unnamed_addr {
start:
  %mask = shl nuw i64 1, %shift
  %bit = and i64 %mask, %num
  %bool = icmp eq i64 %bit, 0
  ret i1 %bool
}

define noundef zeroext i1 @bit_test_1(i50 noundef %num, i50 noundef %shift) unnamed_addr {
start:
  %mask = shl nuw i50 1, %shift
  %bit = and i50 %mask, %num
  %bool = icmp eq i50 %bit, 0
  ret i1 %bool
}

define noundef zeroext i1 @bit_test_2(i128 noundef %num, i128 noundef %shift) unnamed_addr {
start:
  %mask = shl nuw i128 1, %shift
  %bit = and i128 %mask, %num
  %bool = icmp eq i128 %bit, 0
  ret i1 %bool
}

define noundef zeroext i1 @bit_test_3(i20 noundef %num, i20 noundef %shift) unnamed_addr {
start:
  %mask = shl nuw i20 1, %shift
  %bit = and i20 %mask, %num
  %bool = icmp eq i20 %bit, 0
  ret i1 %bool
}
Generated assembly
bit_test_0:                             # @bit_test_0
        bt      rdi, rsi
        setae   al
        ret
bit_test_1:                             # @bit_test_1
        mov     rcx, rsi
        mov     eax, 1
        shl     rax, cl
        and     rax, rdi
        shl     rax, 14
        sete    al
        ret
bit_test_2:                             # @bit_test_2
        mov     rcx, rdx
        xor     eax, eax
        mov     edx, 1
        xor     r8d, r8d
        shld    r8, rdx, cl
        shl     rdx, cl
        test    cl, 64
        cmovne  r8, rdx
        cmovne  rdx, rax
        and     r8, rsi
        and     rdx, rdi
        or      rdx, r8
        sete    al
        ret
bit_test_3:                             # @bit_test_3
        mov     ecx, esi
        mov     eax, 1
        shl     eax, cl
        and     eax, edi
        test    eax, 1048575
        sete    al
        ret

Currently, LLVM only emits bt if the integer bit width is exactly that of a register. Other integers do not get this treatment. The 128-bit integer example is particulary important since it can be easily created using Rust/C (u128, __int128) while being quite slow.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions