Commit graph

53 commits

Author SHA1 Message Date
Timothy Flynn
8f5bdce8e7 LibJS: Add initial support for creating PrimitiveStrings with AK::String
This will temporarily bloat the size of PrimitiveString as LibJS is
transitioned to use String throughout, but will make doing so piecemeal
much easier.
2023-01-15 01:00:20 +00:00
Timothy Flynn
ca655f5e7d LibJS: Rename VM::string_cache to deprecated_string_cache
And rename the member variable from m_string_cache to
m_deprecated_string_cache to match.
2023-01-15 01:00:20 +00:00
Andreas Kling
7826cb2556 LibJS: Use a work queue instead of the C++ stack for the GC mark phase
This fixes an issue where we'd run out of C++ stack while traversing
large GC heap graphs.
2023-01-10 15:30:07 -05:00
Andrew Kaster
4e7bdcfeea LibJS: Only start ElapsedTimer for GC metrics when printing is enabled
We don't need to be checking the current time unconditionally when we
only observe the results if we're going to dump the GC stats.

This saves two trips to clock_gettime at the cost of an extra branch.
2023-01-07 14:51:04 +01:00
Andreas Kling
51579810bd LibJS: Add Cell::must_survive_garbage_collection() mechanism
This allows cells to prevent themselves from being garbage collected,
even when there are no references to them.
2022-10-24 18:06:55 +02:00
Andreas Kling
07a36c8f80 LibJS: Add a finalization pass to the garbage collector
Doing things in the destructor of a GC-allocated object isn't always
safe, in case it involves accessing other GC-allocated objects.
If they were already swept by GC, we'd be poking into freed memory.

This patch adds a separate finalization pass where GC calls finalize()
on every unmarked cell that's about to be deleted.

It's safe to access other GC objects in finalize(), even if they're
also unmarked.
2022-10-20 19:36:59 +02:00
Andrew Kaster
828441852f Everywhere: Replace uses of __serenity__ with AK_OS_SERENITY
Now that we have OS macros for essentially every supported OS, let's try
to use them everywhere.
2022-10-10 12:23:12 +02:00
Andreas Kling
131c3f50de LibJS: Add JS::SafeFunction, like Function but protects captures from GC
SafeFunction automatically registers its closure memory area in a place
where the JS garbage collector can find it.

This means that you can capture JS::Value and arbitrary pointers into
the GC heap in closures, as long as you're using a SafeFunction, and the
GC will not zap those values!

There's probably some performance impact from this, and there's a lot of
things that could be nicer/smarter about it, but let's build something
that ensures safety first, and we can worry about performance later. :^)
2022-09-24 12:23:29 +02:00
Daniel Bertalan
2b69af2dfe AK+LibJS: Handle NaN-boxing pointers on AArch64
JS::Value stores 48 bit pointers to separately allocated objects in its
payload. On x86-64, canonical addresses have their top 16 bits set to
the same value as bit 47, effectively meaning that the value has to be
sign-extended to get the pointer. AArch64, however, expects the topmost
bits to be all zeros.

This commit gates sign extension behind `#if ARCH(X86_64)`, and adds an
`#error` for unsupported architectures, so that we do not forget to
think about pointer handling when porting to a new architecture.

Fixes #15290
Fixes SerenityOS/ladybird#56
2022-09-21 11:55:57 +02:00
Linus Groh
f3117d46dc LibJS: Remove GlobalObject from VM::throw_completion()
This is a continuation of the previous five commits.

A first big step into the direction of no longer having to pass a realm
(or currently, a global object) trough layers upon layers of AOs!
Unlike the create() APIs we can safely assume that this is only ever
called when a running execution context and therefore current realm
exists. If not, you can always manually allocate the Error and put it in
a Completion :^)

In the spec, throw exceptions implicitly use the current realm's
intrinsics as well: https://tc39.es/ecma262/#sec-throw-an-exception
2022-08-23 13:58:30 +01:00
Linus Groh
b99cc7d050 LibJS+LibWeb: Replace GlobalObject with Realm in create() functions
This is a continuation of the previous two commits.

