Commit Graph

123 Commits

Author SHA1 Message Date
Andreas Kling
62781f4818 LibJS: Use return_value register for "last completion" return values
This simplifies the epilogue in run_executable().
2025-10-31 08:56:02 +01:00
Andreas Kling
5706831328 LibJS: Make run_executable() return simple ThrowCompletionOr<Value>
We don't need to return two values; running an executable only ever
produces a throw completion, or a normal completion, i.e a Value.

This necessitated a few minor changes, such as adding a way to check
if a JS::Cell is a GeneratorResult.
2025-10-31 08:56:02 +01:00
Andreas Kling
cdcbbcf48b LibJS: Allocate callee execution contexts in call instruction handler
By handling call instructions in an inline (C++) function, we were
breaking the alloca() optimization and adding stack overhead. We fix
this by using a macro instead. It looks awful but it works.

1.07x speedup on MicroBench/call-00-args.js
2025-10-30 08:54:45 +01:00
Andreas Kling
667354fd12 LibJS: Always assume module bindings access is in strict mode
Modules are always in strict mode anyway, no need to look at the
strictness flag here.
2025-10-30 08:54:45 +01:00
Andreas Kling
9dae1acc31 LibJS: Pass ExecutionContext to Interpreter::run_executable()
This avoids having to get it from the VM's context stack, since most
callers already have it on hand.
2025-10-29 21:20:10 +01:00
Andreas Kling
a7d13b107e LibJS: Move all per-frame state from Interpreter to ExecutionContext
This simplifies function entry/exit and lets us just walk away from the
used ExecutionContext instead of resetting a bunch of its state when
returning control to the caller.
2025-10-29 21:20:10 +01:00
Andreas Kling
59ce6c9b41 LibJS: Shrink two u64 fields in ExecutionContext to u32
To shrink ExecutionContext, these two fields are made 32-bit:

- skip_when_determining_incumbent_counter
- program_counter
2025-10-29 21:20:10 +01:00
Andreas Kling
fdb85a330e LibJS: Stop tracking whether execution context is strict mode or not
This was only used for basic testing, and forced us to plumb this flag
flag in a bunch of places.
2025-10-29 21:20:10 +01:00
Andreas Kling
fb05063dde LibJS: Let bytecode instructions know whether they are in strict mode
This commits puts the strict mode flag in the header of every bytecode
instruction. This allows us to check for strict mode without looking at
the currently running execution context.
2025-10-29 21:20:10 +01:00
Andreas Kling
3fb678b376 LibGC: Delete operators ! and bool from GC::Ref
The GC::Ref smart pointer is always non-null, so there's no need for it
to be convertible to bool.

This exposed a small number of unnecessary null checks which we remove.
2025-10-29 21:20:10 +01:00
Andreas Kling
9312a9f86f LibJS: Move InstantiateOrdinaryFunctionExpression into interpreter
This is execution time stuff and doesn't belong in the AST.
2025-10-27 21:14:33 +01:00
Andreas Kling
44fa9566a8 LibJS: Generate bytecode for the BlockDeclarationInstantiation AO
This necessitated adding some new instructions for creating mutable and
immutable bindings.
2025-10-27 21:14:33 +01:00
Andreas Kling
892c7d980e LibJS: Let JS::Script remember whether its code is strict mode
We don't want to rely on having the AST node just to answer the question
"is this script strict mode?"
2025-10-27 21:14:33 +01:00
Luke Wilde
d4deafe5fe LibJS: Track current shape dictionary generation in PropertyLookupCache
When an object becomes too big (currently 64 properties or more), we
change its shape to a dictionary and don't do any further transitions.

However, this means the Shape of the object no longer changes, so the
cache invalidation check of `current_shape != cache.shape` is no longer
a valid check.

This fixes that by keeping track of a generation number for the Shape
both on the Shape object and in the cache, allowing that to be checked
instead of the Shape identity. The generation is incremented whenever
the dictionary is mutated.

Fixes stale cache lookups on Gmail preventing emails from being
displayed.

I was not able to produce a reproduction for this, plus the generation
count was over the 20k mark on Gmail.
2025-10-24 15:35:04 +02:00
Andreas Kling
7ee8645b9c LibJS: Avoid redundant GC::Weak checks in hot inline cache code paths
By copying out pointees to a temporary, we can avoid some redundant
pointer chasing inside GC::Weak when hitting our inline caches.
2025-10-17 17:22:16 +02:00
Andreas Kling
26c1dea22a LibJS: Move GetById logic to a separate header to prepare for reuse
We also make the code a bit more generic by making callers provide
(templated) callbacks that produce the property name and base expression
string if any.
2025-10-14 15:47:38 +02:00
Andreas Kling
0e4450f4b3 LibJS: Avoid function call if @@hasInstance is default implementation
This makes the instanceof operator signficantly faster by avoiding a
generic function call to @@hasInstance unless it has been overridden.

