Commit graph

696 commits

Author SHA1 Message Date
Dan Klishch
78491204d9 LibJS: Don't use null DFS for break/continue statements without a label 2024-02-24 15:06:52 -07:00
Andreas Kling
6402ad29a6 LibJS/Bytecode: Don't clobber dst when assigning from object expression
When compiling code like this:

    x = { foo: x }

We don't want to put a new JS::Object in `x` until *after* we've
evaluated `x` for the `foo` field.

This fixes an issue when loading https://puter.com/ :^)
2024-02-23 14:34:00 +01:00
Timothy Flynn
8eaf48888e LibJS: Remove FLATTEN attribute from Interpreter::run_bytecode
This is what caused stack usage to increase so much with the new BC.
Revert it for now so we can restore our old stack limit.
2024-02-20 16:24:09 -05:00
Andreas Kling
9a0a5a79f4 LibJS/Bytecode: Put arguments directly in the Call instruction
Instead of having Call refer to a range of VM registers, it now has
a trailing list of argument operands as part of the instruction.

This means we no longer have to shuffle every argument value into
a register before making a call, making bytecode smaller & faster. :^)
2024-02-20 21:25:18 +01:00
Andreas Kling
da107ec9fb LibJS/Bytecode: Add fast paths for many binary expression instructions
By handling common cases like Int32 arithmetic directly in the
instruction handler, we can avoid the cost of calling the generic helper
functions in Value.cpp.
2024-02-20 21:25:18 +01:00
Andreas Kling
9d9b737a58 LibJS/Bytecode: Dedicated instructions for postfix increment/decrement
Instead of splitting the postfix variants into ToNumeric + Inc/Dec,
we now have dedicated PostfixIncrement and PostfixDecrement instructions
that handle both outputs in one go.
2024-02-20 21:25:18 +01:00
Andreas Kling
e46b217e42 LibJS/Bytecode: Move to a new bytecode format
This patch moves us away from the accumulator-based bytecode format to
one with explicit source and destination registers.

The new format has multiple benefits:

- ~25% faster on the Kraken and Octane benchmarks :^)
- Fewer instructions to accomplish the same thing
- Much easier for humans to read(!)

Because this change requires a fundamental shift in how bytecode is
generated, it is quite comprehensive.

Main implementation mechanism: generate_bytecode() virtual function now
takes an optional "preferred dst" operand, which allows callers to
communicate when they have an operand that would be optimal for the
result to go into. It also returns an optional "actual dst" operand,
which is where the completion value (if any) of the AST node is stored
after the node has "executed".

One thing of note that's new: because instructions can now take locals
as operands, this means we got rid of the GetLocal instruction.
A side-effect of that is we have to think about the temporal deadzone
(TDZ) a bit differently for locals (GetLocal would previously check
for empty values and interpret that as a TDZ access and throw).
We now insert special ThrowIfTDZ instructions in places where a local
access may be in the TDZ, to maintain the correct behavior.

There are a number of progressions and regressions from this test:

A number of async generator tests have been accidentally fixed while
converting the implementation to the new bytecode format. It didn't
seem useful to preserve bugs in the original code when converting it.

Some "does eval() return the correct completion value" tests have
regressed, in particular ones related to propagating the appropriate
completion after control flow statements like continue and break.
These are all fairly obscure issues, and I believe we can continue
working on them separately.

The net test262 result is a progression though. :^)
2024-02-19 21:45:27 +01:00
Andreas Kling
7f1a62a1d3 LibJS/Bytecode: Add Operand in/out to all the bytecode codegen helpers
This is pure prep work for refactoring the bytecode to use more operands
instead of only registers.

generate_bytecode() virtuals now return an Optional<Operand>, and the
idea is to return an Operand referring to the value produced by this
AST node.