As allocating a JS cell already primarily involves a realm instead of a
global object, and we'll need to pass one to the allocate() function
itself eventually (it's bridged via the global object right now), the
create() functions need to receive a realm as well.
The plan is for this to be the highest-level function that actually
receives a realm and passes it around, AOs on an even higher level will
use the "current realm" concept via VM::current_realm() as that's what
the spec assumes; passing around realms (or global objects, for that
matter) on higher AO levels is pointless and unlike for allocating
individual objects, which may happen outside of regular JS execution, we
don't need control over the specific realm that is being used there.
2022-08-23 13:58:30 +01:00
Linus Groh
5dd5896588 LibJS+LibWeb: Replace GlobalObject with Realm in initialize() functions
This is a continuation of the previous commit.

Calling initialize() is the first thing that's done after allocating a
cell on the JS heap - and in the common case of allocating an object,
that's where properties are assigned and intrinsics occasionally
accessed.
Since those are supposed to live on the realm eventually, this is
another step into that direction.
2022-08-23 13:58:30 +01:00
davidot
e746360b9a LibJS: Use NaN boxing to decrease the memory size of Values
Using the fact that there are 2^52-2 NaN representations we can
"NaN-box" all the Values possible. This means that Value no longer has
an explicit "Type" but that information is now stored in the bits of a
double. This is done by "tagging" the top two bytes of the double.
For a full explanation see the large comment with asserts at the top of
Value.

We can also use the exact representation of the tags to make checking
properties like nullish, or is_cell quicker. But the largest gains are
in the fact that the size of a Value is now halved.

The SunSpider and other benchmarks have been ran to confirm that there
are no regressions in performance compared to the previous
implementation. The tests never performed worse and in some cases
performed better. But the biggest differences can be seen in memory
usage when large arrays are allocated. A simple test which allocates a
1000 arrays of size 100000 has roughly half the memory usage.

There is also space in the representations for future expansions such as
tuples and records.

To ensure that Values on the stack and registers are not lost during
garbage collection we also have to add a check to the Heap to check for
any of the cell tags and extracting the canonical form of the pointer
if it matches.
2022-08-15 17:11:25 +02:00
Idan Horowitz
086969277e Everywhere: Run clang-format 2022-04-01 21:24:45 +01:00
Lenny Maiorani
d00b79568f Libraries: Use default constructors/destructors in LibJS
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cother-other-default-operation-rules

"The compiler is more likely to get the default semantics right and
you cannot implement these functions better than the compiler."
2022-03-16 16:19:40 +00:00
Linus Groh
7676b1b925 LibJS: Remove MarkedValueList in favor of MarkedVector<Value> :^) 2022-02-09 12:25:27 +00:00
Linus Groh
a863363b06 LibJS: Let MarkedVector<T> inherit from Vector and handle Cell* + Value
Note: MarkedVector is still relatively new and has zero users right now,
so these changes don't affect any code other than the class itself.

Reasons for this are the rather limited API:

- Despite the name and unlike MarkedValueList, MarkedVector isn't
  actually a Vector, it *wraps* a Vector. This means that plenty of
  convenient APIs are unavailable and have to be exported on the class
  separately and forwarded to the internal Vector, or need to go through
  the exposed Span - both not great options.
- Exposing append(Cell*) and prepend(Cell*) on the base class means that
  it was possible to append any Cell type, not just T! All the strong
  typing guarantees are basically gone, and MarkedVector doesn't do much
  more than casting Cells to the appropriate type through the exposed
  Span.

All of this combined means that MarkedVector - in its current form -
doesn't provide much value over MarkedValueList, and that we have to
maintain two separate, yet almost identical classes.

Let's fix this!

The updated MarkedVector steals various concepts from the existing
MarkedValueList, especially the ability to copy. On the other hand, it
remains generic enough to handle both Cell* and Value for T, making
MarkedValueList effectively redundant :^)

Additionally, by inheriting from Vector we get all the current and
future APIs without having to select and expose them separately.

MarkedVectorBase remains and takes care of communicating creation and
destruction of the class to the heap. Visiting the contained values is
handled via a pure virtual method gather_roots(), which is being called
by the Heap's function of the same name; much like the VM has one.
From there, values are added to the roots HashTable if they are cells
for T = Value, and unconditionally for any other T.

As a small additional improvement the template now also takes an
inline_capacity parameter, defaulting to 32, and forwards it to the
Vector template; allowing for possible future optimizations of current
uses of MarkedValueList, which hard-codes it to 32.
2022-02-09 12:25:27 +00:00
davidot
8da6c01d8f LibJS: Remove the JS_TRACK_ZOMBIE_CELLS option
This feature had bitrotted somewhat and would trigger errors because
PrimitiveStrings were "destroyed" but because of this mode they were not
removed from the string cache. Even fixing that case running test-js
with the options still failed in more places.
2022-02-05 11:52:51 +01:00
Andreas Kling
6b5f6d6c0e LibJS: Add a 96-byte CellAllocator
Two of our most frequently allocated objects are Shape (88 bytes)
and DeclarativeEnvironment (80 bytes). Putting these into 128-byte
cells was quite wasteful, so let's add a more suitable allocator
for them.
2022-01-31 16:19:23 +01:00
Andreas Kling
8bb9fe63b7 LibJS: Add MarkedVector<T>
This abstracts a vector of Cell* with a strongly typed span() accessor
that gives you Span<T*> instead of Span<Cell*>.