1.15x speed-up on Octane/earley-boyer.js
2025-10-13 17:15:44 +02:00
Andreas Kling
a76f420207 LibJS: Add fast path for division of two numbers
We already had fast paths for Add, Sub and Mul. Might as well do Div.

1.18x speed-up on this micro-benchmark:

    (() => {
        let a = 1234;
        for (let i = 0; i < 100_000_000; ++i)
            a / a;
    })()
2025-10-11 20:08:58 +02:00
Andreas Kling
7462c10ee2 LibJS: Sprinkle [[unlikely]] on the PutBy* cache miss code paths
These caches are gonna be hitting most of the time, so let's give the
compiler a hint about this.

1.01x speed-up on MicroBench/pic-add-own.js
2025-10-11 20:08:58 +02:00
Andreas Kling
b47f8f94fe LibJS: Split PutBy* instructions into specialized per-kind variants
This allows the compiler to fold away lots of unused code for each kind.

1.10x speed-up on MicroBench/pic-add-own.js :^)
2025-10-11 20:08:58 +02:00
Andreas Kling
d13b4f3e39 LibJS: Remove unused member PutPrivateById::m_kind
Private property puts never have a PutKind.
2025-10-11 20:08:58 +02:00
Andreas Kling
e7a3c4dbad LibJS: Rename Bytecode::Op::PropertyKind => Bytecode::PutKind
This is only used to specify how a property is being added to an object
by Put* instructions, so let's call it PutKind.

Also add an enumeration X macro for it to prepare for upcoming
specializations.
2025-10-11 20:08:58 +02:00
Andreas Kling
695f02e8e5 LibJS: Give Interpreter a direct pointer to the identifier table
This gets rid of a lot of pointer chasing from interpreter to executable
to identifier table to the actual identifier.

1.05x speed-up on Kraken/ai-astar.js
2025-10-07 23:50:51 +02:00
Andreas Kling
d0df7c8c62 LibJS: Use [[likely]] annotations for cached environment lookups
These will generally be cached the vast majority of the time except on
first encounter, and sprinkling [[likely]] gives us a nice boost.

1.10x speed-up on this micro-benchmark:

    (() => {
        var a = 3;
        for (let i = 0; i < 100_000_000; ++i) { a; }
        eval("");
    })();
2025-10-07 23:50:51 +02:00
Andreas Kling
38e224cd9f LibJS: Make overflowing arithmetic on 2x Int32 values faster
Instead of converting them to doubles and doing double math, just do the
arithmetic operation in i64 space instead.

This gives us a ~1.25x speed-up on this kind of micro-benchmark:

    (() => {
        let a = -2124299999;
        for (let i = 0; i < 100_000_000; ++i) {
            a + a;
        }
    })()

Same idea for Add, Sub, and Mul.

There's a fair bit of overflowing Int32 arithmetic in some of the
JetStream benchmarks, and this seems like an obvious improvement.
2025-10-07 18:28:26 +02:00
Aliaksandr Kalenik
1d41cf10f4 LibJS: Optimize keys deduplication in get_object_property_iterator()
This change implements a part responsible for this invariant in a more
efficient way:
"Enumerating the properties of the target object includes enumerating
properties of its prototype, and the prototype of the prototype, and so
on, recursively; but a property of a prototype is not processed if it
has the same name as a property that has already been processed by the
iterator's next method."

Previously we inserted `(key, enumerable)` pairs into an
`OrderedHashTable`. That always built and maintained a hash table, even
when no prototype-level filtering was needed.

Now we:
- Collect only enumerable keys into `Vector<PropertyKey>`.
- Track `seen_non_enumerable_properties` so a non-enumerable own
  property still shadows prototype properties with the same name.
- Lazily materialize `HashTable<PropertyKey>` only if we encounter an
  enumerable property on a prototype and must check for duplicates. In
  the common case materialization is avoided, because default Object or
  Array prototype properties are non-enumerable.
2025-09-21 15:06:32 +02:00
Aliaksandr Kalenik
451c947c3f LibJS: Fast-path own-property enumeration and reduce descriptor lookups
Before this change, PropertyNameIterator (used by for..in) and
`Object::enumerable_own_property_names()` (used by `Object.keys()`,
`Object.values()`, and `Object.entries()`) enumerated an object's own
enumerable properties exactly as the spec prescribes:
- Call `internal_own_property_keys()`, allocating a list of JS::Value
  keys.
