Commit graph

104 commits

Author SHA1 Message Date
Andreas Kling
790386e114 LibJS: Preserve Promise job ordering across calls
Only drain the standalone Promise job queue when unwinding the outermost
bytecode execution. Draining after every nested run_executable() return
let sibling Promise reactions run before current-job reactions.

Plain async function returns were relying on that extra drain because
return values were compiled as implicit awaits. Resolve completed async
functions through the Promise resolving function instead, while keeping
the async generator return-await path.

Add Promise ordering coverage and update the asm inline-call regression
to check the pre-drain state.
2026-05-22 01:56:57 +02:00
Andreas Kling
a9e54bd745 LibJS: Preserve resolved assignment bindings
Resolve dynamic identifier assignment targets before evaluating the
right hand side, then store through that saved environment record. This
matches the ECMA-262 ordering for simple assignment and var initializers
when a with binding is deleted or direct eval creates a nearer var.

Add runtime coverage for with and direct-eval cases and refresh bytecode
expectations for dynamic assignments that now snapshot the binding
before storing.
2026-05-22 01:56:57 +02:00
Andreas Kling
2f99653b33 LibJS: Resolve super constructor before arguments
Match ECMA-262's SuperCall evaluation order by resolving the super
constructor before evaluating the argument list. This preserves the
constructor across argument-side prototype mutations and still lets
abrupt argument evaluation happen before the constructor check.

Add LibJS coverage and update bytecode baselines for the new saved
super-constructor operand. The test262 superCallOrder staging test now
passes.
2026-05-22 01:56:57 +02:00
Andreas Kling
410f0fdbb0 LibJS: Yield delegated iterator results directly
Sync yield* must suspend with the delegated IteratorResult object itself
on non-terminal steps. We were reading value and yielding that value,
causing Generator.prototype.next() to create a fresh result object.

Add a bytecode op for this path so ordinary yield keeps wrapping its
value, and teach generator resume to return the prebuilt result object.
This also avoids touching value before the delegated iterator is done.

Add regression coverage and update the yield* bytecode baseline.
2026-05-22 01:56:57 +02:00
Andreas Kling
7a246b63c7 LibJS: Infer computed property function names
Use a runtime SetFunctionName bytecode operation when object literal
property keys are not known until evaluation. This lets anonymous
function and class expressions, methods, and accessors receive names
from numeric, computed, and Symbol property keys.

Store inferred ECMAScript function names on each function object instead
of mutating shared function data. That keeps repeated evaluations with
different computed keys from leaking names across closures, while still
using the per-instance name for stack metadata.

Add regression coverage for computed object property names, repeated
computed-key evaluations, and preserving unnamed functions that are only
referenced by a computed property value.
2026-05-22 01:56:57 +02:00
Andreas Kling
4ac744082b LibJS: Cache dynamic environment coordinates
Dynamic environment binding opcodes lost the old coordinate warmup.
They were split away from the static coordinate opcodes. Hot closures
and eval-sensitive functions then resolved the same binding by name on
every execution, which regressed JS benchmark throughput badly.

Give each dynamic environment opcode a per-executable coordinate cache
slot. The cache keeps the bytecode stream immutable while letting both
interpreters take a direct declarative environment fast path after the
first lookup. Keep the existing eval invalidation behavior and only warm
caches for declarative-only chains so with environments continue to
observe object shadowing.

Reject cached bytecode that uses the no-cache sentinel for dynamic
environment coordinate cache operands, since execution indexes those
cache arrays unconditionally.

Rebaseline bytecode expectations for the instruction size changes. Add
coverage for with-object shadowing across repeated dynamic lookups and
for rejecting corrupt dynamic environment cache indices.
2026-05-19 15:54:23 +02:00
Andreas Kling
0a49dd1c28 LibJS: Reduce inline cache memory usage
Store bytecode property lookup caches as tiered handles instead of
eagerly allocating four entries for every cache slot. Each slot starts
empty, grows to one monomorphic entry after the first cacheable lookup,
and promotes to the existing four-entry table when another cache key
appears.