It is intended to replace MarkedValueList in situations where you only
need to store pointers to Cell (or an even more specific type of Cell).

The API can definitely be improved, it's just the bare basics for now.
2021-12-16 22:48:17 +01:00
Daniel Bertalan
4a81b33c07 Everywhere: Fix -Winconsistent-missing-override warnings from Clang
This option is already enabled when building Lagom, so let's enable it
for the main build too. We will no longer be surprised by Lagom Clang
CI builds failing while everything compiles locally.

Furthermore, the stronger `-Wsuggest-override` warning is enabled in
this commit, which enforces the use of the `override` keyword in all
classes, not just those which already have some methods marked as
`override`. This works with both GCC and Clang.
2021-12-11 13:14:15 -08:00
Andreas Kling
2495460f6e LibJS: Prune WeakContainers before freeing HeapBlocks
WeakContainers need to look at the Cell::State bits to know if their
weak pointees got swept by garbage collection. So we must do this before
potentially freeing one or more HeapBlocks by notifying the allocator
that a block became empty.
2021-10-08 19:47:25 +02:00
Andreas Kling
83bd675477 LibJS: Make WeakContainer pruning do less work
Instead of iterating *all* swept cells when pruning weak containers,
only iterate the cells actually *in* the container.

Also, instead of compiling a list of all swept cells, we can simply
check the Cell::state() flag to know if something should be pruned.
2021-10-05 18:52:00 +02:00
Andreas Kling
6a1b82df2b LibJS: Put zombie cell tracking code behind a compile-time flag
Since this is a debug-only feature, let's not have it impact GC marking
performance when you don't need it.
2021-10-02 16:39:28 +02:00
Andreas Kling
f290c59dd8 LibJS: Keep track of PrimitiveStrings and share them
VM now has a string cache which tracks all live PrimitiveStrings and
reuses an existing one if possible. This drastically reduces the number
of GC-allocated strings in many real-word situations.
2021-10-02 16:39:28 +02:00
Brian Gianforcaro
12024aeda9 LibJS: Use ElapsedTimer::start_new(); 2021-09-12 17:24:44 +00:00
Andreas Kling
c364520c24 LibJS+js+test-js: Add GC debug mode that keeps cells "alive" as zombies
This patch adds a `-z` option to js and test-js. When run in this mode,
garbage cells are never actually destroyed. We instead keep them around
in a special zombie state.

This allows us to validate that zombies don't get marked in future GC
scans (since there were not supposed to be any more references!) :^)

Cells get notified when they become a zombie (via did_become_zombie())
and this is used by WeakContainer cells to deregister themselves from
the heap.
2021-09-11 16:52:03 +02:00
Andreas Kling
470c99a2a6 LibJS: Tweak the WeakContainer::remove_swept_cells() API a little bit
Make this API take a Span<Cell*> instead of a Vector<Cell*>&.
This is behavior neutral, but stops the API looking like it wants to
do mutable things to the Vector.
2021-09-11 12:09:55 +02:00
davidot
43b17f27a3 test-js: Add a mark_as_garbage method to force GC to collect that object
This should fix the flaky tests of test-js.
It also fixes the tests when running with the -g flag since the values
will not be garbage collected too soon.
2021-09-08 08:53:02 +01:00
Andreas Kling
1e90a3a542 Kernel: Make sys$perf_register_string() generate the string ID's
Making userspace provide a global string ID was silly, and made the API
extremely difficult to use correctly in a global profiling context.