- For each key, call internal_get_own_property() to obtain a
  descriptor and check `[[Enumerable]]`.

While that is required in the general case (e.g. for Proxy objects or
platform/exotic objects that override `[[OwnPropertyKeys]]`), it's
overkill for ordinary JS objects that store their own properties in the
shape table and indexed-properties storage.

This change introduces `for_each_own_property_with_enumerability()`,
which, for objects where
`eligible_for_own_property_enumeration_fast_path()` is `true`, lets us
read the enumerability directly from shape metadata (and from
indexed-properties storage) without a per-property descriptor lookup.
When we cannot avoid `internal_get_own_property()`, we still
benefit by skipping the temporary `Vector<Value>` of keys and avoiding
the unnecessary round-trip between PropertyKey and Value.
2025-09-21 15:06:32 +02:00
Aliaksandr Kalenik
c3b0eabf18 LibJS: Capture PrototypeChainValidity before executing internal_get()
- Capture PrototypeChainValidity before invoking `internal_get()`. A
  getter may mutate the prototype chain (e.g., delete itself). Capturing
  earlier ensures such mutations invalidate the cached entry and prevent
  stale GetById hits.
- When caching, take PrototypeChainValidity from the base object
  (receiver), not from the prototype where the property was found.
  Otherwise, changes to an intermediate prototype between the base
  object and the cached prototype object go unnoticed, leading to
  incorrect cache hits.
2025-09-18 15:56:20 +02:00
Aliaksandr Kalenik
d45f8a3081 LibJS: Add inline caching for adding new own properties to objects
We already had IC support in PutById for the following cases:
- Changing an existing own property
- Calling a setter located in the prototype chain

This was enough to speed up code where structurally identical objects
(same shape) are processed in a loop:
```js
const arr = [{ a: 1 }, { a: 2 }, { a: 3 }];
for (let obj of arr) {
    obj.a += 1;
}
```

However, creating structurally identical objects in a loop was still
slow:
```js
for (let i = 0; i < 10_000_000; i++) {
    const o = {};
    o.a = 1;
    o.b = 2;
    o.c = 3;
}
```

This change addresses that by adding a new IC type that caches both the
source and target shapes, allowing property additions to be fast-pathed
by directly jumping to the shape that already includes the new property.
2025-09-17 12:44:44 +02:00
Aliaksandr Kalenik
0076ab74b2 LibJS: Don't attempt to cast property key to number in PutByIdWithThis
Property key for this instruction cannot be a number.
2025-09-13 20:02:28 +02:00
Aliaksandr Kalenik
e81833423b LibJS: Add PutByNumericId and change PutById to be string key only
Previously, PutById constructed a PropertyKey from the identifier,
which coerced numeric-like strings to numbers. This moves that decision
to bytecode generation: the bytecode generator now emits PutByNumericId
for numeric keys and PutById for string keys. This removes per-execution
parsing from the interpreter.

1.4x speedup on the following microbenchmark:
```js
const o = {};
for (let i = 0; i < 10_000_000; i++) {
    o.a = 1;
    o.b = 2;
    o.c = 3;
}
```
2025-09-13 20:02:28 +02:00
Andreas Kling
f89afe8e27 LibJS: Allocate context up front in SuperCallWithArgumentArray
This also removes the last user of Interpreter's argument buffer
allocation API, which we've used to repeatedly shoot ourselves in the
foot. Good-bye!
2025-08-31 15:24:37 +02:00
Andreas Kling
996ea109b3 LibJS: Allocate context up front when calling with argument array
This necessitated splitting CallWithArgumentArray into three variants,
one for each call type (call, construct and direct eval).
2025-08-31 15:24:37 +02:00
Andreas Kling
e5b07858a2 LibJS: Allocate Call{Construct,DirectEval,Builtin) contexts up front
We already do this for normal Call contexts, so this is just continuing
to propagate the same pattern to other instructions.

Fixes #6026
2025-08-31 15:24:37 +02:00
Timothy Flynn
70db474cf0 LibJS+LibWeb: Port interned bytecode strings to UTF-16
This was almost a no-op, except we intern JS exception messages. So the
bulk of this patch is porting exception messages to UTF-16.
2025-08-14 10:27:08 +02:00
Timothy Flynn
c87122eb32 LibJS: Add a method to stringify a BigInt to UTF-16
And remove the ByteString variant while we are here.
2025-08-14 10:27:08 +02:00
Timothy Flynn
62d85dd90a LibJS: Port RegExp flags and patterns to UTF-16 2025-08-13 09:56:13 -04:00
Timothy Flynn
b955c9b2a9 LibJS: Port the Identifier AST (and related) nodes to UTF-16
This eliminates quite a lot of UTF-8 / UTF-16 churn.
2025-08-13 09:56:13 -04:00
Timothy Flynn
0efa98a57a LibJS+LibWeb+WebContent: Port JS::PropertyKey to UTF-16
This has quite a lot of fall out. But the majority of it is just type or
UDL substitution, where the changes just fall through to other function
calls.