Global variable caches keep one inline property entry because they
usually warm up as global object property accesses. This keeps common
global access allocation-free while still avoiding the inherited
polymorphic cache cost.

Generated asm offsets now point at the lazy property-cache storage and
the inline global entry, and asm fast paths bail out for empty property
cache slots.
2026-05-19 01:12:36 +02:00
Andreas Kling
ef74c1ca55 LibJS: Keep cached bytecode file-backed
Teach Bytecode::Executable to store its instruction stream as either
an owned Vector or a retained Core::ImmutableBytes range. Cached
bytecode materialization now clones the immutable blob owner and lets
the executable point directly into the file-backed cache blob instead
of copying instruction bytes back onto the heap.

Keep a cached instruction data pointer inside the stream wrapper so the
asm interpreter still has a direct hot-path load. Align executable
bytecode payloads in the cache format so mmap-backed instruction
streams satisfy validator and interpreter alignment requirements.
2026-05-18 20:35:14 +02:00
Andreas Kling
a5ba300186 LibJS: Split dynamic environment lookups from coordinates
Add separate bytecode instructions for environment lookups that must
stay dynamic, such as eval- and with-sensitive scopes. Keep the
coordinate variants for eagerly resolved declarative environments so
their operands can be treated as immutable at runtime.

This removes cached-coordinate mutation from the interpreter paths and
updates the bytecode expectations for the new dynamic lookup opcodes.
2026-05-18 20:35:14 +02:00
Andreas Kling
1ce4242b4b LibJS: Store bytecode cache indexes instead of pointers
Store compact cache indexes in bytecode instructions instead of raw
pointers to the executable cache vectors. This keeps the instruction
stream independent from heap addresses and removes pointer fixups when
materializing cached bytecode.

Resolve the mutable cache pointers at execution time from the current
Executable. Bytecode test expectations are updated for the smaller cache
operands and resulting instruction offsets.
2026-05-18 20:35:14 +02:00
Andreas Kling
694112d31d LibJS: Move declarative state into rare data
Store the environment serial number and catch-environment flag in
DeclarativeEnvironment::RareData instead of on every environment.
The serial is only used by global variable caches, and the catch flag
is only needed for catch clause environments, so shaped function
environments should not retain dedicated fields for either value.

Teach the asm global cache fast path to load the serial through rare
data and treat a missing sidecar as serial 0.
2026-05-15 14:04:47 +02:00
Andreas Kling
d9d508a53e LibJS: Move declarative metadata into rare data
Move names, local flags, deletion state, binding lookup, dispose
state, and shape construction bookkeeping out of DeclarativeEnvironment
and into a lazily allocated sidecar. Shaped function environments keep
the shape pointer and value vector directly on the object, but do not
retain empty metadata containers after their shared shape has been
installed.

Keep the asm interpreter fast paths direct by loading local binding
flags from the sidecar only for unshaped environments or dynamic
bindings that come after a shared shape.
2026-05-15 14:04:47 +02:00
Andreas Kling
da65105cd6 LibJS: Share declarative binding flags in shapes
Let shaped declarative environments read static binding flags from their
EnvironmentShape instead of retaining a per-environment copy. The local
flag vector now mirrors the local name vector and only stores dynamic
bindings created after the shared shape.

Split EnvironmentShape binding descriptors into separate name and flag
vectors so the shared metadata is compact as well.
2026-05-15 14:04:47 +02:00
Andreas Kling
b0372ca84c LibJS: Use empty values for declarative TDZ
Store uninitialized declarative bindings as js_special_empty_value()
in the existing value slot instead of tracking initialization in a
separate bitmap. This removes one bitmap allocation from every
declarative environment while preserving the same TDZ checks.

Update the asm interpreter fast paths to test the value slot directly
and let initialization store the first non-empty value.
2026-05-15 14:04:47 +02:00
Andreas Kling
a392b3051c LibJS: Split declarative environment binding storage
Store declarative environment values, names, flags, and initialization
state in separate containers instead of one Binding vector. This packs
per-binding flags into a byte and moves initialization state into an
AK::Bitmap. This reduces the per-environment storage we still need
before binding metadata can move into a shared EnvironmentShape.

