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