1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef V8_EXECUTION_ISOLATE_DATA_H_
6 #define V8_EXECUTION_ISOLATE_DATA_H_
7
8 #include "src/builtins/builtins.h"
9 #include "src/codegen/constants-arch.h"
10 #include "src/codegen/external-reference-table.h"
11 #include "src/execution/stack-guard.h"
12 #include "src/execution/thread-local-top.h"
13 #include "src/heap/linear-allocation-area.h"
14 #include "src/roots/roots.h"
15 #include "src/sandbox/external-pointer-table.h"
16 #include "src/utils/utils.h"
17 #include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck
18
19 namespace v8 {
20 namespace internal {
21
22 class Isolate;
23
24 // IsolateData fields, defined as: V(Offset, Size, Name)
25 #define ISOLATE_DATA_FIELDS(V) \
26 /* Misc. fields. */ \
27 V(kCageBaseOffset, kSystemPointerSize, cage_base) \
28 V(kStackGuardOffset, StackGuard::kSizeInBytes, stack_guard) \
29 /* Tier 0 tables (small but fast access). */ \
30 V(kBuiltinTier0EntryTableOffset, \
31 Builtins::kBuiltinTier0Count* kSystemPointerSize, \
32 builtin_tier0_entry_table) \
33 V(kBuiltinsTier0TableOffset, \
34 Builtins::kBuiltinTier0Count* kSystemPointerSize, builtin_tier0_table) \
35 /* Misc. fields. */ \
36 V(kEmbedderDataOffset, Internals::kNumIsolateDataSlots* kSystemPointerSize, \
37 embedder_data) \
38 V(kFastCCallCallerFPOffset, kSystemPointerSize, fast_c_call_caller_fp) \
39 V(kFastCCallCallerPCOffset, kSystemPointerSize, fast_c_call_caller_pc) \
40 V(kFastApiCallTargetOffset, kSystemPointerSize, fast_api_call_target) \
41 V(kLongTaskStatsCounterOffset, kSizetSize, long_task_stats_counter) \
42 /* Full tables (arbitrary size, potentially slower access). */ \
43 V(kRootsTableOffset, RootsTable::kEntriesCount* kSystemPointerSize, \
44 roots_table) \
45 V(kExternalReferenceTableOffset, ExternalReferenceTable::kSizeInBytes, \
46 external_reference_table) \
47 V(kThreadLocalTopOffset, ThreadLocalTop::kSizeInBytes, thread_local_top) \
48 V(kBuiltinEntryTableOffset, Builtins::kBuiltinCount* kSystemPointerSize, \
49 builtin_entry_table) \
50 V(kBuiltinTableOffset, Builtins::kBuiltinCount* kSystemPointerSize, \
51 builtin_table) \
52 /* Linear allocation areas for the heap's new and old space */ \
53 V(kNewAllocationInfo, LinearAllocationArea::kSize, new_allocation_info) \
54 V(kOldAllocationInfo, LinearAllocationArea::kSize, old_allocation_info) \
55 ISOLATE_DATA_FIELDS_SANDBOXED_EXTERNAL_POINTERS(V) \
56 V(kStackIsIterableOffset, kUInt8Size, stack_is_iterable)
57
58 #ifdef V8_SANDBOXED_EXTERNAL_POINTERS
59 #define ISOLATE_DATA_FIELDS_SANDBOXED_EXTERNAL_POINTERS(V) \
60 V(kExternalPointerTableOffset, ExternalPointerTable::kSize, \
61 external_pointer_table)
62 #else
63 #define ISOLATE_DATA_FIELDS_SANDBOXED_EXTERNAL_POINTERS(V)
64 #endif // V8_SANDBOXED_EXTERNAL_POINTERS
65
66 // This class contains a collection of data accessible from both C++ runtime
67 // and compiled code (including builtins, interpreter bytecode handlers and
68 // optimized code). The compiled code accesses the isolate data fields
69 // indirectly via the root register.
70 class IsolateData final {
71 public:
IsolateData(Isolate * isolate,Address cage_base)72 IsolateData(Isolate* isolate, Address cage_base)
73 : cage_base_(cage_base), stack_guard_(isolate) {}
74
75 IsolateData(const IsolateData&) = delete;
76 IsolateData& operator=(const IsolateData&) = delete;
77
78 static constexpr intptr_t kIsolateRootBias = kRootRegisterBias;
79
80 // The value of the kRootRegister.
isolate_root()81 Address isolate_root() const {
82 return reinterpret_cast<Address>(this) + kIsolateRootBias;
83 }
84
85 // Root-register-relative offsets.
86
87 #define V(Offset, Size, Name) \
88 static constexpr int Name##_offset() { return Offset - kIsolateRootBias; }
ISOLATE_DATA_FIELDS(V)89 ISOLATE_DATA_FIELDS(V)
90 #undef V
91
92 static constexpr int root_slot_offset(RootIndex root_index) {
93 return roots_table_offset() + RootsTable::offset_of(root_index);
94 }
95
BuiltinEntrySlotOffset(Builtin id)96 static constexpr int BuiltinEntrySlotOffset(Builtin id) {
97 DCHECK(Builtins::IsBuiltinId(id));
98 return (Builtins::IsTier0(id) ? builtin_tier0_entry_table_offset()
99 : builtin_entry_table_offset()) +
100 Builtins::ToInt(id) * kSystemPointerSize;
101 }
102 // TODO(ishell): remove in favour of typified id version.
builtin_slot_offset(int builtin_index)103 static constexpr int builtin_slot_offset(int builtin_index) {
104 return BuiltinSlotOffset(Builtins::FromInt(builtin_index));
105 }
BuiltinSlotOffset(Builtin id)106 static constexpr int BuiltinSlotOffset(Builtin id) {
107 return (Builtins::IsTier0(id) ? builtin_tier0_table_offset()
108 : builtin_table_offset()) +
109 Builtins::ToInt(id) * kSystemPointerSize;
110 }
111
112 #define V(Offset, Size, Name) \
113 Address Name##_address() { return reinterpret_cast<Address>(&Name##_); }
ISOLATE_DATA_FIELDS(V)114 ISOLATE_DATA_FIELDS(V)
115 #undef V
116
117 Address fast_c_call_caller_fp() const { return fast_c_call_caller_fp_; }
fast_c_call_caller_pc()118 Address fast_c_call_caller_pc() const { return fast_c_call_caller_pc_; }
fast_api_call_target()119 Address fast_api_call_target() const { return fast_api_call_target_; }
120 // The value of kPointerCageBaseRegister.
cage_base()121 Address cage_base() const { return cage_base_; }
stack_guard()122 StackGuard* stack_guard() { return &stack_guard_; }
builtin_tier0_entry_table()123 Address* builtin_tier0_entry_table() { return builtin_tier0_entry_table_; }
builtin_tier0_table()124 Address* builtin_tier0_table() { return builtin_tier0_table_; }
roots()125 RootsTable& roots() { return roots_table_; }
roots()126 const RootsTable& roots() const { return roots_table_; }
external_reference_table()127 ExternalReferenceTable* external_reference_table() {
128 return &external_reference_table_;
129 }
thread_local_top()130 ThreadLocalTop& thread_local_top() { return thread_local_top_; }
thread_local_top()131 ThreadLocalTop const& thread_local_top() const { return thread_local_top_; }
builtin_entry_table()132 Address* builtin_entry_table() { return builtin_entry_table_; }
builtin_table()133 Address* builtin_table() { return builtin_table_; }
stack_is_iterable()134 uint8_t stack_is_iterable() const { return stack_is_iterable_; }
135
136 // Returns true if this address points to data stored in this instance. If
137 // it's the case then the value can be accessed indirectly through the root
138 // register.
contains(Address address)139 bool contains(Address address) const {
140 STATIC_ASSERT(std::is_unsigned<Address>::value);
141 Address start = reinterpret_cast<Address>(this);
142 return (address - start) < sizeof(*this);
143 }
144
145 private:
146 // Static layout definition.
147 //
148 // Note: The location of fields within IsolateData is significant. The
149 // closer they are to the value of kRootRegister (i.e.: isolate_root()), the
150 // cheaper it is to access them. See also: https://crbug.com/993264.
151 // The recommended guideline is to put frequently-accessed fields close to
152 // the beginning of IsolateData.
153 #define FIELDS(V) \
154 ISOLATE_DATA_FIELDS(V) \
155 /* This padding aligns IsolateData size by 8 bytes. */ \
156 V(kPaddingOffset, \
157 8 + RoundUp<8>(static_cast<int>(kPaddingOffset)) - kPaddingOffset) \
158 /* Total size. */ \
159 V(kSize, 0)
160
161 DEFINE_FIELD_OFFSET_CONSTANTS(0, FIELDS)
162 #undef FIELDS
163
164 const Address cage_base_;
165
166 // Fields related to the system and JS stack. In particular, this contains
167 // the stack limit used by stack checks in generated code.
168 StackGuard stack_guard_;
169
170 // Tier 0 tables. See also builtin_entry_table_ and builtin_table_.
171 Address builtin_tier0_entry_table_[Builtins::kBuiltinTier0Count] = {};
172 Address builtin_tier0_table_[Builtins::kBuiltinTier0Count] = {};
173
174 // These fields are accessed through the API, offsets must be kept in sync
175 // with v8::internal::Internals (in include/v8-internal.h) constants. The
176 // layout consistency is verified in Isolate::CheckIsolateLayout() using
177 // runtime checks.
178 void* embedder_data_[Internals::kNumIsolateDataSlots] = {};
179
180 // Stores the state of the caller for TurboAssembler::CallCFunction so that
181 // the sampling CPU profiler can iterate the stack during such calls. These
182 // are stored on IsolateData so that they can be stored to with only one move
183 // instruction in compiled code.
184 //
185 // The FP and PC that are saved right before TurboAssembler::CallCFunction.
186 Address fast_c_call_caller_fp_ = kNullAddress;
187 Address fast_c_call_caller_pc_ = kNullAddress;
188 // The address of the fast API callback right before it's executed from
189 // generated code.
190 Address fast_api_call_target_ = kNullAddress;
191
192 // Used for implementation of LongTaskStats. Counts the number of potential
193 // long tasks.
194 size_t long_task_stats_counter_ = 0;
195
196 RootsTable roots_table_;
197 ExternalReferenceTable external_reference_table_;
198
199 ThreadLocalTop thread_local_top_;
200
201 // The entry points for builtins. This corresponds to
202 // Code::InstructionStart() for each Code object in the builtins table below.
203 // The entry table is in IsolateData for easy access through kRootRegister.
204 Address builtin_entry_table_[Builtins::kBuiltinCount] = {};
205
206 // The entries in this array are tagged pointers to Code objects.
207 Address builtin_table_[Builtins::kBuiltinCount] = {};
208
209 LinearAllocationArea new_allocation_info_;
210 LinearAllocationArea old_allocation_info_;
211
212 // Table containing pointers to external objects.
213 #ifdef V8_SANDBOXED_EXTERNAL_POINTERS
214 ExternalPointerTable external_pointer_table_;
215 #endif
216
217 // Whether the SafeStackFrameIterator can successfully iterate the current
218 // stack. Only valid values are 0 or 1.
219 uint8_t stack_is_iterable_ = 1;
220
221 // Ensure the size is 8-byte aligned in order to make alignment of the field
222 // following the IsolateData field predictable. This solves the issue with
223 // C++ compilers for 32-bit platforms which are not consistent at aligning
224 // int64_t fields.
225 // In order to avoid dealing with zero-size arrays the padding size is always
226 // in the range [8, 15).
227 STATIC_ASSERT(kPaddingOffsetEnd + 1 - kPaddingOffset >= 8);
228 char padding_[kPaddingOffsetEnd + 1 - kPaddingOffset];
229
230 V8_INLINE static void AssertPredictableLayout();
231
232 friend class Isolate;
233 friend class Heap;
234 FRIEND_TEST(HeapTest, ExternalLimitDefault);
235 FRIEND_TEST(HeapTest, ExternalLimitStaysAboveDefaultForExplicitHandling);
236 };
237
238 // IsolateData object must have "predictable" layout which does not change when
239 // cross-compiling to another platform. Otherwise there may be compatibility
240 // issues because of different compilers used for snapshot generator and
241 // actual V8 code.
AssertPredictableLayout()242 void IsolateData::AssertPredictableLayout() {
243 STATIC_ASSERT(std::is_standard_layout<RootsTable>::value);
244 STATIC_ASSERT(std::is_standard_layout<ThreadLocalTop>::value);
245 STATIC_ASSERT(std::is_standard_layout<ExternalReferenceTable>::value);
246 STATIC_ASSERT(std::is_standard_layout<IsolateData>::value);
247 #define V(Offset, Size, Name) \
248 STATIC_ASSERT(offsetof(IsolateData, Name##_) == Offset);
249 ISOLATE_DATA_FIELDS(V)
250 #undef V
251 STATIC_ASSERT(sizeof(IsolateData) == IsolateData::kSize);
252 }
253
254 #undef ISOLATE_DATA_FIELDS_SANDBOXED_EXTERNAL_POINTERS
255 #undef ISOLATE_DATA_FIELDS
256
257 } // namespace internal
258 } // namespace v8
259
260 #endif // V8_EXECUTION_ISOLATE_DATA_H_
261