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 | #include "src/compiler/serializer-for-background-compilation.h" |
6 | |
7 | #include <sstream> |
8 | |
9 | #include "src/compiler/js-heap-broker.h" |
10 | #include "src/handles-inl.h" |
11 | #include "src/interpreter/bytecode-array-iterator.h" |
12 | #include "src/objects/code.h" |
13 | #include "src/objects/shared-function-info-inl.h" |
14 | #include "src/vector-slot-pair.h" |
15 | #include "src/zone/zone.h" |
16 | |
17 | namespace v8 { |
18 | namespace internal { |
19 | namespace compiler { |
20 | |
21 | using BytecodeArrayIterator = interpreter::BytecodeArrayIterator; |
22 | |
23 | CompilationSubject::CompilationSubject(Handle<JSFunction> closure, |
24 | Isolate* isolate) |
25 | : blueprint_{handle(closure->shared(), isolate), |
26 | handle(closure->feedback_vector(), isolate)}, |
27 | closure_(closure) { |
28 | CHECK(closure->has_feedback_vector()); |
29 | } |
30 | |
31 | Hints::Hints(Zone* zone) |
32 | : constants_(zone), maps_(zone), function_blueprints_(zone) {} |
33 | |
34 | const ConstantsSet& Hints::constants() const { return constants_; } |
35 | |
36 | const MapsSet& Hints::maps() const { return maps_; } |
37 | |
38 | const BlueprintsSet& Hints::function_blueprints() const { |
39 | return function_blueprints_; |
40 | } |
41 | |
42 | void Hints::AddConstant(Handle<Object> constant) { |
43 | constants_.insert(constant); |
44 | } |
45 | |
46 | void Hints::AddMap(Handle<Map> map) { maps_.insert(map); } |
47 | |
48 | void Hints::AddFunctionBlueprint(FunctionBlueprint function_blueprint) { |
49 | function_blueprints_.insert(function_blueprint); |
50 | } |
51 | |
52 | void Hints::Add(const Hints& other) { |
53 | for (auto x : other.constants()) AddConstant(x); |
54 | for (auto x : other.maps()) AddMap(x); |
55 | for (auto x : other.function_blueprints()) AddFunctionBlueprint(x); |
56 | } |
57 | |
58 | bool Hints::IsEmpty() const { |
59 | return constants().empty() && maps().empty() && function_blueprints().empty(); |
60 | } |
61 | |
62 | std::ostream& operator<<(std::ostream& out, |
63 | const FunctionBlueprint& blueprint) { |
64 | out << Brief(*blueprint.shared) << std::endl; |
65 | out << Brief(*blueprint.feedback_vector) << std::endl; |
66 | return out; |
67 | } |
68 | |
69 | std::ostream& operator<<(std::ostream& out, const Hints& hints) { |
70 | !hints.constants().empty() && |
71 | out << "\t\tConstants (" << hints.constants().size() << "):" << std::endl; |
72 | for (auto x : hints.constants()) out << Brief(*x) << std::endl; |
73 | !hints.maps().empty() && out << "\t\tMaps (" << hints.maps().size() |
74 | << "):" << std::endl; |
75 | for (auto x : hints.maps()) out << Brief(*x) << std::endl; |
76 | !hints.function_blueprints().empty() && |
77 | out << "\t\tBlueprints (" << hints.function_blueprints().size() |
78 | << "):" << std::endl; |
79 | for (auto x : hints.function_blueprints()) out << x; |
80 | return out; |
81 | } |
82 | |
83 | void Hints::Clear() { |
84 | constants_.clear(); |
85 | maps_.clear(); |
86 | function_blueprints_.clear(); |
87 | DCHECK(IsEmpty()); |
88 | } |
89 | |
90 | class SerializerForBackgroundCompilation::Environment : public ZoneObject { |
91 | public: |
92 | Environment(Zone* zone, CompilationSubject function); |
93 | Environment(Zone* zone, Isolate* isolate, CompilationSubject function, |
94 | base::Optional<Hints> new_target, const HintsVector& arguments); |
95 | |
96 | // When control flow bytecodes are encountered, e.g. a conditional jump, |
97 | // the current environment needs to be stashed together with the target jump |
98 | // address. Later, when this target bytecode is handled, the stashed |
99 | // environment will be merged into the current one. |
100 | void Merge(Environment* other); |
101 | |
102 | friend std::ostream& operator<<(std::ostream& out, const Environment& env); |
103 | |
104 | FunctionBlueprint function() const { return function_; } |
105 | |
106 | Hints& accumulator_hints() { return environment_hints_[accumulator_index()]; } |
107 | Hints& register_hints(interpreter::Register reg) { |
108 | int local_index = RegisterToLocalIndex(reg); |
109 | DCHECK_LT(local_index, environment_hints_.size()); |
110 | return environment_hints_[local_index]; |
111 | } |
112 | Hints& return_value_hints() { return return_value_hints_; } |
113 | |
114 | // Clears all hints except those for the return value and the closure. |
115 | void ClearEphemeralHints() { |
116 | DCHECK_EQ(environment_hints_.size(), function_closure_index() + 1); |
117 | for (int i = 0; i < function_closure_index(); ++i) { |
118 | environment_hints_[i].Clear(); |
119 | } |
120 | } |
121 | |
122 | // Appends the hints for the given register range to {dst} (in order). |
123 | void ExportRegisterHints(interpreter::Register first, size_t count, |
124 | HintsVector& dst); |
125 | |
126 | private: |
127 | int RegisterToLocalIndex(interpreter::Register reg) const; |
128 | |
129 | Zone* zone() const { return zone_; } |
130 | int parameter_count() const { return parameter_count_; } |
131 | int register_count() const { return register_count_; } |
132 | |
133 | Zone* const zone_; |
134 | // Instead of storing the blueprint here, we could extract it from the |
135 | // (closure) hints but that would be cumbersome. |
136 | FunctionBlueprint const function_; |
137 | int const parameter_count_; |
138 | int const register_count_; |
139 | |
140 | // environment_hints_ contains hints for the contents of the registers, |
141 | // the accumulator and the parameters. The layout is as follows: |
142 | // [ parameters | registers | accumulator | context | closure ] |
143 | // The first parameter is the receiver. |
144 | HintsVector environment_hints_; |
145 | int accumulator_index() const { return parameter_count() + register_count(); } |
146 | int current_context_index() const { return accumulator_index() + 1; } |
147 | int function_closure_index() const { return current_context_index() + 1; } |
148 | int environment_hints_size() const { return function_closure_index() + 1; } |
149 | |
150 | Hints return_value_hints_; |
151 | }; |
152 | |
153 | SerializerForBackgroundCompilation::Environment::Environment( |
154 | Zone* zone, CompilationSubject function) |
155 | : zone_(zone), |
156 | function_(function.blueprint()), |
157 | parameter_count_(function_.shared->GetBytecodeArray()->parameter_count()), |
158 | register_count_(function_.shared->GetBytecodeArray()->register_count()), |
159 | environment_hints_(environment_hints_size(), Hints(zone), zone), |
160 | return_value_hints_(zone) { |
161 | Handle<JSFunction> closure; |
162 | if (function.closure().ToHandle(&closure)) { |
163 | environment_hints_[function_closure_index()].AddConstant(closure); |
164 | } else { |
165 | environment_hints_[function_closure_index()].AddFunctionBlueprint( |
166 | function.blueprint()); |
167 | } |
168 | } |
169 | |
170 | SerializerForBackgroundCompilation::Environment::Environment( |
171 | Zone* zone, Isolate* isolate, CompilationSubject function, |
172 | base::Optional<Hints> new_target, const HintsVector& arguments) |
173 | : Environment(zone, function) { |
174 | // Copy the hints for the actually passed arguments, at most up to |
175 | // the parameter_count. |
176 | size_t param_count = static_cast<size_t>(parameter_count()); |
177 | for (size_t i = 0; i < std::min(arguments.size(), param_count); ++i) { |
178 | environment_hints_[i] = arguments[i]; |
179 | } |
180 | |
181 | // Pad the rest with "undefined". |
182 | Hints undefined_hint(zone); |
183 | undefined_hint.AddConstant(isolate->factory()->undefined_value()); |
184 | for (size_t i = arguments.size(); i < param_count; ++i) { |
185 | environment_hints_[i] = undefined_hint; |
186 | } |
187 | |
188 | interpreter::Register new_target_reg = |
189 | function_.shared->GetBytecodeArray() |
190 | ->incoming_new_target_or_generator_register(); |
191 | if (new_target_reg.is_valid()) { |
192 | DCHECK(register_hints(new_target_reg).IsEmpty()); |
193 | if (new_target.has_value()) { |
194 | register_hints(new_target_reg).Add(*new_target); |
195 | } |
196 | } |
197 | } |
198 | |
199 | void SerializerForBackgroundCompilation::Environment::Merge( |
200 | Environment* other) { |
201 | // Presumably the source and the target would have the same layout |
202 | // so this is enforced here. |
203 | CHECK_EQ(parameter_count(), other->parameter_count()); |
204 | CHECK_EQ(register_count(), other->register_count()); |
205 | CHECK_EQ(environment_hints_size(), other->environment_hints_size()); |
206 | |
207 | for (size_t i = 0; i < environment_hints_.size(); ++i) { |
208 | environment_hints_[i].Add(other->environment_hints_[i]); |
209 | } |
210 | return_value_hints_.Add(other->return_value_hints_); |
211 | } |
212 | |
213 | std::ostream& operator<<( |
214 | std::ostream& out, |
215 | const SerializerForBackgroundCompilation::Environment& env) { |
216 | std::ostringstream output_stream; |
217 | output_stream << "Function " ; |
218 | env.function_.shared->Name()->Print(output_stream); |
219 | output_stream << "Parameter count: " << env.parameter_count() << std::endl; |
220 | output_stream << "Register count: " << env.register_count() << std::endl; |
221 | |
222 | output_stream << "Hints (" << env.environment_hints_.size() << "):\n" ; |
223 | for (size_t i = 0; i < env.environment_hints_.size(); ++i) { |
224 | if (env.environment_hints_[i].IsEmpty()) continue; |
225 | |
226 | output_stream << "\tSlot " << i << std::endl; |
227 | output_stream << env.environment_hints_[i]; |
228 | } |
229 | output_stream << "Return value:\n" ; |
230 | output_stream << env.return_value_hints_ |
231 | << "===========================================\n" ; |
232 | |
233 | out << output_stream.str(); |
234 | return out; |
235 | } |
236 | |
237 | int SerializerForBackgroundCompilation::Environment::RegisterToLocalIndex( |
238 | interpreter::Register reg) const { |
239 | // TODO(mslekova): We also want to gather hints for the context. |
240 | if (reg.is_current_context()) return current_context_index(); |
241 | if (reg.is_function_closure()) return function_closure_index(); |
242 | if (reg.is_parameter()) { |
243 | return reg.ToParameterIndex(parameter_count()); |
244 | } else { |
245 | return parameter_count() + reg.index(); |
246 | } |
247 | } |
248 | |
249 | SerializerForBackgroundCompilation::SerializerForBackgroundCompilation( |
250 | JSHeapBroker* broker, Zone* zone, Handle<JSFunction> closure) |
251 | : broker_(broker), |
252 | zone_(zone), |
253 | environment_(new (zone) Environment(zone, {closure, broker_->isolate()})), |
254 | stashed_environments_(zone) { |
255 | JSFunctionRef(broker, closure).Serialize(); |
256 | } |
257 | |
258 | SerializerForBackgroundCompilation::SerializerForBackgroundCompilation( |
259 | JSHeapBroker* broker, Zone* zone, CompilationSubject function, |
260 | base::Optional<Hints> new_target, const HintsVector& arguments) |
261 | : broker_(broker), |
262 | zone_(zone), |
263 | environment_(new (zone) Environment(zone, broker_->isolate(), function, |
264 | new_target, arguments)), |
265 | stashed_environments_(zone) { |
266 | Handle<JSFunction> closure; |
267 | if (function.closure().ToHandle(&closure)) { |
268 | JSFunctionRef(broker, closure).Serialize(); |
269 | } |
270 | } |
271 | |
272 | Hints SerializerForBackgroundCompilation::Run() { |
273 | SharedFunctionInfoRef shared(broker(), environment()->function().shared); |
274 | FeedbackVectorRef feedback_vector(broker(), |
275 | environment()->function().feedback_vector); |
276 | if (shared.IsSerializedForCompilation(feedback_vector)) { |
277 | return Hints(zone()); |
278 | } |
279 | shared.SetSerializedForCompilation(feedback_vector); |
280 | feedback_vector.SerializeSlots(); |
281 | TraverseBytecode(); |
282 | return environment()->return_value_hints(); |
283 | } |
284 | |
285 | void SerializerForBackgroundCompilation::TraverseBytecode() { |
286 | BytecodeArrayRef bytecode_array( |
287 | broker(), handle(environment()->function().shared->GetBytecodeArray(), |
288 | broker()->isolate())); |
289 | BytecodeArrayIterator iterator(bytecode_array.object()); |
290 | |
291 | for (; !iterator.done(); iterator.Advance()) { |
292 | MergeAfterJump(&iterator); |
293 | switch (iterator.current_bytecode()) { |
294 | #define DEFINE_BYTECODE_CASE(name) \ |
295 | case interpreter::Bytecode::k##name: \ |
296 | Visit##name(&iterator); \ |
297 | break; |
298 | SUPPORTED_BYTECODE_LIST(DEFINE_BYTECODE_CASE) |
299 | #undef DEFINE_BYTECODE_CASE |
300 | default: { |
301 | environment()->ClearEphemeralHints(); |
302 | break; |
303 | } |
304 | } |
305 | } |
306 | } |
307 | |
308 | void SerializerForBackgroundCompilation::VisitIllegal( |
309 | BytecodeArrayIterator* iterator) { |
310 | UNREACHABLE(); |
311 | } |
312 | |
313 | void SerializerForBackgroundCompilation::VisitWide( |
314 | BytecodeArrayIterator* iterator) { |
315 | UNREACHABLE(); |
316 | } |
317 | |
318 | void SerializerForBackgroundCompilation::( |
319 | BytecodeArrayIterator* iterator) { |
320 | UNREACHABLE(); |
321 | } |
322 | |
323 | void SerializerForBackgroundCompilation::VisitGetSuperConstructor( |
324 | BytecodeArrayIterator* iterator) { |
325 | interpreter::Register dst = iterator->GetRegisterOperand(0); |
326 | environment()->register_hints(dst).Clear(); |
327 | |
328 | for (auto constant : environment()->accumulator_hints().constants()) { |
329 | // For JSNativeContextSpecialization::ReduceJSGetSuperConstructor. |
330 | if (!constant->IsJSFunction()) continue; |
331 | MapRef map(broker(), |
332 | handle(HeapObject::cast(*constant)->map(), broker()->isolate())); |
333 | map.SerializePrototype(); |
334 | ObjectRef proto = map.prototype(); |
335 | if (proto.IsHeapObject() && proto.AsHeapObject().map().is_constructor()) { |
336 | environment()->register_hints(dst).AddConstant(proto.object()); |
337 | } |
338 | } |
339 | } |
340 | |
341 | void SerializerForBackgroundCompilation::VisitLdaTrue( |
342 | BytecodeArrayIterator* iterator) { |
343 | environment()->accumulator_hints().Clear(); |
344 | environment()->accumulator_hints().AddConstant( |
345 | broker()->isolate()->factory()->true_value()); |
346 | } |
347 | |
348 | void SerializerForBackgroundCompilation::VisitLdaFalse( |
349 | BytecodeArrayIterator* iterator) { |
350 | environment()->accumulator_hints().Clear(); |
351 | environment()->accumulator_hints().AddConstant( |
352 | broker()->isolate()->factory()->false_value()); |
353 | } |
354 | |
355 | void SerializerForBackgroundCompilation::VisitLdaTheHole( |
356 | BytecodeArrayIterator* iterator) { |
357 | environment()->accumulator_hints().Clear(); |
358 | environment()->accumulator_hints().AddConstant( |
359 | broker()->isolate()->factory()->the_hole_value()); |
360 | } |
361 | |
362 | void SerializerForBackgroundCompilation::VisitLdaUndefined( |
363 | BytecodeArrayIterator* iterator) { |
364 | environment()->accumulator_hints().Clear(); |
365 | environment()->accumulator_hints().AddConstant( |
366 | broker()->isolate()->factory()->undefined_value()); |
367 | } |
368 | |
369 | void SerializerForBackgroundCompilation::VisitLdaNull( |
370 | BytecodeArrayIterator* iterator) { |
371 | environment()->accumulator_hints().Clear(); |
372 | environment()->accumulator_hints().AddConstant( |
373 | broker()->isolate()->factory()->null_value()); |
374 | } |
375 | |
376 | void SerializerForBackgroundCompilation::VisitLdaZero( |
377 | BytecodeArrayIterator* iterator) { |
378 | environment()->accumulator_hints().Clear(); |
379 | environment()->accumulator_hints().AddConstant( |
380 | handle(Smi::FromInt(0), broker()->isolate())); |
381 | } |
382 | |
383 | void SerializerForBackgroundCompilation::VisitLdaSmi( |
384 | BytecodeArrayIterator* iterator) { |
385 | environment()->accumulator_hints().Clear(); |
386 | environment()->accumulator_hints().AddConstant(handle( |
387 | Smi::FromInt(iterator->GetImmediateOperand(0)), broker()->isolate())); |
388 | } |
389 | |
390 | void SerializerForBackgroundCompilation::VisitLdaConstant( |
391 | BytecodeArrayIterator* iterator) { |
392 | environment()->accumulator_hints().Clear(); |
393 | environment()->accumulator_hints().AddConstant( |
394 | handle(iterator->GetConstantForIndexOperand(0), broker()->isolate())); |
395 | } |
396 | |
397 | void SerializerForBackgroundCompilation::VisitLdar( |
398 | BytecodeArrayIterator* iterator) { |
399 | environment()->accumulator_hints().Clear(); |
400 | environment()->accumulator_hints().Add( |
401 | environment()->register_hints(iterator->GetRegisterOperand(0))); |
402 | } |
403 | |
404 | void SerializerForBackgroundCompilation::VisitStar( |
405 | BytecodeArrayIterator* iterator) { |
406 | interpreter::Register reg = iterator->GetRegisterOperand(0); |
407 | environment()->register_hints(reg).Clear(); |
408 | environment()->register_hints(reg).Add(environment()->accumulator_hints()); |
409 | } |
410 | |
411 | void SerializerForBackgroundCompilation::VisitMov( |
412 | BytecodeArrayIterator* iterator) { |
413 | interpreter::Register src = iterator->GetRegisterOperand(0); |
414 | interpreter::Register dst = iterator->GetRegisterOperand(1); |
415 | environment()->register_hints(dst).Clear(); |
416 | environment()->register_hints(dst).Add(environment()->register_hints(src)); |
417 | } |
418 | |
419 | void SerializerForBackgroundCompilation::VisitCreateClosure( |
420 | BytecodeArrayIterator* iterator) { |
421 | Handle<SharedFunctionInfo> shared( |
422 | SharedFunctionInfo::cast(iterator->GetConstantForIndexOperand(0)), |
423 | broker()->isolate()); |
424 | |
425 | Handle<FeedbackCell> feedback_cell = |
426 | environment()->function().feedback_vector->GetClosureFeedbackCell( |
427 | iterator->GetIndexOperand(1)); |
428 | Handle<Object> cell_value(feedback_cell->value(), broker()->isolate()); |
429 | |
430 | environment()->accumulator_hints().Clear(); |
431 | if (cell_value->IsFeedbackVector()) { |
432 | environment()->accumulator_hints().AddFunctionBlueprint( |
433 | {shared, Handle<FeedbackVector>::cast(cell_value)}); |
434 | } |
435 | } |
436 | |
437 | void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver( |
438 | BytecodeArrayIterator* iterator) { |
439 | ProcessCallVarArgs(iterator, ConvertReceiverMode::kNullOrUndefined); |
440 | } |
441 | |
442 | void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver0( |
443 | BytecodeArrayIterator* iterator) { |
444 | const Hints& callee = |
445 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
446 | FeedbackSlot slot = iterator->GetSlotOperand(1); |
447 | |
448 | Hints receiver(zone()); |
449 | receiver.AddConstant(broker()->isolate()->factory()->undefined_value()); |
450 | |
451 | HintsVector parameters({receiver}, zone()); |
452 | ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); |
453 | } |
454 | |
455 | void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver1( |
456 | BytecodeArrayIterator* iterator) { |
457 | const Hints& callee = |
458 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
459 | const Hints& arg0 = |
460 | environment()->register_hints(iterator->GetRegisterOperand(1)); |
461 | FeedbackSlot slot = iterator->GetSlotOperand(2); |
462 | |
463 | Hints receiver(zone()); |
464 | receiver.AddConstant(broker()->isolate()->factory()->undefined_value()); |
465 | |
466 | HintsVector parameters({receiver, arg0}, zone()); |
467 | ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); |
468 | } |
469 | |
470 | void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver2( |
471 | BytecodeArrayIterator* iterator) { |
472 | const Hints& callee = |
473 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
474 | const Hints& arg0 = |
475 | environment()->register_hints(iterator->GetRegisterOperand(1)); |
476 | const Hints& arg1 = |
477 | environment()->register_hints(iterator->GetRegisterOperand(2)); |
478 | FeedbackSlot slot = iterator->GetSlotOperand(3); |
479 | |
480 | Hints receiver(zone()); |
481 | receiver.AddConstant(broker()->isolate()->factory()->undefined_value()); |
482 | |
483 | HintsVector parameters({receiver, arg0, arg1}, zone()); |
484 | ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); |
485 | } |
486 | |
487 | void SerializerForBackgroundCompilation::VisitCallAnyReceiver( |
488 | BytecodeArrayIterator* iterator) { |
489 | ProcessCallVarArgs(iterator, ConvertReceiverMode::kAny); |
490 | } |
491 | |
492 | void SerializerForBackgroundCompilation::VisitCallProperty( |
493 | BytecodeArrayIterator* iterator) { |
494 | ProcessCallVarArgs(iterator, ConvertReceiverMode::kNullOrUndefined); |
495 | } |
496 | |
497 | void SerializerForBackgroundCompilation::VisitCallProperty0( |
498 | BytecodeArrayIterator* iterator) { |
499 | const Hints& callee = |
500 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
501 | const Hints& receiver = |
502 | environment()->register_hints(iterator->GetRegisterOperand(1)); |
503 | FeedbackSlot slot = iterator->GetSlotOperand(2); |
504 | |
505 | HintsVector parameters({receiver}, zone()); |
506 | ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); |
507 | } |
508 | |
509 | void SerializerForBackgroundCompilation::VisitCallProperty1( |
510 | BytecodeArrayIterator* iterator) { |
511 | const Hints& callee = |
512 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
513 | const Hints& receiver = |
514 | environment()->register_hints(iterator->GetRegisterOperand(1)); |
515 | const Hints& arg0 = |
516 | environment()->register_hints(iterator->GetRegisterOperand(2)); |
517 | FeedbackSlot slot = iterator->GetSlotOperand(3); |
518 | |
519 | HintsVector parameters({receiver, arg0}, zone()); |
520 | ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); |
521 | } |
522 | |
523 | void SerializerForBackgroundCompilation::VisitCallProperty2( |
524 | BytecodeArrayIterator* iterator) { |
525 | const Hints& callee = |
526 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
527 | const Hints& receiver = |
528 | environment()->register_hints(iterator->GetRegisterOperand(1)); |
529 | const Hints& arg0 = |
530 | environment()->register_hints(iterator->GetRegisterOperand(2)); |
531 | const Hints& arg1 = |
532 | environment()->register_hints(iterator->GetRegisterOperand(3)); |
533 | FeedbackSlot slot = iterator->GetSlotOperand(4); |
534 | |
535 | HintsVector parameters({receiver, arg0, arg1}, zone()); |
536 | ProcessCallOrConstruct(callee, base::nullopt, parameters, slot); |
537 | } |
538 | |
539 | void SerializerForBackgroundCompilation::VisitCallWithSpread( |
540 | BytecodeArrayIterator* iterator) { |
541 | ProcessCallVarArgs(iterator, ConvertReceiverMode::kAny, true); |
542 | } |
543 | |
544 | Hints SerializerForBackgroundCompilation::RunChildSerializer( |
545 | CompilationSubject function, base::Optional<Hints> new_target, |
546 | const HintsVector& arguments, bool with_spread) { |
547 | if (with_spread) { |
548 | DCHECK_LT(0, arguments.size()); |
549 | // Pad the missing arguments in case we were called with spread operator. |
550 | // Drop the last actually passed argument, which contains the spread. |
551 | // We don't know what the spread element produces. Therefore we pretend |
552 | // that the function is called with the maximal number of parameters and |
553 | // that we have no information about the parameters that were not |
554 | // explicitly provided. |
555 | HintsVector padded = arguments; |
556 | padded.pop_back(); // Remove the spread element. |
557 | // Fill the rest with empty hints. |
558 | padded.resize( |
559 | function.blueprint().shared->GetBytecodeArray()->parameter_count(), |
560 | Hints(zone())); |
561 | return RunChildSerializer(function, new_target, padded, false); |
562 | } |
563 | |
564 | TRACE_BROKER(broker(), "Will run child serializer with environment:\n" |
565 | << *environment()); |
566 | |
567 | SerializerForBackgroundCompilation child_serializer( |
568 | broker(), zone(), function, new_target, arguments); |
569 | return child_serializer.Run(); |
570 | } |
571 | |
572 | namespace { |
573 | base::Optional<HeapObjectRef> GetHeapObjectFeedback( |
574 | JSHeapBroker* broker, Handle<FeedbackVector> feedback_vector, |
575 | FeedbackSlot slot) { |
576 | if (slot.IsInvalid()) return base::nullopt; |
577 | FeedbackNexus nexus(feedback_vector, slot); |
578 | VectorSlotPair feedback(feedback_vector, slot, nexus.ic_state()); |
579 | DCHECK(feedback.IsValid()); |
580 | if (nexus.IsUninitialized()) return base::nullopt; |
581 | HeapObject object; |
582 | if (!nexus.GetFeedback()->GetHeapObject(&object)) return base::nullopt; |
583 | return HeapObjectRef(broker, handle(object, broker->isolate())); |
584 | } |
585 | } // namespace |
586 | |
587 | void SerializerForBackgroundCompilation::ProcessCallOrConstruct( |
588 | Hints callee, base::Optional<Hints> new_target, |
589 | const HintsVector& arguments, FeedbackSlot slot, bool with_spread) { |
590 | // Incorporate feedback into hints. |
591 | base::Optional<HeapObjectRef> feedback = GetHeapObjectFeedback( |
592 | broker(), environment()->function().feedback_vector, slot); |
593 | if (feedback.has_value() && feedback->map().is_callable()) { |
594 | if (new_target.has_value()) { |
595 | // Construct; feedback is new_target, which often is also the callee. |
596 | new_target->AddConstant(feedback->object()); |
597 | callee.AddConstant(feedback->object()); |
598 | } else { |
599 | // Call; feedback is callee. |
600 | callee.AddConstant(feedback->object()); |
601 | } |
602 | } |
603 | |
604 | environment()->accumulator_hints().Clear(); |
605 | |
606 | for (auto hint : callee.constants()) { |
607 | if (!hint->IsJSFunction()) continue; |
608 | |
609 | Handle<JSFunction> function = Handle<JSFunction>::cast(hint); |
610 | if (!function->shared()->IsInlineable() || !function->has_feedback_vector()) |
611 | continue; |
612 | |
613 | environment()->accumulator_hints().Add(RunChildSerializer( |
614 | {function, broker()->isolate()}, new_target, arguments, with_spread)); |
615 | } |
616 | |
617 | for (auto hint : callee.function_blueprints()) { |
618 | if (!hint.shared->IsInlineable()) continue; |
619 | environment()->accumulator_hints().Add(RunChildSerializer( |
620 | CompilationSubject(hint), new_target, arguments, with_spread)); |
621 | } |
622 | } |
623 | |
624 | void SerializerForBackgroundCompilation::ProcessCallVarArgs( |
625 | BytecodeArrayIterator* iterator, ConvertReceiverMode receiver_mode, |
626 | bool with_spread) { |
627 | const Hints& callee = |
628 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
629 | interpreter::Register first_reg = iterator->GetRegisterOperand(1); |
630 | int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2)); |
631 | FeedbackSlot slot = iterator->GetSlotOperand(3); |
632 | |
633 | HintsVector arguments(zone()); |
634 | // The receiver is either given in the first register or it is implicitly |
635 | // the {undefined} value. |
636 | if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { |
637 | Hints receiver(zone()); |
638 | receiver.AddConstant(broker()->isolate()->factory()->undefined_value()); |
639 | arguments.push_back(receiver); |
640 | } |
641 | environment()->ExportRegisterHints(first_reg, reg_count, arguments); |
642 | |
643 | ProcessCallOrConstruct(callee, base::nullopt, arguments, slot); |
644 | } |
645 | |
646 | void SerializerForBackgroundCompilation::ProcessJump( |
647 | interpreter::BytecodeArrayIterator* iterator) { |
648 | int jump_target = iterator->GetJumpTargetOffset(); |
649 | int current_offset = iterator->current_offset(); |
650 | if (current_offset >= jump_target) return; |
651 | |
652 | stashed_environments_[jump_target] = new (zone()) Environment(*environment()); |
653 | } |
654 | |
655 | void SerializerForBackgroundCompilation::MergeAfterJump( |
656 | interpreter::BytecodeArrayIterator* iterator) { |
657 | int current_offset = iterator->current_offset(); |
658 | auto stash = stashed_environments_.find(current_offset); |
659 | if (stash != stashed_environments_.end()) { |
660 | environment()->Merge(stash->second); |
661 | stashed_environments_.erase(stash); |
662 | } |
663 | } |
664 | |
665 | void SerializerForBackgroundCompilation::VisitReturn( |
666 | BytecodeArrayIterator* iterator) { |
667 | environment()->return_value_hints().Add(environment()->accumulator_hints()); |
668 | environment()->ClearEphemeralHints(); |
669 | } |
670 | |
671 | void SerializerForBackgroundCompilation::Environment::ExportRegisterHints( |
672 | interpreter::Register first, size_t count, HintsVector& dst) { |
673 | dst.resize(dst.size() + count, Hints(zone())); |
674 | int reg_base = first.index(); |
675 | for (int i = 0; i < static_cast<int>(count); ++i) { |
676 | dst.push_back(register_hints(interpreter::Register(reg_base + i))); |
677 | } |
678 | } |
679 | |
680 | void SerializerForBackgroundCompilation::VisitConstruct( |
681 | BytecodeArrayIterator* iterator) { |
682 | const Hints& callee = |
683 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
684 | interpreter::Register first_reg = iterator->GetRegisterOperand(1); |
685 | size_t reg_count = iterator->GetRegisterCountOperand(2); |
686 | FeedbackSlot slot = iterator->GetSlotOperand(3); |
687 | const Hints& new_target = environment()->accumulator_hints(); |
688 | |
689 | HintsVector arguments(zone()); |
690 | environment()->ExportRegisterHints(first_reg, reg_count, arguments); |
691 | |
692 | ProcessCallOrConstruct(callee, new_target, arguments, slot); |
693 | } |
694 | |
695 | void SerializerForBackgroundCompilation::VisitConstructWithSpread( |
696 | BytecodeArrayIterator* iterator) { |
697 | const Hints& callee = |
698 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
699 | interpreter::Register first_reg = iterator->GetRegisterOperand(1); |
700 | size_t reg_count = iterator->GetRegisterCountOperand(2); |
701 | FeedbackSlot slot = iterator->GetSlotOperand(3); |
702 | const Hints& new_target = environment()->accumulator_hints(); |
703 | |
704 | HintsVector arguments(zone()); |
705 | environment()->ExportRegisterHints(first_reg, reg_count, arguments); |
706 | |
707 | ProcessCallOrConstruct(callee, new_target, arguments, slot, true); |
708 | } |
709 | |
710 | GlobalAccessFeedback const* |
711 | SerializerForBackgroundCompilation::ProcessFeedbackForGlobalAccess( |
712 | FeedbackSlot slot) { |
713 | if (slot.IsInvalid()) return nullptr; |
714 | if (environment()->function().feedback_vector.is_null()) return nullptr; |
715 | FeedbackSource source(environment()->function().feedback_vector, slot); |
716 | |
717 | if (broker()->HasFeedback(source)) { |
718 | return broker()->GetGlobalAccessFeedback(source); |
719 | } |
720 | |
721 | const GlobalAccessFeedback* feedback = |
722 | broker()->ProcessFeedbackForGlobalAccess(source); |
723 | broker()->SetFeedback(source, feedback); |
724 | return feedback; |
725 | } |
726 | |
727 | void SerializerForBackgroundCompilation::VisitLdaGlobal( |
728 | BytecodeArrayIterator* iterator) { |
729 | FeedbackSlot slot = iterator->GetSlotOperand(1); |
730 | environment()->accumulator_hints().Clear(); |
731 | GlobalAccessFeedback const* feedback = ProcessFeedbackForGlobalAccess(slot); |
732 | if (feedback != nullptr) { |
733 | // We may be able to contribute to accumulator constant hints. |
734 | base::Optional<ObjectRef> value = feedback->GetConstantHint(); |
735 | if (value.has_value()) { |
736 | environment()->accumulator_hints().AddConstant(value->object()); |
737 | } |
738 | } |
739 | } |
740 | |
741 | void SerializerForBackgroundCompilation::VisitLdaGlobalInsideTypeof( |
742 | BytecodeArrayIterator* iterator) { |
743 | VisitLdaGlobal(iterator); |
744 | } |
745 | |
746 | void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlot( |
747 | BytecodeArrayIterator* iterator) { |
748 | VisitLdaGlobal(iterator); |
749 | } |
750 | |
751 | void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlotInsideTypeof( |
752 | BytecodeArrayIterator* iterator) { |
753 | VisitLdaGlobal(iterator); |
754 | } |
755 | |
756 | void SerializerForBackgroundCompilation::VisitStaGlobal( |
757 | BytecodeArrayIterator* iterator) { |
758 | FeedbackSlot slot = iterator->GetSlotOperand(1); |
759 | ProcessFeedbackForGlobalAccess(slot); |
760 | } |
761 | |
762 | namespace { |
763 | template <class MapContainer> |
764 | MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) { |
765 | MapHandles result; |
766 | for (Handle<Map> map : maps) { |
767 | if (Map::TryUpdate(isolate, map).ToHandle(&map) && |
768 | !map->is_abandoned_prototype_map()) { |
769 | DCHECK(!map->is_deprecated()); |
770 | result.push_back(map); |
771 | } |
772 | } |
773 | return result; |
774 | } |
775 | } // namespace |
776 | |
777 | // Note: We never use the same feedback slot for multiple access modes. |
778 | void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess( |
779 | FeedbackSlot slot, AccessMode mode) { |
780 | if (slot.IsInvalid()) return; |
781 | if (environment()->function().feedback_vector.is_null()) return; |
782 | |
783 | FeedbackNexus nexus(environment()->function().feedback_vector, slot); |
784 | FeedbackSource source(nexus); |
785 | if (broker()->HasFeedback(source)) return; |
786 | |
787 | if (nexus.GetKeyType() == PROPERTY) { |
788 | CHECK_NE(mode, AccessMode::kStoreInLiteral); |
789 | return; // TODO(neis): Support named access. |
790 | } |
791 | DCHECK_EQ(nexus.GetKeyType(), ELEMENT); |
792 | CHECK(nexus.GetName().is_null()); |
793 | |
794 | MapHandles maps; |
795 | nexus.ExtractMaps(&maps); |
796 | ElementAccessFeedback const* processed = |
797 | broker()->ProcessFeedbackMapsForElementAccess( |
798 | GetRelevantReceiverMaps(broker()->isolate(), maps)); |
799 | broker()->SetFeedback(source, processed); |
800 | if (processed == nullptr) return; |
801 | |
802 | for (ElementAccessFeedback::MapIterator it = processed->all_maps(broker()); |
803 | !it.done(); it.advance()) { |
804 | switch (mode) { |
805 | case AccessMode::kHas: |
806 | case AccessMode::kLoad: |
807 | it.current().SerializeForElementLoad(); |
808 | break; |
809 | case AccessMode::kStore: |
810 | it.current().SerializeForElementStore(); |
811 | break; |
812 | case AccessMode::kStoreInLiteral: |
813 | // This operation is fairly local and simple, nothing to serialize. |
814 | break; |
815 | } |
816 | } |
817 | } |
818 | |
819 | void SerializerForBackgroundCompilation::ProcessKeyedPropertyAccess( |
820 | Hints const& receiver, Hints const& key, FeedbackSlot slot, |
821 | AccessMode mode) { |
822 | ProcessFeedbackForKeyedPropertyAccess(slot, mode); |
823 | |
824 | for (Handle<Object> hint : receiver.constants()) { |
825 | ObjectRef receiver_ref(broker(), hint); |
826 | |
827 | // For JSNativeContextSpecialization::ReduceElementAccess. |
828 | if (receiver_ref.IsJSTypedArray()) { |
829 | receiver_ref.AsJSTypedArray().Serialize(); |
830 | } |
831 | |
832 | // For JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant. |
833 | if (mode == AccessMode::kLoad || mode == AccessMode::kHas) { |
834 | for (Handle<Object> hint : key.constants()) { |
835 | ObjectRef key_ref(broker(), hint); |
836 | // TODO(neis): Do this for integer-HeapNumbers too? |
837 | if (key_ref.IsSmi() && key_ref.AsSmi() >= 0) { |
838 | base::Optional<ObjectRef> element = |
839 | receiver_ref.GetOwnConstantElement(key_ref.AsSmi(), true); |
840 | if (!element.has_value() && receiver_ref.IsJSArray()) { |
841 | // We didn't find a constant element, but if the receiver is a |
842 | // cow-array we can exploit the fact that any future write to the |
843 | // element will replace the whole elements storage. |
844 | receiver_ref.AsJSArray().GetOwnCowElement(key_ref.AsSmi(), true); |
845 | } |
846 | } |
847 | } |
848 | } |
849 | } |
850 | |
851 | environment()->accumulator_hints().Clear(); |
852 | } |
853 | |
854 | void SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess( |
855 | MapRef const& map, NameRef const& name) { |
856 | // For JSNativeContextSpecialization::ReduceNamedAccess. |
857 | if (map.IsMapOfCurrentGlobalProxy()) { |
858 | broker()->native_context().global_proxy_object().GetPropertyCell(name, |
859 | true); |
860 | } |
861 | } |
862 | |
863 | // Note: We never use the same feedback slot for multiple names. |
864 | void SerializerForBackgroundCompilation::ProcessFeedbackForNamedPropertyAccess( |
865 | FeedbackSlot slot, NameRef const& name) { |
866 | if (slot.IsInvalid()) return; |
867 | if (environment()->function().feedback_vector.is_null()) return; |
868 | |
869 | FeedbackNexus nexus(environment()->function().feedback_vector, slot); |
870 | FeedbackSource source(nexus); |
871 | if (broker()->HasFeedback(source)) return; |
872 | |
873 | MapHandles maps; |
874 | nexus.ExtractMaps(&maps); |
875 | for (Handle<Map> map : GetRelevantReceiverMaps(broker()->isolate(), maps)) { |
876 | ProcessMapForNamedPropertyAccess(MapRef(broker(), map), name); |
877 | } |
878 | |
879 | // NamedProperty support is still WIP. For now we don't have any actual data |
880 | // to store, so use nullptr to at least record that we processed the feedback. |
881 | broker()->SetFeedback(source, nullptr); |
882 | } |
883 | |
884 | void SerializerForBackgroundCompilation::VisitLdaKeyedProperty( |
885 | BytecodeArrayIterator* iterator) { |
886 | Hints const& key = environment()->accumulator_hints(); |
887 | Hints const& receiver = |
888 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
889 | FeedbackSlot slot = iterator->GetSlotOperand(1); |
890 | ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kLoad); |
891 | } |
892 | |
893 | void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess( |
894 | Hints const& receiver, NameRef const& name, FeedbackSlot slot, |
895 | AccessMode mode) { |
896 | if (!slot.IsInvalid()) ProcessFeedbackForNamedPropertyAccess(slot, name); |
897 | |
898 | for (Handle<Map> map : |
899 | GetRelevantReceiverMaps(broker()->isolate(), receiver.maps())) { |
900 | ProcessMapForNamedPropertyAccess(MapRef(broker(), map), name); |
901 | } |
902 | |
903 | JSGlobalProxyRef global_proxy = |
904 | broker()->native_context().global_proxy_object(); |
905 | |
906 | for (Handle<Object> hint : receiver.constants()) { |
907 | ObjectRef object(broker(), hint); |
908 | // For JSNativeContextSpecialization::ReduceNamedAccessFromNexus. |
909 | if (object.equals(global_proxy)) { |
910 | global_proxy.GetPropertyCell(name, true); |
911 | } |
912 | // For JSNativeContextSpecialization::ReduceJSLoadNamed. |
913 | if (mode == AccessMode::kLoad && object.IsJSFunction() && |
914 | name.equals(ObjectRef( |
915 | broker(), broker()->isolate()->factory()->prototype_string()))) { |
916 | object.AsJSFunction().Serialize(); |
917 | } |
918 | } |
919 | |
920 | environment()->accumulator_hints().Clear(); |
921 | } |
922 | |
923 | void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess( |
924 | BytecodeArrayIterator* iterator, AccessMode mode) { |
925 | Hints const& receiver = |
926 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
927 | Handle<Name> name(Name::cast(iterator->GetConstantForIndexOperand(1)), |
928 | broker()->isolate()); |
929 | FeedbackSlot slot = iterator->GetSlotOperand(2); |
930 | ProcessNamedPropertyAccess(receiver, NameRef(broker(), name), slot, mode); |
931 | } |
932 | |
933 | void SerializerForBackgroundCompilation::VisitLdaNamedProperty( |
934 | BytecodeArrayIterator* iterator) { |
935 | ProcessNamedPropertyAccess(iterator, AccessMode::kLoad); |
936 | } |
937 | |
938 | void SerializerForBackgroundCompilation::VisitStaNamedProperty( |
939 | BytecodeArrayIterator* iterator) { |
940 | ProcessNamedPropertyAccess(iterator, AccessMode::kStore); |
941 | } |
942 | |
943 | void SerializerForBackgroundCompilation::VisitTestIn( |
944 | BytecodeArrayIterator* iterator) { |
945 | Hints const& receiver = environment()->accumulator_hints(); |
946 | Hints const& key = |
947 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
948 | FeedbackSlot slot = iterator->GetSlotOperand(1); |
949 | ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kHas); |
950 | } |
951 | |
952 | void SerializerForBackgroundCompilation::VisitStaKeyedProperty( |
953 | BytecodeArrayIterator* iterator) { |
954 | Hints const& receiver = |
955 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
956 | Hints const& key = |
957 | environment()->register_hints(iterator->GetRegisterOperand(1)); |
958 | FeedbackSlot slot = iterator->GetSlotOperand(2); |
959 | ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStore); |
960 | } |
961 | |
962 | void SerializerForBackgroundCompilation::VisitStaInArrayLiteral( |
963 | BytecodeArrayIterator* iterator) { |
964 | Hints const& receiver = |
965 | environment()->register_hints(iterator->GetRegisterOperand(0)); |
966 | Hints const& key = |
967 | environment()->register_hints(iterator->GetRegisterOperand(1)); |
968 | FeedbackSlot slot = iterator->GetSlotOperand(2); |
969 | ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral); |
970 | } |
971 | |
972 | #define DEFINE_CLEAR_ENVIRONMENT(name, ...) \ |
973 | void SerializerForBackgroundCompilation::Visit##name( \ |
974 | BytecodeArrayIterator* iterator) { \ |
975 | environment()->ClearEphemeralHints(); \ |
976 | } |
977 | CLEAR_ENVIRONMENT_LIST(DEFINE_CLEAR_ENVIRONMENT) |
978 | #undef DEFINE_CLEAR_ENVIRONMENT |
979 | |
980 | #define DEFINE_CLEAR_ACCUMULATOR(name, ...) \ |
981 | void SerializerForBackgroundCompilation::Visit##name( \ |
982 | BytecodeArrayIterator* iterator) { \ |
983 | environment()->accumulator_hints().Clear(); \ |
984 | } |
985 | CLEAR_ACCUMULATOR_LIST(DEFINE_CLEAR_ACCUMULATOR) |
986 | #undef DEFINE_CLEAR_ACCUMULATOR |
987 | |
988 | #define DEFINE_CONDITIONAL_JUMP(name, ...) \ |
989 | void SerializerForBackgroundCompilation::Visit##name( \ |
990 | BytecodeArrayIterator* iterator) { \ |
991 | ProcessJump(iterator); \ |
992 | } |
993 | CONDITIONAL_JUMPS_LIST(DEFINE_CONDITIONAL_JUMP) |
994 | #undef DEFINE_CONDITIONAL_JUMP |
995 | |
996 | #define DEFINE_UNCONDITIONAL_JUMP(name, ...) \ |
997 | void SerializerForBackgroundCompilation::Visit##name( \ |
998 | BytecodeArrayIterator* iterator) { \ |
999 | ProcessJump(iterator); \ |
1000 | environment()->ClearEphemeralHints(); \ |
1001 | } |
1002 | UNCONDITIONAL_JUMPS_LIST(DEFINE_UNCONDITIONAL_JUMP) |
1003 | #undef DEFINE_UNCONDITIONAL_JUMP |
1004 | |
1005 | #define DEFINE_IGNORE(name, ...) \ |
1006 | void SerializerForBackgroundCompilation::Visit##name( \ |
1007 | BytecodeArrayIterator* iterator) {} |
1008 | IGNORED_BYTECODE_LIST(DEFINE_IGNORE) |
1009 | #undef DEFINE_IGNORE |
1010 | |
1011 | } // namespace compiler |
1012 | } // namespace internal |
1013 | } // namespace v8 |
1014 | |