Update the asm interpreter offsets and fast binding paths to load
values, flags, and initialization bits from the new storage. Keep
direct binding indices stable across deletion by clearing the slot
while removing the name from the lookup map. Cover that eval deletion
path in the local eval deoptimization tests.
2026-05-15 14:04:47 +02:00
Ali Mohammad Pur
0bb987e809 LibJS+LibWeb: Allow instantiating DataBlock with an external buffer
This requires the minimal API exposed by ByteBuffer, allowing external
users to implement them as needed instead of being forced to use a
ByteBuffer.
2026-05-10 16:41:42 +02:00
Andreas Kling
dafd624177 LibJS: Assert asmint call frame ranges
Assert that inline Call frame-top arithmetic does not wrap after adding
the current interpreter stack top, and that raw native frames copy valid
caller lexical and variable environments.

Also check that the raw native ThrowCompletionOr variant index stays in
the expected 0-or-1 range before the asm fast path branches on it.
2026-05-03 13:10:48 +02:00
Andreas Kling
955de5392f LibJS: Assert asmint boolean helper results
Assert that asm_helper_to_boolean returns only 0 or 1 before the asm
interpreter branches on the raw helper result. This documents the helper
ABI used by jump and logical-not fast paths without adding code to
release or distribution builds.
2026-05-03 13:10:48 +02:00
Andreas Kling
a01d4fd6fe LibJS: Assert asmint shape and storage pointers
Add assertion-only checks for object shapes, binding storage, UTF-16
string backing storage, and iterator receiver shapes before the asm
interpreter relies on those pointers for follow-up loads.

These checks document invariants that are already required by the fast
paths, while still compiling away from release and distribution builds.
2026-05-03 13:10:48 +02:00
Andreas Kling
4f74458024 LibJS: Use asmint tag assertions for value contracts
Replace assertion-only tag extraction around String helper return
values with assert_tag so release and distribution builds do not carry
code that only exists to feed assertions.

Asserting and non-asserting asmint generation both handle the updated
handlers.
2026-05-03 13:10:48 +02:00
Andreas Kling
c1ff3c07e2 LibJS: Assert asmint cache data pointers
Add debug-only assertions for typed-array cached data and property
iterator cache pointers before the asm fast paths dereference them. The
checks only consume values already loaded for normal execution, so
release and distribution builds still compile them out completely.

asmintgen successfully lowers this file for x86_64 with assertions
enabled.
2026-05-03 13:10:48 +02:00
Andreas Kling
15563aa9d4 LibJS: Assert asmint value contracts
Add debug-only assertions for value and frame contracts that cross the
asm/C++ boundary. Check resumed inline frames have executable bytecode,
lexical environments exist before boxing, and string builtin helpers
return string values for the fast paths that store their results.

asmintgen successfully lowers this file for x86_64 with assertions
enabled.
2026-05-03 13:10:48 +02:00
Andreas Kling
27b4527d8b LibJS: Assert asmint size invariants
Add debug-only checks for relationships between fast-path metadata and
addressing bounds. These catch inconsistent inline-call slot counts,
argument padding, and flattened iterator cache lengths before the asm
path uses them for addressing.

asmintgen successfully lowers this file for x86_64 with assertions
enabled.
2026-05-03 13:10:48 +02:00
Andreas Kling
5ffcd4f5b9 LibJS: Assert asmint call-frame invariants
Add debug-only assertions around inline Call frame construction and
state restoration. The checks cover executable metadata, stack cursors,
realm/environment pointers, constant storage, raw native entry points,
and the VM state reloaded after helper calls.

asmintgen successfully lowers this file for x86_64 with assertions
enabled.
2026-05-03 13:10:48 +02:00
Andreas Kling
69425b9892 LibJS: Assert asmint pointer invariants
Add debug-only asmint assertions for cache and backing-store pointers
that fast paths dereference after metadata has selected a cache hit.
These checks catch stale property/global caches, broken environment
coordinates, and inconsistent indexed storage state without changing
release or distribution builds.

