Commit graph

51 commits

Author SHA1 Message Date
Andreas Kling
0c746366cc LibJS: Keep return value in a call frame register 2023-09-28 06:09:16 +02:00
Andreas Kling
c833885fb5 LibJS: Keep cached this value in a call frame register
Just moving more things to call frame registers..
2023-09-28 06:09:16 +02:00
Andreas Kling
3887b840a3 LibJS: Keep current exception in a call frame register
Instead of keeping it in a Bytecode::Interpreter member, move it into
a dedicated call frame register.
2023-09-28 06:09:16 +02:00
Andreas Kling
3ccac0cf6e LibJS: Remove unused realm parameter from run_and_return_frame() 2023-09-21 16:19:13 +02:00
Andreas Kling
1c06111cbd LibJS: Add file & line number to bytecode VM stack traces :^)
This works by adding source start/end offset to every bytecode
instruction. In the future we can make this more efficient by keeping
a map of bytecode ranges to source ranges in the Executable instead,
but let's just get traces working first.

Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
2023-09-02 15:37:53 +02:00
Andreas Kling
e2c8d5859e LibJS: Remove ability to disable bytecode VM 2023-08-08 13:07:13 +02:00
Andreas Kling
d1d24569f8 LibJS/Bytecode: Remove unused AST interpreter hacks from bytecode VM 2023-08-08 13:07:13 +02:00
Aliaksandr Kalenik
2bdc69c42c LibJS: Add caching of this value in ResolveThisBinding instruction
Because "this" value cannot be changed during function execution it is
safe to compute it once and then use for future access.

This optimization makes ai-astar.js run 8% faster.
2023-07-29 04:23:49 +02:00
Andreas Kling
9f06e130a2 LibJS/Bytecode: Keep saved return value in call frame register
This fixes an issue where returning inside a `try` block and then
calling a function inside `finally` would clobber the saved return
value from the `try` block.

Note that we didn't need to change the base of register allocation,
since it was already 1 too high.

With this fixed, https://microsoft.com/edge loads in bytecode mode. :^)

Thanks to Luke for reducing the issue!
2023-07-21 19:15:33 +02:00
Timothy Flynn
77d7f715e3 LibJS+CI: Remove bytecode optimization passes for now
These passes have not been shown to actually optimize any JS, and tests
have become very flaky with optimizations enabled. Until some measurable
benefit is shown, remove the optimization passes to reduce overhead of
maintaining bytecode operations and to reduce CI churn. The framework
for optimizations will live on in git history, and can be restored once
proven useful.
2023-07-21 19:47:36 +03:30
Andreas Kling
6de22ec789 LibJS/Bytecode: Rename RegisterWindow to CallFrame
This is a better name for what it actually represents.
2023-07-21 14:14:00 +02:00
Andreas Kling
de8e4b1853 LibJS/Bytecode: Cache object own property accesses
The instructions GetById and GetByIdWithThis now remember the last-seen
Shape, and if we see the same object again, we reuse the property offset
from last time without doing a new lookup.

This allows us to use Object::get_direct(), bypassing the entire lookup
machinery and saving lots of time.

~23% speed-up on Kraken/ai-astar.js :^)
2023-07-09 12:54:06 +02:00
Andreas Kling
dc884aa0d3 LibJS/Bytecode: Remove unnecessary variable environment stack
The var environments will unwind as needed with the ExecutionContext
and there's no need to include it in the unwind info.

We still need to do this for lexical environments though, since they
can have short local lifetimes inside a function.
2023-07-02 18:30:02 +02:00
Andreas Kling
fb979dcf34 LibJS/Bytecode: Make Bytecode::Interpreter participate in GC marking
Since the relationship between VM and Bytecode::Interpreter is now
clear, we can have VM ask the Interpreter for roots in the GC marking
pass. This avoids having to register and unregister handles and
MarkedVectors over and over.

Since GeneratorObject can also own a RegisterWindow, we share the code
in a RegisterWindow::visit_edges() helper.

~4% speed-up on Kraken/stanford-crypto-ccm.js :^)
2023-07-02 14:50:02 +02:00
Andreas Kling
1efe4b58aa LibJS/Bytecode: Store current interpreter register window as a Span
This avoids a bunch of indirections on every single register access.

