summaryrefslogtreecommitdiff
path: root/deps/v8/src/execution/isolate-data.h
blob: 6eb23db2a22adb306e2188dd05bad8a2d66d8767 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
// 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/stack-guard.h"
#include "src/execution/thread-local-top.h"
#include "src/roots/roots.h"
#include "src/utils/utils.h"
#include "testing/gtest/include/gtest/gtest_prod.h"

namespace v8 {
namespace internal {

class Isolate;

// This class contains a collection of data accessible from both C++ runtime
// and compiled code (including assembly stubs, builtins, interpreter bytecode
// handlers and optimized code).
// In particular, it contains pointer to the V8 heap roots table, external
// reference table and builtins array.
// The compiled code accesses the isolate data fields indirectly via the root
// register.
class IsolateData final {
 public:
  explicit IsolateData(Isolate* isolate) : stack_guard_(isolate) {}

  static constexpr intptr_t kIsolateRootBias = kRootRegisterBias;

  // The value of the kRootRegister.
  Address isolate_root() const {
    return reinterpret_cast<Address>(this) + kIsolateRootBias;
  }

  // Root-register-relative offset of the roots table.
  static constexpr int roots_table_offset() {
    return kRootsTableOffset - kIsolateRootBias;
  }

  // Root-register-relative offset of the given root table entry.
  static constexpr int root_slot_offset(RootIndex root_index) {
    return roots_table_offset() + RootsTable::offset_of(root_index);
  }

  // Root-register-relative offset of the external reference table.
  static constexpr int external_reference_table_offset() {
    return kExternalReferenceTableOffset - kIsolateRootBias;
  }

  // Root-register-relative offset of the builtin entry table.
  static constexpr int builtin_entry_table_offset() {
    return kBuiltinEntryTableOffset - kIsolateRootBias;
  }

  // Root-register-relative offset of the builtins table.
  static constexpr int builtins_table_offset() {
    return kBuiltinsTableOffset - kIsolateRootBias;
  }

  // Root-register-relative offset of the given builtin table entry.
  // TODO(ishell): remove in favour of typified id version.
  static int builtin_slot_offset(int builtin_index) {
    DCHECK(Builtins::IsBuiltinId(builtin_index));
    return builtins_table_offset() + builtin_index * kSystemPointerSize;
  }

  // Root-register-relative offset of the builtin table entry.
  static int builtin_slot_offset(Builtins::Name id) {
    return builtins_table_offset() + id * kSystemPointerSize;
  }

  // Root-register-relative offset of the virtual call target register value.
  static constexpr int virtual_call_target_register_offset() {
    return kVirtualCallTargetRegisterOffset - kIsolateRootBias;
  }

  // The FP and PC that are saved right before TurboAssembler::CallCFunction.
  Address* fast_c_call_caller_fp_address() { return &fast_c_call_caller_fp_; }
  Address* fast_c_call_caller_pc_address() { return &fast_c_call_caller_pc_; }
  StackGuard* stack_guard() { return &stack_guard_; }
  uint8_t* stack_is_iterable_address() { return &stack_is_iterable_; }
  Address fast_c_call_caller_fp() { return fast_c_call_caller_fp_; }
  Address fast_c_call_caller_pc() { return fast_c_call_caller_pc_; }
  uint8_t stack_is_iterable() { return stack_is_iterable_; }

  // 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<Address>::value);
    Address start = reinterpret_cast<Address>(this);
    return (address - start) < sizeof(*this);
  }

  ThreadLocalTop& thread_local_top() { return thread_local_top_; }
  ThreadLocalTop const& thread_local_top() const { return thread_local_top_; }

  RootsTable& roots() { return roots_; }
  const RootsTable& roots() const { return roots_; }

  ExternalReferenceTable* external_reference_table() {
    return &external_reference_table_;
  }

  Address* builtin_entry_table() { return builtin_entry_table_; }
  Address* builtins() { return builtins_; }

 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 recommend guideline is to put frequently-accessed fields close to the
  // beginning of IsolateData.
#define FIELDS(V)                                                             \
  V(kEmbedderDataOffset, Internals::kNumIsolateDataSlots* kSystemPointerSize) \
  V(kExternalMemoryOffset, kInt64Size)                                        \
  V(kExternalMemoryLlimitOffset, kInt64Size)                                  \
  V(kExternalMemoryAtLastMarkCompactOffset, kInt64Size)                       \
  V(kFastCCallCallerFPOffset, kSystemPointerSize)                             \
  V(kFastCCallCallerPCOffset, kSystemPointerSize)                             \
  V(kStackGuardOffset, StackGuard::kSizeInBytes)                              \
  V(kRootsTableOffset, RootsTable::kEntriesCount* kSystemPointerSize)         \
  V(kExternalReferenceTableOffset, ExternalReferenceTable::kSizeInBytes)      \
  V(kThreadLocalTopOffset, ThreadLocalTop::kSizeInBytes)                      \
  V(kBuiltinEntryTableOffset, Builtins::builtin_count* kSystemPointerSize)    \
  V(kBuiltinsTableOffset, Builtins::builtin_count* kSystemPointerSize)        \
  V(kVirtualCallTargetRegisterOffset, kSystemPointerSize)                     \
  V(kStackIsIterableOffset, kUInt8Size)                                       \
  /* This padding aligns IsolateData size by 8 bytes. */                      \
  V(kPaddingOffset,                                                           \
    8 + RoundUp<8>(static_cast<int>(kPaddingOffset)) - kPaddingOffset)        \
  /* Total size. */                                                           \
  V(kSize, 0)

