Commit Graph

5224 Commits

Author SHA1 Message Date
Andreas Kling
b45f55b199 LibJS/Bytecode: Fix wonky serialization of instruction value lists 2024-05-07 09:15:40 +02:00
Andreas Kling
9cbf17f181 LibJS/Bytecode: Emit do...while body before test in codegen
This makes the code flow naturally and allows jump elision to work.
2024-05-07 09:15:40 +02:00
Andreas Kling
68507b7e55 LibJS/Bytecode: Store SetLocal's local index as a u32
Same size as local indexes everywhere else.
2024-05-07 09:15:40 +02:00
Andreas Kling
e43d96f310 LibJS/Bytecode: Remove Instruction::m_length field
Now that the interpreter is unrolled, we can advance the program counter
manually based on the current instruction type.

This makes most instructions a bit smaller. :^)
2024-05-07 09:15:40 +02:00
Andreas Kling
ce93000757 LibJS/Bytecode: Unroll the bytecode interpreter
This commit adds a HANDLE_INSTRUCTION macro that expands to everything
needed to handle a single instruction (invoking the handler function,
checking for exceptions, and advancing the program counter).

This gives a ~15% speed-up on a for loop microbenchmark, and makes
basically everything faster.
2024-05-07 09:15:40 +02:00
Andreas Kling
fae1527a18 LibJS/Bytecode: Turn JumpIf condition,@a,@next into JumpTrue/JumpFalse
If one of the jump targets is the very next block, we can convert the
jump instruction into a smaller JumpTrue or JumpFalse.
2024-05-07 09:15:40 +02:00
Andreas Kling
37d722f4a6 LibJS/Bytecode: Make IdentifierTableIndex a 32-bit index
This makes a bunch of instructions smaller.
2024-05-07 09:15:40 +02:00
Andreas Kling
95759dcc6d LibJS/Bytecode: Put end block last in for statement codegen 2024-05-07 09:15:40 +02:00
Andreas Kling
f3d57db774 LibJS/Bytecode: Stop emitting unnecessary jump at end of for statement 2024-05-07 09:15:40 +02:00
Andreas Kling
69cc64e94c LibJS/Bytecode: Emit for condition check before update statement
This allows jump elision to eliminate an unnecessary jump since things
now get laid out naturally in memory (normal execution order).
2024-05-07 09:15:40 +02:00
Andreas Kling
24d8b056c7 LibJS/Bytecode: Elide jumps that land in the very next block
Jumping to the next block is effectively a no-op, so let's save time and
space by just not emitting those jumps.
2024-05-07 09:15:40 +02:00
Andreas Kling
3a73eb99ac LibJS/Bytecode: Store labels as basic block index during compilation
Instead of storing a BasicBlock* and forcing the size of Label to be
sizeof(BasicBlock*), we now store the basic block index as a u32.

This means the final version of the bytecode is able to keep labels
at sizeof(u32), shrinking the size of many instructions. :^)
2024-05-07 09:15:40 +02:00
Andreas Kling
5a08544138 LibJS/Bytecode: Keep instruction source mappings in Executable
Instead of storing source offsets with each instruction, we now keep
them in a side table in Executable.

This shrinks each instruction by 8 bytes, further improving locality.
2024-05-07 09:15:40 +02:00
Andreas Kling
4cf4ea92a7 LibJS/Bytecode: Store Instruction length as u32
This makes every instruction 8 bytes smaller.
2024-05-07 09:15:40 +02:00
Andreas Kling
f6aee2b9e8 LibJS/Bytecode: Flatten bytecode to a contiguous representation
Instead of keeping bytecode as a set of disjoint basic blocks on the
malloc heap, bytecode is now a contiguous sequence of bytes(!)

The transformation happens at the end of Bytecode::Generator::generate()
and the only really hairy part is rerouting jump labels.

This required solving a few problems:

- The interpreter execution loop had to change quite a bit, since we
  were storing BasicBlock pointers all over the place, and control
  transfer was done by redirecting the interpreter's current block.

- Exception handlers & finalizers are now stored per-bytecode-range
  in a side table in Executable.

- The interpreter now has a plain program counter instead of a stream
  iterator. This actually makes error stack generation a bit nicer
  since we just have to deal with a number instead of reaching into
  the iterator.

This yields a 25% performance improvement on this microbenchmark:

    for (let i = 0; i < 1_000_000; ++i) { }

But basically everything gets faster. :^)
2024-05-07 09:15:40 +02:00
Andreas Kling
c2d3d9d1d4 LibJS/Bytecode: Make each Jump instruction inherit Instruction directly
Before this change, all JumpFoo instructions inherited from Jump, which
forced the unconditional Jump to have an unusued "false target" member.
Also, labels were unnecessarily wrapped in Optional<>.

By defining each jump instruction separately, they all shrink in size,
and all ambiguity is removed.
2024-05-07 09:15:40 +02:00
Aliaksandr Kalenik
f21c0f9dcd LibJS: Skip some declarative env allocations in function instantiation
If all lexical declaration use local variables then there is no need
to allocate declarative environment.