~17% speed-up on Kraken/stanford-crypto-aes.js :^)
2023-07-01 20:29:12 +01:00
Daniel Bertalan
cc9ec6693b LibJS: Remove the concept of bytecode optimization levels
While this would be useful in the future for implementing a multi-tiered
optimization strategy, currently a binary on/off is enough for us. This
removes the confusingly on-by-default `OptimizationLevel::None` option
which made the optimization pipeline a no-op even if
`Bytecode::Interpreter::set_optimizations_enabled` had been called.

Fixes #15982
2023-06-27 14:35:23 +03:30
Daniel Bertalan
e012565898 test262-runner+js: Respect the bytecode optimizations enabled flag 2023-06-27 14:35:23 +03:30
Andreas Kling
6537ed8fff LibJS/Bytecode: Simplify Bytecode::Interpreter lifetime model
The JS::VM now owns the one Bytecode::Interpreter. We no longer have
multiple bytecode interpreters, and there is no concept of a "current"
bytecode interpreter.

If you ask for VM::bytecode_interpreter_if_exists(), it will return null
if we're not running the program in "bytecode enabled" mode.

If you ask for VM::bytecode_interpreter(), it will return a bytecode
interpreter in all modes. This is used for situations where even the AST
interpreter switches to bytecode mode (generators, etc.)
2023-06-22 21:16:17 +02:00
Andreas Kling
9c568282dc Ladybird+LibJS: Add CLI option to run browser with LibJS bytecode VM
This required quite a bit of plumbing, but now you can run

    ladybird --use-bytecode
2023-06-17 14:16:45 +02:00
Andreas Kling
d063f35afd LibJS/Bytecode: Leave GlobalDeclarationInstantiation in C++
Don't try to implement this AO in bytecode. Instead, the bytecode
Interpreter class now has a run() API with the same inputs as the AST
interpreter. It sets up the necessary environments etc, including
invoking the GlobalDeclarationInstantiation AO.
2023-06-16 08:40:45 +02:00
Matthew Olsson
7c0c1c8f49 LibJS+LibWeb: Wrap raw JS::Cell*/& fields in GCPtr/NonnullGCPtr 2023-03-15 08:48:49 +01:00
Hendiadyoin1
de514f29ad LibJS: Align codegen AwaitExpressions to YieldExpressions
We use generators in bytecode to approximate async functions, but the
code generated by AwaitExpressions did not have the value processing
paths that Yield requires, eg the `generator.throw()` path, which is
used by AsyncFunctionDriverWrapper to signal Promise rejections.
2023-02-26 19:40:09 +01:00
Hendiadyoin1
f5376cb282 LibJS: Generate unwind chains for break in Bytecode
This uses a newly added instruction `ScheduleJump`
This instruction tells the finally proceeding it, that instead of
jumping to it's next block it should jump to the designated block.
2023-02-26 19:40:09 +01:00
Hendiadyoin1
fcc3348bc8 LibJS: Intercept returns through finally blocks in Bytecode
This is still not perfect, as we now actually crash in the
`try-finally-continue` tests, while we now succeed all
`try-catch-finally-*` tests.

Note that we do not yet go through the finally block when exiting the
unwind context through a break or continue.
2022-12-06 16:09:24 +03:30
Linus Groh
6e19ab2bbc AK+Everywhere: Rename String to DeprecatedString
We have a new, improved string type coming up in AK (OOM aware, no null
state), and while it's going to use UTF-8, the name UTF8String is a
mouthful - so let's free up the String name by renaming the existing
class.
Making the old one have an annoying name will hopefully also help with
quick adoption :^)
2022-12-06 08:54:33 +01:00
Hendiadyoin1
8c4717fc6e LibJS: Add a debug_position helper to the Bytecode Interpreter
This also changes argument_list_evaluation's dbgln to use it.
2022-12-03 17:07:30 +03:30
Luke Wilde
277132f70d LibJS/Bytecode: Store unwind contexts inside RegisterWindow
Unwind contexts need to be preserved as we exit and re-enter a
generator.

