llvm/llvm-project

[DAG][X86] Use shrdq to insert into a bitfield

Open

#112,488 opened on Oct 16, 2024

View on GitHub
 (15 comments) (0 reactions) (1 assignee)C++ (10,782 forks)batch import
backend:X86good first issuemissed-optimization

Repository metrics

Stars
 (26,378 stars)
PR merge metrics
 (Avg merge 1d 2h) (1,000 merged PRs in 30d)

Description

Consider inserting a value into a "manually managed" bit field (that is, without using : 10 in a struct):

uint64_t updateTop10Bits(uint64_t A, uint64_t B) {
  uint64_t Mask = (1ULL << 54) - 1;
  return (A & Mask) | (B << 54);
}

This takes the lower 10 bits from B and inserts into the top 10 bits of A.

clang -O3 -march=skylake generates:

        movb    $54, %al
        bzhiq   %rax, %rdi, %rax
        shlq    $54, %rsi
        orq     %rsi, %rax
        retq

We could left-shift the destination to remove the 10 bits and then perform a double-precision right-shift like so:

        movq    %rdi, %rax       ; Copy A into RAX.
        shlq    $10, %rax        ; Shift RAX left by 10 bits.
        shrdq   $10, %rsi, %rax  ; Shift RAX right by 10 bits while taking 10 bits from B.
        retq

The LLVM IR for the C++ code snippet above is:

define dso_local noundef i64 @updateTop10Bits(unsigned long, unsigned long)(i64 noundef %A, i64 noundef %B) local_unnamed_addr {
entry:
  %and = and i64 %A, 18014398509481983
  %shl = shl i64 %B, 54
  %or = or disjoint i64 %shl, %and
  ret i64 %or
}

https://godbolt.org/z/fdcWqMnvW

Contributor guide