Instead, simply make the kernel do the string ID allocation for us.
This also allows us to convert the string storage to a Vector in the
kernel (and an array in the JSON profile data.)
2021-08-12 00:03:39 +02:00
Andreas Kling
56e84a63ca LibJS: Emit a profile signpost when starting a garbage collection 2021-08-12 00:03:39 +02:00
Andreas Kling
cdc1315dc8 LibJS: Use IntrusiveList for keeping track of WeakContainers 2021-07-21 20:17:55 +02:00
Andreas Kling
3fe1be20b7 LibJS: Use IntrusiveList for keeping track of MarkedValueLists 2021-07-21 19:51:19 +02:00
Andreas Kling
746b310061 LibJS: Use IntrusiveList for keeping track of HandleImpls
This allows us to remove a HashTable from heap and cuts down on
some of the malloc traffic when creating handles.
2021-07-21 19:45:21 +02:00
Andreas Kling
cd53d046b2 LibJS: Fix typo "sweeped" => "swept" everywhere 2021-06-27 22:40:49 +02:00
Idan Horowitz
1a8ee5d8d7 LibJS: Generify the garbage collector's weak container notifications
This will allow us to use the same interface for other JS weak
containers like the WeakMap & WeakRef.
2021-06-12 10:44:28 +01:00
Idan Horowitz
a00d154522 LibJS: Notify WeakSets when heap cells are sweeped
This is an implementation of the following optional optimization:
https://tc39.es/ecma262/#sec-weakref-execution
2021-06-09 21:52:25 +01:00
Linus Groh
e7bfd34ea7 LibJS: Add dbgln() to Heap::allocator_for_size() before crashing
If we can't get a CellAllocator for the requested cell size, at least
print a debug message before dying.
2021-06-06 23:08:15 +01:00
Andrew Kaster
81a5dcde84 LibJS: Expose minimum possible cell size of JS::Heap
Use this to avoid creating a 16 byte cell allocator on x86_64, where the
size of FreelistEntry is 24 bytes. Every JS::Cell must be at least the
size of the FreelistEntry or things start crashing, so the 16 byte
allocator was wasted on that platform.
2021-05-29 17:47:29 +01:00
Andreas Kling
9b699bad94 LibJS: Rename Allocator => CellAllocator
Now that we have a BlockAllocator as well, it seems appropriate to name
the allocator-that-allocates-cells something more specific to match.
2021-05-27 19:56:12 +02:00
Andreas Kling
cfe3895991 LibJS: Fix broken dbgln_if(HEAP_DEBUG) 2021-05-25 19:44:32 +02:00
Andreas Kling
2de9139532 LibJS: Fix clang-tidy warnings about redundant types in Heap.cpp 2021-05-25 19:04:15 +02:00
Andreas Kling
47a4b2ba9f LibJS: Make Value::as_cell() return a Cell& 2021-05-25 18:48:11 +02:00
Andreas Kling
0de954e86b LibJS: Make Cell::Visitor::visit_impl() take a Cell&
Passing a null cell pointer is not supported.
2021-05-25 18:39:01 +02:00
Andreas Kling
789d20ebb7 LibJS: Replace Cell live bit with a cell state
So far we only have two states: Live and Dead. In the future, we can
add additional states to support incremental sweeping and/or multi-
stage cell destruction.
2021-05-25 18:37:37 +02:00
Andrew Kaster
f90a19ba4c LibJS: Make sure all allocators are 8-byte aligned
Absolutely massive allocations > 1024 bytes would go into the size
class which was 3172 bytes. 3172 happens to not be 8 byte aligned, and
so made UBSAN very sad on x86_64. Change the largest allocator to be
3072 bytes, which is in fact a multiple of 8 :^)
2021-05-14 08:34:00 +01:00
Brian Gianforcaro
1682f0b760 Everything: Move to SPDX license identifiers in all files.
SPDX License Identifiers are a more compact / standardized
way of representing file license information.

See: https://spdx.dev/resources/use/#identifiers

This was done with the `ambr` search and replace tool.

 ambr --no-parent-ignore --key-from-file --rep-from-file key.txt rep.txt *
2021-04-22 11:22:27 +02:00
Linus Groh
87a43fa87c LibJS: Use 'if constexpr' instead of '#if HEAP_DEBUG' 2021-04-18 18:14:44 +02:00
Hendiadyoin1
c0c4e99c74 LibJS: Use dbgln_if in Heap.cpp 2021-04-08 23:57:16 +02:00
Andreas Kling
5d180d1f99 Everywhere: Rename ASSERT => VERIFY
(...and ASSERT_NOT_REACHED => VERIFY_NOT_REACHED)

Since all of these checks are done in release builds as well,
let's rename them to VERIFY to prevent confusion, as everyone is
used to assertions being compiled out in release.

We can introduce a new ASSERT macro that is specifically for debug
checks, but I'm doing this wholesale conversion first since we've
accumulated thousands of these already, and it's not immediately
obvious which ones are suitable for ASSERT.
2021-02-23 20:56:54 +01:00