Commit Graph

5277 Commits

Author SHA1 Message Date
Aliaksandr Kalenik
d79438a2a6 LibJS: Join locals, constants and registers into single vector
Merging registers, constants and locals into single vector means:
- Better data locality
- No need to check type in Interpreter::get() and Interpreter::set()
  which are very hot functions

Performance improvement is visible in almost all Octane and Kraken
tests.
2024-05-13 19:54:11 +02:00
Andreas Kling
6f5201b759 LibJS: Add fast paths in TypedArrayPrototype.fill()
Filling a typed array with an integer shouldn't have to go through the
generic Set for every element.

This knocks a 7% item down to <1% in profiles of Another World JS at
https://cyxx.github.io/another_js/
2024-05-13 17:29:37 +02:00
Andreas Kling
d79353a477 LibJS/Bytecode: Add fast paths for compare-and-jump with 2 numbers
When comparing two numbers, we can avoid a lot of implicit type
conversion nonsense and go straight to comparison, saving time in the
most common case.
2024-05-13 17:29:37 +02:00
Andreas Kling
855f6417df LibJS/Bytecode: Move environment variable caches into instructions
These were out-of-line because we had some ideas about marking
instruction streams PROT_READ only, but that seems pretty arbitrary and
there's a lot of performance to be gained by putting these inline.
2024-05-13 09:22:14 +02:00
Andreas Kling
a06441c88c LibJS/Bytecode: Defer GetGlobal identifier lookup until cache misses
This way we avoid looking up the identifier when the cache hits.
2024-05-13 09:22:14 +02:00
Andreas Kling
6ec4d6f668 LibJS/Bytecode: Cache the running execution context in interpreter 2024-05-13 09:22:14 +02:00
Andreas Kling
8447f6f6da LibJS: Inline more of cached environment variable access in interpreter
And stop passing VM strictness to direct access, since it doesn't care
about strictness anyway.
2024-05-13 09:22:14 +02:00
Aliaksandr Kalenik
8bcaf68023 LibJS: Remove VM::execute_ast_node() 2024-05-12 19:10:25 +02:00
Aliaksandr Kalenik
6fb1d9e516 LibJS: Stop using execute_ast_node() for class property evaluation
Instead, generate bytecode to execute their AST nodes and save the
resulting operands inside the NewClass instruction.

Moving property expression evaluation to happen before NewClass
execution also moves along creation of new private environment and
its population with private members (private members should be visible
during property evaluation).

Before:
- NewClass

After:
- CreatePrivateEnvironment
- AddPrivateName
- ...
- AddPrivateName
- NewClass
- LeavePrivateEnvironment
2024-05-12 19:10:25 +02:00
Aliaksandr Kalenik
b46fc47bf7 LibJS: Remove execute_ast_node() usage for ClassMethod function creation
Call ECMAScriptFunctionObject constructor directly instead of using
execute_ast_node() to emit NewFunction instruction.
2024-05-12 19:10:25 +02:00
Aliaksandr Kalenik
0c8e76cbd7 LibJS: Delete named_evaluation_if_anonymous_function()
This code was a leftover from AST interpreter.
2024-05-11 18:16:15 +02:00
Andreas Kling
6ad6e09be1 LibJS: Make Environment::is_declarative_environment() non-virtual
This function was moderately hot (2%) on the clusters demo, and making
it non-virtual costs us nothing.
2024-05-11 15:22:36 +02:00
Andreas Kling
b5a070e8ce LibJS/Bytecode: Don't create empty lexical environments
This patch stops emitting the BlockDeclarationInstantiation instruction
when there are no locals, and no function declarations in the scope.

We were spending 20% of CPU time on https://ventrella.com/Clusters/ just
creating empty environments for no reason.
2024-05-11 15:22:36 +02:00
Andreas Kling
239b9d8662 LibJS: Manually limit the recursion depth in Proxy
Instead of relying on native stack overflows to kick us out of circular
proxy chains, we now keep track of the recursion depth and kick
ourselves out if it exceeds 10'000.

This fixes an issue where compiler tail/sibling call optimizations would
turn infinite recursion into infinite loops, and thus never hit a stack
overflow to kick itself out.