By changing property key storage to UTF-16, the main affected areas are:
* NativeFunction names must now be UTF-16
* Bytecode identifiers must now be UTF-16
* Module/binding names must now be UTF-16
2025-08-05 07:07:15 -04:00
Aliaksandr Kalenik
285bc005cb LibJS: Do more comprehensive check if next() fast path is possible
Before this change each built-in iterator object has a boolean
`m_next_method_was_redefined`. If user code later changed the iterator’s
prototype (e.g. `Object.setPrototypeOf()`), we still believed the
built-in fast-path was safe and skipped the user supplied override,
producing wrong results.

With this change
`BuiltinIterator::as_builtin_iterator_if_next_is_not_redefined()` looks
up the current `next` property and verifies that it is still the
built-in native function.
2025-06-02 00:15:36 +02:00
R-Goc
96c197faf1 LibJS: Add minimum changes to build on Windows and run js.exe
This commit adds the minimal export macros needed to run js.exe on
windows. A followup commit is planned to move to explicit export
entirely.

A static_assert for the size of a struct is also ifdef'ed out as the
semantics around object layout and inheritance are different on MSVC abi
and the struct IteratorRecord ends up being 40 bytes not 32.
2025-05-29 03:26:23 -06:00
Aliaksandr Kalenik
1647d7b34c LibJS: Use CallBuiltin for Math.tan() 2025-05-26 21:52:43 +02:00
Aliaksandr Kalenik
878cc16d7a LibJS: Use CallBuiltin for Math.cos() 2025-05-26 21:52:43 +02:00
Aliaksandr Kalenik
c02535e9f9 LibJS: Use CallBuiltin for Math.sin()
Improves performance on https://pierre.co/
2025-05-26 21:52:43 +02:00
Shannon Booth
20dba8f1ab LibJS: Make Op::Return value required
It turns out we do not have any scenario where this is not provided.
2025-05-23 03:25:55 +02:00
Aliaksandr Kalenik
95e1ec4abc LibJS: Skip caching get_by_id() if object's shape is changed by a getter
Fixes a bug that reproduces with the following steps:
1. Create an object with a getter for property "a" in its prototype,
   where the getter adds an "a" property to the object itself.
2. Call the "a" getter in a loop for the first time. This triggers
   caching of metadata indicating that the "a" property is located in
   the prototype chain.
3. Call the "a" getter in a loop for the second time. Oops, the cache
   says the getter is in the prototype chain, but the object now
   also has its own "a" property that was added by the first getter
   call.
2025-05-20 19:10:56 -04:00
Aliaksandr Kalenik
b559965448 LibJS+LibWeb: Replace StringOrSymbol usage with PropertyKey
- Avoids unnecessary conversions between StringOrSymbol and PropertyKey
  on the hot path of property access.
- Simplifies the code by removing StringOrSymbol and using PropertyKey
  directly. There was no reason to have a separate StringOrSymbol type
  representing the same data as PropertyKey, just with the index key
  stored as a string.

PropertyKey has been updated to use a tagged pointer instead of a
Variant, so it still occupies 8 bytes, same as StringOrSymbol.

12% improvement on JetStream/gcc-loops.cpp.js
12% improvement on MicroBench/object-assign.js
7% improvement on MicroBench/object-keys.js
2025-05-17 10:08:37 -04:00
Tim Ledbetter
2903defcfc Revert "LibJS+LibWeb: Return Vector<PropertyKey> from…
internal_own_property_keys"

This reverts commit 5ee810f772.
2025-05-16 06:33:09 +01:00
Aliaksandr Kalenik
5ee810f772 LibJS+LibWeb: Return Vector<PropertyKey> from internal_own_property_keys
By doing that we avoid lots of `PropertyKey` -> `Value` -> `PropertyKey`
transforms, which are quite expensive because of underlying
`FlyString` -> `PrimitiveString` -> `FlyString` conversions.

10% improvement on MicroBench/object-keys.js
2025-05-15 14:12:18 -04:00
R-Goc
34c14d4e6d LibJS: Add fast path for float to int put
This commit adds a fast path for putting values into a TypedArray of an
integer type, when the value being put in is a double. This leads to a
6% speedup on JetStream/gcc-loops.js.
2025-05-14 12:33:35 +02:00