• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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