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

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

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