Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: src/compiler/js-global-specialization.cc

Issue 1396333010: [turbofan] Initial support for monomorphic/polymorphic property loads. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Fix broken tests. Add documentation and TODOs. Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/compiler/js-global-specialization.h ('k') | src/compiler/js-graph.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/compiler/js-global-specialization.h" 5 #include "src/compiler/js-global-specialization.h"
6 6
7 #include "src/compilation-dependencies.h" 7 #include "src/compilation-dependencies.h"
8 #include "src/compiler/access-builder.h" 8 #include "src/compiler/access-builder.h"
9 #include "src/compiler/js-graph.h" 9 #include "src/compiler/js-graph.h"
10 #include "src/compiler/js-operator.h" 10 #include "src/compiler/js-operator.h"
11 #include "src/contexts.h" 11 #include "src/contexts.h"
12 #include "src/field-index-inl.h"
12 #include "src/lookup.h" 13 #include "src/lookup.h"
13 #include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker! 14 #include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker!
15 #include "src/type-feedback-vector.h"
14 16
15 namespace v8 { 17 namespace v8 {
16 namespace internal { 18 namespace internal {
17 namespace compiler { 19 namespace compiler {
18 20
19 struct JSGlobalSpecialization::ScriptContextTableLookupResult { 21 struct JSGlobalSpecialization::ScriptContextTableLookupResult {
20 Handle<Context> context; 22 Handle<Context> context;
21 bool immutable; 23 bool immutable;
22 int index; 24 int index;
23 }; 25 };
24 26
25 27
26 JSGlobalSpecialization::JSGlobalSpecialization( 28 JSGlobalSpecialization::JSGlobalSpecialization(
27 Editor* editor, JSGraph* jsgraph, Flags flags, 29 Editor* editor, JSGraph* jsgraph, Flags flags,
28 Handle<GlobalObject> global_object, CompilationDependencies* dependencies) 30 Handle<GlobalObject> global_object, CompilationDependencies* dependencies,
31 Zone* zone)
29 : AdvancedReducer(editor), 32 : AdvancedReducer(editor),
30 jsgraph_(jsgraph), 33 jsgraph_(jsgraph),
31 flags_(flags), 34 flags_(flags),
32 global_object_(global_object), 35 global_object_(global_object),
33 dependencies_(dependencies), 36 dependencies_(dependencies),
34 simplified_(graph()->zone()) {} 37 simplified_(graph()->zone()),
38 zone_(zone) {}
35 39
36 40
37 Reduction JSGlobalSpecialization::Reduce(Node* node) { 41 Reduction JSGlobalSpecialization::Reduce(Node* node) {
38 switch (node->opcode()) { 42 switch (node->opcode()) {
39 case IrOpcode::kJSLoadGlobal: 43 case IrOpcode::kJSLoadGlobal:
40 return ReduceJSLoadGlobal(node); 44 return ReduceJSLoadGlobal(node);
41 case IrOpcode::kJSStoreGlobal: 45 case IrOpcode::kJSStoreGlobal:
42 return ReduceJSStoreGlobal(node); 46 return ReduceJSStoreGlobal(node);
47 case IrOpcode::kJSLoadNamed:
48 return ReduceJSLoadNamed(node);
43 default: 49 default:
44 break; 50 break;
45 } 51 }
46 return NoChange(); 52 return NoChange();
47 } 53 }
48 54
49 55
50 Reduction JSGlobalSpecialization::ReduceJSLoadGlobal(Node* node) { 56 Reduction JSGlobalSpecialization::ReduceJSLoadGlobal(Node* node) {
51 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); 57 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
52 Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); 58 Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
224 break; 230 break;
225 } 231 }
226 } 232 }
227 effect = graph()->NewNode( 233 effect = graph()->NewNode(
228 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), 234 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()),
229 jsgraph()->Constant(property_cell), value, effect, control); 235 jsgraph()->Constant(property_cell), value, effect, control);
230 return Replace(node, value, effect, control); 236 return Replace(node, value, effect, control);
231 } 237 }
232 238
233 239
240 // This class encapsulates all information required to access a certain
241 // object property, either on the object itself or on the prototype chain.
242 class JSGlobalSpecialization::PropertyAccessInfo final {
243 public:
244 enum Kind { kInvalid, kData, kDataConstant };
245
246 static PropertyAccessInfo DataConstant(Type* receiver_type,
247 Handle<Object> constant,
248 MaybeHandle<JSObject> holder) {
249 return PropertyAccessInfo(holder, constant, receiver_type);
250 }
251 static PropertyAccessInfo Data(Type* receiver_type, FieldIndex field_index,
252 Representation field_representation,
253 MaybeHandle<JSObject> holder) {
254 return PropertyAccessInfo(holder, field_index, field_representation,
255 receiver_type);
256 }
257
258 PropertyAccessInfo() : kind_(kInvalid) {}
259 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant,
260 Type* receiver_type)
261 : kind_(kDataConstant),
262 receiver_type_(receiver_type),
263 constant_(constant),
264 holder_(holder) {}
265 PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index,
266 Representation field_representation, Type* receiver_type)
267 : kind_(kData),
268 receiver_type_(receiver_type),
269 holder_(holder),
270 field_index_(field_index),
271 field_representation_(field_representation) {}
272
273 bool IsDataConstant() const { return kind() == kDataConstant; }
274 bool IsData() const { return kind() == kData; }
275
276 Kind kind() const { return kind_; }
277 MaybeHandle<JSObject> holder() const { return holder_; }
278 Handle<Object> constant() const { return constant_; }
279 FieldIndex field_index() const { return field_index_; }
280 Representation field_representation() const { return field_representation_; }
281 Type* receiver_type() const { return receiver_type_; }
282
283 private:
284 Kind kind_;
285 Type* receiver_type_;
286 Handle<Object> constant_;
287 MaybeHandle<JSObject> holder_;
288 FieldIndex field_index_;
289 Representation field_representation_;
290 };
291
292
293 namespace {
294
295 bool CanInlinePropertyAccess(Handle<Map> map) {
296 // TODO(bmeurer): Do something about the number stuff.
297 if (map->instance_type() == HEAP_NUMBER_TYPE) return false;
298 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true;
299 return map->IsJSObjectMap() && !map->is_dictionary_map() &&
300 !map->has_named_interceptor() &&
301 // TODO(verwaest): Whitelist contexts to which we have access.
302 !map->is_access_check_needed();
303 }
304
305 } // namespace
306
307
308 bool JSGlobalSpecialization::ComputePropertyAccessInfo(
309 Handle<Map> map, Handle<Name> name, PropertyAccessInfo* info) {
310 MaybeHandle<JSObject> holder;
311 Type* receiver_type = Type::Class(map, graph()->zone());
312 while (CanInlinePropertyAccess(map)) {
313 // Lookup the named property on the {map}.
314 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
315 int const number = descriptors->SearchWithCache(*name, *map);
316 if (number != DescriptorArray::kNotFound) {
317 PropertyDetails const details = descriptors->GetDetails(number);
318 if (details.type() == DATA_CONSTANT) {
319 *info = PropertyAccessInfo::DataConstant(
320 receiver_type, handle(descriptors->GetValue(number), isolate()),
321 holder);
322 return true;
323 } else if (details.type() == DATA) {
324 int index = descriptors->GetFieldIndex(number);
325 Representation field_representation = details.representation();
326 FieldIndex field_index = FieldIndex::ForPropertyIndex(
327 *map, index, field_representation.IsDouble());
328 *info = PropertyAccessInfo::Data(receiver_type, field_index,
329 field_representation, holder);
330 return true;
331 } else {
332 // TODO(bmeurer): Add support for accessors.
333 break;
334 }
335 }
336
337 // Don't search on the prototype chain for special indices in case of
338 // integer indexed exotic objects (see ES6 section 9.4.5).
339 if (map->IsJSTypedArrayMap() && name->IsString() &&
340 IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) {
341 break;
342 }
343
344 // Walk up the prototype chain.
345 if (!map->prototype()->IsJSObject()) {
346 // TODO(bmeurer): Handle the not found case if the prototype is null.
347 break;
348 }
349 Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
350 if (map_prototype->map()->is_deprecated()) {
351 // Try to migrate the prototype object so we don't embed the deprecated
352 // map into the optimized code.
353 JSObject::TryMigrateInstance(map_prototype);
354 }
355 map = handle(map_prototype->map(), isolate());
356 holder = map_prototype;
357 }
358 return false;
359 }
360
361
362 bool JSGlobalSpecialization::ComputePropertyAccessInfos(
363 MapHandleList const& maps, Handle<Name> name,
364 ZoneVector<PropertyAccessInfo>* infos) {
365 for (Handle<Map> map : maps) {
366 PropertyAccessInfo info;
367 if (!ComputePropertyAccessInfo(map, name, &info)) return false;
368 infos->push_back(info);
369 }
370 return true;
371 }
372
373
374 Reduction JSGlobalSpecialization::ReduceJSLoadNamed(Node* node) {
Jarin 2015/10/16 12:33:09 Could we rename the file/class, please?
Benedikt Meurer 2015/10/16 13:22:15 As discussed offline: Separate CL :-)
375 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
376 LoadNamedParameters const p = LoadNamedParametersOf(node->op());
377 Handle<Name> name = p.name();
378 Node* receiver = NodeProperties::GetValueInput(node, 0);
379 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
380 Node* effect = NodeProperties::GetEffectInput(node);
381 Node* control = NodeProperties::GetControlInput(node);
382
383 // Not much we can do if deoptimization support is disabled.
384 if (!(flags() & kDeoptimizationEnabled)) return NoChange();
385
386 // Extract receiver maps from the LOAD_IC using the LoadICNexus.
387 MapHandleList receiver_maps;
388 if (!p.feedback().IsValid()) return NoChange();
389 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
390 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
391 DCHECK_LT(0, receiver_maps.length());
392
393 // Compute property access infos for the receiver maps.
394 ZoneVector<PropertyAccessInfo> infos(zone());
395 if (!ComputePropertyAccessInfos(receiver_maps, name, &infos)) {
396 return NoChange();
397 }
398 DCHECK(!infos.empty());
399
400 // The final states for every polymorphic branch. We join them with
401 // Merge+Phi+EffectPhi at the bottom.
402 ZoneVector<Node*> values(zone());
403 ZoneVector<Node*> effects(zone());
404 ZoneVector<Node*> controls(zone());
405
406 // The list of "exiting" controls, which currently go to a single deoptimize.
407 // TODO(bmeurer): Consider using an IC as fallback.
408 Node* const exit_effect = effect;
409 ZoneVector<Node*> exit_controls(zone());
410
411 // Ensure that {receiver} is a heap object.
412 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
413 Node* branch =
414 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
415 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
416 control = graph()->NewNode(common()->IfFalse(), branch);
417
418 // Load the {receiver} map. The resulting effect is the dominating effect for
419 // all (polymorphic) branches.
420 Node* receiver_map = effect =
421 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
422 receiver, effect, control);
423
424 // Generate code for the various different property access patterns.
425 for (PropertyAccessInfo const& info : infos) {
Jarin 2015/10/16 12:33:09 info -> access_info? In general, names in this me
Benedikt Meurer 2015/10/16 13:22:15 Done.
426 Node* this_value = receiver;
427 Node* this_effect = effect;
428 Node* this_control;
429
430 // Perform map check on {receiver}.
431 Type* receiver_type = info.receiver_type();
432 if (receiver_type->Is(Type::String())) {
433 // Emit an instance type check for strings.
434 Node* receiver_instance_type = this_effect = graph()->NewNode(
435 simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
436 receiver_map, this_effect, control);
437 Node* check =
438 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type,
439 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE));
440 Node* branch =
441 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
442 control = graph()->NewNode(common()->IfFalse(), branch);
Jarin 2015/10/16 12:33:09 control -> exit_control? or maybe even check_fail_
Benedikt Meurer 2015/10/16 13:22:15 Ok, renamed to fallthrough_control as discussed of
443 this_control = graph()->NewNode(common()->IfTrue(), branch);
444 } else {
445 // Emit a (sequence of) map checks for other properties.
446 ZoneVector<Node*> this_controls(zone());
447 for (auto i = info.receiver_type()->Classes(); !i.Done(); i.Advance()) {
448 Handle<Map> map = i.Current();
449 Node* check =
450 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()),
451 receiver_map, jsgraph()->Constant(map));
452 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
453 check, control);
454 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
455 control = graph()->NewNode(common()->IfFalse(), branch);
456 }
457 int const this_control_count = static_cast<int>(this_controls.size());
458 this_control =
459 (this_control_count == 1)
460 ? this_controls.front()
461 : graph()->NewNode(common()->Merge(this_control_count),
462 this_control_count, &this_controls.front());
463 }
464
465 // Determine actual holder and perform prototype chain checks.
466 Handle<JSObject> holder;
467 if (info.holder().ToHandle(&holder)) {
468 this_value = jsgraph()->Constant(holder);
469 for (auto i = info.receiver_type()->Classes(); !i.Done(); i.Advance()) {
470 Handle<Map> map = i.Current();
471 PrototypeIterator j(map);
472 while (true) {
473 // Check that the {prototype} still has the same map. For stable
474 // maps, we can add a stability dependency on the prototype map;
475 // for everything else we need to perform a map check at runtime.
476 Handle<JSReceiver> prototype =
477 PrototypeIterator::GetCurrent<JSReceiver>(j);
478 if (prototype->map()->is_stable()) {
479 dependencies()->AssumeMapStable(
480 handle(prototype->map(), isolate()));
481 } else {
482 Node* prototype_map = this_effect = graph()->NewNode(
483 simplified()->LoadField(AccessBuilder::ForMap()),
484 jsgraph()->Constant(prototype), this_effect, this_control);
485 Node* check = graph()->NewNode(
486 simplified()->ReferenceEqual(Type::Internal()), prototype_map,
487 jsgraph()->Constant(handle(prototype->map(), isolate())));
488 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
489 check, this_control);
490 exit_controls.push_back(
491 graph()->NewNode(common()->IfFalse(), branch));
492 this_control = graph()->NewNode(common()->IfTrue(), branch);
493 }
494 // Stop once we get to the holder.
495 if (prototype.is_identical_to(holder)) break;
496 j.Advance();
497 }
498 }
499 }
500
501 // Generate the actual property access.
502 if (info.IsDataConstant()) {
503 this_value = jsgraph()->Constant(info.constant());
504 } else {
505 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some
506 // common code once we also have support for stores.
507 DCHECK(info.IsData());
508 FieldIndex const field_index = info.field_index();
509 Representation const field_representation = info.field_representation();
510 if (!field_index.is_inobject()) {
511 this_value = this_effect = graph()->NewNode(
512 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()),
513 this_value, this_effect, this_control);
514 }
515 FieldAccess field_access;
516 field_access.base_is_tagged = kTaggedBase;
517 field_access.offset = field_index.offset();
518 field_access.name = name;
519 field_access.type = Type::Any();
520 field_access.machine_type = kMachAnyTagged;
521 if (field_representation.IsSmi()) {
522 field_access.type = Type::Intersect(
523 Type::SignedSmall(), Type::TaggedSigned(), graph()->zone());
524 } else if (field_representation.IsDouble()) {
525 if (!field_index.is_inobject() || field_index.is_hidden_field() ||
526 !FLAG_unbox_double_fields) {
527 this_value = this_effect =
528 graph()->NewNode(simplified()->LoadField(field_access),
529 this_value, this_effect, this_control);
530 field_access.offset = HeapNumber::kValueOffset;
531 field_access.name = MaybeHandle<Name>();
532 }
533 field_access.type = Type::Intersect(
534 Type::Number(), Type::UntaggedFloat64(), graph()->zone());
535 field_access.machine_type = kMachFloat64;
536 } else if (field_representation.IsHeapObject()) {
537 field_access.type = Type::TaggedPointer();
538 }
539 this_value = this_effect =
540 graph()->NewNode(simplified()->LoadField(field_access), this_value,
541 this_effect, this_control);
542 }
543
544 // Remember the final state for this property access.
545 values.push_back(this_value);
546 effects.push_back(this_effect);
547 controls.push_back(this_control);
548 }
549
550 // Generate the single "exit" point.
Jarin 2015/10/16 12:33:09 Please better comment here. It should say that we
Benedikt Meurer 2015/10/16 13:22:15 Done.
551 exit_controls.push_back(control);
552 int const exit_control_count = static_cast<int>(exit_controls.size());
553 Node* exit_control =
554 (exit_control_count == 1)
555 ? exit_controls.front()
556 : graph()->NewNode(common()->Merge(exit_control_count),
557 exit_control_count, &exit_controls.front());
558 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
559 exit_effect, exit_control);
560 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
561 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
562
563 // Generate the final merge point for all (polymorphic) branches.
564 Node* value;
565 int const control_count = static_cast<int>(controls.size());
566 if (control_count == 1) {
567 value = values.front();
568 effect = effects.front();
569 control = controls.front();
570 } else {
571 control = graph()->NewNode(common()->Merge(control_count), control_count,
572 &controls.front());
573 values.push_back(control);
574 value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count),
575 control_count + 1, &values.front());
576 effects.push_back(control);
577 effect = graph()->NewNode(common()->EffectPhi(control_count),
578 control_count + 1, &effects.front());
579 }
580 return Replace(node, value, effect, control);
581 }
582
583
234 Reduction JSGlobalSpecialization::Replace(Node* node, Handle<Object> value) { 584 Reduction JSGlobalSpecialization::Replace(Node* node, Handle<Object> value) {
235 // TODO(bmeurer): Move this to JSGraph::HeapConstant instead?
236 if (value->IsConsString()) {
237 value = String::Flatten(Handle<String>::cast(value), TENURED);
238 }
239 return Replace(node, jsgraph()->Constant(value)); 585 return Replace(node, jsgraph()->Constant(value));
240 } 586 }
241 587
242 588
243 bool JSGlobalSpecialization::LookupInScriptContextTable( 589 bool JSGlobalSpecialization::LookupInScriptContextTable(
244 Handle<Name> name, ScriptContextTableLookupResult* result) { 590 Handle<Name> name, ScriptContextTableLookupResult* result) {
245 if (!name->IsString()) return false; 591 if (!name->IsString()) return false;
246 Handle<ScriptContextTable> script_context_table( 592 Handle<ScriptContextTable> script_context_table(
247 global_object()->native_context()->script_context_table()); 593 global_object()->native_context()->script_context_table());
248 ScriptContextTable::LookupResult lookup_result; 594 ScriptContextTable::LookupResult lookup_result;
(...skipping 12 matching lines...) Expand all
261 607
262 608
263 Graph* JSGlobalSpecialization::graph() const { return jsgraph()->graph(); } 609 Graph* JSGlobalSpecialization::graph() const { return jsgraph()->graph(); }
264 610
265 611
266 Isolate* JSGlobalSpecialization::isolate() const { 612 Isolate* JSGlobalSpecialization::isolate() const {
267 return jsgraph()->isolate(); 613 return jsgraph()->isolate();
268 } 614 }
269 615
270 616
617 MachineOperatorBuilder* JSGlobalSpecialization::machine() const {
618 return jsgraph()->machine();
619 }
620
621
271 CommonOperatorBuilder* JSGlobalSpecialization::common() const { 622 CommonOperatorBuilder* JSGlobalSpecialization::common() const {
272 return jsgraph()->common(); 623 return jsgraph()->common();
273 } 624 }
274 625
275 626
276 JSOperatorBuilder* JSGlobalSpecialization::javascript() const { 627 JSOperatorBuilder* JSGlobalSpecialization::javascript() const {
277 return jsgraph()->javascript(); 628 return jsgraph()->javascript();
278 } 629 }
279 630
280 } // namespace compiler 631 } // namespace compiler
281 } // namespace internal 632 } // namespace internal
282 } // namespace v8 633 } // namespace v8
OLDNEW
« no previous file with comments | « src/compiler/js-global-specialization.h ('k') | src/compiler/js-graph.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698