asmintgen successfully lowers this file for x86_64 with assertions
enabled.
2026-05-03 13:10:48 +02:00
Aliaksandr Kalenik
2171563daf LibJS: Avoid function envs for lexical-this arrows
Track whether a function needs environment-backed this resolution
separately from whether it needs to allocate its own function
environment. Arrow functions that only capture lexical this can now
resolve through the outer environment without allocating an empty
function environment for every call.

Keep the asm Call path conservative by routing functions that still need
lexical-this resolution through the C++ inline-call helper, so the call
receiver is not cached as the arrow function's this value.

Microbenchmark:

    function makeLexicalThisArrow() {
        return () => this.value;
    }

    let object = { value: 1, makeLexicalThisArrow };
    let fn = object.makeLexicalThisArrow();
    for (let i = 0; i < 20_000_000; ++i)
        fn();

Measured with the same Release build toggling this patch:

    baseline:  1069.2 ms mean over 12 runs
    optimized:  501.2 ms mean over 12 runs
    speedup:    2.13 times faster
2026-04-30 18:44:34 +02:00
Andreas Kling
928a9dfbf7 LibJS+AsmIntGen: Retire the positional t0..t8 / ft0..ft3 DSL aliases
asmint.asm no longer references any positional temp register name --
every handler and macro declares its temporaries by name with `temp` /
`ftemp` and lets the register allocator place them. Migrate the last
two macros holding out:

  * dispatch_current uses a macro-local `opcode` temp for the load8 +
    indirect jmp.
  * pop_inline_frame_and_resume names its return-pc, dst-index, value-
    address, vm-pointer, and executable temps explicitly.

With nothing left referring to the positional aliases, drop the
tN / ftN -> physical-register fallback from registers::resolve_register
and update the DSL reference comments at the top of asmint.asm and in
main.rs to describe the named-temp model. The two pre-existing codegen
tests that probed the old positional behavior get rewritten to use the
post-allocation physical-register names directly, since that is now
the actual contract of resolve_op.
2026-04-26 13:29:56 +02:00
Andreas Kling
3950e434cb LibJS: Migrate the asm-managed Call handler to named DSL temporaries
The Call handler is the asm interpreter's hot path for inline JS-to-JS
dispatch, and it carries roughly fifty distinct named values across
its body (callee object, packed metadata word, formal/passed/total
counts, two stack-side pointers, the argument-copy cursor, the
script-or-module pair-load scratch, the native return value and
variant tag, ...).

The native-exception path adds a small `mov helper_arg, native_return`
bridge between call_raw_native (output pinned to rax by
fixed_operands) and call_helper (input pinned to rcx by
fixed_operands), since the two ABIs land their values in different
registers and the explicit mov is the cleanest way to express the
hand-off.
2026-04-26 13:29:56 +02:00
Andreas Kling
e8cd0ef7b8 LibJS: Migrate ObjectPropertyIteratorNext to named DSL temporaries
Convert the for-in iteration fast path to named temps. The handler
threads ~25 distinct values through its body (the iterator value,
its tag, the unboxed iterator, the fast-path discriminator, the
property cache pointer, the cached and current shapes, the receiver,
the dictionary-generation pair, the indexed-count and next-indexed
counters, the named-index, the named-key data pointer, and a few
scratch slots), so the migrated form is much easier to read than the
positional spaghetti where t0..t8 each meant five different things in
different basic blocks.
2026-04-26 13:29:56 +02:00
Andreas Kling
d930f135d2 LibJS: Migrate GetByValue to named DSL temporaries
Convert the array / typed-array load fast paths in GetByValue, the
mirror of PutByValue. Roughly the same shape: ~18 named GPR temps
and a slot_dbl FPR temp covering the loaded value, the bytes-or-int
raw integer view, the address scratch, and the negative-zero compare.

