mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-09 01:29:52 +00:00
fb4eed381d
Summary:
Previously in D54095 i have added support for extraction of `lshr` from `X` if we are to produce `BEXTR`.
That was good, but the fix was partial, there was still [[ https://bugs.llvm.org/show_bug.cgi?id=36419 | PR36419 ]].
That pattern can also appear, roughly, when you have a large (64-bit) storage, and the consume bits from it.
It will not be unexpected if you will be doing further computations in 32-bit width.
And then the current code breaks, as the tests show.
The basic idea/pattern here is following:
1. We have `i64` input
2. We perform `i64` right-shift on it.
3. We `trunc`ate that shifted value
4. We do all further work (masking) in `i32`
Since we see `trunc`ation and not `lshr`, we give up, and stop trying to extract that right-shift.
BUT. The mask is `i32`, therefore we can extend both of the operands of the masking (`and`) to `i64`
and truncate the result after masking: https://rise4fun.com/Alive/K4B
```
Name: @bextr64_32_b1 -> @bextr64_32_b0
%shiftedval = lshr i64 %val, %numskipbits
%truncshiftedval = trunc i64 %shiftedval to i32
%widenumlowbits1 = zext i8 %numlowbits to i32
%notmask1 = shl nsw i32 -1, %widenumlowbits1
%mask1 = xor i32 %notmask1, -1
%res = and i32 %truncshiftedval, %mask1
=>
%shiftedval = lshr i64 %val, %numskipbits
%widenumlowbits = zext i8 %numlowbits to i64
%notmask = shl nsw i64 -1, %widenumlowbits
%mask = xor i64 %notmask, -1
%wideres = and i64 %shiftedval, %mask
%res = trunc i64 %wideres to i32
```
Thus, we are again able to extract that `lshr` into `BEXTR`'s control.
Now, the perf (via `llvm-exegesis`) of the snippet suggests that it is not a good idea:
```
$ cat /tmp/old.s
# bextr64_32_b1
# LLVM-EXEGESIS-LIVEIN RSI
# LLVM-EXEGESIS-LIVEIN EDX
# LLVM-EXEGESIS-LIVEIN RDI
movq %rsi, %rcx
shrq %cl, %rdi
shll $8, %edx
bextrl %edx, %edi, %eax
$ cat /tmp/old.s | ./bin/llvm-exegesis -mode=latency -snippets-file=-
Check generated assembly with: /usr/bin/objdump -d /tmp/snippet-1e0082.o
---
mode: latency
key:
instructions:
- 'MOV64rr RCX RSI'
- 'SHR64rCL RDI RDI'
- 'SHL32ri EDX EDX i_0x8'
- 'BEXTR32rr EAX EDI EDX'
config: ''
register_initial_values: []
cpu_name: bdver2
llvm_triple: x86_64-unknown-linux-gnu
num_repetitions: 10000
measurements:
- { key: latency, value: 0.6638, per_snippet_value: 2.6552 }
error: ''
info: ''
assembled_snippet: 4889F148D3EFC1E208C4E268F7C74889F148D3EFC1E208C4E268F7C74889F148D3EFC1E208C4E268F7C74889F148D3EFC1E208C4E268F7C7C3
...
$ cat /tmp/old.s | ./bin/llvm-exegesis -mode=uops -snippets-file=-
Check generated assembly with: /usr/bin/objdump -d /tmp/snippet-43e346.o
---
mode: uops
key:
instructions:
- 'MOV64rr RCX RSI'
- 'SHR64rCL RDI RDI'
- 'SHL32ri EDX EDX i_0x8'
- 'BEXTR32rr EAX EDI EDX'
config: ''
register_initial_values: []
cpu_name: bdver2
llvm_triple: x86_64-unknown-linux-gnu
num_repetitions: 10000
measurements:
- { key: PdFPU0, value: 0, per_snippet_value: 0 }
- { key: PdFPU1, value: 0, per_snippet_value: 0 }
- { key: PdFPU2, value: 0, per_snippet_value: 0 }
- { key: PdFPU3, value: 0, per_snippet_value: 0 }
- { key: NumMicroOps, value: 1.2571, per_snippet_value: 5.0284 }
error: ''
info: ''
assembled_snippet: 4889F148D3EFC1E208C4E268F7C74889F148D3EFC1E208C4E268F7C74889F148D3EFC1E208C4E268F7C74889F148D3EFC1E208C4E268F7C7C3
...
```
vs
```
$ cat /tmp/new.s
# bextr64_32_b1
# LLVM-EXEGESIS-LIVEIN RDX
# LLVM-EXEGESIS-LIVEIN SIL
# LLVM-EXEGESIS-LIVEIN RDI
shlq $8, %rdx
movzbl %sil, %eax
orq %rdx, %rax
bextrq %rax, %rdi, %rax
$ cat /tmp/new.s | ./bin/llvm-exegesis -mode=latency -snippets-file=-
Check generated assembly with: /usr/bin/objdump -d /tmp/snippet-8944f1.o
---
mode: latency
key:
instructions:
- 'SHL64ri RDX RDX i_0x8'
- 'MOVZX32rr8 EAX SIL'
- 'OR64rr RAX RAX RDX'
- 'BEXTR64rr RAX RDI RAX'
config: ''
register_initial_values: []
cpu_name: bdver2
llvm_triple: x86_64-unknown-linux-gnu
num_repetitions: 10000
measurements:
- { key: latency, value: 0.7454, per_snippet_value: 2.9816 }
error: ''
info: ''
assembled_snippet: 48C1E208400FB6C64809D0C4E2F8F7C748C1E208400FB6C64809D0C4E2F8F7C748C1E208400FB6C64809D0C4E2F8F7C748C1E208400FB6C64809D0C4E2F8F7C7C3
...
$ cat /tmp/new.s | ./bin/llvm-exegesis -mode=uops -snippets-file=-
Check generated assembly with: /usr/bin/objdump -d /tmp/snippet-da403c.o
---
mode: uops
key:
instructions:
- 'SHL64ri RDX RDX i_0x8'
- 'MOVZX32rr8 EAX SIL'
- 'OR64rr RAX RAX RDX'
- 'BEXTR64rr RAX RDI RAX'
config: ''
register_initial_values: []
cpu_name: bdver2
llvm_triple: x86_64-unknown-linux-gnu
num_repetitions: 10000
measurements:
- { key: PdFPU0, value: 0, per_snippet_value: 0 }
- { key: PdFPU1, value: 0, per_snippet_value: 0 }
- { key: PdFPU2, value: 0, per_snippet_value: 0 }
- { key: PdFPU3, value: 0, per_snippet_value: 0 }
- { key: NumMicroOps, value: 1.2571, per_snippet_value: 5.0284 }
error: ''
info: ''
assembled_snippet: 48C1E208400FB6C64809D0C4E2F8F7C748C1E208400FB6C64809D0C4E2F8F7C748C1E208400FB6C64809D0C4E2F8F7C748C1E208400FB6C64809D0C4E2F8F7C7C3
...
```
^ latency increased (worse).
Except //maybe// not really.
Like with all synthetic benchmarks, they //may// be misleading.
Let's take a look on some actual real-world hotpath.
In this case it's 'my' [[ https://github.com/darktable-org/rawspeed | RawSpeed ]]'s `BitStream<>::peekBitsNoFill()`, in [[
|
||
---|---|---|
.. | ||
AArch64 | ||
AMDGPU | ||
ARC | ||
ARM | ||
AVR | ||
BPF | ||
Generic | ||
Hexagon | ||
Inputs | ||
Lanai | ||
Mips | ||
MIR | ||
MSP430 | ||
NVPTX | ||
PowerPC | ||
RISCV | ||
SPARC | ||
SystemZ | ||
Thumb | ||
Thumb2 | ||
WebAssembly | ||
WinCFGuard | ||
WinEH | ||
X86 | ||
XCore |