mirror of
https://github.com/RGBCube/serenity
synced 2025-05-30 15:18:11 +00:00

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. :^)
192 lines
6.4 KiB
C++
192 lines
6.4 KiB
C++
/*
|
|
* Copyright (c) 2021-2024, 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(ArrayAppend) \
|
|
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(Dump) \
|
|
O(End) \
|
|
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(GreaterThan) \
|
|
O(GreaterThanEquals) \
|
|
O(HasPrivateId) \
|
|
O(ImportCall) \
|
|
O(In) \
|
|
O(Increment) \
|
|
O(InstanceOf) \
|
|
O(IteratorClose) \
|
|
O(IteratorNext) \
|
|
O(IteratorToArray) \
|
|
O(Jump) \
|
|
O(JumpIf) \
|
|
O(JumpNullish) \
|
|
O(JumpUndefined) \
|
|
O(LeaveLexicalEnvironment) \
|
|
O(LeaveUnwindContext) \
|
|
O(LeftShift) \
|
|
O(LessThan) \
|
|
O(LessThanEquals) \
|
|
O(LooselyEquals) \
|
|
O(LooselyInequals) \
|
|
O(Mod) \
|
|
O(Mov) \
|
|
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(StrictlyEquals) \
|
|
O(StrictlyInequals) \
|
|
O(Sub) \
|
|
O(SuperCallWithArgumentArray) \
|
|
O(Throw) \
|
|
O(ThrowIfNotObject) \
|
|
O(ThrowIfNullish) \
|
|
O(ThrowIfTDZ) \
|
|
O(ToNumeric) \
|
|
O(Typeof) \
|
|
O(TypeofVariable) \
|
|
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; }
|
|
ByteString to_byte_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;
|
|
};
|
|
|
|
}
|