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
17namespace v8 {
18namespace internal {
19namespace compiler {
20
21using BytecodeArrayIterator = interpreter::BytecodeArrayIterator;
22
23CompilationSubject::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
31Hints::Hints(Zone* zone)
32 : constants_(zone), maps_(zone), function_blueprints_(zone) {}
33
34const ConstantsSet& Hints::constants() const { return constants_; }
35
36const MapsSet& Hints::maps() const { return maps_; }
37
38const BlueprintsSet& Hints::function_blueprints() const {
39 return function_blueprints_;
40}
41
42void Hints::AddConstant(Handle<Object> constant) {
43 constants_.insert(constant);
44}
45
46void Hints::AddMap(Handle<Map> map) { maps_.insert(map); }
47
48void Hints::AddFunctionBlueprint(FunctionBlueprint function_blueprint) {
49 function_blueprints_.insert(function_blueprint);
50}
51
52void 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
58bool Hints::IsEmpty() const {
59 return constants().empty() && maps().empty() && function_blueprints().empty();
60}
61
62std::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
69std::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
83void Hints::Clear() {
84 constants_.clear();
85 maps_.clear();
86 function_blueprints_.clear();
87 DCHECK(IsEmpty());
88}
89
90class 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
153SerializerForBackgroundCompilation::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
170SerializerForBackgroundCompilation::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
199void 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
213std::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
237int 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
249SerializerForBackgroundCompilation::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
258SerializerForBackgroundCompilation::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
272Hints 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
285void 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
308void SerializerForBackgroundCompilation::VisitIllegal(
309 BytecodeArrayIterator* iterator) {
310 UNREACHABLE();
311}
312
313void SerializerForBackgroundCompilation::VisitWide(
314 BytecodeArrayIterator* iterator) {
315 UNREACHABLE();
316}
317
318void SerializerForBackgroundCompilation::VisitExtraWide(
319 BytecodeArrayIterator* iterator) {
320 UNREACHABLE();
321}
322
323void 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
341void SerializerForBackgroundCompilation::VisitLdaTrue(
342 BytecodeArrayIterator* iterator) {
343 environment()->accumulator_hints().Clear();
344 environment()->accumulator_hints().AddConstant(
345 broker()->isolate()->factory()->true_value());
346}
347
348void SerializerForBackgroundCompilation::VisitLdaFalse(
349 BytecodeArrayIterator* iterator) {
350 environment()->accumulator_hints().Clear();
351 environment()->accumulator_hints().AddConstant(
352 broker()->isolate()->factory()->false_value());
353}
354
355void SerializerForBackgroundCompilation::VisitLdaTheHole(
356 BytecodeArrayIterator* iterator) {
357 environment()->accumulator_hints().Clear();
358 environment()->accumulator_hints().AddConstant(
359 broker()->isolate()->factory()->the_hole_value());
360}
361
362void SerializerForBackgroundCompilation::VisitLdaUndefined(
363 BytecodeArrayIterator* iterator) {
364 environment()->accumulator_hints().Clear();
365 environment()->accumulator_hints().AddConstant(
366 broker()->isolate()->factory()->undefined_value());
367}
368
369void SerializerForBackgroundCompilation::VisitLdaNull(
370 BytecodeArrayIterator* iterator) {
371 environment()->accumulator_hints().Clear();
372 environment()->accumulator_hints().AddConstant(
373 broker()->isolate()->factory()->null_value());
374}
375
376void SerializerForBackgroundCompilation::VisitLdaZero(
377 BytecodeArrayIterator* iterator) {
378 environment()->accumulator_hints().Clear();
379 environment()->accumulator_hints().AddConstant(
380 handle(Smi::FromInt(0), broker()->isolate()));
381}
382
383void 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
390void SerializerForBackgroundCompilation::VisitLdaConstant(
391 BytecodeArrayIterator* iterator) {
392 environment()->accumulator_hints().Clear();
393 environment()->accumulator_hints().AddConstant(
394 handle(iterator->GetConstantForIndexOperand(0), broker()->isolate()));
395}
396
397void SerializerForBackgroundCompilation::VisitLdar(
398 BytecodeArrayIterator* iterator) {
399 environment()->accumulator_hints().Clear();
400 environment()->accumulator_hints().Add(
401 environment()->register_hints(iterator->GetRegisterOperand(0)));
402}
403
404void 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
411void 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
419void 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
437void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver(
438 BytecodeArrayIterator* iterator) {
439 ProcessCallVarArgs(iterator, ConvertReceiverMode::kNullOrUndefined);
440}
441
442void 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
455void 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
470void 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
487void SerializerForBackgroundCompilation::VisitCallAnyReceiver(
488 BytecodeArrayIterator* iterator) {
489 ProcessCallVarArgs(iterator, ConvertReceiverMode::kAny);
490}
491
492void SerializerForBackgroundCompilation::VisitCallProperty(
493 BytecodeArrayIterator* iterator) {
494 ProcessCallVarArgs(iterator, ConvertReceiverMode::kNullOrUndefined);
495}
496
497void 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
509void 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
523void 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
539void SerializerForBackgroundCompilation::VisitCallWithSpread(
540 BytecodeArrayIterator* iterator) {
541 ProcessCallVarArgs(iterator, ConvertReceiverMode::kAny, true);
542}
543
544Hints 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
572namespace {
573base::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
587void 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
624void 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
646void 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
655void 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
665void SerializerForBackgroundCompilation::VisitReturn(
666 BytecodeArrayIterator* iterator) {
667 environment()->return_value_hints().Add(environment()->accumulator_hints());
668 environment()->ClearEphemeralHints();
669}
670
671void 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
680void 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
695void 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
710GlobalAccessFeedback const*
711SerializerForBackgroundCompilation::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
727void 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
741void SerializerForBackgroundCompilation::VisitLdaGlobalInsideTypeof(
742 BytecodeArrayIterator* iterator) {
743 VisitLdaGlobal(iterator);
744}
745
746void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlot(
747 BytecodeArrayIterator* iterator) {
748 VisitLdaGlobal(iterator);
749}
750
751void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlotInsideTypeof(
752 BytecodeArrayIterator* iterator) {
753 VisitLdaGlobal(iterator);
754}
755
756void SerializerForBackgroundCompilation::VisitStaGlobal(
757 BytecodeArrayIterator* iterator) {
758 FeedbackSlot slot = iterator->GetSlotOperand(1);
759 ProcessFeedbackForGlobalAccess(slot);
760}
761
762namespace {
763template <class MapContainer>
764MapHandles 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.
778void 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
819void 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
854void 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.
864void 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
884void 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
893void 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
923void 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
933void SerializerForBackgroundCompilation::VisitLdaNamedProperty(
934 BytecodeArrayIterator* iterator) {
935 ProcessNamedPropertyAccess(iterator, AccessMode::kLoad);
936}
937
938void SerializerForBackgroundCompilation::VisitStaNamedProperty(
939 BytecodeArrayIterator* iterator) {
940 ProcessNamedPropertyAccess(iterator, AccessMode::kStore);
941}
942
943void 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
952void 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
962void 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 }
977CLEAR_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 }
985CLEAR_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 }
993CONDITIONAL_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 }
1002UNCONDITIONAL_JUMPS_LIST(DEFINE_UNCONDITIONAL_JUMP)
1003#undef DEFINE_UNCONDITIONAL_JUMP
1004
1005#define DEFINE_IGNORE(name, ...) \
1006 void SerializerForBackgroundCompilation::Visit##name( \
1007 BytecodeArrayIterator* iterator) {}
1008IGNORED_BYTECODE_LIST(DEFINE_IGNORE)
1009#undef DEFINE_IGNORE
1010
1011} // namespace compiler
1012} // namespace internal
1013} // namespace v8
1014