ReviewOS

zig-utils/zig-js

Perf: inline number bitwise operators in VM dispatch

#67
Closed chris opened this 9 days ago ·
9 days ago

Parent: #62 Related: #56

Problem

The bytecode VM already computes number-number arithmetic, comparison, and equality inline, but number-number bitwise and shift bytecodes still fall through Interpreter.applyBinary. That repeats generic ToPrimitive and BigInt dispatch even when both tagged operands already prove ordinary Number semantics.

The post-#66 object_churn profile shows this general dispatch as a meaningful remaining cost: Interpreter.applyBinary has 39 top-of-stack samples, Interpreter.toPrimitive 30, and Value.toInt32 19. The workload naturally exercises index masks and bounded checksum masks, but the same path affects ordinary JavaScript bitwise code.

Scope

  • extend the existing VM number-number fast path to bitwise AND, OR, XOR, signed shifts, and unsigned right shift;
  • preserve ECMAScript ToInt32/ToUint32 behavior for NaN, infinities, negative values, negative zero, and large shift counts;
  • retain the generic path for BigInt, objects, strings, Symbols, and every case with observable coercion;
  • add focused VM coverage that distinguishes the fast numeric path from coercion fallbacks;
  • measure clean pinned A/B object_churn evidence with exact checksum equality.

Acceptance criteria

  • Number-number bitwise and shift bytecodes avoid Interpreter.applyBinary.
  • BigInt results and mixed BigInt/Number errors remain correct.
  • Object valueOf/toString and Symbol failures remain observable in spec order.
  • Edge-case Number conversion and shift behavior matches existing semantics.
  • Focused tests and comparison-harness validation pass.
  • Clean pinned A/B demonstrates no regression and records the exact cross-engine checksum.
  • Commit references this issue and the measured checkpoint is added to #62.
9 days ago

Implemented and pushed directly to main in 3a8eda3.

The VM number-number fast path now handles bitwise AND, OR, XOR, signed shifts, and unsigned right shift with the existing ToInt32 and ToUint32 conversions. BigInt, mixed types, objects, strings, Symbols, and observable coercion continue through Interpreter.applyBinary.

Focused coverage proves the optimized path is reached for ordinary Numbers and covers NaN, Infinity, negative values, negative zero, 32-bit wrapping, and masked shift counts. It also proves object valueOf order, BigInt results, mixed BigInt/Number errors, and Symbol failures stay on the fallback.

Validation:

  • zig-js: 753/753 tests passed, 0 skipped, failed, or leaked
  • comparison harness: 7/7 tests passed
  • exact object_churn checksum: 1030552128 in every clean A/B and cross-engine sample

Clean pinned order-balanced A/B against ecd5bb4, with the same zig-gc and zig-regex revisions:

  • baseline median: 669.566 ms
  • current median: 636.429 ms
  • elapsed reduction: 4.949 percent, 1.0521x throughput

In the post-change sample, Interpreter.applyBinary, Interpreter.toPrimitive, and Value.toInt32 no longer appear among top-of-stack leaves at the five-sample reporting threshold. Young-GC sweep and cell-slab release are now the dominant named costs.

Fresh post-gate 100-job, 7-sample medians:

  • zig-js: 693.224 ms
  • JSC: 126.226 ms
  • remaining gap: 5.492x

Acceptance criteria are met.

Sign in to comment on this issue.