ladybird/Libraries/LibJS/Bytecode/AsmInterpreter/gen_asm_offsets.cpp
Andreas Kling 0a49dd1c28 LibJS: Reduce inline cache memory usage
Store bytecode property lookup caches as tiered handles instead of
eagerly allocating four entries for every cache slot. Each slot starts
empty, grows to one monomorphic entry after the first cacheable lookup,
and promotes to the existing four-entry table when another cache key
appears.

Global variable caches keep one inline property entry because they
usually warm up as global object property accesses. This keeps common
global access allocation-free while still avoiding the inherited
polymorphic cache cost.

Generated asm offsets now point at the lazy property-cache storage and
the inline global entry, and asm fast paths bail out for empty property
cache slots.
2026-05-19 01:12:36 +02:00

414 lines
26 KiB
C++

/*
* Copyright (c) 2026, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
// Small build-time tool that prints struct field offsets as DSL constants.
// Compiled with the same flags as LibJS so layouts match exactly.
#include <AK/Format.h>
#include <AK/StringBase.h>
#include <AK/Utf16StringData.h>
#include <LibJS/Bytecode/Builtins.h>
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/PropertyNameIterator.h>
#include <LibJS/Bytecode/PutKind.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/DeclarativeEnvironment.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
#include <LibJS/Runtime/ExecutionContext.h>
#include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/GlobalEnvironment.h>
#include <LibJS/Runtime/IndexedProperties.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Realm.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/SharedFunctionInstanceData.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/VM.h>
#define EMIT_OFFSET(name, type, member) \
outln("const " #name " = {}", offsetof(type, member))
#define EMIT_SIZEOF(name, type) \
outln("const " #name " = {}", sizeof(type))
int main()
{
using namespace JS;
using namespace JS::Bytecode;
struct PrimitiveStringLayoutAccessor : PrimitiveString {
using PrimitiveString::DeferredKind;
using PrimitiveString::m_deferred_kind;
using PrimitiveString::m_utf16_string;
};
outln("# Generated by gen_asm_offsets -- DO NOT EDIT\n");
// Object layout
outln("# Object layout");
EMIT_OFFSET(OBJECT_SHAPE, Object, m_shape);
EMIT_OFFSET(OBJECT_NAMED_PROPERTIES, Object, m_named_properties);
EMIT_OFFSET(OBJECT_INDEXED_ELEMENTS, Object, m_indexed_elements);
EMIT_OFFSET(OBJECT_INDEXED_STORAGE_KIND, Object, m_indexed_storage_kind);
EMIT_OFFSET(OBJECT_INDEXED_ARRAY_LIKE_SIZE, Object, m_indexed_array_like_size);
EMIT_SIZEOF(OBJECT_SIZE, Object);
// Object flags byte
outln("\n# Object flags");
EMIT_OFFSET(OBJECT_FLAGS, Object, m_flags);
outln("const OBJECT_FLAG_HAS_MAGICAL_LENGTH = {}", Object::Flag::HasMagicalLengthProperty);
outln("const OBJECT_FLAG_MAY_INTERFERE = {}", Object::Flag::MayInterfereWithIndexedPropertyAccess);
outln("const OBJECT_FLAG_IS_TYPED_ARRAY = {}", Object::Flag::IsTypedArray);
outln("const OBJECT_FLAG_IS_FUNCTION = {}", Object::Flag::IsFunction);
outln("const OBJECT_FLAG_IS_ECMASCRIPT_FUNCTION_OBJECT = {}", Object::Flag::IsECMAScriptFunctionObject);
outln("const OBJECT_FLAG_IS_RAW_NATIVE_FUNCTION = {}", Object::Flag::IsRawNativeFunction);
// Shape layout
outln("\n# Shape layout");
EMIT_OFFSET(SHAPE_REALM, Shape, m_realm);
EMIT_OFFSET(SHAPE_PROTOTYPE, Shape, m_prototype);
EMIT_OFFSET(SHAPE_DICTIONARY_GENERATION, Shape, m_dictionary_generation);
EMIT_SIZEOF(SHAPE_SIZE, Shape);
// PropertyLookupCache layout
outln("\n# PropertyLookupCache layout");
EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_DATA, PropertyLookupCache, m_data);
outln("const PROPERTY_LOOKUP_CACHE_POLYMORPHIC_DATA_TAG = {}", PropertyLookupCache::polymorphic_data_tag);
EMIT_SIZEOF(PROPERTY_LOOKUP_CACHE_SIZE, PropertyLookupCache);
// PropertyLookupCache::Entry layout
outln("\n# PropertyLookupCache::Entry layout");
EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_PROPERTY_OFFSET, PropertyLookupCache::Entry, property_offset);
EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_DICTIONARY_GENERATION, PropertyLookupCache::Entry, shape_dictionary_generation);
EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_FROM_SHAPE, PropertyLookupCache::Entry, from_shape);
EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_SHAPE, PropertyLookupCache::Entry, shape);
EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_PROTOTYPE, PropertyLookupCache::Entry, prototype);
EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_PROTOTYPE_CHAIN_VALIDITY, PropertyLookupCache::Entry, prototype_chain_validity);
EMIT_SIZEOF(PROPERTY_LOOKUP_CACHE_ENTRY_SIZE, PropertyLookupCache::Entry);
// ObjectPropertyIteratorCacheData layout
outln("\n# ObjectPropertyIteratorCacheData layout");
EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROPERTIES, ObjectPropertyIteratorCacheData, m_properties);
EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROPERTY_VALUES, ObjectPropertyIteratorCacheData, m_property_values);
EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_SHAPE, ObjectPropertyIteratorCacheData, m_shape);
EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROTOTYPE_CHAIN_VALIDITY, ObjectPropertyIteratorCacheData, m_prototype_chain_validity);
EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_INDEXED_PROPERTY_COUNT, ObjectPropertyIteratorCacheData, m_indexed_property_count);
EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_SHAPE_DICTIONARY_GENERATION, ObjectPropertyIteratorCacheData, m_shape_dictionary_generation);
EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_FAST_PATH, ObjectPropertyIteratorCacheData, m_fast_path);
// ObjectPropertyIteratorCache layout
outln("\n# ObjectPropertyIteratorCache layout");
EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PTR, ObjectPropertyIteratorCache, data);
EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_REUSABLE_PROPERTY_NAME_ITERATOR, ObjectPropertyIteratorCache, reusable_property_name_iterator);
// PropertyNameIterator layout
outln("\n# PropertyNameIterator layout");
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_OBJECT, PropertyNameIterator, m_object);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_PROPERTY_CACHE, PropertyNameIterator, m_property_cache);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_SHAPE, PropertyNameIterator, m_shape);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_PROTOTYPE_CHAIN_VALIDITY, PropertyNameIterator, m_prototype_chain_validity);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_ITERATOR_CACHE_SLOT, PropertyNameIterator, m_iterator_cache_slot);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_INDEXED_PROPERTY_COUNT, PropertyNameIterator, m_indexed_property_count);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_NEXT_INDEXED_PROPERTY, PropertyNameIterator, m_next_indexed_property);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_NEXT_PROPERTY, PropertyNameIterator, m_next_property);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_SHAPE_IS_DICTIONARY, PropertyNameIterator, m_shape_is_dictionary);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_SHAPE_DICTIONARY_GENERATION, PropertyNameIterator, m_shape_dictionary_generation);
EMIT_OFFSET(PROPERTY_NAME_ITERATOR_FAST_PATH, PropertyNameIterator, m_fast_path);
// Executable layout
outln("\n# Executable layout");
EMIT_OFFSET(EXECUTABLE_CONSTANTS, Executable, constants);
EMIT_OFFSET(EXECUTABLE_PROPERTY_LOOKUP_CACHES, Executable, property_lookup_caches);
EMIT_OFFSET(EXECUTABLE_GLOBAL_VARIABLE_CACHES, Executable, global_variable_caches);
EMIT_OFFSET(EXECUTABLE_REGISTERS_AND_LOCALS_COUNT, Executable, registers_and_locals_count);
EMIT_OFFSET(EXECUTABLE_REGISTERS_AND_LOCALS_AND_CONSTANTS_COUNT, Executable, registers_and_locals_and_constants_count);
EMIT_OFFSET(EXECUTABLE_ASM_CONSTANTS_SIZE, Executable, asm_constants_size);
EMIT_OFFSET(EXECUTABLE_ASM_CONSTANTS_DATA, Executable, asm_constants_data);
// ExecutionContext layout
outln("\n# ExecutionContext layout");
VERIFY(alignof(ExecutionContext) == sizeof(Value));
EMIT_OFFSET(EXECUTION_CONTEXT_EXECUTABLE, ExecutionContext, executable);
EMIT_OFFSET(EXECUTION_CONTEXT_FUNCTION, ExecutionContext, function);
EMIT_OFFSET(EXECUTION_CONTEXT_REALM, ExecutionContext, realm);
EMIT_OFFSET(EXECUTION_CONTEXT_SCRIPT_OR_MODULE, ExecutionContext, script_or_module);
EMIT_OFFSET(EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, ExecutionContext, lexical_environment);
EMIT_OFFSET(EXECUTION_CONTEXT_VARIABLE_ENVIRONMENT, ExecutionContext, variable_environment);
EMIT_OFFSET(EXECUTION_CONTEXT_PRIVATE_ENVIRONMENT, ExecutionContext, private_environment);
EMIT_OFFSET(EXECUTION_CONTEXT_SKIP_WHEN_DETERMINING_INCUMBENT_COUNTER, ExecutionContext, skip_when_determining_incumbent_counter);
EMIT_OFFSET(EXECUTION_CONTEXT_YIELD_CONTINUATION, ExecutionContext, yield_continuation);
EMIT_OFFSET(EXECUTION_CONTEXT_YIELD_IS_AWAIT, ExecutionContext, yield_is_await);
EMIT_OFFSET(EXECUTION_CONTEXT_CALLER_IS_CONSTRUCT, ExecutionContext, caller_is_construct);
EMIT_OFFSET(EXECUTION_CONTEXT_THIS_VALUE, ExecutionContext, this_value);
EMIT_OFFSET(EXECUTION_CONTEXT_CALLER_FRAME, ExecutionContext, caller_frame);
EMIT_OFFSET(EXECUTION_CONTEXT_PASSED_ARGUMENT_COUNT, ExecutionContext, passed_argument_count);
EMIT_OFFSET(EXECUTION_CONTEXT_CALLER_RETURN_PC, ExecutionContext, caller_return_pc);
EMIT_OFFSET(EXECUTION_CONTEXT_CALLER_DST_RAW, ExecutionContext, caller_dst_raw);
EMIT_OFFSET(EXECUTION_CONTEXT_PROGRAM_COUNTER, ExecutionContext, program_counter);
EMIT_OFFSET(EXECUTION_CONTEXT_REGISTERS_AND_CONSTANTS_AND_LOCALS_AND_ARGUMENTS_COUNT, ExecutionContext, registers_and_constants_and_locals_and_arguments_count);
EMIT_OFFSET(EXECUTION_CONTEXT_ARGUMENT_COUNT, ExecutionContext, argument_count);
EMIT_SIZEOF(SIZEOF_EXECUTION_CONTEXT, ExecutionContext);
outln("const ALIGNOF_EXECUTION_CONTEXT = {}", alignof(ExecutionContext));
outln("const EXECUTION_CONTEXT_NO_YIELD_CONTINUATION = {}", ExecutionContext::no_yield_continuation);
outln("const SIZEOF_SCRIPT_OR_MODULE = {}", sizeof(ScriptOrModule));
// InterpreterStack layout
outln("\n# InterpreterStack layout");
EMIT_OFFSET(INTERPRETER_STACK_LIMIT, InterpreterStack, m_limit);
EMIT_OFFSET(INTERPRETER_STACK_TOP, InterpreterStack, m_top);
// Realm layout
outln("\n# Realm layout");
EMIT_OFFSET(REALM_GLOBAL_ENVIRONMENT, Realm, m_global_environment);
EMIT_OFFSET(REALM_GLOBAL_OBJECT, Realm, m_global_object);
EMIT_OFFSET(REALM_GLOBAL_DECLARATIVE_ENVIRONMENT, Realm, m_global_declarative_environment);
// VM layout
outln("\n# VM layout");
EMIT_OFFSET(VM_RUNNING_EXECUTION_CONTEXT, VM, m_running_execution_context);
EMIT_OFFSET(VM_INTERPRETER_STACK, VM, m_interpreter_stack);
EMIT_OFFSET(VM_STACK_INFO, VM, m_stack_info);
EMIT_OFFSET(VM_EXECUTION_GENERATION, VM, m_execution_generation);
outln("const VM_INTERPRETER_STACK_TOP = {}", offsetof(VM, m_interpreter_stack) + offsetof(InterpreterStack, m_top));
#if defined(HAS_ADDRESS_SANITIZER)
outln("const VM_STACK_SPACE_LIMIT = {}", 96 * KiB);
#else
outln("const VM_STACK_SPACE_LIMIT = {}", 32 * KiB);
#endif
// StackInfo layout
outln("\n# StackInfo layout");
EMIT_OFFSET(STACK_INFO_BASE, StackInfo, m_base);
// IndexedStorageKind enum values
outln("\n# IndexedStorageKind enum values");
outln("const INDEXED_STORAGE_KIND_NONE = {}", static_cast<u8>(IndexedStorageKind::None));
outln("const INDEXED_STORAGE_KIND_PACKED = {}", static_cast<u8>(IndexedStorageKind::Packed));
outln("const INDEXED_STORAGE_KIND_HOLEY = {}", static_cast<u8>(IndexedStorageKind::Holey));
outln("const INDEXED_STORAGE_KIND_DICTIONARY = {}", static_cast<u8>(IndexedStorageKind::Dictionary));
// ObjectPropertyIteratorFastPath enum values
outln("\n# ObjectPropertyIteratorFastPath enum values");
outln("const OBJECT_PROPERTY_ITERATOR_FAST_PATH_NONE = {}", static_cast<u8>(ObjectPropertyIteratorFastPath::None));
outln("const OBJECT_PROPERTY_ITERATOR_FAST_PATH_PLAIN_NAMED = {}", static_cast<u8>(ObjectPropertyIteratorFastPath::PlainNamed));
outln("const OBJECT_PROPERTY_ITERATOR_FAST_PATH_PACKED_INDEXED = {}", static_cast<u8>(ObjectPropertyIteratorFastPath::PackedIndexed));
// Vector<Value> layout (used for bytecode)
outln("\n# Vector<Value> layout");
{
Vector<Value> v;
auto base = reinterpret_cast<uintptr_t>(&v);
auto vec_data = reinterpret_cast<uintptr_t>(&v.m_metadata.outline_buffer) - base;
auto vec_size = reinterpret_cast<uintptr_t>(&v.m_size) - base;
outln("const VECTOR_DATA = {}", vec_data);
outln("const VECTOR_SIZE = {}", vec_size);
// Composite offset for Executable.bytecode data pointer
outln("const EXECUTABLE_BYTECODE_DATA = {}", offsetof(Executable, bytecode) + InstructionStream::data_member_offset());
outln("const EXECUTABLE_PROPERTY_LOOKUP_CACHES_DATA = {}", offsetof(Executable, property_lookup_caches) + vec_data);
outln("const EXECUTABLE_GLOBAL_VARIABLE_CACHES_DATA = {}", offsetof(Executable, global_variable_caches) + vec_data);
outln("const EXECUTABLE_CONSTANTS_DATA = {}", offsetof(Executable, constants) + vec_data);
outln("const EXECUTABLE_CONSTANTS_SIZE = {}", offsetof(Executable, constants) + vec_size);
outln("const OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROPERTY_VALUES_DATA = {}", offsetof(ObjectPropertyIteratorCacheData, m_property_values) + vec_data);
outln("const OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROPERTY_VALUES_SIZE = {}", offsetof(ObjectPropertyIteratorCacheData, m_property_values) + vec_size);
}
// PutKind enum
outln("\n# PutKind enum");
outln("const PUT_KIND_NORMAL = {}", static_cast<u8>(JS::Bytecode::PutKind::Normal));
// PrototypeChainValidity layout
outln("\n# PrototypeChainValidity layout");
EMIT_OFFSET(PROTOTYPE_CHAIN_VALIDITY_VALID, PrototypeChainValidity, m_valid);
// DeclarativeEnvironment layout
outln("\n# DeclarativeEnvironment layout");
EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_RARE_DATA, DeclarativeEnvironment, m_rare_data);
EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_RARE_DATA_SERIAL, DeclarativeEnvironment::RareData, m_environment_serial_number);
// GlobalVariableCache layout
outln("\n# GlobalVariableCache layout");
EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_ENTRY, GlobalVariableCache, entry);
EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_ENVIRONMENT_SERIAL, GlobalVariableCache, environment_serial_number);
EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_ENVIRONMENT_BINDING_INDEX, GlobalVariableCache, environment_binding_index);
EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_HAS_ENVIRONMENT_BINDING, GlobalVariableCache, has_environment_binding_index);
EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_IN_MODULE_ENVIRONMENT, GlobalVariableCache, in_module_environment);
EMIT_SIZEOF(GLOBAL_VARIABLE_CACHE_SIZE, GlobalVariableCache);
outln("const GLOBAL_VARIABLE_CACHE_ENTRY_PROPERTY_OFFSET = {}", offsetof(GlobalVariableCache, entry) + offsetof(PropertyLookupCache::Entry, property_offset));
outln("const GLOBAL_VARIABLE_CACHE_ENTRY_DICTIONARY_GENERATION = {}", offsetof(GlobalVariableCache, entry) + offsetof(PropertyLookupCache::Entry, shape_dictionary_generation));
outln("const GLOBAL_VARIABLE_CACHE_ENTRY_SHAPE = {}", offsetof(GlobalVariableCache, entry) + offsetof(PropertyLookupCache::Entry, shape));
// Builtin enum values
outln("\n# Builtin enum values");
outln("const BUILTIN_MATH_ABS = {}", static_cast<u8>(Bytecode::Builtin::MathAbs));
outln("const BUILTIN_MATH_FLOOR = {}", static_cast<u8>(Bytecode::Builtin::MathFloor));
outln("const BUILTIN_MATH_CEIL = {}", static_cast<u8>(Bytecode::Builtin::MathCeil));
outln("const BUILTIN_MATH_ROUND = {}", static_cast<u8>(Bytecode::Builtin::MathRound));
outln("const BUILTIN_MATH_SQRT = {}", static_cast<u8>(Bytecode::Builtin::MathSqrt));
outln("const BUILTIN_MATH_EXP = {}", static_cast<u8>(Bytecode::Builtin::MathExp));
outln("const BUILTIN_STRING_FROM_CHAR_CODE = {}", static_cast<u8>(Bytecode::Builtin::StringFromCharCode));
outln("const BUILTIN_STRING_PROTOTYPE_CHAR_CODE_AT = {}", static_cast<u8>(Bytecode::Builtin::StringPrototypeCharCodeAt));
outln("const BUILTIN_STRING_PROTOTYPE_CHAR_AT = {}", static_cast<u8>(Bytecode::Builtin::StringPrototypeCharAt));
// FunctionObject layout
outln("\n# FunctionObject layout");
EMIT_OFFSET(FUNCTION_OBJECT_BUILTIN, FunctionObject, m_builtin);
{
// Optional<Builtin> has a value byte and a has_value byte.
auto base = offsetof(FunctionObject, m_builtin);
// Optional<Builtin> stores value at offset 0, has_value bool at offset 1.
// Verify this assumption at compile time:
Optional<Bytecode::Builtin> opt_test { Bytecode::Builtin::MathFloor };
auto const* raw = reinterpret_cast<u8 const*>(&opt_test);
VERIFY(raw[0] == static_cast<u8>(Bytecode::Builtin::MathFloor));
VERIFY(raw[1] == 1); // has_value
outln("const FUNCTION_OBJECT_BUILTIN_VALUE = {}", base);
outln("const FUNCTION_OBJECT_BUILTIN_HAS_VALUE = {}", base + 1);
}
// RawNativeFunction layout
outln("\n# RawNativeFunction layout");
EMIT_OFFSET(RAW_NATIVE_FUNCTION_NATIVE_FUNCTION, RawNativeFunction, m_native_function);
// ECMAScriptFunctionObject layout
outln("\n# ECMAScriptFunctionObject layout");
EMIT_OFFSET(ECMASCRIPT_FUNCTION_OBJECT_SHARED_DATA, ECMAScriptFunctionObject, m_shared_data);
EMIT_OFFSET(ECMASCRIPT_FUNCTION_OBJECT_ENVIRONMENT, ECMAScriptFunctionObject, m_environment);
EMIT_OFFSET(ECMASCRIPT_FUNCTION_OBJECT_PRIVATE_ENVIRONMENT, ECMAScriptFunctionObject, m_private_environment);
EMIT_OFFSET(ECMASCRIPT_FUNCTION_OBJECT_SCRIPT_OR_MODULE, ECMAScriptFunctionObject, m_script_or_module);
// SharedFunctionInstanceData layout
outln("\n# SharedFunctionInstanceData layout");
EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_EXECUTABLE, SharedFunctionInstanceData, m_executable);
EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA, SharedFunctionInstanceData, m_asm_call_metadata);
EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_FORMAL_PARAMETER_COUNT, SharedFunctionInstanceData, m_formal_parameter_count);
EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_STRICT, SharedFunctionInstanceData, m_strict);
EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_FUNCTION_ENVIRONMENT_NEEDED, SharedFunctionInstanceData, m_function_environment_needed);
EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_USES_THIS, SharedFunctionInstanceData, m_uses_this);
EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_CAN_INLINE_CALL, SharedFunctionInstanceData, m_can_inline_call);
outln("const SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA_CAN_INLINE_CALL = {}", SharedFunctionInstanceData::asm_call_metadata_can_inline_call);
outln("const SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA_NEEDS_ENVIRONMENT_OR_THIS_VALUE_RESOLUTION = {}", SharedFunctionInstanceData::asm_call_metadata_needs_environment_or_this_value_resolution);
outln("const SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA_USES_THIS = {}", SharedFunctionInstanceData::asm_call_metadata_uses_this);
outln("const SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA_STRICT = {}", SharedFunctionInstanceData::asm_call_metadata_strict);
// GlobalEnvironment layout
outln("\n# GlobalEnvironment layout");
EMIT_OFFSET(GLOBAL_ENVIRONMENT_GLOBAL_THIS_VALUE, GlobalEnvironment, m_global_this_value);
// PrimitiveString layout
outln("\n# PrimitiveString layout");
EMIT_OFFSET(PRIMITIVE_STRING_DEFERRED_KIND, PrimitiveStringLayoutAccessor, m_deferred_kind);
EMIT_OFFSET(PRIMITIVE_STRING_UTF16_STRING, PrimitiveStringLayoutAccessor, m_utf16_string);
outln("const PRIMITIVE_STRING_DEFERRED_KIND_NONE = {}", static_cast<u8>(PrimitiveStringLayoutAccessor::DeferredKind::None));
// Utf16String / ShortString layout
outln("\n# Utf16String layout");
outln("const UTF16_SHORT_STRING_FLAG = {}", AK::Detail::StringBase::SHORT_STRING_FLAG);
outln("const UTF16_SHORT_STRING_BYTE_COUNT_SHIFT_COUNT = {}", AK::Detail::StringBase::SHORT_STRING_BYTE_COUNT_SHIFT_COUNT);
EMIT_OFFSET(UTF16_SHORT_STRING_BYTE_COUNT_AND_FLAG, AK::Detail::ShortString, byte_count_and_short_string_flag);
EMIT_OFFSET(UTF16_SHORT_STRING_STORAGE, AK::Detail::ShortString, storage);
outln("const PRIMITIVE_STRING_UTF16_SHORT_STRING_BYTE_COUNT_AND_FLAG = {}", offsetof(PrimitiveStringLayoutAccessor, m_utf16_string) + offsetof(AK::Detail::ShortString, byte_count_and_short_string_flag));
outln("const PRIMITIVE_STRING_UTF16_SHORT_STRING_STORAGE = {}", offsetof(PrimitiveStringLayoutAccessor, m_utf16_string) + offsetof(AK::Detail::ShortString, storage));
// Utf16StringData layout
outln("\n# Utf16StringData layout");
EMIT_OFFSET(UTF16_STRING_DATA_LENGTH_IN_CODE_UNITS, AK::Detail::Utf16StringData, m_length_in_code_units);
outln("const UTF16_STRING_DATA_STRING_STORAGE = {}", AK::Detail::Utf16StringData::offset_of_string_storage());
// Environment layout
outln("\n# Environment layout");
EMIT_OFFSET(ENVIRONMENT_SCREWED_BY_EVAL, Environment, m_permanently_screwed_by_eval);
EMIT_OFFSET(ENVIRONMENT_OUTER, Environment, m_outer_environment);
// DeclarativeEnvironment binding storage layout
outln("\n# DeclarativeEnvironment binding storage layout");
EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_SHAPE, DeclarativeEnvironment, m_shape);
EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_BINDING_VALUES, DeclarativeEnvironment, m_binding_values);
EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_RARE_DATA_BINDING_FLAGS, DeclarativeEnvironment::RareData, m_binding_flags);
EMIT_OFFSET(ENVIRONMENT_SHAPE_BINDING_FLAGS, EnvironmentShape, m_binding_flags);
outln("const BINDING_FLAG_MUTABLE = {}", 1 << 1);
// Vector<T> layout: m_size(0), m_capacity(8), m_metadata.outline_buffer(16)
outln("const BINDING_VALUES_DATA_PTR = {}", offsetof(DeclarativeEnvironment, m_binding_values) + sizeof(size_t) * 2);
outln("const BINDING_FLAGS_DATA_PTR = {}", offsetof(DeclarativeEnvironment::RareData, m_binding_flags) + sizeof(size_t) * 2);
outln("const ENVIRONMENT_SHAPE_BINDING_FLAGS_DATA_PTR = {}", offsetof(EnvironmentShape, m_binding_flags) + sizeof(size_t) * 2);
// EnvironmentCoordinate layout
outln("\n# EnvironmentCoordinate layout");
outln("const ENVIRONMENT_COORDINATE_HOPS = {}", offsetof(EnvironmentCoordinate, hops));
outln("const ENVIRONMENT_COORDINATE_INDEX = {}", offsetof(EnvironmentCoordinate, index));
outln("const ENVIRONMENT_COORDINATE_INVALID = 0x{:X}", EnvironmentCoordinate::invalid_marker);
// TypedArrayBase layout
outln("\n# TypedArrayBase layout");
EMIT_OFFSET(TYPED_ARRAY_ELEMENT_SIZE, TypedArrayBase, m_element_size);
EMIT_OFFSET(TYPED_ARRAY_ARRAY_LENGTH, TypedArrayBase, m_array_length);
EMIT_OFFSET(TYPED_ARRAY_BYTE_OFFSET, TypedArrayBase, m_byte_offset);
EMIT_OFFSET(TYPED_ARRAY_KIND, TypedArrayBase, m_kind);
EMIT_OFFSET(TYPED_ARRAY_CACHED_DATA_PTR, TypedArrayBase, m_data);
// ByteLength (Variant<Auto, Detached, u32>) layout
outln("\n# ByteLength layout");
{
// For Variant<Auto, Detached, u32>: u32 index 2 means it holds a u32
outln("const BYTE_LENGTH_U32_INDEX = 2");
EMIT_SIZEOF(BYTE_LENGTH_SIZE, ByteLength);
// Composite: array_length value (u32) and index byte within TypedArrayBase
auto ta_al = offsetof(TypedArrayBase, m_array_length);
// Variant<Auto, Detached, u32> stores m_data[4] then m_index (u8)
outln("const TYPED_ARRAY_ARRAY_LENGTH_VALUE = {}", ta_al); // u32 data at start
outln("const TYPED_ARRAY_ARRAY_LENGTH_INDEX = {}", ta_al + 4); // index byte after 4 bytes of data
}
// TypedArrayBase::Kind enum values
outln("\n# TypedArrayBase::Kind values");
outln("const TYPED_ARRAY_KIND_UINT8 = {}", static_cast<u8>(TypedArrayBase::Kind::Uint8Array));
outln("const TYPED_ARRAY_KIND_UINT8_CLAMPED = {}", static_cast<u8>(TypedArrayBase::Kind::Uint8ClampedArray));
outln("const TYPED_ARRAY_KIND_UINT16 = {}", static_cast<u8>(TypedArrayBase::Kind::Uint16Array));
outln("const TYPED_ARRAY_KIND_UINT32 = {}", static_cast<u8>(TypedArrayBase::Kind::Uint32Array));
outln("const TYPED_ARRAY_KIND_INT8 = {}", static_cast<u8>(TypedArrayBase::Kind::Int8Array));
outln("const TYPED_ARRAY_KIND_INT16 = {}", static_cast<u8>(TypedArrayBase::Kind::Int16Array));
outln("const TYPED_ARRAY_KIND_INT32 = {}", static_cast<u8>(TypedArrayBase::Kind::Int32Array));
outln("const TYPED_ARRAY_KIND_FLOAT32 = {}", static_cast<u8>(TypedArrayBase::Kind::Float32Array));
outln("const TYPED_ARRAY_KIND_FLOAT64 = {}", static_cast<u8>(TypedArrayBase::Kind::Float64Array));
// Value tags
outln("\n# Value tags");
outln("const OBJECT_TAG = 0x{:X}", static_cast<u64>(OBJECT_TAG));
outln("const STRING_TAG = 0x{:X}", static_cast<u64>(STRING_TAG));
outln("const SYMBOL_TAG = 0x{:X}", static_cast<u64>(SYMBOL_TAG));
outln("const BIGINT_TAG = 0x{:X}", static_cast<u64>(BIGINT_TAG));
outln("const ACCESSOR_TAG = 0x{:X}", static_cast<u64>(ACCESSOR_TAG));
outln("const INT32_TAG = 0x{:X}", static_cast<u64>(INT32_TAG));
outln("const BOOLEAN_TAG = 0x{:X}", static_cast<u64>(BOOLEAN_TAG));
outln("const UNDEFINED_TAG = 0x{:X}", static_cast<u64>(UNDEFINED_TAG));
outln("const NULL_TAG = 0x{:X}", static_cast<u64>(NULL_TAG));
// Shifted value constants
outln("\n# Shifted value constants");
outln("const OBJECT_TAG_SHIFTED = 0x{:X}", static_cast<u64>(OBJECT_TAG << GC::TAG_SHIFT));
outln("const EMPTY_VALUE = 0x{:X}", static_cast<u64>(EMPTY_TAG << GC::TAG_SHIFT));
outln("const INT32_TAG_SHIFTED = 0x{:X}", static_cast<u64>(INT32_TAG << GC::TAG_SHIFT));
outln("const BOOLEAN_TRUE = 0x{:X}", static_cast<u64>((BOOLEAN_TAG << GC::TAG_SHIFT) | 1));
outln("const BOOLEAN_FALSE = 0x{:X}", static_cast<u64>(BOOLEAN_TAG << GC::TAG_SHIFT));
outln("const UNDEFINED_SHIFTED = 0x{:X}", static_cast<u64>(UNDEFINED_TAG << GC::TAG_SHIFT));
outln("const EMPTY_TAG_SHIFTED = 0x{:X}", static_cast<u64>(EMPTY_TAG << GC::TAG_SHIFT));
outln("const NAN_BASE_TAG = 0x{:X}", static_cast<u64>(GC::BASE_TAG));
outln("const CANON_NAN_BITS = 0x{:X}", static_cast<u64>(GC::CANON_NAN_BITS));
outln("const DOUBLE_ONE = 0x{:X}", bit_cast<u64>(1.0));
outln("const NEGATIVE_ZERO = 0x{:X}", static_cast<u64>(NEGATIVE_ZERO_BITS));
outln("const CELL_TAG_SHIFTED = 0x{:X}", static_cast<u64>(GC::SHIFTED_IS_CELL_PATTERN));
outln("const ACCUMULATOR_REG_OFFSET = {}", static_cast<size_t>(Register::accumulator().index()) * sizeof(Value));
outln("const EXCEPTION_REG_OFFSET = {}", static_cast<size_t>(Register::exception().index()) * sizeof(Value));
outln("const THIS_VALUE_REG_OFFSET = {}", static_cast<size_t>(Register::this_value().index()) * sizeof(Value));
outln("const RETURN_VALUE_REG_OFFSET = {}", static_cast<size_t>(Register::return_value().index()) * sizeof(Value));
outln("const SAVED_LEXICAL_ENVIRONMENT_REG_OFFSET = {}", static_cast<size_t>(Register::saved_lexical_environment().index()) * sizeof(Value));
outln("const RESERVED_REGISTERS_SIZE = {}", static_cast<size_t>(Register::reserved_register_count) * sizeof(Value));
return 0;
}