.text:00404200; =============== S U B R O U T I N E ===============
.text:00404200
.text:00404200

Bit tricks compilers use

.text:00404200; author: Can Ozdemir date: 2024-04-06 size: 0xCB0 tags: x86-64, compilers, tutorial
.text:00404200
.text:00404200sub_404200procnear
.text:00404201

Optimising compilers avoid slow instructions, especially division. The result is code that looks nothing like the source until you know the patterns. All the listings here are real GCC 13 output at -O2.

.text:004042CD

Masks and shifts

.text:004042E0
unsigned get_bits(unsigned x) { return (x >> 4) & 0xF; }
.text:00404318
mov  eax, edi
shr  eax, 0x4
and  eax, 0xf
.text:00404341

Shift right, then AND with a mask: "extract bits 4 to 7". Whenever you see shr followed by and with a constant like 0xf, 0xff or 0x3ff, write down which bit field is being read. Protocol parsers and flag checks are full of these.

.text:00404430

Power of two test

.text:00404444
int is_power_of_two(unsigned x) { return x && !(x & (x - 1)); }
.text:00404483
xor   eax, eax
test  edi, edi
je    done            ; x == 0 -> return 0
lea   eax, [rdi-0x1]
test  eax, edi        ; (x - 1) & x
sete  al              ; 1 if the result was zero
movzx eax, al
.text:00404543

x & (x - 1) clears the lowest set bit, so it's zero only for powers of two. sete turns a flag into a 0/1 value: that's how C comparisons become bools. And xor eax, eax is just the shortest way to write eax = 0.

.text:0040461F

Multiply with lea

.text:00404633
unsigned mul10(unsigned x) { return x * 10; }
.text:00404660
lea  eax, [rdi+rdi*4]   ; x * 5
add  eax, eax           ; * 2
.text:0040469D

lea computes an address without touching memory, and the addressing mode can do base + index*scale with scale 1, 2, 4 or 8. Compilers use it as a cheap three-operand multiply-and-add. Treat lea with no memory access as plain arithmetic.

.text:0040478F

Division by a power of two

.text:004047AC
unsigned div8(unsigned x) { return x / 8; }
int     sdiv8(int x)      { return x / 8; }
.text:00404803
; unsigned
mov    eax, edi
shr    eax, 0x3

; signed
test   edi, edi
lea    eax, [rdi+0x7]
cmovns eax, edi         ; negative? use x + 7
sar    eax, 0x3
.text:0040489B

Unsigned is a plain shift. Signed needs care: C rounds toward zero, but an arithmetic shift (sar) rounds toward minus infinity. Adding 7 to negative values first fixes that. So test + lea +7 + cmovns + sar 3 is simply "signed divide by 8".

.text:00404994

Division by a constant: magic numbers

.text:004049BC
unsigned div10(unsigned x) { return x / 10; }
.text:004049E9
mov   eax, edi
mov   edx, 0xcccccccd
imul  rax, rdx
shr   rax, 0x23         ; >> 35
.text:00404A3C

No div at all. 0xCCCCCCCD is about 2^35 / 10, so multiplying by it and shifting right by 35 gives x / 10, exactly, for every 32-bit x. Signed division by 3 looks like this:

.text:00404AF0
movsxd rdx, eax
imul   rdx, rdx, 0x55555556
shr    rdx, 0x20         ; high 32 bits of the product
sar    eax, 0x1f         ; -1 if x was negative, else 0
sub    edx, eax          ; round toward zero
.text:00404BB7

To decode a magic number: divide 2^(32 + shift) by it and round. 2^35 / 0xCCCCCCCD ≈ 10, and 2^32 / 0x55555556 ≈ 3. Decompilers like Ghidra and IDA's Hex-Rays do this for you and show x / 10, but in a raw listing you'll meet these constants often: 0xCCCCCCCD (/10 or /5), 0xAAAAAAAB (/3 unsigned), 0x55555556 (/3 signed), 0x92492493 (/7).

.text:00404D17

Takeaway

.text:00404D22

When a block of arithmetic looks absurd, assume the compiler was clever, not the author. Shift-and-mask, lea arithmetic, sete/cmov, and magic multiplies cover a large share of what you'll see.

.text:00404DE8
.text:00404DE8retn
.text:00404DE8sub_404200endp
.text:00404DE8; © 2026 Can Ozdemir. All rights reserved. terms
.text:00404DE8
.text:00404DE9; -------- cross references --------
Output
Loading ozdc.us… 34 functions, 3 data segments.
Type help in the command line below and press Enter.