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);
This commit is contained in:
Andreas Kling 2023-12-07 10:44:41 +01:00
parent 4966c083df
commit 4699c81fc1
Notes: sideshowbarker 2024-07-16 22:14:49 +09:00
23 changed files with 226 additions and 144 deletions

View file

@ -338,7 +338,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
}
// e. Let iteratorRecord be undefined.
Optional<IteratorRecord> iterator_record;
GCPtr<IteratorRecord> iterator_record;
// f. If usingAsyncIterator is not undefined, then
if (using_async_iterator) {
@ -354,7 +354,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
}
// h. If iteratorRecord is not undefined, then
if (iterator_record.has_value()) {
if (iterator_record) {
GCPtr<Object> array;
// i. If IsConstructor(C) is true, then
@ -377,7 +377,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
auto error = vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
// b. Return ? AsyncIteratorClose(iteratorRecord, error).
return *TRY(async_iterator_close(vm, iterator_record.value(), move(error)));
return *TRY(async_iterator_close(vm, *iterator_record, move(error)));
}
// 2. Let Pk be ! ToString(𝔽(k)).
@ -433,7 +433,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
// b. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).
if (mapped_value_or_error.is_error()) {
TRY(async_iterator_close(vm, iterator_record.value(), mapped_value_or_error));
TRY(async_iterator_close(vm, *iterator_record, mapped_value_or_error));
return mapped_value_or_error;
}
@ -442,7 +442,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
// d. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).
if (mapped_value_or_error.is_error()) {
TRY(async_iterator_close(vm, iterator_record.value(), mapped_value_or_error));
TRY(async_iterator_close(vm, *iterator_record, mapped_value_or_error));
return mapped_value_or_error;
}
@ -458,7 +458,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from_async)
// 9. If defineStatus is an abrupt completion, return ? AsyncIteratorClose(iteratorRecord, defineStatus).
if (define_status.is_error())
return *TRY(iterator_close(vm, iterator_record.value(), define_status.release_error()));
return *TRY(iterator_close(vm, *iterator_record, define_status.release_error()));
// 10. Set k to k + 1.
}