They also take an Optional<Operand> "preferred_dst" input. This is
intended to communicate the caller's preference for an output operand,
if any. This will be used to elide temporaries when we can store the
result directly in a local, for example.
2024-02-19 21:45:27 +01:00
Andreas Kling
e46de4eb59 LibJS/Bytecode: Add constants table to Bytecode::Executable 2024-02-19 21:45:27 +01:00
Andreas Kling
3466771492 LibJS/Bytecode: Add Bytecode::Operand
An Operand is either a register, a local, or a constant (index into the
executable's constant table)
2024-02-19 21:45:27 +01:00
Andreas Kling
c0ec924dc9 LibJS/Bytecode: Exclude extra tables from bytecode executable dumps 2024-02-19 21:45:27 +01:00
Andreas Kling
1d29f9081f LibJS: Remove JIT compiler
The JIT compiler was an interesting experiment, but ultimately the
security & complexity cost of doing arbitrary code generation at runtime
is far too high.

In subsequent commits, the bytecode format will change drastically, and
instead of rewriting the JIT to fit the new bytecode, this patch simply
removes the JIT instead.

Other engines, JavaScriptCore in particular, have already proven that
it's possible to handle the vast majority of contemporary web content
with an interpreter. They are currently ~5x faster than us on benchmarks
when running without a JIT. We need to catch up to them before
considering performance techniques with a heavy security cost.
2024-02-19 21:45:27 +01:00
Tim Ledbetter
6c31f2a68a LibJS: Don't crash when attempting to load from an invalid reference
Previously, attempting to load a value from an invalid reference would
cause a crash. We now return a CodeGenerationError rather than hitting
an assertion. This is not a complete solution, as ideally we would want
to return a ReferenceError, but this now matches the behavior we see
when we attempt to store something to an invalid reference.
2024-02-08 07:55:07 -07:00
Timothy Flynn
18847fca50 LibJS: Use IteratorStepValue in ECMA-262
This is an editorial change in the ECMA-262 spec. See:
https://github.com/tc39/ecma262/commit/12d3687

Note they have not yet updated all potential consumers to use this new
AO.
2024-02-03 14:07:26 -05:00
Linus Groh
e1d1aac7bc LibJS/Bytecode: Apply BigInt/Symbol ToObject avoidance in another place
Same as d667721b2 but in a different place.
2024-01-28 19:49:51 -05:00
Andreas Kling
9326ded5a4 LibJS: Fast path for Increment of Int32 value in bytecode interpreter
5% speed-up on Kraken/ai-astar.js in interpreter mode. :^)
2024-01-27 22:29:49 +01:00
Andreas Kling
9fcd6776cf LibJS: Add fast path for Array.length in the bytecode interpreter
13.8% speed-up on Kraken/ai-astar.js in interpreter mode. :^)
2024-01-27 22:29:49 +01:00
Andreas Kling
514af065a5 LibJS: Move shared bytecode/JIT helpers to a header file
This allows them to be inlined into the bytecode interpreter.

7.6% speedup on Kraken/ai-astar.js in interpreter mode. :^)
2024-01-27 22:29:49 +01:00
Andreas Kling
8d0344a636 LibJS: Avoid unnecessary MarkedVector in Bytecode::Op::Call::execute()
perform_call() wants a ReadonlySpan<Value>, so just grab a slice of the
current register window instead of making a MarkedVector.

10% speed-up on this function call microbenchmark:

    function callee(a, b, c) { }

    function caller(callee) {
        for (let i = 0; i < 10_000_000; ++i)
            callee(1, 2, 3)
    }

    caller(callee)
2024-01-22 23:05:16 +01:00
Tim Ledbetter
48a3a02238 LibCrypto: Make constructing a BigInteger from string fallible
Previously, constructing a `UnsignedBigInteger::from_base()` could
produce an incorrect result if the input string contained a valid
Base36 digit that was out of range of the given base. The same method
would also crash if the input string contained an invalid Base36 digit.
An error is now returned in both these cases.

Constructing a BigFraction from string is now also fallible, so that we
can handle the case where we are given an input string with invalid
digits.
2024-01-13 19:01:35 -07:00
Timothy Flynn
a1e2f131c4 LibJS: Use existing AOs to validate bytecode/JIT TypedArray indices
The IsValidIntegerIndex AO performs the checks we are interested in. The
manual implementation we currently have will no longer compile once the
resizable ArrayBuffer spec is implemented. The AO will be updated with
the spec implementation, so let's use it now to avoid breakage.
2023-12-26 11:16:10 +01:00
Timothy Flynn
98cdf36fb0 LibJS: Rename TypedArray element get and set methods
This renames IntegerIndexedElementGet to TypedArrayGetElement, and
IntegerIndexedElementSet to TypedArraySetElement.

This also renames the indexedPosition variable inside these method
definitions to byteIndexInBuffer.

These are part of a couple editorial changes in the ECMA-262 spec. See:
https://github.com/tc39/ecma262/commit/03e4410
https://github.com/tc39/ecma262/commit/a1a4d48