The .ta_float32 / .ta_float64 paths become readable: where the
positional code had "Exclude negative zero early (t1 gets clobbered
by double_to_int32)" because t1 had to do double duty, the migrated
form names slot, slot_dbl, neg_zero, and raw separately and the
allocator lays them out without the implicit clobber dance.
2026-04-26 13:29:56 +02:00
Andreas Kling
5015715c9d LibJS: Migrate PutByValue to named DSL temporaries
Convert the array / typed-array store fast paths in PutByValue --
the largest single handler so far at 150+ positional t-uses -- to
declare its 19 GPR temps and 1 FPR temp by name (kind, base, prop,
index, obj, flags, storage_kind, size, elements, src, capacity_addr,
capacity, slot, empty_tag, kind_byte, addr, src_int32, max, result,
src_dbl, plus base_tag/prop_tag scratches reused across paths).

The migrated body removes the "save in t0 before load_operand
clobbers it" / "compute store address before check_is_double clobbers
t4" maneuvers that the positional code needed -- the allocator
handles those constraints automatically.
2026-04-26 13:29:56 +02:00
Andreas Kling
f816e43fbb LibJS: Migrate string builtins and the utf16-code-unit load macro
Convert the load_primitive_string_utf16_code_unit macro to take its
inputs (string pointer, index) and output (code_unit) as explicit
parameters, plus migrate CallBuiltinStringFromCharCode,
CallBuiltinStringPrototypeCharCodeAt, and
CallBuiltinStringPrototypeCharAt.

The macro previously hard-coded inputs to t2/t4 and output to t0,
which forced callers to remember those slot assignments and to wrap
the call_helper interactions around them. The migrated form -- where
the caller names what it wants in each slot -- removes another
"clobbers t3, t5" comment.
2026-04-26 13:29:56 +02:00
Andreas Kling
f0db84b1ae LibJS: Migrate GetLength, GetGlobal, SetGlobal to named DSL temporaries
Convert the global variable IC and the array-length fast path to
named temps. These three handlers are large -- GetGlobal and
SetGlobal carry ~20 named values across each path (realm pointer,
global object, declarative environment, cache pointer, two serial
numbers, the shape, the cached shape, the dictionary generations,
the property offset, the named-properties pointer, the loaded value
and tag, plus environment-binding state) -- and the migration makes
those values easy to read instead of having to remember which slot
each piece of state was occupying.

GetLength's "magical" int-to-double widening picks up a named
temp for the sign-bit check and the result, which removes another
"NB: load_operand clobbers t0" comment.
2026-04-26 13:29:56 +02:00
Andreas Kling
85e6930806 LibJS: Migrate GetById and PutById to named DSL temporaries
Convert the inline-cache fast paths for property load/store to named
temps. Both handlers carry a substantial amount of state across the
ic-hit check (the boxed base value, its tag, the unboxed Object*,
the shape, the PLC pointer, the cached shape and prototype, the
property offset, the current and cached dictionary generations, the
named-properties pointer, and the loaded value), so giving each
piece an explicit name removes a real readability burden. The IC-miss
path uses the new 3-operand call_interp form.

Notably, PutById's old code carried a "save property offset in t4
before load_operand clobbers t0 (rax)" maneuver -- that is exactly
the kind of cross-cut the allocator handles automatically once the
offset is named. The migrated handler just keeps using `prop_offset`
across the load_operand and the store, no scratch shuffle needed.
2026-04-26 13:29:56 +02:00
Andreas Kling
8259a229d5 LibJS: Migrate validate_callee_builtin and Math builtins
Convert the validate_callee_builtin macro and the Math.abs / floor /
ceil / sqrt / exp / and ResolveThisBinding handlers to named DSL
temporaries. Math.exp uses the 3-operand call_helper form; the
allocator pins its `arg` to t1 (rcx) and `result` to t0 (rax)
automatically.
2026-04-26 13:29:56 +02:00
Andreas Kling
9e6a205575 LibJS: Migrate Div and Mod handlers to named DSL temporaries
Convert the Div and Mod handlers to named DSL temporaries. Mod is the
first migrated handler that uses divmod, with its quot/rem pinned to
rax/rdx by fixed_operands and its dividend/divisor kept off both by
the operand-vs-implicit-output interference rule.
2026-04-26 13:29:56 +02:00
Andreas Kling
a124af2abb LibJS: Migrate walk_env_chain and the binding handlers
Convert the walk_env_chain macro to take its outputs (target_env,
bind_index) as explicit parameters, then migrate every handler that
walks the environment chain: GetBinding, GetInitializedBinding,
InitializeLexicalBinding, SetLexicalBinding, and
GetCalleeAndThisFromEnvironment.

