blob: 3839cb6ed7ec7bb2c25df9209d8016f392c05545 [file] [log] [blame] [edit]
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_EXECUTION_ISOLATE_DATA_H_
#define V8_EXECUTION_ISOLATE_DATA_H_
#include "src/builtins/builtins.h"
#include "src/codegen/constants-arch.h"
#include "src/codegen/external-reference-table.h"
#include "src/execution/isolate-data-fields.h"
#include "src/execution/stack-guard.h"
#include "src/execution/thread-local-top.h"
#include "src/heap/linear-allocation-area.h"
#include "src/init/isolate-group.h"
#include "src/roots/roots.h"
#include "src/sandbox/code-pointer-table.h"
#include "src/sandbox/cppheap-pointer-table.h"
#include "src/sandbox/external-pointer-table.h"
#include "src/sandbox/trusted-pointer-table.h"
#include "src/utils/utils.h"
#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck
namespace v8 {
namespace internal {
class Isolate;
class TrustedPointerPublishingScope;
namespace wasm {
class StackMemory;
}
#define BUILTINS_WITH_DISPATCH_ADAPTER(V, CamelName, underscore_name, ...) \
V(CamelName, CamelName##SharedFun)
#define BUILTINS_WITH_DISPATCH_LIST(V) \
BUILTINS_WITH_SFI_LIST_GENERATOR(BUILTINS_WITH_DISPATCH_ADAPTER, V)
struct JSBuiltinDispatchHandleRoot {
enum Idx {
#define CASE(builtin_name, ...) k##builtin_name,
BUILTINS_WITH_DISPATCH_LIST(CASE)
kCount,
kFirst = 0
#undef CASE
};
static constexpr size_t kPadding = Idx::kCount * sizeof(JSDispatchHandle) %
kSystemPointerSize /
sizeof(JSDispatchHandle);
static constexpr size_t kTableSize = Idx::kCount + kPadding;
static inline Builtin to_builtin(Idx idx) {
#define CASE(builtin_name, ...) Builtin::k##builtin_name,
return std::array<Builtin, Idx::kCount>{
BUILTINS_WITH_DISPATCH_LIST(CASE)}[idx];
#undef CASE
}
static inline Idx to_idx(Builtin builtin) {
switch (builtin) {
#define CASE(builtin_name, ...) \
case Builtin::k##builtin_name: \
return Idx::k##builtin_name;
BUILTINS_WITH_DISPATCH_LIST(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
static inline Idx to_idx(RootIndex root_idx) {
switch (root_idx) {
#define CASE(builtin_name, shared_fun_name, ...) \
case RootIndex::k##shared_fun_name: \
return Idx::k##builtin_name;
BUILTINS_WITH_DISPATCH_LIST(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
};
// This class contains a collection of data accessible from both C++ runtime
// and compiled code (including builtins, interpreter bytecode handlers and
// optimized code). The compiled code accesses the isolate data fields
// indirectly via the root register.
class IsolateData final {
public:
IsolateData(Isolate* isolate, IsolateGroup* group)
:
#ifdef V8_COMPRESS_POINTERS
cage_base_(group->GetPtrComprCageBase()),
#endif
stack_guard_(isolate)
#ifdef V8_ENABLE_SANDBOX
,
trusted_cage_base_(group->GetTrustedPtrComprCageBase()),
code_pointer_table_base_address_(
group->code_pointer_table()->base_address())
#endif
{
}
IsolateData(const IsolateData&) = delete;
IsolateData& operator=(const IsolateData&) = delete;
static constexpr intptr_t kIsolateRootBias = kRootRegisterBias;
// The value of the kRootRegister.
Address isolate_root() const {
return reinterpret_cast<Address>(this) + kIsolateRootBias;
}
// Root-register-relative offsets.
static constexpr int isolate_data_offset() { return -kIsolateRootBias; }
Address isolate_data_address() const {
return reinterpret_cast<Address>(this);
}
#define V(CamelName, Size, hacker_name) \
static constexpr int hacker_name##_offset() { \
return k##CamelName##Offset - kIsolateRootBias; \
} \
Address hacker_name##_address() const { \
return reinterpret_cast<Address>(&hacker_name##_); \
}
ISOLATE_DATA_FIELDS(V)
#undef V
#define V(CamelName, hacker_name, holder_field_name, FieldOffset) \
static constexpr int hacker_name##_offset() { \
return holder_field_name##_offset() + FieldOffset; \
} \
Address hacker_name##_address() const { \
return holder_field_name##_address() + FieldOffset; \
}
ISOLATE_DATA_SUBFIELDS(V)
#undef V
static constexpr int root_slot_offset(RootIndex root_index) {
return roots_table_offset() + RootsTable::offset_of(root_index);
}
static constexpr int BuiltinEntrySlotOffset(Builtin id) {
DCHECK(Builtins::IsBuiltinId(id));
return (Builtins::IsTier0(id) ? builtin_tier0_entry_table_offset()
: builtin_entry_table_offset()) +
Builtins::ToInt(id) * kSystemPointerSize;
}
// TODO(ishell): remove in favour of typified id version.
static constexpr int builtin_slot_offset(int builtin_index) {
return BuiltinSlotOffset(Builtins::FromInt(builtin_index));
}
static constexpr int BuiltinSlotOffset(Builtin id) {
return (Builtins::IsTier0(id) ? builtin_tier0_table_offset()
: builtin_table_offset()) +
Builtins::ToInt(id) * kSystemPointerSize;
}
LinearAllocationArea& new_allocation_info() { return new_allocation_info_; }
LinearAllocationArea& old_allocation_info() { return old_allocation_info_; }
Address fast_c_call_caller_fp() const { return fast_c_call_caller_fp_; }
Address fast_c_call_caller_pc() const { return fast_c_call_caller_pc_; }
Address fast_api_call_target() const { return fast_api_call_target_; }
// The value of kPointerCageBaseRegister.
Address cage_base() const { return cage_base_; }
StackGuard* stack_guard() { return &stack_guard_; }
int32_t* regexp_static_result_offsets_vector() const {
return regexp_static_result_offsets_vector_;
}
void set_regexp_static_result_offsets_vector(int32_t* value) {
regexp_static_result_offsets_vector_ = value;
}
Address* builtin_tier0_entry_table() { return builtin_tier0_entry_table_; }
Address* builtin_tier0_table() { return builtin_tier0_table_; }
RootsTable& roots() { return roots_table_; }
Address api_callback_thunk_argument() const {
return api_callback_thunk_argument_;
}
Address regexp_exec_vector_argument() const {
return regexp_exec_vector_argument_;
}
Tagged<Object> continuation_preserved_embedder_data() const {
return continuation_preserved_embedder_data_;
}
void set_continuation_preserved_embedder_data(Tagged<Object> data) {
continuation_preserved_embedder_data_ = data;
}
const RootsTable& roots() const { return roots_table_; }
ExternalReferenceTable* external_reference_table() {
return &external_reference_table_;
}
ThreadLocalTop& thread_local_top() { return thread_local_top_; }
ThreadLocalTop const& thread_local_top() const { return thread_local_top_; }
Address* builtin_entry_table() { return builtin_entry_table_; }
Address* builtin_table() { return builtin_table_; }
#if V8_ENABLE_WEBASSEMBLY
wasm::StackMemory* active_stack() { return active_stack_; }
void set_active_stack(wasm::StackMemory* stack) { active_stack_ = stack; }
Tagged<WasmSuspenderObject> active_suspender() { return active_suspender_; }
void set_active_suspender(Tagged<WasmSuspenderObject> v) {
active_suspender_ = v;
}
#endif
#if !V8_STATIC_DISPATCH_HANDLES_BOOL
JSDispatchHandle builtin_dispatch_handle(Builtin builtin) {
return builtin_dispatch_table_[JSBuiltinDispatchHandleRoot::to_idx(
builtin)];
}
#endif // !V8_STATIC_DISPATCH_HANDLES_BOOL
// Accessors for storage of raw arguments for runtime functions.
template <typename T>
requires(std::is_integral_v<T> || std::is_floating_point_v<T>)
T GetRawArgument(uint32_t index) const {
static_assert(sizeof(T) <= kDoubleSize);
DCHECK_LT(index, GetRawArgumentCount());
return *reinterpret_cast<const T*>(&raw_arguments_[index].storage_);
}
static constexpr uint32_t GetRawArgumentCount() {
return arraysize(raw_arguments_);
}
bool stack_is_iterable() const {
DCHECK(stack_is_iterable_ == 0 || stack_is_iterable_ == 1);
return stack_is_iterable_ != 0;
}
bool is_marking() const { return is_marking_flag_; }
// Returns true if this address points to data stored in this instance. If
// it's the case then the value can be accessed indirectly through the root
// register.
bool contains(Address address) const {
static_assert(std::is_unsigned_v<Address>);
Address start = reinterpret_cast<Address>(this);
return (address - start) < sizeof(*this);
}
static constexpr int32_t GetOffset(IsolateFieldId id) {
switch (id) {
#define CASE(CamelName, size, name) \
case IsolateFieldId::k##CamelName: \
return name##_offset();
ISOLATE_DATA_FIELDS(CASE)
#undef CASE
#define CASE(CamelName, hacker_name, ...) \
case IsolateFieldId::k##CamelName: \
return hacker_name##_offset();
ISOLATE_DATA_SUBFIELDS(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
Address GetAddress(IsolateFieldId id) const {
switch (id) {
#define CASE(CamelName, size, name) \
case IsolateFieldId::k##CamelName: \
return name##_address();
ISOLATE_DATA_FIELDS(CASE)
#undef CASE
#define CASE(CamelName, hacker_name, ...) \
case IsolateFieldId::k##CamelName: \
return hacker_name##_address();
ISOLATE_DATA_SUBFIELDS(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
private:
// Static layout definition.
//
// Note: The location of fields within IsolateData is significant. The
// closer they are to the value of kRootRegister (i.e.: isolate_root()), the
// cheaper it is to access them. See also: https://crbug.com/993264.
// The recommended guideline is to put frequently-accessed fields close to
// the beginning of IsolateData.
#define FIELDS(V) \
ISOLATE_DATA_FIELDS(V) \
/* This padding aligns IsolateData size by 8 bytes. */ \
PADDING_FIELD(8, V, TrailingPadding, trailing_padding) \
/* Total size. */ \
V(Size, 0)
DEFINE_FIELD_OFFSET_CONSTANTS_WITH_PURE_NAME(0, FIELDS)
#undef FIELDS
const Address cage_base_ = kNullAddress;
// Fields related to the system and JS stack. In particular, this contains
// the stack limit used by stack checks in generated code.
StackGuard stack_guard_;
//
// Hot flags that are regularly checked.
//
// These flags are regularly checked by write barriers.
// Only valid values are 0 or 1.
uint8_t is_marking_flag_ = false;
uint8_t is_minor_marking_flag_ = false;
uint8_t is_shared_space_isolate_flag_ = false;
uint8_t uses_shared_heap_flag_ = false;
// Storage for is_profiling and should_check_side_effects booleans.
// This value is checked on every API callback/getter call.
base::Flags<IsolateExecutionModeFlag, uint8_t, std::atomic<uint8_t>>
execution_mode_ = {IsolateExecutionModeFlag::kNoFlags};
static_assert(sizeof(execution_mode_) == 1);
//
// Not super hot flags, which are put here because we have to align the
// builtin entry table to kSystemPointerSize anyway.
//
// Whether the StackFrameIteratorForProfiler can successfully iterate the
// current stack. The only valid values are 0 or 1.
uint8_t stack_is_iterable_ = 1;
// Field to pass value for error throwing builtins. Currently, it is used to
// pass the type of the `Dataview` operation to print out operation's name in
// case of an error.
uint8_t error_message_param_;
// Whether we are using SetPrototypeProperties together with LazyClosures.
uint8_t has_lazy_closures_ = 0;
// Ensure the following tables are kSystemPointerSize-byte aligned.
V8_NO_UNIQUE_ADDRESS uint8_t
tables_alignment_padding_[kTablesAlignmentPaddingSize];
// A pointer to the static offsets vector (used to pass results from the
// irregexp engine to the rest of V8), or nullptr if the static offsets
// vector is currently in use.
int32_t* regexp_static_result_offsets_vector_ = nullptr;
// Tier 0 tables. See also builtin_entry_table_ and builtin_table_.
Address builtin_tier0_entry_table_[Builtins::kBuiltinTier0Count] = {};
Address builtin_tier0_table_[Builtins::kBuiltinTier0Count] = {};
LinearAllocationArea new_allocation_info_;
LinearAllocationArea old_allocation_info_;
Address last_young_allocation_;
// Aligns fast_c_call_XXX fields so that they stay in the same CPU cache line.
Address fast_c_call_alignment_padding_[kFastCCallAlignmentPaddingCount];
// Stores the state of the caller for MacroAssembler::CallCFunction so that
// the sampling CPU profiler can iterate the stack during such calls. These
// are stored on IsolateData so that they can be stored to with only one move
// instruction in compiled code.
// Note that the PC field is right before FP. This is necessary for simulator
// builds for ARM64. This ensures that the PC is written before the FP with
// the stp instruction.
struct {
// The FP and PC that are saved right before MacroAssembler::CallCFunction.
Address fast_c_call_caller_pc_ = kNullAddress;
Address fast_c_call_caller_fp_ = kNullAddress;
};
// The address of the fast API callback right before it's executed from
// generated code.
Address fast_api_call_target_ = kNullAddress;
// Used for implementation of LongTaskStats. Counts the number of potential
// long tasks.
size_t long_task_stats_counter_ = 0;
ThreadLocalTop thread_local_top_;
HandleScopeData handle_scope_data_;
// These fields are accessed through the API, offsets must be kept in sync
// with v8::internal::Internals (in include/v8-internal.h) constants. The
// layout consistency is verified in Isolate::CheckIsolateLayout() using
// runtime checks.
void* embedder_data_[Internals::kNumIsolateDataSlots] = {};
// Tables containing pointers to objects outside of the V8 sandbox.
#ifdef V8_COMPRESS_POINTERS
ExternalPointerTable external_pointer_table_;
ExternalPointerTable* shared_external_pointer_table_ = nullptr;
CppHeapPointerTable cpp_heap_pointer_table_;
#endif // V8_COMPRESS_POINTERS
#ifdef V8_ENABLE_SANDBOX
const Address trusted_cage_base_;
TrustedPointerTable trusted_pointer_table_;
TrustedPointerTable* shared_trusted_pointer_table_ = nullptr;
TrustedPointerPublishingScope* trusted_pointer_publishing_scope_ = nullptr;
const Address code_pointer_table_base_address_;
#endif // V8_ENABLE_SANDBOX
JSDispatchTable js_dispatch_table_;
// This is a storage for an additional argument for the Api callback thunk
// functions, see InvokeAccessorGetterCallback and InvokeFunctionCallback.
Address api_callback_thunk_argument_ = kNullAddress;
// Storage for an additional (untagged) argument for
// Runtime::kRegExpExecInternal2, required since runtime functions only
// accept tagged arguments.
Address regexp_exec_vector_argument_ = kNullAddress;
// This is data that should be preserved on newly created continuations.
Tagged<Object> continuation_preserved_embedder_data_ = Smi::zero();
RootsTable roots_table_;
ExternalReferenceTable external_reference_table_;
// The entry points for builtins. This corresponds to
// InstructionStream::InstructionStart() for each InstructionStream object in
// the builtins table below. The entry table is in IsolateData for easy access
// through kRootRegister.
Address builtin_entry_table_[Builtins::kBuiltinCount] = {};
// The entries in this array are tagged pointers to Code objects.
Address builtin_table_[Builtins::kBuiltinCount] = {};
#if V8_ENABLE_WEBASSEMBLY
wasm::StackMemory* active_stack_ = nullptr;
Tagged<WasmSuspenderObject> active_suspender_;
#endif
// Stamp value which is increased on every
// v8::Isolate::DateTimeConfigurationChangeNotification(..).
int32_t date_cache_stamp_ = 0;
// Boolean value indicating that DateCache is used (i.e. JSDate instances
// were created in this Isolate).
uint8_t is_date_cache_used_ = false;
// Padding for aligning raw_arguments_.
V8_NO_UNIQUE_ADDRESS uint8_t raw_arguments_padding_[kRawArgumentsPaddingSize];
// Storage for raw values passed from CSA/Torque to runtime functions.
struct RawArgument {
uint8_t storage_[kDoubleSize];
} raw_arguments_[2] = {};
// Counts deopt points if deopt_every_n_times is enabled.
uint64_t stress_deopt_count_ = 0;
#if !V8_STATIC_DISPATCH_HANDLES_BOOL
// The entries in this array are dispatch handles for builtins with SFI's.
JSDispatchHandle* builtin_dispatch_table() { return builtin_dispatch_table_; }
JSDispatchHandle
builtin_dispatch_table_[JSBuiltinDispatchHandleRoot::kTableSize] = {};
#endif // !V8_STATIC_DISPATCH_HANDLES_BOOL
// Ensure the size is 8-byte aligned in order to make alignment of the field
// following the IsolateData field predictable. This solves the issue with
// C++ compilers for 32-bit platforms which are not consistent at aligning
// int64_t fields.
V8_NO_UNIQUE_ADDRESS uint8_t trailing_padding_[kTrailingPaddingSize];
V8_INLINE static void AssertPredictableLayout();
friend class Isolate;
friend class Heap;
FRIEND_TEST(HeapTest, ExternalLimitDefault);
FRIEND_TEST(HeapTest, ExternalLimitStaysAboveDefaultForExplicitHandling);
};
// IsolateData object must have "predictable" layout which does not change when
// cross-compiling to another platform. Otherwise there may be compatibility
// issues because of different compilers used for snapshot generator and
// actual V8 code.
void IsolateData::AssertPredictableLayout() {
static_assert(std::is_standard_layout_v<StackGuard>);
static_assert(std::is_standard_layout_v<RootsTable>);
static_assert(std::is_standard_layout_v<ThreadLocalTop>);
static_assert(std::is_standard_layout_v<ExternalReferenceTable>);
static_assert(std::is_standard_layout_v<IsolateData>);
static_assert(std::is_standard_layout_v<LinearAllocationArea>);
#define V(PureName, Size, Name) \
static_assert(std::is_standard_layout_v<decltype(IsolateData::Name##_)>); \
static_assert(offsetof(IsolateData, Name##_) == k##PureName##Offset);
ISOLATE_DATA_FIELDS(V)
#undef V
static_assert(sizeof(IsolateData) == IsolateData::kSizeOffset);
}
} // namespace internal
} // namespace v8
#endif // V8_EXECUTION_ISOLATE_DATA_H_