The remainder of the changes in those commits apply to the resizable
ArrayBuffer spec, which is not implemented in LibJS as of this commit.
2023-12-26 11:16:10 +01:00
Ali Mohammad Pur
5e1499d104 Everywhere: Rename {Deprecated => Byte}String
This commit un-deprecates DeprecatedString, and repurposes it as a byte
string.
As the null state has already been removed, there are no other
particularly hairy blockers in repurposing this type as a byte string
(what it _really_ is).

This commit is auto-generated:
  $ xs=$(ack -l \bDeprecatedString\b\|deprecated_string AK Userland \
    Meta Ports Ladybird Tests Kernel)
  $ perl -pie 's/\bDeprecatedString\b/ByteString/g;
    s/deprecated_string/byte_string/g' $xs
  $ clang-format --style=file -i \
    $(git diff --name-only | grep \.cpp\|\.h)
  $ gn format $(git ls-files '*.gn' '*.gni')
2023-12-17 18:25:10 +03:30
Andreas Kling
3d92c26445 LibJS: Stop making shapes unique
We previously had a concept of unique shapes, which meant that they
couldn't be shared between multiple objects.

Object shapes became unique in three situations:

- They were the shape of the global object.
- They had more than 100 properties added to them.
- They had one or more properties deleted from them.

Unfortunately, unique shapes presented an annoying problem for inline
caches, and we added a "unique shape serial number" for being able to
tell that a unique shape had been mutated.

This patch gets rid of the concept of unique shapes, simplifying all
the caching code, since inline caches can now simply perform a shape
check and then we're good.

To make this possible, we now have the concept of delete transitions,
which occur when a property is deleted from a shape.

Note that this patch by itself introduces a performance regression in
some situtations, since we now create a lot more shapes, and marking
their property keys can be very heavy. This will be addressed in a
subsequent patch.
2023-12-11 20:36:15 +01:00
Aliaksandr Kalenik
6ac43274b2 LibWeb+LibJS: Use JS::GCPtr for pointers to GC-allocated objects
Fixes warnings found by LibJSGCVerifier
2023-12-11 16:55:25 +01:00
Andreas Kling
37b5c05ec5 LibJS: Add fast path for Uint8ClampedArray Get/Put
These are just like Uint8Array, except Put values have to be clamped
in the 0..255 range.

Takes CPU usage from 40% to 30% on the "Canvas Cycle" demo at
http://www.effectgames.com/demos/canvascycle/ :^)
2023-12-10 13:49:00 +01:00
Andreas Kling
350e6c54d7 LibJS: Remove dedicated iterator result instructions in favor of GetById
When iterating over an iterable, we get back a JS object with the fields
"value" and "done".

Before this change, we've had two dedicated instructions for retrieving
the two fields: IteratorResultValue and IteratorResultDone. These had no
fast path whatsoever and just did a generic [[Get]] access to fetch the
corresponding property values.

By replacing the instructions with GetById("value") and GetById("done"),
they instantly get caching and JIT fast paths for free, making iterating
over iterables much faster. :^)

26% speed-up on this microbenchmark:

    function go(a) {
        for (const p of a) {
        }
    }
    const a = [];
    a.length = 1_000_000;
    go(a);
2023-12-07 18:12:24 +01:00
Andreas Kling
4699c81fc1 LibJS: Stop converting between Object <-> IteratorRecord all the time
This patch makes IteratorRecord an Object. Although it's not exposed to
author code, this does allow us to store it in a VM register.

Now that we can store it in a VM register, we don't need to convert it
back and forth between IteratorRecord and Object when accessing it from
bytecode.

The big win here is avoiding 3 [[Get]] accesses on every iteration step
of for..of loops. There are also a bunch of smaller efficiencies gained.

20% speed-up on this microbenchmark:

    function go(a) {
        for (const p of a) {
        }
    }
    const a = [];
    a.length = 1_000_000;
    go(a);
2023-12-07 14:06:34 +01:00
Andreas Kling
c9f0f0fc70 LibJS: Elide empty lexical environment in for..in/of blocks
When all the variables in a for..in/of block's lexical scope have been
turned into locals, we don't need to create and immediately abandon an
empty environment for them.

This avoid environment allocation in cases like this:

    function foo(a) {
        for (const x of a) {
        }
    }
2023-12-07 10:52:57 +01:00
Todderod
e335354b30 LibJS: Call builtins directly in the bytecode interpreter
Allows the bytecode interpreter to call the builtins c++
implementation directly without making a javascript call
just as the JIT.