The macro's internal scratch (the EnvironmentCoordinate address, the
hops counter, the invalid-coord sentinel, and the screwed-by-eval
flag) is now declared as macro-local temps that the allocator places
without touching whatever the caller has live. Each handler picks
its own names for env / idx / binding / value, instead of remembering
that the macro happens to leave them in t3 and t2.
2026-04-26 13:29:56 +02:00
Andreas Kling
bb23e784dc LibJS: Migrate bitwise/UnaryPlus path and coerce_to_int32s macro
Convert coerce_to_int32s and bitwise_op to take operand temps as
explicit parameters, then migrate UnaryPlus.

The bitwise_op macro is the actual body of BitwiseAnd, BitwiseOr, and
BitwiseXor (which just call into it), so a single macro migration
covers all three handlers. UnaryPlus is migrated alongside since it
shares the same coerce-to-int32 conceptual path.
2026-04-26 13:29:56 +02:00
Andreas Kling
3a9661aa22 LibJS: Migrate equality handlers and their core macros
Convert the equality dispatch chain to take operand temps as explicit
parameters: equality_same_tag, double_equality_compare,
strict_equality_core, and loose_equality_core. Then migrate the
StrictlyEquals, StrictlyInequals, LooselyEquals, LooselyInequals,
JumpStrictlyEquals, JumpStrictlyInequals, JumpLooselyEquals, and
JumpLooselyInequals handlers.

These macros are tightly coupled -- strict_equality_core and
loose_equality_core both invoke equality_same_tag and reach
double_equality_compare via .double_compare, a label that crosses
macro boundaries. The existing label-uniquification rule (only
self-contained labels are renamed) keeps that contract working
without further plumbing.
2026-04-26 13:29:56 +02:00
Andreas Kling
86476e80e0 LibJS: Migrate Add/Sub/Mul, comparisons, and their core macros
Convert the heavy comparison and arithmetic macros to take their
operand temps as explicit parameters, then migrate every handler that
uses them: coerce_to_doubles, numeric_compare, numeric_compare_coerce,
boolean_result_epilogue, jump_binary_epilogue, plus the Add, Sub, Mul,
LessThan, LessThanEquals, GreaterThan, GreaterThanEquals,
JumpLessThan, JumpGreaterThan, JumpLessThanEquals, and
JumpGreaterThanEquals handlers.
2026-04-26 13:29:56 +02:00
Andreas Kling
123616d8ef LibJS: Migrate scratch-using helper macros to macro-local temps
Convert check_is_double, check_both_double, and box_double_or_int32 to
declare their internal scratch as macro-local `temp` decls instead of
hardcoding t3/t4. Each invocation now gets a freshly uniquified temp
that the allocator places independently of any other live values in
the caller, so adding a `temp` declaration around a sequence that
calls into one of these macros no longer needs to know which
positional slot the macro happened to be using.

