Skip to content

optimizer: branchless abs via bit manipulation strength reduction#117

Merged
yevbar merged 1 commit intomasterfrom
happy/branchless-abs-optimization
Mar 1, 2026
Merged

optimizer: branchless abs via bit manipulation strength reduction#117
yevbar merged 1 commit intomasterfrom
happy/branchless-abs-optimization

Conversation

@yevbar
Copy link
Copy Markdown
Contributor

@yevbar yevbar commented Mar 1, 2026

Summary

Adds a strength reduction optimization that converts branching abs(x) patterns into branchless bit manipulation.

Before (branching)

if(x >= 0, x, 0 - x)    ;; generates WASM if/else block

After (branchless)

mask = x >> 63           ;; arithmetic shift: 0 for positive, -1 for negative
(x ^ mask) - mask        ;; 4 ALU instructions, no branches

How it works

The classic branchless abs trick for signed 64-bit integers:

  • x >> 63 produces a sign mask: 0 for non-negative, -1 (all ones) for negative
  • x ^ mask flips all bits when negative (one's complement), no-op when positive
  • - mask adds 1 when negative (completing two's complement negation), no-op when positive

WASM impact

Before (branching abs):

local.get $p0
i64.const 0
i64.ge_s
if (result i64)
  local.get $p0
else
  i64.const 0
  local.get $p0
  i64.sub
end

After (branchless abs):

local.get $p0
i64.const 63
i64.shr_s          ;; mask = x >> 63
local.tee $mask
local.get $p0
local.get $mask
i64.xor            ;; x ^ mask
local.get $mask
i64.sub            ;; - mask

Eliminates branch misprediction and reduces code size.

Patterns matched

  1. Trivial operands (var/literal): if(x >= 0, x, 0 - x) → direct branchless form
  2. Block-wrapped (non-trivial operands like a+b): detects let-binding + if pattern from IRGen's abs lowering

Tests

10 new tests covering:

  • Optimizer pattern matching (trivial and block-wrapped forms)
  • Negative cases (non-abs patterns preserved unchanged)
  • End-to-end WAT generation (verifies branchless output, no if/else)
  • Correctness via Wasmex (positive, negative, zero, manhattan distance)

Add strength reduction pass that converts the branching abs pattern:
  if(x >= 0, x, 0 - x)
into the branchless bit manipulation form:
  (x ^ (x >> 63)) - (x >> 63)

This classic trick uses arithmetic right shift to create a sign mask
(-1 for negative, 0 for non-negative), then XOR + subtract to flip
the sign without branching. Benefits:

- Eliminates branch misprediction penalty on abs() calls
- Replaces if/else block (5+ WASM instructions) with 4 ALU ops
  (i64.shr_s, i64.xor, i64.shr_s, i64.sub)
- Handles both trivial operands (vars/literals) and block-wrapped
  abs with let bindings (non-trivial operands like a+b)

The optimization fires in the strength_reduce pass, matching the
exact IR pattern that IRGen produces for abs(x).

Includes 10 tests covering:
- Optimizer pattern matching (trivial and block-wrapped forms)
- Negative cases (non-abs patterns preserved)
- End-to-end WAT generation (verifies branchless output)
- Correctness via Wasmex execution (positive, negative, zero inputs)
- Multi-abs functions (manhattan distance)
@yevbar yevbar merged commit ce6bd3f into master Mar 1, 2026
4 checks passed
@yevbar yevbar deleted the happy/branchless-abs-optimization branch March 1, 2026 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant