LibJS: Skip iterator result object allocation in for..of and for..in

Introduce special instruction for `for..of` and `for..in` loop that
skips `{ value, done }` result object allocation if iterator is builtin
(array, map, set, string). This reduces GC pressure significantly and
avoids extracting the `value` and `done` properties.

This change makes this micro benchmark 48% faster on my computer:
```js
const arr = new Array(10_000_000);
let counter = 0;
for (let _ of arr) {
    counter++;
}
```
This commit is contained in:
Aliaksandr Kalenik 2025-04-30 16:31:26 +03:00 committed by Andreas Kling
parent ab52d86a69
commit 81b6a1100e
Notes: github-actions[bot] 2025-04-30 18:52:38 +00:00
4 changed files with 94 additions and 23 deletions

View file

@ -2721,6 +2721,35 @@ private:
Operand m_iterator_record;
};
class ForOfNext final : public Instruction {
public:
ForOfNext(Operand dst_value, Operand dst_done, Operand iterator_record)
: Instruction(Type::ForOfNext)
, m_dst_value(dst_value)
, m_dst_done(dst_done)
, m_iterator_record(iterator_record)
{
}
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
void visit_operands_impl(Function<void(Operand&)> visitor)
{
visitor(m_dst_value);
visitor(m_dst_done);
visitor(m_iterator_record);
}
Operand dst_value() const { return m_dst_value; }
Operand dst_done() const { return m_dst_done; }
Operand iterator_record() const { return m_iterator_record; }
private:
Operand m_dst_value;
Operand m_dst_done;
Operand m_iterator_record;
};
class ResolveThisBinding final : public Instruction {
public:
ResolveThisBinding()