You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Companion to #47. honest_bench image_conv profile of Perry v0.5.30 shows two back-to-back bl calls per byte read in the inner blur loop: one for the buffer read (#47), one for a NaN-box wrapper. This issue is the second call.
At 0x100c in the profiled binary:
scvtf d0, w0 ; i32 result from buffer_get → f64bl0x10001edac ; NaN-box wrapper — full frame, tag-table setup, check, return
— i.e. it's a generic "coerce any JSValue to double" that sets up the full tag table to handle every possible incoming tag. When called right after scvtf it's doing pointless work: the incoming value is known to be a freshly-converted double from a known-int32 source.
Proposal
Type inference already tracks stability for some locals. Extend it (or add a loop-local pass) to mark a number as int32-stable when:
all assignments in scope are also int32-stable, and
no observable floating-point consumer (e.g. it never flows into Math.sqrt, /, Math.log, etc.).
For such locals, the codegen emits i32 throughout its live range: loads/stores stay as ldr w/str w, arithmetic stays as mul w/add w, and NaN-boxing happens only at the escape boundary (store to heap, argument pass to a generic callee, print, etc.).
Expected impact
Stacks on top of #47. With #47 already in place, the blur loop would be:
— instead of ldrb → scvtf → bl nan_box → fmadd. Removes the second bl entirely (the profile showed it taking more samples than the first one — ~30+ instrs vs js_buffer_get's 9).
On the image_conv workload with #47 already applied, projected additional ~2× speedup. Combined with #47's autovectorization unblock, lands Perry ahead of Rust on tight compute loops.
Hash loops: FNV, MurmurHash, xxhash (the honest_bench/…/perry/image_conv.ts uses a hand-rolled imul32 that would benefit).
Byte parsers: base64, UTF-8 validators, varint.
Counter-shape to be careful of
Locals that look int32 but occasionally hold NaN or Infinity (e.g. const n = parseInt(userInput) where userInput might be "abc") must not be specialized. The analysis has to trace the flow and bail on any edge where a non-int32 can enter. The compile-time type (number in TS) is not enough; actual use has to be checked.
Context
Companion to #47.
honest_benchimage_conv profile of Perry v0.5.30 shows two back-to-backblcalls per byte read in the inner blur loop: one for the buffer read (#47), one for a NaN-box wrapper. This issue is the second call.At
0x100cin the profiled binary:The wrapper function starts:
— i.e. it's a generic "coerce any JSValue to double" that sets up the full tag table to handle every possible incoming tag. When called right after
scvtfit's doing pointless work: the incoming value is known to be a freshly-converted double from a known-int32 source.Proposal
Type inference already tracks stability for some locals. Extend it (or add a loop-local pass) to mark a
numberas int32-stable when:|0,>>> 0,idx * k,+/-/*of other int32-stable locals, or aBuffer[idx]read (after perf: inline Buffer/Uint8Array bracket-read when statically typed (9 instr + bl → 1 instr) #47),Math.sqrt,/,Math.log, etc.).For such locals, the codegen emits
i32throughout its live range: loads/stores stay asldr w/str w, arithmetic stays asmul w/add w, and NaN-boxing happens only at the escape boundary (store to heap, argument pass to a generic callee, print, etc.).Expected impact
Stacks on top of #47. With #47 already in place, the blur loop would be:
— instead of
ldrb → scvtf → bl nan_box → fmadd. Removes the secondblentirely (the profile showed it taking more samples than the first one — ~30+ instrs vs js_buffer_get's 9).On the image_conv workload with #47 already applied, projected additional ~2× speedup. Combined with #47's autovectorization unblock, lands Perry ahead of Rust on tight compute loops.
Scope
honest_bench/…/perry/image_conv.tsuses a hand-rolledimul32that would benefit).Counter-shape to be careful of
Locals that look int32 but occasionally hold
NaNor Infinity (e.g.const n = parseInt(userInput)whereuserInputmight be"abc") must not be specialized. The analysis has to trace the flow and bail on any edge where a non-int32 can enter. The compile-time type (numberin TS) is not enough; actual use has to be checked.