box_double_or_int32 in particular is on the hot arithmetic path
(Add/Sub/Mul/PostfixIncrement/etc.), so this opens the door to
migrating those handlers without conflicting with the macro's
internal scratch use.
2026-04-26 13:29:56 +02:00
Andreas Kling
a19ae44977 LibJS: Migrate PostfixIncrement/PostfixDecrement/UnaryMinus/ToInt32
Convert these handlers to named DSL temporaries.
2026-04-26 13:29:56 +02:00
Andreas Kling
7fcaace5e8 LibJS: Migrate ThrowIf*/BitwiseNot/Shift* to named DSL temporaries
Convert another batch of macro-free handlers to named DSL temporaries.
The shift handlers in particular are nice exercises of the allocator's
fixed-operand support: x86's `shl`/`shr`/`sar` need the count register
to be rcx, and the allocator pins the named `count` temp accordingly
without the user having to remember it.
2026-04-26 13:29:56 +02:00
Andreas Kling
ddc5b995e5 LibJS: Migrate Increment/Decrement/Not/Return/End and friends
Convert another batch of straightforward handlers to named DSL
temporaries: Increment, Decrement, Not, GetLexicalEnvironment, Return,
and End. The migration uncovers a pleasing property of the new
pipeline: when a migrated handler invokes an un-migrated macro
(pop_inline_frame_and_resume here, which still uses positional t2/t3
/t4 internally), the macro body's positional uses appear as physical-
register defs in the flat instruction list and the allocator's
liveness analysis avoids placing live named temps in those registers.
2026-04-26 13:29:56 +02:00
Andreas Kling
9790948279 LibJS: Migrate JumpIf/JumpTrue/JumpFalse to named DSL temporaries
Convert the three boolean-conditional jump handlers to named temps,
exercising the 3-operand call_helper form so the input value and
truthy result no longer have to be pinned to t1/t0 by hand. The
allocator places `condition` in rcx (matching call_helper's input
convention) and `truthy` in rax (matching its output) automatically.
2026-04-26 13:29:56 +02:00
Andreas Kling
3d5b5602b4 LibJS: Migrate simple jump handlers to named DSL temporaries
Convert Jump, JumpNullish, and JumpUndefined to declare named
temporaries instead of pinning values to t0..t2 directly. These three
handlers are macro-free and call-free, so the migration is mechanical
and the allocator picks register assignments equivalent in cost to
the hand-written positional code.

The other Jump* handlers (JumpIf/JumpTrue/JumpFalse) drive
call_helper, which still needs the value to be in t1 (rcx) on x86 by
the existing register convention. Migrating those needs DSL syntax to
attach call_helper's input/output to named temps; until that lands
they stay on the positional form.
2026-04-26 13:29:56 +02:00
Andreas Kling
f1f7039d10 LibJS: Migrate Mov/Mov2/Mov3 handlers to named DSL temporaries
These three trivial data-movement handlers are a useful first
migration target -- they exercise the new `temp` declaration syntax
and the register allocator end-to-end without interacting with macros
or fixed-operand constraints. The allocator picks t1-equivalent
registers for the first temp (which is the smallest live range that
must survive load_operand's rax/x9 scratch use) and reuses the slot
across non-overlapping live ranges where possible.
2026-04-26 13:29:56 +02:00
Andreas Kling
e5d4c5cce8 LibJS: Check TDZ state in asm environment calls
GetCalleeAndThisFromEnvironment treated a binding as initialized when
its value slot was not <empty>. Declarative bindings do not encode TDZ
in that slot, though: uninitialized bindings keep a separate initialized
flag and their value starts as undefined.

That let the first slow-path TDZ failure populate the environment cache,
then a second call at the same site reused the cached coordinate and
turned the required ReferenceError into a TypeError from calling
undefined.

Check Binding.initialized in the asm fast path instead and cover the
cached second-hit case with a regression test.
2026-04-20 11:23:34 +02:00
Andreas Kling
583fa475fb LibJS: Call RawNativeFunction directly from asm Call
The asm interpreter already inlines ECMAScript calls, but builtin calls
still went through the generic C++ Call slow path even when the callee
was a plain native function pointer. That added an avoidable boundary
around hot builtin calls and kept asm from taking full advantage of the
new RawNativeFunction representation.

Teach the asm Call handler to recognize RawNativeFunction, allocate the
callee frame on the interpreter stack, copy the call-site arguments,
and jump straight to the stored C++ entry point.
NativeJavaScriptBackedFunction and other non-raw callees keep falling
through to the existing C++ slow path unchanged.
2026-04-15 15:57:48 +02:00
Andreas Kling
23fea4208c LibJS: Pair-load asm global Realm metadata
Place Realm's cached declarative environment next to its global object
so the asm global access fast paths can fetch the two pointers with a
paired load. These handlers never use the intervening GlobalEnvironment
pointer directly.
2026-04-14 12:37:12 +02:00