242 Commits

Author SHA1 Message Date
Aliaksandr Kalenik
711100e3b7 LibJS: Avoid unnecessary NewArray in yield* iterator method calls
Use `Op::Call` directly instead of creating a single-element array and
using `CallWithArgumentArray` when calling iterator methods (`next`,
`throw`, `return`) in `yield*` expressions.
2025-12-28 19:12:15 +01:00
Andreas Kling
ce0a16d11c LibJS: Simplify how we know which builtin a FunctionObject represents
Instead of storing a list of builtin function objects with the realm,
just move the builtin field from NativeFunction up to FunctionObject.

Now you can ask any FunctionObject for its builtin(), and we no longer
need the get_builtin_value() API.

Fixes 10 test262 tests that were querying the realm builtins at a
bad time.

Regressed in 54b755126c.
2025-12-25 23:59:21 +01:00
Andreas Kling
e7e3c21123 LibJS: Shift PropertyLookupCache::types when finding a new slot
This regressed in c258282094 and broke
the polymorphic lookup cache, which was caught by the pic-* tests in
our MicroBench suite.
2025-12-21 13:00:35 -06:00
Andreas Kling
c258282094 LibJS: Shrink PropertyLookupCache by putting types in its own array
We have so many inline caches that this kind of thing becomes profitable
on complex pages. Also the memory access pattern is slightly nicer for
polymorphic caches.

Reduces memory usage on my x.com home feed by 4.9 MiB.
2025-12-21 10:06:04 -06:00
Andreas Kling
138b8f9607 LibJS: Shrink PropertyLookupCache::Entry size by tweaking layout
Reorder members and use u32 instead of Optional<u32> for things that
didn't actually need the "empty" state other than for assertions.

Reduces memory usage on my x.com home feed by 9.9 MiB.
2025-12-21 10:06:04 -06:00
Andreas Kling
ece0b72e3c LibJS: Don't set [[HomeObject]] for non-method object properties
This fixes an issue where we'd incorrectly retain objects via the
[[HomeObject]] slot. This common pattern was affected:

    Object.defineProperty(o, "foo", {
        get: function() { return 123; }
    });

Above, the object literal would get assigned to the [[HomeObject]]
slot even though "get" is not a "method" per the spec.

This frees about 30,000 objects on my x.com home feed.
2025-12-17 12:50:17 -06:00
Andreas Kling
ee9e24d1dd LibJS: Add dedicated bytecode instruction for x|0 (ToInt32)
This operation is a very common technique to force a value to become
a 32-bit integer.
2025-12-15 08:57:00 -06:00
Andreas Kling
bfe5a87c99 LibJS: Skip some hot bounds checks in the interpreter
Let's allow ourselves to trust the number of property lookup caches
present, along with a few other things.
2025-12-14 08:40:22 -06:00
Andreas Kling
65447faec9 LibJS: Avoid three PropertyKey copies in the interpreter 2025-12-14 08:40:22 -06:00
Andreas Kling
2a776f1d70 LibJS: Put the PropertyLookupCache's shape in a local
This avoids the extra check in Weak::operator->() a bunch of times.
2025-12-14 08:40:22 -06:00
Andreas Kling
54b755126c LibJS: Skip generic call when using regexp builtins in StringPrototype
For StringPrototype functions that defer to RegExpPrototype builtins,
we can skip the generic call stuff (eliding the execution context etc)
and just call the builtin directly.

1.03x speedup on Octane/regexp.js
2025-12-13 13:51:12 -06:00
Andreas Kling
82fe962d96 LibJS: Don't rerun regexp optimizer every time a regexp literal is used 2025-12-12 11:43:35 -06:00
Andreas Kling
499f0a59cf LibJS: Add variant of Object::set() that takes PropertyLookupCache
This allows us to use inline caches when setting properties in C++.
2025-12-12 11:43:35 -06:00
Andreas Kling
9709148512 LibJS: Add Value::is_non_negative_int32()
This helper combines the check for is_int32() and as_i32() >= 0 in a
single mask and compare operation.
2025-12-11 14:34:45 -06:00
Andreas Kling
ab98145451 LibJS: Mark NewTypeError instruction COLD
Missed this one when marking all the various throwing paths in the
interpreter cold/unlikely.
2025-12-11 14:34:45 -06:00
Andreas Kling
a62daf2a88 LibJS: Remove redundant Put*ByNumericId* instructions
These were helpful when PropertyKey instantiation happened in the
interpreter, but now that we've moved it to bytecode generation time,
we can use the basic Put*ById* instructions instead.
2025-12-11 14:34:45 -06:00
Andreas Kling
bad16dc0e0 LibJS: Cache fully-formed PropertyKeys in Executable
Instead of creating PropertyKeys on the fly during interpreter
execution, we now store fully-formed ones in the Executable.