For example, this would previously crash when returning from the try
statement after yielding as we lost the unwind context when yielding,
but still have a LeaveUnwindContext instruction from running
`perform_needed_unwinds` when generating the return statement.
```js
function* a() {
    try {
        return (yield 1);
    } catch {}
}

iter = a();
iter.next();
iter.next();
```
2022-11-26 12:55:59 +01:00
Andreas Kling
67236d9573 LibJS: Disable bytecode optimizations again
Bytecode optimizations still break test262, so let's not enable this
until they have been confirmed to work there.
2022-11-01 11:44:41 +01:00
Hendiadyoin1
4a06e68ecd LibJS: Set default bytecode optimization level to Optimize 2022-11-01 11:21:18 +01:00
Hendiadyoin1
a1f1d9e4a7 LibJS: Expose some information about the bytecode interpreters state
This is quite helpful, when reporting internal errors.
2022-11-01 11:21:18 +01:00
Andreas Kling
d9b543da68 LibJS: Disable bytecode optimizations by default
The optimization passes are not stable, which makes test262 flaky.
Address this by introducing a new OptimizationLevel::None and making it
the default.

This removes all the flakiness from test262 in my testing.

We can enable optimizations by default again once they have been made
stable. :^)
2022-10-19 14:37:57 +02:00
Linus Groh
275dea9d98 LibJS: Remove {Bytecode::,}Interpreter::global_object()
The basic idea is that a global object cannot just come out of nowhere,
it must be associated to a realm - so get it from there, if needed.

This is to enforce the changes from all the previous commits by not
handing out global objects unless you actually have an initialized
realm (either stored somewhere, or the VM's current realm).
2022-08-23 13:58:30 +01:00
Ali Mohammad Pur
d5791c85b4 LibJS: Avoid copying the frame into the interpreter in BC generators 2022-04-18 23:59:30 +04:30
Ali Mohammad Pur
1bbfaf8627 LibJS: More properly implement scoping rules in bytecode codegen
Now we emit CreateVariable and SetVariable with the appropriate
initialization/environment modes, much closer to the spec.
This makes a whole lot of things like let/const variables, function
and variable hoisting and some other things work :^)
2022-02-13 14:41:33 +00:00
Ali Mohammad Pur
d7c207beb9 LibJS: Implement the NewClass opcode 2022-02-13 14:41:33 +00:00
davidot
8108fc7f9c LibJS: Convert Instruction::execute in bytecode to ThrowCompletionOr
This allows us to use TRY in these functions :^).
2022-02-08 09:12:42 +00:00
Ali Mohammad Pur
070d2eaa51 LibJS+LibTest+js: Convert BC::Interpreter::run to ThrowCompletionOr<>
Note that this is just a shallow API change.
2021-11-12 13:01:59 +00:00
Ali Mohammad Pur
e4a7f1a696 LibJS: Make Bytecode::Interpreter return the popped frame
And use it to _correctly_ implement state saving for generators.
Prior to this, we were capturing the caller frame, which is completely
irrelevant to the generator frame.
2021-11-12 13:01:59 +00:00
Andreas Kling
c95dde971b LibJS: Move global "should dump bytecode" flag into LibJS
This will allow us to trigger bytecode executable dumps when generating
bytecode inside LibJS as well, not just in clients like js and test-js.
2021-10-24 17:18:06 +02:00
Linus Groh
2b8d5696ab LibJS: Allocate a Realm next to GlobalObject in Interpreter::create()
Also pass a Realm reference to the Bytecode::Interpreter constructor,
just like we pass the GlobalObject.
2021-09-12 11:10:20 +01:00
Ali Mohammad Pur
1414c7b049 LibJS: Add a basic pass manager and add some basic passes
This commit adds a bunch of passes, the most interesting of which is a
pass that merges blocks together, and a pass that places blocks that
flow into each other next to each other, and a very simply pass that
removes duplicate basic blocks.
Note that this does not remove the jump at the end of each block in that
pass to avoid scope creep in the passes.
2021-06-15 22:06:33 +04:30
Ali Mohammad Pur
3234697eca LibJS: Implement generator functions (only in bytecode mode) 2021-06-11 00:30:09 +02:00
Gunnar Beutner
67cc31a74f LibJS: Implement bytecode generation for try..catch..finally
EnterUnwindContext pushes an unwind context (exception handler and/or
finalizer) onto a stack.

LeaveUnwindContext pops the unwind context from that stack.

Upon return to the interpreter loop we check whether the VM has an
exception pending. If no unwind context is available we return from the
loop. If an exception handler is available we clear the VM's exception,
put the exception value into the accumulator register, clear the unwind
context's handler and jump to the handler. If no handler is available
but a finalizer is available we save the exception value + metadata (for
 later use by ContinuePendingUnwind), clear the VM's exception, pop the