For whatever reason, we've only seen the issue on SerenityOS with UBSAN,
but it could theoretically happen on any platform.
2024-05-11 13:00:46 +02:00
Aliaksandr Kalenik
1e361db3f3 LibJS: Remove VM::binding_initialization()
This function was used for function params initialization but no longer
used after it got replaced by emitting bytecode.
2024-05-11 11:43:05 +02:00
Aliaksandr Kalenik
3d4b13a01c LibJS: Ensure capacity for created lexical and variable environments
If the minimal amount of required bindings is known in advance, it could
be used to ensure capacity to avoid resizing the internal vector that
holds bindings.
2024-05-11 11:43:05 +02:00
Aliaksandr Kalenik
a4f70986a0 LibJS: Emit bytecode for function declaration instantiation
By doing that all instructions required for instantiation are emitted
once in compilation and then reused for subsequent calls, instead of
running generic instantiation process for each call.
2024-05-11 11:43:05 +02:00
Aliaksandr Kalenik
89a007327a LibJS: Change NewFunction instruction to accept FunctionNode
Preparation for upcoming changes where NewFunction will have to be used
with FunctionDeclaration node.
2024-05-11 11:43:05 +02:00
Aliaksandr Kalenik
00018ad415 LibJS: Move BindingPattern bytecode generation into a method
Preparation for upcoming function where binding pattern will have to be
used outside of ASTCodegen.cpp
2024-05-11 11:43:05 +02:00
Andreas Kling
7bdc207d81 LibJS/Bytecode: Make execute_impl() return void for non-throwing ops
This way we can't accidentally ignore exceptions.
2024-05-10 19:53:15 +02:00
Andreas Kling
01ec56f1ed LibJS/Bytecode: Fix register being freed too early in delete codegen
It wasn't actually possible for it to get reused incorrectly here, but
let's fix the bug anyway.
2024-05-10 15:03:24 +00:00
Andreas Kling
353e635535 LibJS/Bytecode: Grab at ThrowCompletionOr errors directly
The interpreter can now grab at the error value directly instead of
instantiating a temporary Completion to hold it.
2024-05-10 15:03:24 +00:00
Andreas Kling
f5efc47905 LibJS: Make ThrowCompletionOr<T> smaller
Instead of storing a full JS::Completion for the "throw completion"
case, we now store a simple JS::Value (wrapped in ErrorValue for the
type system).

This shrinks TCO<void> and TCO<Value> (the most common TCOs) by 8 bytes,
which has a non-trivial impact on performance.
2024-05-10 15:03:24 +00:00
Andreas Kling
ae11a4de1c LibJS: Remove unused target field from Completion
This shrinks Completion by 16 bytes, which has non-trivial impact
on performance.
2024-05-10 15:03:24 +00:00
Andreas Kling
a77c6e15f4 LibJS/Bytecode: Streamline return/yield flow a bit in the interpreter 2024-05-10 15:03:24 +00:00
Andreas Kling
3e1a6fca91 LibJS/Bytecode: Remove exception checks from Return/Await/Yield
These instructions can't throw anyway.
2024-05-10 15:03:24 +00:00
Andreas Kling
8eccfdb98c LibJS/Bytecode: Cache a base pointer to executable constants
Instead of fetching it from the current executable whenever we fetch a
constant, just keep a base pointer to the constants array handy.
2024-05-10 15:03:24 +00:00
Andreas Kling
810a297626 LibJS/Bytecode: Remove Instruction::execute()
Just make sure everyone calls the instruction-specific execute_impl()
instead. :^)
2024-05-10 15:03:24 +00:00
Andreas Kling
601e10d50c LibJS/Bytecode: Make StringTableIndex be a 32-bit index
This makes a bunch of instructions smaller.
2024-05-10 15:03:24 +00:00
Andreas Kling
b99f0a7e22 LibJS/Bytecode: Reorder Call instruction members to make it smaller 2024-05-10 15:03:24 +00:00
Andreas Kling
96511a7d19 LibJS/Bytecode: Avoid unnecessary copy of call expression callee
If the callee is already a temporary register, we don't need to copy it
to *another* temporary before evaluating arguments. None of the
arguments will clobber the existing temporary anyway.
2024-05-10 15:03:24 +00:00
Andreas Kling
56a3ccde1a LibJS/Bytecode: Turn UnaryMinus(NumericLiteral) into a constant 2024-05-10 15:03:24 +00:00
Andreas Kling
e37feaa196 LibJS/Bytecode: Skip unnecessary exception checks in interpreter
Many opcodes cannot ever throw an exception, so we can just avoid
checking it after executing them.
2024-05-10 15:03:24 +00:00
Andreas Kling
34f287087e LibJS/Bytecode: Only copy call/array expression arguments when needed
We only need to make copies of locals here, in case the locals are
modified by something like increment/decrement expressions.

Registers and constants can slip right through, without being Mov'ed
into a temporary first.
2024-05-10 15:03:24 +00:00
Andreas Kling
caf2e675bf LibJS/Bytecode: Don't emit GetGlobal undefined
We know that `undefined` in the global scope is always the proper
undefined value. This commit takes advantage of that by simply emitting
a constant undefined value instead.

