From f659ae4c89f59a66723fcb8dce14854be7a0e4aa Mon Sep 17 00:00:00 2001 From: titzer Date: Tue, 19 May 2015 01:58:43 -0700 Subject: [PATCH] [turbofan] Support vector IC feedback in the JSTypeFeedbackTable. R=mvstanton@chromium.org BUG= Review URL: https://codereview.chromium.org/1133113004 Cr-Commit-Position: refs/heads/master@{#28465} --- src/compiler/ast-graph-builder.cc | 18 +++-- src/compiler/js-type-feedback.cc | 82 ++++++++++++++-------- src/compiler/js-type-feedback.h | 28 +++++--- test/mjsunit/compiler/osr-array-len.js | 22 ++++++ test/mjsunit/unbox-double-field-indexed.js | 23 ++++++ test/mjsunit/unbox-double-field.js | 22 ++++++ test/mjsunit/unbox-smi-field-indexed.js | 23 ++++++ test/mjsunit/unbox-smi-field.js | 22 ++++++ 8 files changed, 197 insertions(+), 43 deletions(-) create mode 100644 test/mjsunit/compiler/osr-array-len.js create mode 100644 test/mjsunit/unbox-double-field-indexed.js create mode 100644 test/mjsunit/unbox-double-field.js create mode 100644 test/mjsunit/unbox-smi-field-indexed.js create mode 100644 test/mjsunit/unbox-smi-field.js diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc index e1f9667412..2d92ca5d82 100644 --- a/src/compiler/ast-graph-builder.cc +++ b/src/compiler/ast-graph-builder.cc @@ -3132,8 +3132,11 @@ Node* AstGraphBuilder::BuildVariableAssignment( static inline Node* Record(JSTypeFeedbackTable* js_type_feedback, Node* node, - TypeFeedbackId id) { - if (js_type_feedback) js_type_feedback->Record(node, id); + TypeFeedbackId id, FeedbackVectorICSlot slot) { + if (js_type_feedback) { + js_type_feedback->Record(node, id); + js_type_feedback->Record(node, slot); + } return node; } @@ -3142,7 +3145,8 @@ Node* AstGraphBuilder::BuildKeyedLoad(Node* object, Node* key, const VectorSlotPair& feedback, TypeFeedbackId id) { const Operator* op = javascript()->LoadProperty(feedback); - return Record(js_type_feedback_, NewNode(op, object, key), id); + return Record(js_type_feedback_, NewNode(op, object, key), id, + feedback.slot()); } @@ -3151,14 +3155,15 @@ Node* AstGraphBuilder::BuildNamedLoad(Node* object, Handle name, TypeFeedbackId id, ContextualMode mode) { const Operator* op = javascript()->LoadNamed(MakeUnique(name), feedback, mode); - return Record(js_type_feedback_, NewNode(op, object), id); + return Record(js_type_feedback_, NewNode(op, object), id, feedback.slot()); } Node* AstGraphBuilder::BuildKeyedStore(Node* object, Node* key, Node* value, TypeFeedbackId id) { const Operator* op = javascript()->StoreProperty(language_mode()); - return Record(js_type_feedback_, NewNode(op, object, key, value), id); + return Record(js_type_feedback_, NewNode(op, object, key, value), id, + FeedbackVectorICSlot::Invalid()); } @@ -3166,7 +3171,8 @@ Node* AstGraphBuilder::BuildNamedStore(Node* object, Handle name, Node* value, TypeFeedbackId id) { const Operator* op = javascript()->StoreNamed(language_mode(), MakeUnique(name)); - return Record(js_type_feedback_, NewNode(op, object, value), id); + return Record(js_type_feedback_, NewNode(op, object, value), id, + FeedbackVectorICSlot::Invalid()); } diff --git a/src/compiler/js-type-feedback.cc b/src/compiler/js-type-feedback.cc index c82972a92f..689e4aca58 100644 --- a/src/compiler/js-type-feedback.cc +++ b/src/compiler/js-type-feedback.cc @@ -25,13 +25,24 @@ namespace compiler { enum LoadOrStore { LOAD, STORE }; +// TODO(turbofan): fix deoptimization problems +#define ENABLE_FAST_PROPERTY_LOADS false +#define ENABLE_FAST_PROPERTY_STORES false + JSTypeFeedbackTable::JSTypeFeedbackTable(Zone* zone) - : map_(TypeFeedbackIdMap::key_compare(), - TypeFeedbackIdMap::allocator_type(zone)) {} + : type_feedback_id_map_(TypeFeedbackIdMap::key_compare(), + TypeFeedbackIdMap::allocator_type(zone)), + feedback_vector_ic_slot_map_(TypeFeedbackIdMap::key_compare(), + TypeFeedbackIdMap::allocator_type(zone)) {} void JSTypeFeedbackTable::Record(Node* node, TypeFeedbackId id) { - map_.insert(std::make_pair(node->id(), id)); + type_feedback_id_map_.insert(std::make_pair(node->id(), id)); +} + + +void JSTypeFeedbackTable::Record(Node* node, FeedbackVectorICSlot slot) { + feedback_vector_ic_slot_map_.insert(std::make_pair(node->id(), slot)); } @@ -136,6 +147,10 @@ static bool GetInObjectFieldAccess(LoadOrStore mode, Handle map, FieldIndex field_index = FieldIndex::ForPropertyIndex(*map, index, is_double); if (field_index.is_inobject()) { + if (is_double && !map->IsUnboxedDoubleField(field_index)) { + // TODO(turbofan): support for out-of-line (MutableHeapNumber) loads. + return false; + } access->offset = field_index.offset(); return true; } @@ -162,19 +177,29 @@ Reduction JSTypeFeedbackSpecializer::ReduceJSLoadNamed(Node* node) { Node* frame_state_before = GetFrameStateBefore(node); if (frame_state_before == nullptr) return NoChange(); - // TODO(turbofan): handle vector-based type feedback. - TypeFeedbackId id = js_type_feedback_->find(node); - if (id.IsNone() || oracle()->LoadInlineCacheState(id) == UNINITIALIZED) { + const LoadNamedParameters& p = LoadNamedParametersOf(node->op()); + Handle name = p.name().handle(); + SmallMapList maps; + + FeedbackVectorICSlot slot = js_type_feedback_->FindFeedbackVectorICSlot(node); + if (slot.IsInvalid() || + oracle()->LoadInlineCacheState(slot) == UNINITIALIZED) { + // No type feedback ids or the load is uninitialized. return NoChange(); } + if (p.load_ic() == NAMED) { + oracle()->PropertyReceiverTypes(slot, name, &maps); + } else { + // The load named was originally a load property. + bool is_string; // Unused. + IcCheckType key_type; // Unused. + oracle()->KeyedPropertyReceiverTypes(slot, &maps, &is_string, &key_type); + } - const LoadNamedParameters& p = LoadNamedParametersOf(node->op()); - SmallMapList maps; - Handle name = p.name().handle(); Node* effect = NodeProperties::GetEffectInput(node); - GatherReceiverTypes(receiver, effect, id, name, &maps); if (maps.length() != 1) return NoChange(); // TODO(turbofan): polymorphism + if (!ENABLE_FAST_PROPERTY_LOADS) return NoChange(); Handle map = maps.first(); FieldAccess field_access; @@ -276,22 +301,35 @@ Reduction JSTypeFeedbackSpecializer::ReduceJSLoadProperty(Node* node) { Reduction JSTypeFeedbackSpecializer::ReduceJSStoreNamed(Node* node) { DCHECK(node->opcode() == IrOpcode::kJSStoreNamed); - if (true) return NoChange(); // TODO(titzer): storenamed is broken Node* frame_state_before = GetFrameStateBefore(node); if (frame_state_before == nullptr) return NoChange(); - TypeFeedbackId id = js_type_feedback_->find(node); - if (id.IsNone() || oracle()->StoreIsUninitialized(id)) return NoChange(); - const StoreNamedParameters& p = StoreNamedParametersOf(node->op()); - SmallMapList maps; Handle name = p.name().handle(); + SmallMapList maps; + TypeFeedbackId id = js_type_feedback_->FindTypeFeedbackId(node); + if (id.IsNone() || oracle()->StoreIsUninitialized(id) == UNINITIALIZED) { + // No type feedback ids or the store is uninitialized. + // TODO(titzer): no feedback from vector ICs from stores. + return NoChange(); + } else { + if (p.store_ic() == NAMED) { + oracle()->PropertyReceiverTypes(id, name, &maps); + } else { + // The named store was originally a store property. + bool is_string; // Unused. + IcCheckType key_type; // Unused. + oracle()->KeyedPropertyReceiverTypes(id, &maps, &is_string, &key_type); + } + } + Node* receiver = node->InputAt(0); Node* effect = NodeProperties::GetEffectInput(node); - GatherReceiverTypes(receiver, effect, id, name, &maps); if (maps.length() != 1) return NoChange(); // TODO(turbofan): polymorphism + if (!ENABLE_FAST_PROPERTY_STORES) return NoChange(); + Handle map = maps.first(); FieldAccess field_access; if (!GetInObjectFieldAccess(STORE, map, name, &field_access)) { @@ -353,18 +391,6 @@ void JSTypeFeedbackSpecializer::BuildMapCheck(Node* receiver, Handle map, } -void JSTypeFeedbackSpecializer::GatherReceiverTypes(Node* receiver, - Node* effect, - TypeFeedbackId id, - Handle name, - SmallMapList* maps) { - // TODO(turbofan): filter maps by initial receiver map if known - // TODO(turbofan): filter maps by native context (if specializing) - // TODO(turbofan): filter maps by effect chain - oracle()->PropertyReceiverTypes(id, name, maps); -} - - // Get the frame state before an operation if it exists and has a valid // bailout id. Node* JSTypeFeedbackSpecializer::GetFrameStateBefore(Node* node) { diff --git a/src/compiler/js-type-feedback.h b/src/compiler/js-type-feedback.h index 4f1f045d6d..17a6b588a4 100644 --- a/src/compiler/js-type-feedback.h +++ b/src/compiler/js-type-feedback.h @@ -27,20 +27,33 @@ class JSTypeFeedbackTable : public ZoneObject { public: explicit JSTypeFeedbackTable(Zone* zone); - // TODO(titzer): support recording the feedback vector slot. - void Record(Node* node, TypeFeedbackId id); + void Record(Node* node, FeedbackVectorICSlot slot); private: friend class JSTypeFeedbackSpecializer; typedef std::map, zone_allocator > TypeFeedbackIdMap; + typedef std::map, + zone_allocator > + FeedbackVectorICSlotMap; - TypeFeedbackIdMap map_; + TypeFeedbackIdMap type_feedback_id_map_; + FeedbackVectorICSlotMap feedback_vector_ic_slot_map_; - TypeFeedbackId find(Node* node) { - TypeFeedbackIdMap::const_iterator it = map_.find(node->id()); - return it == map_.end() ? TypeFeedbackId::None() : it->second; + TypeFeedbackId FindTypeFeedbackId(Node* node) { + TypeFeedbackIdMap::const_iterator it = + type_feedback_id_map_.find(node->id()); + return it == type_feedback_id_map_.end() ? TypeFeedbackId::None() + : it->second; + } + + FeedbackVectorICSlot FindFeedbackVectorICSlot(Node* node) { + FeedbackVectorICSlotMap::const_iterator it = + feedback_vector_ic_slot_map_.find(node->id()); + return it == feedback_vector_ic_slot_map_.end() + ? FeedbackVectorICSlot::Invalid() + : it->second; } }; @@ -90,9 +103,6 @@ class JSTypeFeedbackSpecializer : public AdvancedReducer { void BuildMapCheck(Node* receiver, Handle map, bool smi_check, Node* effect, Node* control, Node** success, Node** fail); - void GatherReceiverTypes(Node* receiver, Node* effect, TypeFeedbackId id, - Handle property, SmallMapList* maps); - Node* GetFrameStateBefore(Node* node); }; diff --git a/test/mjsunit/compiler/osr-array-len.js b/test/mjsunit/compiler/osr-array-len.js new file mode 100644 index 0000000000..aaee860d61 --- /dev/null +++ b/test/mjsunit/compiler/osr-array-len.js @@ -0,0 +1,22 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + + +function fastaRandom(n, table) { + var line = new Array(5); + while (n > 0) { + if (n < line.length) line = new Array(n); + %OptimizeOsr(); + line[0] = n; + n--; + } +} + +print("---BEGIN 1"); +assertEquals(undefined, fastaRandom(6, null)); +print("---BEGIN 2"); +assertEquals(undefined, fastaRandom(6, null)); +print("---END"); diff --git a/test/mjsunit/unbox-double-field-indexed.js b/test/mjsunit/unbox-double-field-indexed.js new file mode 100644 index 0000000000..29dfc79205 --- /dev/null +++ b/test/mjsunit/unbox-double-field-indexed.js @@ -0,0 +1,23 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + +function Foo(x) { + this.x = x; +} + +var f = new Foo(1.25); +var g = new Foo(2.25); + +function add(a, b) { + var name = "x"; + return a[name] + b[name]; +} + +assertEquals(3.5, add(f, g)); +assertEquals(3.5, add(g, f)); +%OptimizeFunctionOnNextCall(add); +assertEquals(3.5, add(f, g)); +assertEquals(3.5, add(g, f)); diff --git a/test/mjsunit/unbox-double-field.js b/test/mjsunit/unbox-double-field.js new file mode 100644 index 0000000000..9fb5479be7 --- /dev/null +++ b/test/mjsunit/unbox-double-field.js @@ -0,0 +1,22 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + +function Foo(x) { + this.x = x; +} + +var f = new Foo(1.25); +var g = new Foo(2.25); + +function add(a, b) { + return a.x + b.x; +} + +assertEquals(3.5, add(f, g)); +assertEquals(3.5, add(g, f)); +%OptimizeFunctionOnNextCall(add); +assertEquals(3.5, add(f, g)); +assertEquals(3.5, add(g, f)); diff --git a/test/mjsunit/unbox-smi-field-indexed.js b/test/mjsunit/unbox-smi-field-indexed.js new file mode 100644 index 0000000000..9e77da03ed --- /dev/null +++ b/test/mjsunit/unbox-smi-field-indexed.js @@ -0,0 +1,23 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + +function Foo(x) { + this.x = x; +} + +var f = new Foo(1); +var g = new Foo(2); + +function add(a, b) { + var name = "x"; + return a[name] + b[name]; +} + +assertEquals(3, add(f, g)); +assertEquals(3, add(g, f)); +%OptimizeFunctionOnNextCall(add); +assertEquals(3, add(f, g)); +assertEquals(3, add(g, f)); diff --git a/test/mjsunit/unbox-smi-field.js b/test/mjsunit/unbox-smi-field.js new file mode 100644 index 0000000000..361911800b --- /dev/null +++ b/test/mjsunit/unbox-smi-field.js @@ -0,0 +1,22 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + +function Foo(x) { + this.x = x; +} + +var f = new Foo(1); +var g = new Foo(2); + +function add(a, b) { + return a.x + b.x; +} + +assertEquals(3, add(f, g)); +assertEquals(3, add(g, f)); +%OptimizeFunctionOnNextCall(add); +assertEquals(3, add(f, g)); +assertEquals(3, add(g, f));