This avoids a whole bunch of busywork in property access instructions
and substantially reduces code size bloat.
2025-12-11 14:34:45 -06:00
Andreas Kling
1dcf137d02 LibJS: Mark all throwing code paths in property access COLD and unlikely 2025-12-10 17:40:57 -06:00
Andreas Kling
34a2129ce5 LibJS: Mark GetObjectPropertyIterator and NewClass NEVER_INLINE
These instructions are not necessarily rarely used, but they are very
large in terms of code size. By putting them out of line we keep the hot
path of the interpreter smaller and tighter.
2025-12-10 09:25:44 -06:00
Andreas Kling
740fab27f2 LibJS: Mark delete operator instruction handlers COLD
The delete operator is very rarely used in JavaScript, so let's keep it
off the hot path.
2025-12-10 09:25:44 -06:00
Andreas Kling
2e172b210a Revert "LibJS: Remove unnecessary ConcatString bytecode instruction"
This reverts commit 420187ba7c.

Caused 41 regressions in test262.
2025-12-10 09:17:11 -06:00
Andreas Kling
7bdeb71448 Revert "LibJS: Move more of GetById/PutById unhappy paths to cold section"
This reverts commit 3e32b6ad7c.

This caused a performance regression on some Linux machines.
2025-12-10 08:52:07 -06:00
Andreas Kling
420187ba7c LibJS: Remove unnecessary ConcatString bytecode instruction
In favor of just using Add instead.
2025-12-09 21:44:13 -06:00
Andreas Kling
4072a96095 LibJS: Mark three uncommon instruction handlers COLD
- with statement
- throw statement
- catch statement

All of these should be rare cases (especially with!)
2025-12-09 21:44:13 -06:00
Andreas Kling
d762f30c50 LibJS: Mark Interpreter::handle_exception() COLD 2025-12-09 21:44:13 -06:00
Andreas Kling
f4fb3f99c8 LibJS: Mark call instruction unhappy paths COLD and [[unlikely]] 2025-12-09 21:44:13 -06:00
Andreas Kling
3e32b6ad7c LibJS: Move more of GetById/PutById unhappy paths to cold section
- The uncached path in GetById (will be cached if executed again)
- Any path that throws an exception
2025-12-09 21:44:13 -06:00
Andreas Kling
cb23d65625 LibJS: Pass JS::Value directly to string formatting functions
We don't need to call .to_string_without_side_effects() when passing
a JS::Value in for string formatting. The Formatter will do it for us.
2025-12-09 21:44:13 -06:00
Andreas Kling
83824f41c0 LibJS: Don't check for exceptions after Throw instruction
There's always an exception, so just *assume* there is one!
2025-12-09 21:44:13 -06:00
Andreas Kling
f3fb02e6ef LibJS: Mark exception_handlers_for_offset() COLD 2025-12-09 21:44:13 -06:00
Andreas Kling
ae21f56bc7 LibJS: Remove unused perform_call() helper function 2025-12-09 21:44:13 -06:00
Andreas Kling
c21f6aa43c LibJS: Mark non-throwing path in GetGlobal [[likely]] 2025-12-09 21:44:13 -06:00
Andreas Kling
ad80d3a25a LibJS: Mark more interpreter fast paths [[likely]]
Let's assume arithmetic and comparison operators are mostly being used
on numbers.
2025-12-09 21:44:13 -06:00
Andreas Kling
b27c1aa649 LibJS: Mark Int32 overflow in interpreter fast paths [[unlikely]] 2025-12-09 21:44:13 -06:00
Andreas Kling
2a4c76fa29 LibJS: Mark Not and Typeof as non-throwing instructions 2025-12-09 21:44:13 -06:00
Andreas Kling
43f01d3f5b LibJS: Mark Int32 paths in Increment and PostfixIncrement [[likely]] 2025-12-09 21:44:13 -06:00
Andreas Kling
aa5ee7866f LibJS: Move CallBuiltin argument count check to bytecode compiler
No need to check this at runtime, we have all the necessary info already
when generating bytecode.

