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/external-pointer-table.h"
12 #include "src/execution/stack-guard.h"
13 #include "src/execution/thread-local-top.h"
14 #include "src/roots/roots.h"
15 #include "src/utils/utils.h"
16 #include "testing/gtest/include/gtest/gtest_prod.h"
17
18 namespace v8 {
19 namespace internal {
20
21 class Isolate;
22
23 // This class contains a collection of data accessible from both C++ runtime
24 // and compiled code (including assembly stubs, builtins, interpreter bytecode
25 // handlers and optimized code).
26 // In particular, it contains pointer to the V8 heap roots table, external
27 // reference table and builtins array.
28 // The compiled code accesses the isolate data fields indirectly via the root
29 // register.
30 class IsolateData final {
31 public:
IsolateData(Isolate * isolate)32 explicit IsolateData(Isolate* isolate) : stack_guard_(isolate) {}
33
34 static constexpr intptr_t kIsolateRootBias = kRootRegisterBias;
35
36 // The value of the kRootRegister.
isolate_root()37 Address isolate_root() const {
38 return reinterpret_cast<Address>(this) + kIsolateRootBias;
39 }
40
41 // Root-register-relative offset of the roots table.
roots_table_offset()42 static constexpr int roots_table_offset() {
43 return kRootsTableOffset - kIsolateRootBias;
44 }
45
46 // Root-register-relative offset of the given root table entry.
root_slot_offset(RootIndex root_index)47 static constexpr int root_slot_offset(RootIndex root_index) {
48 return roots_table_offset() + RootsTable::offset_of(root_index);
49 }
50
51 // Root-register-relative offset of the external reference table.
external_reference_table_offset()52 static constexpr int external_reference_table_offset() {
53 return kExternalReferenceTableOffset - kIsolateRootBias;
54 }
55
56 // Root-register-relative offset of the builtin entry table.
builtin_entry_table_offset()57 static constexpr int builtin_entry_table_offset() {
58 return kBuiltinEntryTableOffset - kIsolateRootBias;
59 }
builtin_entry_slot_offset(Builtins::Name builtin_index)60 static constexpr int builtin_entry_slot_offset(Builtins::Name builtin_index) {
61 CONSTEXPR_DCHECK(Builtins::IsBuiltinId(builtin_index));
62 return builtin_entry_table_offset() + builtin_index * kSystemPointerSize;
63 }
64
65 // Root-register-relative offset of the builtins table.
builtins_table_offset()66 static constexpr int builtins_table_offset() {
67 return kBuiltinsTableOffset - kIsolateRootBias;
68 }
69
fast_c_call_caller_fp_offset()70 static constexpr int fast_c_call_caller_fp_offset() {
71 return kFastCCallCallerFPOffset - kIsolateRootBias;
72 }
73
fast_c_call_caller_pc_offset()74 static constexpr int fast_c_call_caller_pc_offset() {
75 return kFastCCallCallerPCOffset - kIsolateRootBias;
76 }
77
78 // Root-register-relative offset of the given builtin table entry.
79 // TODO(ishell): remove in favour of typified id version.
builtin_slot_offset(int builtin_index)80 static int builtin_slot_offset(int builtin_index) {
81 DCHECK(Builtins::IsBuiltinId(builtin_index));
82 return builtins_table_offset() + builtin_index * kSystemPointerSize;
83 }
84
85 // Root-register-relative offset of the builtin table entry.
builtin_slot_offset(Builtins::Name id)86 static int builtin_slot_offset(Builtins::Name id) {
87 return builtins_table_offset() + id * kSystemPointerSize;
88 }
89
90 // The FP and PC that are saved right before TurboAssembler::CallCFunction.
fast_c_call_caller_fp_address()91 Address* fast_c_call_caller_fp_address() { return &fast_c_call_caller_fp_; }
fast_c_call_caller_pc_address()92 Address* fast_c_call_caller_pc_address() { return &fast_c_call_caller_pc_; }
stack_guard()93 StackGuard* stack_guard() { return &stack_guard_; }
stack_is_iterable_address()94 uint8_t* stack_is_iterable_address() { return &stack_is_iterable_; }
fast_c_call_caller_fp()95 Address fast_c_call_caller_fp() { return fast_c_call_caller_fp_; }
fast_c_call_caller_pc()96 Address fast_c_call_caller_pc() { return fast_c_call_caller_pc_; }
stack_is_iterable()97 uint8_t stack_is_iterable() { return stack_is_iterable_; }
98
99 // Returns true if this address points to data stored in this instance.
100 // If it's the case then the value can be accessed indirectly through the
101 // root register.
contains(Address address)102 bool contains(Address address) const {
103 STATIC_ASSERT(std::is_unsigned<Address>::value);
104 Address start = reinterpret_cast<Address>(this);
105 return (address - start) < sizeof(*this);
106 }
107
thread_local_top()108 ThreadLocalTop& thread_local_top() { return thread_local_top_; }
thread_local_top()109 ThreadLocalTop const& thread_local_top() const { return thread_local_top_; }
110
roots()111 RootsTable& roots() { return roots_; }
roots()112 const RootsTable& roots() const { return roots_; }
113
external_reference_table()114 ExternalReferenceTable* external_reference_table() {
115 return &external_reference_table_;
116 }
117
builtin_entry_table()118 Address* builtin_entry_table() { return builtin_entry_table_; }
builtins()119 Address* builtins() { return builtins_; }
120
121 private:
122 // Static layout definition.
123 //
124 // Note: The location of fields within IsolateData is significant. The
125 // closer they are to the value of kRootRegister (i.e.: isolate_root()), the
126 // cheaper it is to access them. See also: https://crbug.com/993264.
127 // The recommend guideline is to put frequently-accessed fields close to the
128 // beginning of IsolateData.
129 #define FIELDS(V) \
130 V(kEmbedderDataOffset, Internals::kNumIsolateDataSlots* kSystemPointerSize) \
131 V(kFastCCallCallerFPOffset, kSystemPointerSize) \
132 V(kFastCCallCallerPCOffset, kSystemPointerSize) \
133 V(kStackGuardOffset, StackGuard::kSizeInBytes) \
134 V(kRootsTableOffset, RootsTable::kEntriesCount* kSystemPointerSize) \
135 V(kExternalReferenceTableOffset, ExternalReferenceTable::kSizeInBytes) \
136 V(kThreadLocalTopOffset, ThreadLocalTop::kSizeInBytes) \
137 V(kBuiltinEntryTableOffset, Builtins::builtin_count* kSystemPointerSize) \
138 V(kBuiltinsTableOffset, Builtins::builtin_count* kSystemPointerSize) \
139 FIELDS_HEAP_SANDBOX(V) \
140 V(kStackIsIterableOffset, kUInt8Size) \
141 /* This padding aligns IsolateData size by 8 bytes. */ \
142 V(kPaddingOffset, \
143 8 + RoundUp<8>(static_cast<int>(kPaddingOffset)) - kPaddingOffset) \
144 /* Total size. */ \
145 V(kSize, 0)
146
147 #ifdef V8_HEAP_SANDBOX
148 #define FIELDS_HEAP_SANDBOX(V) \
149 V(kExternalPointerTableOffset, kSystemPointerSize * 3)
150 #else
151 #define FIELDS_HEAP_SANDBOX(V)
152 #endif // V8_HEAP_SANDBOX
153
154 DEFINE_FIELD_OFFSET_CONSTANTS(0, FIELDS)
155 #undef FIELDS
156
157 // These fields are accessed through the API, offsets must be kept in sync
158 // with v8::internal::Internals (in include/v8-internal.h) constants.
159 // The layout consitency is verified in Isolate::CheckIsolateLayout() using
160 // runtime checks.
161 void* embedder_data_[Internals::kNumIsolateDataSlots] = {};
162
163 // Stores the state of the caller for TurboAssembler::CallCFunction so that
164 // the sampling CPU profiler can iterate the stack during such calls. These
165 // are stored on IsolateData so that they can be stored to with only one move
166 // instruction in compiled code.
167 Address fast_c_call_caller_fp_ = kNullAddress;
168 Address fast_c_call_caller_pc_ = kNullAddress;
169
170 // Fields related to the system and JS stack. In particular, this contains the
171 // stack limit used by stack checks in generated code.
172 StackGuard stack_guard_;
173
174 RootsTable roots_;
175
176 ExternalReferenceTable external_reference_table_;
177
178 ThreadLocalTop thread_local_top_;
179
180 // The entry points for all builtins. This corresponds to
181 // Code::InstructionStart() for each Code object in the builtins table below.
182 // The entry table is in IsolateData for easy access through kRootRegister.
183 Address builtin_entry_table_[Builtins::builtin_count] = {};
184
185 // The entries in this array are tagged pointers to Code objects.
186 Address builtins_[Builtins::builtin_count] = {};
187
188 // Table containing pointers to external objects.
189 #ifdef V8_HEAP_SANDBOX
190 ExternalPointerTable external_pointer_table_;
191 #endif
192
193 // Whether the SafeStackFrameIterator can successfully iterate the current
194 // stack. Only valid values are 0 or 1.
195 uint8_t stack_is_iterable_ = 1;
196
197 // Ensure the size is 8-byte aligned in order to make alignment of the field
198 // following the IsolateData field predictable. This solves the issue with
199 // C++ compilers for 32-bit platforms which are not consistent at aligning
200 // int64_t fields.
201 // In order to avoid dealing with zero-size arrays the padding size is always
202 // in the range [8, 15).
203 STATIC_ASSERT(kPaddingOffsetEnd + 1 - kPaddingOffset >= 8);
204 char padding_[kPaddingOffsetEnd + 1 - kPaddingOffset];
205
206 V8_INLINE static void AssertPredictableLayout();
207
208 friend class Isolate;
209 friend class Heap;
210 FRIEND_TEST(HeapTest, ExternalLimitDefault);
211 FRIEND_TEST(HeapTest, ExternalLimitStaysAboveDefaultForExplicitHandling);
212
213 DISALLOW_COPY_AND_ASSIGN(IsolateData);
214 };
215
216 // IsolateData object must have "predictable" layout which does not change when
217 // cross-compiling to another platform. Otherwise there may be compatibility
218 // issues because of different compilers used for snapshot generator and
219 // actual V8 code.
AssertPredictableLayout()220 void IsolateData::AssertPredictableLayout() {
221 STATIC_ASSERT(std::is_standard_layout<RootsTable>::value);
222 STATIC_ASSERT(std::is_standard_layout<ThreadLocalTop>::value);
223 STATIC_ASSERT(std::is_standard_layout<ExternalReferenceTable>::value);
224 STATIC_ASSERT(std::is_standard_layout<IsolateData>::value);
225 STATIC_ASSERT(offsetof(IsolateData, roots_) == kRootsTableOffset);
226 STATIC_ASSERT(offsetof(IsolateData, external_reference_table_) ==
227 kExternalReferenceTableOffset);
228 STATIC_ASSERT(offsetof(IsolateData, thread_local_top_) ==
229 kThreadLocalTopOffset);
230 STATIC_ASSERT(offsetof(IsolateData, builtins_) == kBuiltinsTableOffset);
231 STATIC_ASSERT(offsetof(IsolateData, fast_c_call_caller_fp_) ==
232 kFastCCallCallerFPOffset);
233 STATIC_ASSERT(offsetof(IsolateData, fast_c_call_caller_pc_) ==
234 kFastCCallCallerPCOffset);
235 STATIC_ASSERT(offsetof(IsolateData, stack_guard_) == kStackGuardOffset);
236 #ifdef V8_HEAP_SANDBOX
237 STATIC_ASSERT(offsetof(IsolateData, external_pointer_table_) ==
238 kExternalPointerTableOffset);
239 #endif
240 STATIC_ASSERT(offsetof(IsolateData, stack_is_iterable_) ==
241 kStackIsIterableOffset);
242 STATIC_ASSERT(sizeof(IsolateData) == IsolateData::kSize);
243 }
244
245 } // namespace internal
246 } // namespace v8
247
248 #endif // V8_EXECUTION_ISOLATE_DATA_H_
249