With this change we skip ~3x more environment allocations on Github.
2024-05-06 15:09:20 +02:00
Daniel La Rocque
9065b0b0f6 LibJS: Truncate roundingIncrement before range check
This is a normative change in the Temporal spec.

See: https://github.com/tc39/proposal-temporal/commit/8fc8130
2024-05-05 19:59:26 +01:00
Andreas Kling
8ff16c1b57 LibJS: Cache access to properties found in prototype chain
We already had fast access to own properties via shape-based IC.
This patch extends the mechanism to properties on the prototype chain,
using the "validity cell" technique from V8.

- Prototype objects now have unique shape
- Each prototype has an associated PrototypeChainValidity
- When a prototype shape is mutated, every prototype shape "below" it
  in any prototype chain is invalidated.
- Invalidation happens by marking the validity object as invalid,
  and then replacing it with a new validity object.
- Property caches keep a pointer to the last seen valid validity.
  If there is no validity, or the validity is invalid, the cache
  misses and gets repopulated.

This is very helpful when using JavaScript to access DOM objects,
as we frequently have to traverse 4+ prototype objects before finding
the property we're interested in on e.g EventTarget or Node.
2024-05-04 21:42:59 +02:00
Andreas Kling
493a04d5fe LibJS: Add PropertyLookupPhase enum to distinguish Object [[Get]] calls
We can now tell the difference between an own property access and a
subsequent (automatic) prototype chain access.

This will be used to implement caching of prototype chain accesses.
2024-05-04 21:42:59 +02:00
Andreas Kling
3945e1a82a LibJS: Make JS::Cell a Weakable
This makes things easier downstream of Cell, and is preparation for
using weak pointers in prototype chain property caches.
2024-05-04 21:42:59 +02:00
Aliaksandr Kalenik
4d5823a5bc LibWeb+LibJS: Skip function environment allocation if possible
If a function has the following properties:
- uses only local variables and registers
- does not use `this`
- does not use `new.target`
- does not use `super`
- does not use direct eval() calls

then it is possible to entirely skip function environment allocation
because it will never be used

This change adds gathering of information whether a function needs to
access `this` from environment and updates `prepare_for_ordinary_call()`
to skip allocation when possible.

For now, this optimisation is too aggressively blocked; e.g. if `this`
is used in a function scope, then all functions in outer scopes have to
allocate an environment. It could be improved in the future, although
this implementation already allows skipping >80% of environment
allocations on Discord, GitHub and Twitter.
2024-05-04 06:48:07 +02:00
Andreas Kling
5cb127819c LibJS: Fix build after merging CallFrame removal and finally fixes 2024-05-02 07:42:09 +02:00
Hendiadyoin1
ada5027163 LibJS: Cleanup unwind state when transferring control out of a finalizer
This does two things:
* Clear exceptions when transferring control out of a finalizer
  Otherwise they would resurface at the end of the next finalizer
  (see test the new test case), or at the end of a function
* Pop one scheduled jump when transferring control out of a finalizer
  This removes one old FIXME
2024-05-02 07:27:45 +02:00
Hendiadyoin1
27b238d9af LibJS: Stop swallowing exceptions in finalizers
This also fixes one of the try-catch-finally tests, and adds a new one.
2024-05-02 07:27:45 +02:00
Hendiadyoin1
b4b9c4b383 LibJS: Restore scheduled jumps in catch blocks without finalizers 2024-05-02 07:27:45 +02:00
Hendiadyoin1
301a1fc763 LibJS: Propagate finalizers into nested try-catch blocks without them 2024-05-02 07:27:45 +02:00
Aliaksandr Kalenik
865e651a7d LibJS: Merge CallFrame into ExecutionContext
Before this change both ExecutionContext and CallFrame were created
before executing function/module/script with a couple exceptions:
- executable created for default function argument evaluation has to
  run in function's execution context.
- `execute_ast_node()` where executable compiled for ASTNode has to be
  executed in running execution context.

This change moves all members previously owned by CallFrame into
ExecutionContext, and makes two exceptions where an executable that does
not have a corresponding execution context saves and restores registers
before running.

Now, all execution state lives in a single entity, which makes it a bit
easier to reason about and opens opportunities for optimizations, such
as moving registers and local variables into a single array.
2024-05-02 07:26:13 +02:00
Andreas Kling
b92839fad9 LibJS: Fix bug where argument++ happened before call
For this case to work correctly in the current bytecode world:

    func(a, a++)

We have to put the function arguments in temporaries instead of allowing
the postfix increment to modify `a` in place.

This fixes a problem where jQuery.each() would skip over items.
2024-04-27 17:24:29 +02:00
Timothy Flynn
ec492a1a08 Everywhere: Run clang-format
The following command was used to clang-format these files:

    clang-format-18 -i $(find . \
        -not \( -path "./\.*" -prune \) \
        -not \( -path "./Base/*" -prune \) \
        -not \( -path "./Build/*" -prune \) \
        -not \( -path "./Toolchain/*" -prune \) \
        -not \( -path "./Ports/*" -prune \) \
        -type f -name "*.cpp" -o -name "*.mm" -o -name "*.h")

