1 // Copyright 2017 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 #include "src/snapshot/builtin-deserializer-allocator.h"
6
7 #include "src/heap/heap-inl.h"
8 #include "src/interpreter/interpreter.h"
9 #include "src/snapshot/builtin-deserializer.h"
10 #include "src/snapshot/deserializer.h"
11
12 namespace v8 {
13 namespace internal {
14
15 using interpreter::Bytecodes;
16 using interpreter::Interpreter;
17
BuiltinDeserializerAllocator(Deserializer<BuiltinDeserializerAllocator> * deserializer)18 BuiltinDeserializerAllocator::BuiltinDeserializerAllocator(
19 Deserializer<BuiltinDeserializerAllocator>* deserializer)
20 : deserializer_(deserializer) {}
21
~BuiltinDeserializerAllocator()22 BuiltinDeserializerAllocator::~BuiltinDeserializerAllocator() {
23 delete handler_allocations_;
24 }
25
26 namespace {
HandlerAllocationIndex(int code_object_id)27 int HandlerAllocationIndex(int code_object_id) {
28 return code_object_id - BuiltinSnapshotUtils::kFirstHandlerIndex;
29 }
30 } // namespace
31
Allocate(AllocationSpace space,int size)32 Address BuiltinDeserializerAllocator::Allocate(AllocationSpace space,
33 int size) {
34 const int code_object_id = deserializer()->CurrentCodeObjectId();
35 DCHECK_NE(BuiltinDeserializer::kNoCodeObjectId, code_object_id);
36 DCHECK_EQ(CODE_SPACE, space);
37 DCHECK_EQ(deserializer()->ExtractCodeObjectSize(code_object_id), size);
38 #ifdef DEBUG
39 RegisterCodeObjectAllocation(code_object_id);
40 #endif
41
42 if (BSU::IsBuiltinIndex(code_object_id)) {
43 Object* obj = isolate()->builtins()->builtin(code_object_id);
44 DCHECK(Internals::HasHeapObjectTag(obj));
45 return HeapObject::cast(obj)->address();
46 } else if (BSU::IsHandlerIndex(code_object_id)) {
47 if (handler_allocation_ != kNullAddress) {
48 // Lazy deserialization.
49 DCHECK_NULL(handler_allocations_);
50 return handler_allocation_;
51 } else {
52 // Eager deserialization.
53 DCHECK_EQ(kNullAddress, handler_allocation_);
54 DCHECK_NOT_NULL(handler_allocations_);
55 int index = HandlerAllocationIndex(code_object_id);
56 DCHECK_NE(kNullAddress, handler_allocations_->at(index));
57 return handler_allocations_->at(index);
58 }
59 }
60
61 UNREACHABLE();
62 }
63
64 Heap::Reservation
CreateReservationsForEagerBuiltinsAndHandlers()65 BuiltinDeserializerAllocator::CreateReservationsForEagerBuiltinsAndHandlers() {
66 Heap::Reservation result;
67
68 // Reservations for builtins.
69
70 // DeserializeLazy is always the first builtin reservation (to simplify logic
71 // in InitializeBuiltinsTable).
72 {
73 DCHECK(!Builtins::IsLazy(Builtins::kDeserializeLazy));
74 uint32_t builtin_size =
75 deserializer()->ExtractCodeObjectSize(Builtins::kDeserializeLazy);
76 DCHECK_LE(builtin_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
77 result.push_back({builtin_size, kNullAddress, kNullAddress});
78 }
79
80 for (int i = 0; i < BSU::kNumberOfBuiltins; i++) {
81 if (i == Builtins::kDeserializeLazy) continue;
82
83 // Skip lazy builtins. These will be replaced by the DeserializeLazy code
84 // object in InitializeFromReservations and thus require no reserved space.
85 if (deserializer()->IsLazyDeserializationEnabled() && Builtins::IsLazy(i)) {
86 continue;
87 }
88
89 uint32_t builtin_size = deserializer()->ExtractCodeObjectSize(i);
90 DCHECK_LE(builtin_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
91 result.push_back({builtin_size, kNullAddress, kNullAddress});
92 }
93
94 // Reservations for bytecode handlers.
95
96 BSU::ForEachBytecode(
97 [=, &result](Bytecode bytecode, OperandScale operand_scale) {
98 if (!Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
99 // Bytecodes without a handler don't require a reservation.
100 return;
101 } else if (FLAG_lazy_handler_deserialization &&
102 deserializer()->IsLazyDeserializationEnabled() &&
103 Bytecodes::IsLazy(bytecode)) {
104 // Skip lazy handlers. These will be replaced by the DeserializeLazy
105 // code object in InitializeFromReservations and thus require no
106 // reserved space.
107 return;
108 }
109
110 const int index = BSU::BytecodeToIndex(bytecode, operand_scale);
111 uint32_t handler_size = deserializer()->ExtractCodeObjectSize(index);
112 DCHECK_LE(handler_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
113 result.push_back({handler_size, kNullAddress, kNullAddress});
114 });
115
116 return result;
117 }
118
InitializeBuiltinFromReservation(const Heap::Chunk & chunk,int builtin_id)119 void BuiltinDeserializerAllocator::InitializeBuiltinFromReservation(
120 const Heap::Chunk& chunk, int builtin_id) {
121 DCHECK_EQ(deserializer()->ExtractCodeObjectSize(builtin_id), chunk.size);
122 DCHECK_EQ(chunk.size, chunk.end - chunk.start);
123
124 SkipList::Update(chunk.start, chunk.size);
125 isolate()->builtins()->set_builtin(builtin_id,
126 HeapObject::FromAddress(chunk.start));
127
128 #ifdef DEBUG
129 RegisterCodeObjectReservation(builtin_id);
130 #endif
131 }
132
InitializeHandlerFromReservation(const Heap::Chunk & chunk,interpreter::Bytecode bytecode,interpreter::OperandScale operand_scale)133 void BuiltinDeserializerAllocator::InitializeHandlerFromReservation(
134 const Heap::Chunk& chunk, interpreter::Bytecode bytecode,
135 interpreter::OperandScale operand_scale) {
136 DCHECK_EQ(deserializer()->ExtractCodeObjectSize(
137 BSU::BytecodeToIndex(bytecode, operand_scale)),
138 chunk.size);
139 DCHECK_EQ(chunk.size, chunk.end - chunk.start);
140
141 SkipList::Update(chunk.start, chunk.size);
142
143 DCHECK_NOT_NULL(handler_allocations_);
144 const int index =
145 HandlerAllocationIndex(BSU::BytecodeToIndex(bytecode, operand_scale));
146 handler_allocations_->at(index) = chunk.start;
147
148 #ifdef DEBUG
149 RegisterCodeObjectReservation(BSU::BytecodeToIndex(bytecode, operand_scale));
150 #endif
151 }
152
InitializeFromReservations(const Heap::Reservation & reservation)153 void BuiltinDeserializerAllocator::InitializeFromReservations(
154 const Heap::Reservation& reservation) {
155 DCHECK(!AllowHeapAllocation::IsAllowed());
156
157 // Initialize the builtins table.
158
159 Builtins* builtins = isolate()->builtins();
160 int reservation_index = 0;
161
162 // Other builtins can be replaced by DeserializeLazy so it may not be lazy.
163 // It always occupies the first reservation slot.
164 {
165 DCHECK(!Builtins::IsLazy(Builtins::kDeserializeLazy));
166 InitializeBuiltinFromReservation(reservation[reservation_index],
167 Builtins::kDeserializeLazy);
168 reservation_index++;
169 }
170
171 Code* deserialize_lazy = builtins->builtin(Builtins::kDeserializeLazy);
172
173 for (int i = 0; i < BSU::kNumberOfBuiltins; i++) {
174 if (i == Builtins::kDeserializeLazy) continue;
175
176 if (deserializer()->IsLazyDeserializationEnabled() && Builtins::IsLazy(i)) {
177 builtins->set_builtin(i, deserialize_lazy);
178 } else {
179 InitializeBuiltinFromReservation(reservation[reservation_index], i);
180 reservation_index++;
181 }
182 }
183
184 // Initialize interpreter bytecode handler reservations.
185
186 DCHECK_NULL(handler_allocations_);
187 handler_allocations_ = new std::vector<Address>(BSU::kNumberOfHandlers);
188
189 BSU::ForEachBytecode(
190 [=, &reservation_index](Bytecode bytecode, OperandScale operand_scale) {
191 if (!Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
192 // Bytecodes without a handler don't have a reservation.
193 return;
194 } else if (FLAG_lazy_handler_deserialization &&
195 deserializer()->IsLazyDeserializationEnabled() &&
196 Bytecodes::IsLazy(bytecode)) {
197 // Likewise, bytecodes with lazy handlers don't either.
198 return;
199 }
200
201 InitializeHandlerFromReservation(reservation[reservation_index],
202 bytecode, operand_scale);
203 reservation_index++;
204 });
205
206 DCHECK_EQ(reservation.size(), reservation_index);
207 }
208
ReserveAndInitializeBuiltinsTableForBuiltin(int builtin_id)209 void BuiltinDeserializerAllocator::ReserveAndInitializeBuiltinsTableForBuiltin(
210 int builtin_id) {
211 DCHECK(AllowHeapAllocation::IsAllowed());
212 DCHECK(isolate()->builtins()->is_initialized());
213 DCHECK(Builtins::IsBuiltinId(builtin_id));
214 DCHECK_NE(Builtins::kDeserializeLazy, builtin_id);
215 DCHECK_EQ(Builtins::kDeserializeLazy,
216 isolate()->builtins()->builtin(builtin_id)->builtin_index());
217
218 const uint32_t builtin_size =
219 deserializer()->ExtractCodeObjectSize(builtin_id);
220 DCHECK_LE(builtin_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
221
222 Handle<HeapObject> o =
223 isolate()->factory()->NewCodeForDeserialization(builtin_size);
224
225 // Note: After this point and until deserialization finishes, heap allocation
226 // is disallowed. We currently can't safely assert this since we'd need to
227 // pass the DisallowHeapAllocation scope out of this function.
228
229 // Write the allocated filler object into the builtins table. It will be
230 // returned by our custom Allocate method below once needed.
231
232 isolate()->builtins()->set_builtin(builtin_id, *o);
233
234 #ifdef DEBUG
235 RegisterCodeObjectReservation(builtin_id);
236 #endif
237 }
238
ReserveForHandler(Bytecode bytecode,OperandScale operand_scale)239 void BuiltinDeserializerAllocator::ReserveForHandler(
240 Bytecode bytecode, OperandScale operand_scale) {
241 DCHECK(AllowHeapAllocation::IsAllowed());
242 DCHECK(isolate()->interpreter()->IsDispatchTableInitialized());
243
244 const int code_object_id = BSU::BytecodeToIndex(bytecode, operand_scale);
245 const uint32_t handler_size =
246 deserializer()->ExtractCodeObjectSize(code_object_id);
247 DCHECK_LE(handler_size, MemoryAllocator::PageAreaSize(CODE_SPACE));
248
249 handler_allocation_ =
250 isolate()->factory()->NewCodeForDeserialization(handler_size)->address();
251
252 // Note: After this point and until deserialization finishes, heap allocation
253 // is disallowed. We currently can't safely assert this since we'd need to
254 // pass the DisallowHeapAllocation scope out of this function.
255
256 #ifdef DEBUG
257 RegisterCodeObjectReservation(code_object_id);
258 #endif
259 }
260
261 #ifdef DEBUG
RegisterCodeObjectReservation(int code_object_id)262 void BuiltinDeserializerAllocator::RegisterCodeObjectReservation(
263 int code_object_id) {
264 const auto result = unused_reservations_.emplace(code_object_id);
265 CHECK(result.second); // False, iff builtin_id was already present in set.
266 }
267
RegisterCodeObjectAllocation(int code_object_id)268 void BuiltinDeserializerAllocator::RegisterCodeObjectAllocation(
269 int code_object_id) {
270 const size_t removed_elems = unused_reservations_.erase(code_object_id);
271 CHECK_EQ(removed_elems, 1);
272 }
273
ReservationsAreFullyUsed() const274 bool BuiltinDeserializerAllocator::ReservationsAreFullyUsed() const {
275 // Not 100% precise but should be good enough.
276 return unused_reservations_.empty();
277 }
278 #endif // DEBUG
279
isolate() const280 Isolate* BuiltinDeserializerAllocator::isolate() const {
281 return deserializer()->isolate();
282 }
283
deserializer() const284 BuiltinDeserializer* BuiltinDeserializerAllocator::deserializer() const {
285 return static_cast<BuiltinDeserializer*>(deserializer_);
286 }
287
288 } // namespace internal
289 } // namespace v8
290