  DEFINE_FIELD_OFFSET_CONSTANTS(0, FIELDS)
#undef FIELDS

  // 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 consitency is verified in Isolate::CheckIsolateLayout() using
  // runtime checks.
  void* embedder_data_[Internals::kNumIsolateDataSlots] = {};

  // TODO(ishell): Move these external memory counters back to Heap once the
  // Node JS bot issue is solved.
  // The amount of external memory registered through the API.
  int64_t external_memory_ = 0;

  // The limit when to trigger memory pressure from the API.
  int64_t external_memory_limit_ = kExternalAllocationSoftLimit;

  // Caches the amount of external memory registered at the last MC.
  int64_t external_memory_at_last_mark_compact_ = 0;

  // Stores the state of the caller for TurboAssembler::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.
  Address fast_c_call_caller_fp_ = kNullAddress;
  Address fast_c_call_caller_pc_ = 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_;

  RootsTable roots_;

  ExternalReferenceTable external_reference_table_;

  ThreadLocalTop thread_local_top_;

  // The entry points for all builtins. This corresponds to
  // Code::InstructionStart() for each Code object in the builtins table below.
  // The entry table is in IsolateData for easy access through kRootRegister.
  Address builtin_entry_table_[Builtins::builtin_count] = {};

  // The entries in this array are tagged pointers to Code objects.
  Address builtins_[Builtins::builtin_count] = {};

  // For isolate-independent calls on ia32.
  // TODO(v8:6666): Remove once wasm supports pc-relative jumps to builtins on
  // ia32 (otherwise the arguments adaptor call runs out of registers).
  void* virtual_call_target_register_ = nullptr;

  // Whether the SafeStackFrameIterator can successfully iterate the current
  // stack. Only valid values are 0 or 1.
  uint8_t stack_is_iterable_ = 1;

  // 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.
  // In order to avoid dealing with zero-size arrays the padding size is always
  // in the range [8, 15).
  STATIC_ASSERT(kPaddingOffsetEnd + 1 - kPaddingOffset >= 8);
  char padding_[kPaddingOffsetEnd + 1 - kPaddingOffset];

  V8_INLINE static void AssertPredictableLayout();

  friend class Isolate;
  friend class Heap;
  FRIEND_TEST(HeapTest, ExternalLimitDefault);
  FRIEND_TEST(HeapTest, ExternalLimitStaysAboveDefaultForExplicitHandling);

  DISALLOW_COPY_AND_ASSIGN(IsolateData);
};

// 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<RootsTable>::value);
  STATIC_ASSERT(std::is_standard_layout<ThreadLocalTop>::value);
  STATIC_ASSERT(std::is_standard_layout<ExternalReferenceTable>::value);
  STATIC_ASSERT(std::is_standard_layout<IsolateData>::value);
  STATIC_ASSERT(offsetof(IsolateData, roots_) == kRootsTableOffset);
  STATIC_ASSERT(offsetof(IsolateData, external_reference_table_) ==
                kExternalReferenceTableOffset);
  STATIC_ASSERT(offsetof(IsolateData, thread_local_top_) ==
                kThreadLocalTopOffset);
  STATIC_ASSERT(offsetof(IsolateData, builtins_) == kBuiltinsTableOffset);
  STATIC_ASSERT(offsetof(IsolateData, virtual_call_target_register_) ==
                kVirtualCallTargetRegisterOffset);
  STATIC_ASSERT(offsetof(IsolateData, external_memory_) ==
                kExternalMemoryOffset);
  STATIC_ASSERT(offsetof(IsolateData, external_memory_limit_) ==
                kExternalMemoryLlimitOffset);
  STATIC_ASSERT(offsetof(IsolateData, external_memory_at_last_mark_compact_) ==
                kExternalMemoryAtLastMarkCompactOffset);
  STATIC_ASSERT(offsetof(IsolateData, fast_c_call_caller_fp_) ==
                kFastCCallCallerFPOffset);
  STATIC_ASSERT(offsetof(IsolateData, fast_c_call_caller_pc_) ==
                kFastCCallCallerPCOffset);
  STATIC_ASSERT(offsetof(IsolateData, stack_guard_) == kStackGuardOffset);
  STATIC_ASSERT(offsetof(IsolateData, stack_is_iterable_) ==
                kStackIsIterableOffset);
  STATIC_ASSERT(sizeof(IsolateData) == IsolateData::kSize);
}

}  // namespace internal
}  // namespace v8

#endif  // V8_EXECUTION_ISOLATE_DATA_H_