Kraken test speedups: imaging-gaussian-blur.js (1.5x) and
audio-oscillator.js (1.2x)
2023-12-01 13:01:26 +01:00
Andreas Kling
3fc0333ee6 LibJS: Put Bytecode::CallFrame + register slots in a single allocation
The number of registers in a call frame never changes, so we can
allocate it at the end of the CallFrame object and save ourselves the
cost of allocating separate Vector storage for every call frame.
2023-11-29 09:48:18 +01:00
Andreas Kling
3dc5f467a8 LibJS: Always allocate ExecutionContext objects on the malloc heap
Instead of allocating these in a mixture of ways, we now always put
them on the malloc heap, and keep an intrusive linked list of them
that we can iterate for GC marking purposes.
2023-11-29 09:48:18 +01:00
Andreas Kling
ecfcc9aef3 LibJS: Make Bytecode::Executable GC-allocated
This is a step towards making ExecutionContext easier to allocate.
2023-11-29 09:48:18 +01:00
Andreas Kling
ece961f882 LibJS: Pass call/construct argument lists as ReadonlySpan<Value>
(Instead of MarkedVector<Value>.) This is a step towards not storing
argument lists in MarkedVector<Value> at all. Note that they still end
up in MarkedVectors since that's what ExecutionContext has.
2023-11-29 09:48:18 +01:00
Andreas Kling
cb46a7fd65 LibJS: Add specialized fast paths for 8/16/32-bit integer TypedArrays
By checking a few conditions up front, we can do a very specialized
direct access into the underlying byte storage for 8/16/32-bit typed
arrays. This relies on the fact that typed arrays are guaranteed to
be type-appropriately aligned within the underlying array buffer.
2023-11-26 19:32:51 +01:00
Andreas Kling
1bc58333f8 LibJS: Add common fast path for GetByValue from TypedArray
Same exact idea as the previous commit, just for GetByValue. :^)
2023-11-26 10:14:55 +01:00
Andreas Kling
51ac0d8821 LibJS: Add common fast path for PutByValue into TypedArray
When putting to a numeric indexed property, we can skip a lot of the
ceremony and go directly to IntegerIndexedElementSet. :^)
2023-11-26 10:14:55 +01:00
Andreas Kling
afeb551d57 LibJS/JIT: Add builtin for Math.exp() 2023-11-24 12:49:15 +01:00
Andreas Kling
8447544e17 LibJS/JIT: Add builtin for Math.round() 2023-11-24 12:49:15 +01:00
Andreas Kling
c2ff238467 LibJS/JIT: Add builtin for Math.ceil() 2023-11-24 12:49:15 +01:00
Andreas Kling
08590adf40 LibJS/JIT: Add builtin for Math.floor() 2023-11-24 12:49:15 +01:00
Andreas Kling
5e976d611e LibJS/JIT: Add builtin for Math.pow() 2023-11-24 12:49:15 +01:00
Andreas Kling
94b634f029 LibJS/JIT: Add builtin for Math.sqrt() 2023-11-24 12:49:15 +01:00
Andreas Kling
1d8a601f96 LibJS/JIT: Add builtin for Math.log()
Note that we still call out to a C++ helper, but by having a builtin,
we still avoid the cost of a full JS function call.
2023-11-24 12:49:15 +01:00
Stephan Vedder
84eecbb10e LibJS/JIT: Add fastpath for set variable 2023-11-19 22:36:07 +01:00
Idan Horowitz
f19349e1b6 LibJS: Instantiate primitive array expressions using a single operation
This will not meaningfully affect short array literals, but it does
give us a bit of extra perf when evaluating huge array expressions like
in Kraken/imaging-darkroom.js
2023-11-18 08:37:34 +01:00
Simon Wanner
6c8ab1ca0d LibJS/JIT: Add a builtin for Math.abs 2023-11-17 19:06:25 +01:00
Simon Wanner
86b85aa68b LibJS: Introduce Builtins
Builtins are functions that can be detected during bytecode generation
and enable fast-paths in the JIT.
2023-11-17 19:06:25 +01:00
iliadsh
ddea710933 LibJS: Expose various offsets for GetGlobal JIT fast path 2023-11-13 13:33:43 +01:00
Andreas Kling
cb7169d73f LibJS/JIT: Support the EnterObjectEnvironment bytecode instruction
We can now use `with` statements in jitted code. :^)
2023-11-12 14:21:41 +01:00