Also mark the "yes, we are indeed calling the builtin" path [[likely]]
since it's exceedingly rare for anyone to replace the global functions.
2025-12-09 21:44:13 -06:00
Andreas Kling
ebb57bf56a LibJS: Move IsCallable and IsConstructor out of the interpreter loop
There's no reason for these to live in the main interpreter loop.
2025-12-09 21:44:13 -06:00
Andreas Kling
d2f9f91e45 LibJS: Remove FLATTEN from bytecode interpreter
This doesn't appear to improve performance on my machine anymore.
It also very modestly reduces interpreter size.
2025-12-09 21:44:13 -06:00
Andreas Kling
fca29400af LibJS: Mark more error paths in the interpreter as [[unlikely]] 2025-12-09 21:44:13 -06:00
Andreas Kling
f43143e7b9 LibJS: Mark weird error path in CreateVariable as [[unlikely]] 2025-12-09 21:44:13 -06:00
Andreas Kling
333724edc1 LibJS: Mark with path in GetCalleeAndThisFromEnvironment [[unlikely]]
This doesn't affect interpreter size directly, but let's inform the
compiler that we're not terribly worried about code using the `with`
statement in JS.
2025-12-09 21:44:13 -06:00
Andreas Kling
904d8e0eda LibJS: Mark some throwing paths in interpreter as [[unlikely]] 2025-12-09 21:44:13 -06:00
Andreas Kling
a585955744 LibJS: Pass Optional<T> const& by value more in the interpreter code 2025-12-09 21:44:13 -06:00
Andreas Kling
7523dc7141 LibJS: Simplify NewClass instruction handler a bit 2025-12-09 21:44:13 -06:00
Andreas Kling
d8381cf7fb LibJS: Avoid Utf16FlyString copy constructors in the interpreter
Just a minor thing to reduce code bloat.
2025-12-09 21:44:13 -06:00
Andreas Kling
9f822345bf LibJS: Flatten Operand to 32-bit index in bytecode instruction stream
While we're in the bytecode compiler, we want to know which type of
Operand we're dealing with, but once we've generated the bytecode
stream, we only ever need its index.

This patch simplifies Operand by removing the aarch64 bitfield hacks
and makes it 32-bit on all platforms. We keep 3 type bits in the high
bits of the index while compiling, and then zero them out when
flattening the final bytecode stream.

This makes bytecode more compact on x86_64, and avoids bit twiddling
on aarch64. Everyone wins something!

When stringifying bytecode for debugging output, we now have an API in
Executable that can look at a raw operand index and tell you what type
of operand it was, based on known quantities of each type in the stack
frame.
2025-12-09 21:44:13 -06:00
Andreas Kling
8289b24a7e LibJS: Introduce VM::the() and use it instead of caching VM pointer
In our process architecture, there's only ever one JS::VM per process.
This allows us to have a VM::the() singleton getter that optimizes
down to a single global access everywhere.

Seeing 1-2% speed-up on all JS benchmarks from this.
2025-12-09 11:58:39 -06:00
Luke Wilde
0eceee0a05 LibJS: Replace Array.fromAsync with a native JavaScript implementation
This allows us to use the bytecode implementation of await, which
correctly suspends execution contexts and handles completion
injections.

This gains us 4 test262 tests around mutating Array.fromAsync's
iterable whilst it's suspended as well.

This is also one step towards removing spin_until, which the
non-bytecode implementation of await uses.

```
Duration:
     -5.98s

Summary:
    Diff Tests:
        +4     -4 

Diff Tests:
    [...]/Array/fromAsync/asyncitems-array-add-to-singleton.js  -> 
    [...]/Array/fromAsync/asyncitems-array-add.js               -> 
    [...]/Array/fromAsync/asyncitems-array-mutate.js            -> 
    [...]/Array/fromAsync/asyncitems-array-remove.js            -> 
```
2025-11-30 11:54:54 +01:00
Luke Wilde
a63b0cfaba LibJS: Introduce NativeJavaScriptBackedFunction
This hosts the ability to compile and run JavaScript to implement
native functions. This is particularly useful for any native function
that is not a normal function, for example async functions such as
Array.fromAsync, which require yielding.

These functions are not allowed to observe anything from outside their
environment. Any global identifiers will instead be assumed to be a
reference to an abstract operation or a constant. The generator will
inject the appropriate bytecode if the name of the global identifier
matches a known name. Anything else will cause a code generation error.
2025-11-30 11:54:54 +01:00