Unfortunately we can't be so sure in other scopes.
2024-05-10 15:03:24 +00:00
Andreas Kling
448f837d38 LibJS/Bytecode: Round constant operands of bitwise binary expressions
This helps some of the Cloudflare Turnstile stuff run faster, since they
are deliberately screwing with JS engines by asking us to do a bunch of
bitwise operations on e.g 65535.56

By rounding such values in bytecode generation, the interpreter can stay
on the happy path while executing, and finish quite a bit faster.
2024-05-10 15:03:24 +00:00
Andreas Kling
ca8dc8f61f LibJS/Bytecode: Turn JumpIf true/false into unconditional Jump 2024-05-10 15:03:24 +00:00
Andreas Kling
7654da3851 LibJS/Bytecode: Do basic compare-and-jump peephole optimization
We now fuse sequences like [LessThan, JumpIf] to JumpLessThan.
This is only allowed for temporaries (i.e VM registers) with no other
references to them.
2024-05-10 15:03:24 +00:00
Andreas Kling
f164e18a55 LibJS/Bytecode: Bunch all tests together in switch statement codegen
Before this change, switch codegen would interleave bytecode like this:

    (test for case 1)
    (code for case 1)
    (test for case 2)
    (code for case 2)

This meant that we often had to make many large jumps while looking for
the matching case, since code for each case can be huge.

It now looks like this instead:

    (test for case 1)
    (test for case 2)
    (code for case 1)
    (code for case 2)

This way, we can just fall through the tests until we hit one that fits,
without having to make any large jumps.
2024-05-09 09:12:13 +02:00
Andreas Kling
18b8fae85c LibJS/Bytecode: Remove pointless basic block in SwitchStatement codegen 2024-05-09 09:12:13 +02:00
Andreas Kling
6873628317 LibJS/Bytecode: Make NewArray a variable-length instruction
This removes a layer of indirection in the bytecode where we had to make
sure all the initializer elements were laid out in sequential registers.

Array expressions no longer clobber registers permanently, and they can
be reused immediately afterwards.
2024-05-09 09:12:13 +02:00
Andreas Kling
cea59b6642 LibJS/Bytecode: Reuse bytecode registers
This patch adds a register freelist to Bytecode::Generator and switches
all operands inside the generator to a new ScopedOperand type that is
ref-counted and automatically frees the register when nothing uses it.

This dramatically reduces the size of bytecode executable register
windows, which were often in the several thousands of registers for
large functions. Most functions now use less than 100 registers.
2024-05-09 09:12:13 +02:00
Andreas Kling
f537d0b3cf LibJS/Bytecode: Allow all basic blocks to use cached this from block 0
The first block in every executable will always execute first, so if it
ends up doing a ResolveThisBinding, it's fine for all other blocks
within the same executable to use the same `this` value.
2024-05-09 09:12:13 +02:00
Andreas Kling
55a4b0a21e LibJS: Mark Value as a trivial type for AK collection purposes
It's perfectly fine to memcpy() a bunch of JS::Values around, and if
that helps Vector<Value> go faster, let's allow it.
2024-05-09 09:12:13 +02:00
Andreas Kling
161298b5d1 LibJS/Bytecode: Inline indexed property access in GetByVal better 2024-05-09 09:12:13 +02:00
Andreas Kling
3170ad2ee3 LibJS: Avoid string trimming in GlobalObject.parseInt() when possible
In the common case, parseInt() is being called with strings that don't
have leading whitespace. By checking for that first, we can avoid the
cost of trimming and multiple malloc/GC allocations.
2024-05-09 09:12:13 +02:00
Andreas Kling
0f70ff9a67 LibJS/Bytecode: Only emit ResolveThisBinding once per basic block
Once executed, this instruction will always produce the same result
in subsequent executions, so it's okay to cache it.

Unfortunately it may throw, so we can't just hoist it to the top of
every executable, since that would break observable execution order.
2024-05-09 09:12:13 +02:00
Timothy Flynn
323c9edbb9 LibJS: Increase the stack limit on macOS with ASAN enabled
The macOS 14 runners on GitHub Actions fail with a stack overflow
otherwise.
2024-05-08 14:46:39 -06:00
Hendiadyoin1
e1aecff2ab LibJS: Fail compilation earlier when trying to emit with wrong arguments
This makes the compilation backtraces a lot nicer, and allows clangd to
see the mistake as well.
2024-05-08 22:01:58 +02:00
Hendiadyoin1
af94e4c05d LibJS: Save and restore exceptions on yields in finalizers
Also removes a bunch of related old FIXMEs.
2024-05-08 22:01:58 +02:00