There are a couple of weird cases where clang-format now thinks that a
pointer access in an initializer list, e.g. `m_member(ptr->foo)`, is a
lambda return statement, and it puts spaces around the `->`.
2024-04-24 16:50:01 -04:00
Aliaksandr Kalenik
1072d9f15d LibJS: Save min and max block addresses in CellAllocator
This allows to skip iterating through all allocated blocks in
`find_min_and_max_block_addresses()`.

With this change `collect_garbage()` in profiles of Discord goes down
from 17% to 8%.
2024-04-24 11:42:15 +02:00
Andreas Kling
0c84508f87 LibJS: Fix GC leaks in promise reaction job setup
There's no need to capture things as Handle when using HeapFunction.
In this case, it was even creating a strong reference cycle, which ended
up leaking.
2024-04-23 12:50:40 +02:00
Andreas Kling
4db1712f90 LibJS+LibWeb: Make Console, ConsoleClient & subclasses GC-allocated
These objects had confusing ownership semantics. Let's just throw them
all on the GC heap and stop worrying about it.
2024-04-21 09:12:25 +02:00
Andreas Kling
e993fb53d5 LibJS: Simplify iteration in GC heap dumping code 2024-04-21 09:12:25 +02:00
Jess
ecb7d4b40f LibJS: Throw RangeError in StringPrototype::repeat if OOM
currently crashes with an assertion failure in `String::repeated` if
malloc can't serve a `count * input_size` sized request, so add
`String::repeated_with_error` to propagate the error.
2024-04-20 19:23:46 -04:00
Dan Klishch
5ed7cd6e32 Everywhere: Use east const in more places
These changes are compatible with clang-format 16 and will be mandatory
when we eventually bump clang-format version. So, since there are no
real downsides, let's commit them now.
2024-04-19 06:31:19 -04:00
Andreas Kling
5f9a905793 Ladybird+LibJS: Add optional logging of *all* JS exceptions
When running with --log-all-js-exceptions, we will print the message
and backtrace for every single JS exception that is thrown, not just
the ones nobody caught.

This can sometimes be very helpful in debugging sites that swallow
important exceptions.
2024-04-16 16:57:06 +02:00
Andreas Kling
53d0dd4a2e LibJS+LibWeb: Use new Cell::Visitor helpers to avoid manual iteration 2024-04-16 07:40:01 +02:00
Andreas Kling
1a6d025793 LibJS: Add helpers to Cell::Visitor for visiting AK collections 2024-04-16 07:40:01 +02:00
Space Meyer
f870841bee LibJS: Use toolchain agnostic NO_SANITIZE_ADDRESS 2024-04-15 21:16:22 -06:00
Linus Groh
cad95ce274 LibJS: Implement Promise.try()
See: https://github.com/tc39/proposal-promise-try
2024-04-09 10:18:35 +02:00
Matthew Olsson
d62c0fcbdc LibJS: Add calls to JS_{DECLARE,DEFINE}_ALLOCATOR() 2024-04-09 09:13:06 +02:00
Matthew Olsson
edf484a5ab LibJS: Force a semicolon after JS_{DECLARE,DEFINE}_ALLOCATOR()
This matches the style of other macros, and prevents IDEs from flagging
the semicolon as unnecessary.
2024-04-09 09:13:06 +02:00
Matthew Olsson
d47f656a3a LibJS+LibWeb: Mark a few variables as IGNORE_USE_IN_ESCAPING_LAMBDA
This is a bit noisy, but it'll be better once we upgrade to C++23.
2024-04-09 09:10:44 +02:00
Matthew Olsson
ff00d21d58 Everywhere: Mark a bunch of function parameters as NOESCAPE
This fixes the relevant warnings when running LibJSGCVerifier. Note that
the analysis is only performed over LibJS-adjacent code, but could be
performed over the entire codebase. That will have to wait for a future
commit.
2024-04-09 09:10:44 +02:00
Andreas Kling
7cbbd4dd7e LibJS: Suppress LibJSGCVerifier warning about Map::m_keys 2024-04-07 18:01:50 +02:00
Matthew Olsson
8b8ada292e LibJS: Fix some GCVerifier warnings 2024-04-07 07:03:13 +02:00
Idan Horowitz
b018114bee LibWeb: Add missing visit_edges implementation to RegExpObject 2024-04-06 06:59:36 +02:00
Idan Horowitz
f357998e81 LibWeb: Add missing visit_edges implementation to ModuleNamespaceObject 2024-04-06 06:59:36 +02:00
Idan Horowitz
c84cd1d668 LibJSGCVerifier: Support marking GCPtr members as raw references
This lets us avoid false positives when a GCPtr-wrapped member is only
a weak reference which is automatically updated by the GC when the
member's gc state is updated.
2024-04-06 06:59:36 +02:00