unwind context and jump to the finalizer.

ContinuePendingUnwind checks whether a saved exception is available. If
no saved exception is available it jumps to the resume label. Otherwise
it stores the exception into the VM.

The Jump after LeaveUnwindContext could be integrated into the
LeaveUnwindContext instruction. I've kept them separate for now to make
the bytecode more readable.

> try { 1; throw "x" } catch (e) { 2 } finally { 3 }; 4
1:
[   0] EnterScope
[  10] EnterUnwindContext handler:@4 finalizer:@3
[  38] EnterScope
[  48] LoadImmediate 1
[  60] NewString 1 ("x")
[  70] Throw
<for non-terminated blocks: insert LeaveUnwindContext + Jump @3 here>
2:
[   0] LoadImmediate 4
3:
[   0] EnterScope
[  10] LoadImmediate 3
[  28] ContinuePendingUnwind resume:@2
4:
[   0] SetVariable 0 (e)
[  10] EnterScope
[  20] LoadImmediate 2
[  38] LeaveUnwindContext
[  3c] Jump @3

String Table:
0: e
1: x
2021-06-10 21:59:46 +02:00
Gunnar Beutner
6a0d1fa259 LibJS: Store strings in a string table
Instead of using Strings in the bytecode ops this adds a global string
table to the Executable struct which individual operations can refer
to using indices. This brings bytecode ops one step closer to being
pointer free.
2021-06-09 17:42:52 +02:00
Andreas Kling
b61f198d22 LibJS: Rename Bytecode::ExecutionUnit => Bytecode::Executable 2021-06-09 09:24:32 +02:00
Ali Mohammad Pur
01e8f0889a LibJS: Generate bytecode in basic blocks instead of one big block
This limits the size of each block (currently set to 1K), and gets us
closer to a canonical, more easily analysable bytecode format.
As a result of this, "Labels" are now simply entries to basic blocks.
Since there is no more 'conditional' jump (as all jumps are always
taken), JumpIf{True,False} are unified to JumpConditional, and
JumpIfNullish is renamed to JumpNullish.
Also fixes #7914 as a result of reimplementing the loop logic.
2021-06-09 09:07:29 +02:00
Matthew Olsson
9bed2e4f4a LibJS: Introduce an accumulator register to Bytecode::Interpreter
This commit introduces the concept of an accumulator register to
LibJS's bytecode interpreter. The accumulator register is always
register 0, and most simple instructions use it for reading and
writing.

Not only does this slim down the AST, but it also simplifies a lot of
the code. For example, the generate_bytecode methods no longer need
to return an Optional<Register>, as any opcode which has a "return"
value will always put it into the accumulator.

This also renames the old Op::Load to Op::LoadImmediate, and uses
Op::Load to load from a register into the accumulator. There is
also an Op::Store to put the value in the accumulator into another
register.
2021-06-08 21:00:12 +02:00
Andreas Kling
80b1604b0a LibJS: Compile ScriptFunctions into bytecode and run them that way :^)
If there's a current Bytecode::Interpreter in action, ScriptFunction
will now compile itself into bytecode and execute in that context.

This patch also adds the Return bytecode instruction so that we can
actually return values from called functions. :^)

Return values are propagated from callee to caller via the caller's
$0 register. Bytecode::Interpreter now keeps a stack of register
"windows". These are not very efficient, but it should be pretty
straightforward to convert them to e.g a sliding register window
architecture later on.

This is pretty dang cool! :^)
2021-06-07 18:11:59 +02:00
Andreas Kling
6ae9346cd3 LibJS: Add basic support for while loops in the bytecode engine
This introduces two new instructions: Jump and JumpIfFalse.
Jumps are made to a Bytecode::Label, which is a simple object that
represents a location in the bytecode stream.

Note that you may not always know the target of a jump when adding the
jump instruction itself, but we can just update the instruction later
on during codegen once we know where the jump target is.

The Bytecode::Interpreter now implements jumping via a jump slot that
gets checked after each instruction to see if a jump is pending.
If not, we just increment the PC as usual.
2021-06-07 18:11:59 +02:00
Andreas Kling
6da5d17416 LibJS: Add a VM accessor to Bytecode::Interpreter :^) 2021-06-07 18:11:59 +02:00