mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-13 14:42:51 +00:00
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);
193 lines
6.4 KiB
C++
193 lines
6.4 KiB
C++
/*
|
|
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Forward.h>
|
|
#include <AK/Span.h>
|
|
#include <LibJS/Bytecode/Executable.h>
|
|
#include <LibJS/Forward.h>
|
|
#include <LibJS/SourceRange.h>
|
|
|
|
#define ENUMERATE_BYTECODE_OPS(O) \
|
|
O(Add) \
|
|
O(Append) \
|
|
O(AsyncIteratorClose) \
|
|
O(Await) \
|
|
O(BitwiseAnd) \
|
|
O(BitwiseNot) \
|
|
O(BitwiseOr) \
|
|
O(BitwiseXor) \
|
|
O(BlockDeclarationInstantiation) \
|
|
O(Call) \
|
|
O(CallWithArgumentArray) \
|
|
O(Catch) \
|
|
O(ConcatString) \
|
|
O(ContinuePendingUnwind) \
|
|
O(CopyObjectExcludingProperties) \
|
|
O(CreateLexicalEnvironment) \
|
|
O(CreateVariable) \
|
|
O(Decrement) \
|
|
O(DeleteById) \
|
|
O(DeleteByIdWithThis) \
|
|
O(DeleteByValue) \
|
|
O(DeleteByValueWithThis) \
|
|
O(DeleteVariable) \
|
|
O(Div) \
|
|
O(EnterUnwindContext) \
|
|
O(EnterObjectEnvironment) \
|
|
O(Exp) \
|
|
O(GetById) \
|
|
O(GetByIdWithThis) \
|
|
O(GetByValue) \
|
|
O(GetByValueWithThis) \
|
|
O(GetCalleeAndThisFromEnvironment) \
|
|
O(GetIterator) \
|
|
O(GetObjectFromIteratorRecord) \
|
|
O(GetMethod) \
|
|
O(GetNewTarget) \
|
|
O(GetNextMethodFromIteratorRecord) \
|
|
O(GetImportMeta) \
|
|
O(GetObjectPropertyIterator) \
|
|
O(GetPrivateById) \
|
|
O(GetVariable) \
|
|
O(GetGlobal) \
|
|
O(GetLocal) \
|
|
O(GreaterThan) \
|
|
O(GreaterThanEquals) \
|
|
O(HasPrivateId) \
|
|
O(ImportCall) \
|
|
O(In) \
|
|
O(Increment) \
|
|
O(InstanceOf) \
|
|
O(IteratorClose) \
|
|
O(IteratorNext) \
|
|
O(IteratorToArray) \
|
|
O(Jump) \
|
|
O(JumpConditional) \
|
|
O(JumpNullish) \
|
|
O(JumpUndefined) \
|
|
O(LeaveLexicalEnvironment) \
|
|
O(LeaveUnwindContext) \
|
|
O(LeftShift) \
|
|
O(LessThan) \
|
|
O(LessThanEquals) \
|
|
O(Load) \
|
|
O(LoadImmediate) \
|
|
O(LooselyEquals) \
|
|
O(LooselyInequals) \
|
|
O(Mod) \
|
|
O(Mul) \
|
|
O(NewArray) \
|
|
O(NewBigInt) \
|
|
O(NewClass) \
|
|
O(NewFunction) \
|
|
O(NewObject) \
|
|
O(NewPrimitiveArray) \
|
|
O(NewRegExp) \
|
|
O(NewString) \
|
|
O(NewTypeError) \
|
|
O(Not) \
|
|
O(PutById) \
|
|
O(PutByIdWithThis) \
|
|
O(PutByValue) \
|
|
O(PutByValueWithThis) \
|
|
O(PutPrivateById) \
|
|
O(ResolveThisBinding) \
|
|
O(ResolveSuperBase) \
|
|
O(Return) \
|
|
O(RightShift) \
|
|
O(ScheduleJump) \
|
|
O(SetVariable) \
|
|
O(SetLocal) \
|
|
O(Store) \
|
|
O(StrictlyEquals) \
|
|
O(StrictlyInequals) \
|
|
O(Sub) \
|
|
O(SuperCallWithArgumentArray) \
|
|
O(Throw) \
|
|
O(ThrowIfNotObject) \
|
|
O(ThrowIfNullish) \
|
|
O(ToNumeric) \
|
|
O(Typeof) \
|
|
O(TypeofVariable) \
|
|
O(TypeofLocal) \
|
|
O(UnaryMinus) \
|
|
O(UnaryPlus) \
|
|
O(UnsignedRightShift) \
|
|
O(Yield)
|
|
|
|
namespace JS::Bytecode {
|
|
|
|
class alignas(void*) Instruction {
|
|
public:
|
|
constexpr static bool IsTerminator = false;
|
|
|
|
enum class Type {
|
|
#define __BYTECODE_OP(op) \
|
|
op,
|
|
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
|
|
#undef __BYTECODE_OP
|
|
};
|
|
|
|
Type type() const { return m_type; }
|
|
size_t length() const { return m_length; }
|
|
DeprecatedString to_deprecated_string(Bytecode::Executable const&) const;
|
|
ThrowCompletionOr<void> execute(Bytecode::Interpreter&) const;
|
|
static void destroy(Instruction&);
|
|
|
|
// FIXME: Find a better way to organize this information
|
|
void set_source_record(SourceRecord rec) { m_source_record = rec; }
|
|
SourceRecord source_record() const { return m_source_record; }
|
|
|
|
protected:
|
|
Instruction(Type type, size_t length)
|
|
: m_type(type)
|
|
, m_length(length)
|
|
{
|
|
}
|
|
|
|
private:
|
|
SourceRecord m_source_record {};
|
|
Type m_type {};
|
|
size_t m_length {};
|
|
};
|
|
|
|
class InstructionStreamIterator {
|
|
public:
|
|
InstructionStreamIterator(ReadonlyBytes bytes, Executable const* executable = nullptr, size_t offset = 0)
|
|
: m_begin(bytes.data())
|
|
, m_end(bytes.data() + bytes.size())
|
|
, m_ptr(bytes.data() + offset)
|
|
, m_executable(executable)
|
|
{
|
|
}
|
|
|
|
size_t offset() const { return m_ptr - m_begin; }
|
|
bool at_end() const { return m_ptr >= m_end; }
|
|
|
|
Instruction const& operator*() const { return dereference(); }
|
|
|
|
ALWAYS_INLINE void operator++()
|
|
{
|
|
m_ptr += dereference().length();
|
|
}
|
|
|
|
UnrealizedSourceRange source_range() const;
|
|
RefPtr<SourceCode> source_code() const;
|
|
|
|
Executable const* executable() const { return m_executable; }
|
|
|
|
private:
|
|
Instruction const& dereference() const { return *reinterpret_cast<Instruction const*>(m_ptr); }
|
|
|
|
u8 const* m_begin { nullptr };
|
|
u8 const* m_end { nullptr };
|
|
u8 const* m_ptr { nullptr };
|
|
GCPtr<Executable const> m_executable;
|
|
};
|
|
|
|
}
|