llvm/llvm-project

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

Open

#112 488 ouverte le 16 oct. 2024

Voir sur GitHub
 (15 commentaires) (0 réactions) (1 assigné)C++ (10 782 forks)batch import
backend:X86good first issuemissed-optimization

Métriques du dépôt

Stars
 (26 378 stars)
Métriques de merge PR
 (Merge moyen 1j 2h) (1 000 PRs mergées en 30 j)

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

Guide contributeur