Chromium Code Reviews| Index: src/compiler/js-type-feedback.cc |
| diff --git a/src/compiler/js-type-feedback.cc b/src/compiler/js-type-feedback.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..46734ccde63bc5446a138f2cc69b22f5b2c80b10 |
| --- /dev/null |
| +++ b/src/compiler/js-type-feedback.cc |
| @@ -0,0 +1,259 @@ |
| +// 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. |
| + |
| +#include "src/compiler/js-type-feedback.h" |
| + |
| +#include "src/property-details.h" |
| + |
| +#include "src/accessors.h" |
| +#include "src/ast.h" |
| +#include "src/type-info.h" |
| + |
| +#include "src/compiler/common-operator.h" |
| +#include "src/compiler/node-aux-data.h" |
| +#include "src/compiler/simplified-operator.h" |
| + |
| +namespace v8 { |
| +namespace internal { |
| +namespace compiler { |
| + |
| +enum LoadOrStore { LOAD, STORE }; |
| + |
| +JSTypeFeedbackTable::JSTypeFeedbackTable(Zone* zone) |
| + : map_(TypeFeedbackIdMap::key_compare(), |
| + TypeFeedbackIdMap::allocator_type(zone)) {} |
| + |
| + |
| +Node* JSTypeFeedbackTable::Record(Node* node, TypeFeedbackId id) { |
| + map_.insert(std::make_pair(node->id(), id)); |
| + return node; |
| +} |
| + |
| + |
| +Reduction JSTypeFeedbackSpecializer::Reduce(Node* node) { |
| + switch (node->opcode()) { |
| + case IrOpcode::kJSLoadProperty: |
| + return ReduceJSLoadProperty(node); |
| + case IrOpcode::kJSLoadNamed: |
| + return ReduceJSLoadNamed(node); |
| + case IrOpcode::kJSStoreNamed: |
| + return ReduceJSStoreNamed(node); |
| + case IrOpcode::kJSStoreProperty: |
| + return ReduceJSStoreProperty(node); |
| + default: |
| + break; |
| + } |
| + return NoChange(); |
| +} |
| + |
| + |
| +static bool GetInObjectFieldAccess(LoadOrStore mode, Handle<Map> map, |
| + Handle<Name> name, FieldAccess* access) { |
| + access->base_is_tagged = kTaggedBase; |
| + access->offset = -1; |
| + access->name = name; |
| + access->type = Type::Any(); |
| + access->machine_type = kMachAnyTagged; |
| + |
| + // Check for properties that have accessors but are JSObject fields. |
| + if (Accessors::IsJSObjectFieldAccessor(map, name, &access->offset)) { |
| + // TODO(turbofan): fill in types for special JSObject field accesses. |
| + return true; |
| + } |
| + |
| + // Check if the map is a dictionary. |
| + if (map->is_dictionary_map()) return false; |
| + |
| + // Search the descriptor array. |
| + DescriptorArray* descriptors = map->instance_descriptors(); |
| + int number = descriptors->SearchWithCache(*name, *map); |
| + if (number == DescriptorArray::kNotFound) return false; |
| + PropertyDetails property_details = descriptors->GetDetails(number); |
| + |
| + bool is_smi = property_details.representation().IsSmi(); |
| + bool is_double = property_details.representation().IsDouble(); |
| + |
| + if (mode == STORE) { |
| + if (property_details.IsReadOnly()) return false; |
| + if (is_smi) { |
| + // TODO(turbofan): SMI stores. |
| + return false; |
| + } |
| + if (is_double) { |
| + // TODO(turbofan): double stores. |
| + return false; |
| + } |
| + if (property_details.cell_type() == PropertyCellType::kConstant) { |
|
Toon Verwaest
2015/03/24 09:42:39
This is not how constants are encoded. PropertyDet
titzer
2015/03/24 10:49:31
So the condition should be "property_details.type(
Toon Verwaest
2015/03/24 12:56:12
indeed
titzer
2015/03/24 14:52:06
Done.
|
| + // TODO(turbofan): constant stores. |
| + return false; |
| + } |
| + } else { |
| + // Check property details for loads. |
| + if (property_details.cell_type() == PropertyCellType::kConstant) { |
| + // TODO(turbofan): constants using code dependencies. |
|
Toon Verwaest
2015/03/24 09:42:39
Fast-mode-map-based constants don't use code depen
titzer
2015/03/24 10:49:32
So I can just remove this condition?
The actual t
Toon Verwaest
2015/03/24 12:56:12
Indeed. This condition makes no sense. You'll agai
titzer
2015/03/24 14:52:06
Done.
|
| + return false; |
| + } |
| + if (is_smi) { |
| + access->type = Type::SignedSmall(); |
| + access->machine_type = static_cast<MachineType>(kTypeInt32 | kRepTagged); |
| + } |
| + if (is_double) { |
| + access->type = Type::Number(); |
| + access->machine_type = kMachFloat64; |
| + } |
| + } |
| + |
| + int index = map->instance_descriptors()->GetFieldIndex(number); |
| + FieldIndex field_index = FieldIndex::ForPropertyIndex(*map, index, is_double); |
| + |
| + if (field_index.is_inobject()) { |
| + access->offset = field_index.offset(); |
| + return true; |
| + } |
| + |
| + // TODO(turbofan): handle out of object properties. |
| + return false; |
| +} |
| + |
| + |
| +Reduction JSTypeFeedbackSpecializer::ReduceJSLoadNamed(Node* node) { |
| + DCHECK(node->opcode() == IrOpcode::kJSLoadNamed); |
| + TypeFeedbackId id = js_type_feedback_->find(node); |
| + if (id.IsNone() || oracle()->LoadIsUninitialized(id)) return NoChange(); |
| + |
| + const LoadNamedParameters& p = LoadNamedParametersOf(node->op()); |
| + SmallMapList maps; |
| + Handle<Name> name = p.name().handle(); |
| + 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 |
| + |
| + Handle<Map> map = maps.first(); |
| + FieldAccess field_access; |
| + if (!GetInObjectFieldAccess(LOAD, map, name, &field_access)) { |
| + return NoChange(); |
| + } |
| + |
| + Node* control = NodeProperties::GetControlInput(node); |
| + Node* check_success; |
| + Node* check_failed; |
| + BuildMapCheck(receiver, map, true, effect, control, &check_success, |
| + &check_failed); |
| + |
| + // Build the actual load. |
| + Node* load = graph()->NewNode(simplified()->LoadField(field_access), receiver, |
| + effect, check_success); |
| + |
| + // TODO(turbofan): handle slow case instead of deoptimizing. |
| + // TODO(titzer): frame state should be from before the load. |
| + Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); |
| + Node* deopt = graph()->NewNode(common()->Deoptimize(), frame_state, effect, |
| + check_failed); |
| + NodeProperties::MergeControlToEnd(graph(), common(), deopt); |
| + NodeProperties::ReplaceWithValue(node, load, load, check_success); |
| + return Replace(load); |
| +} |
| + |
| + |
| +Reduction JSTypeFeedbackSpecializer::ReduceJSLoadProperty(Node* node) { |
| + return NoChange(); |
| +} |
| + |
| + |
| +Reduction JSTypeFeedbackSpecializer::ReduceJSStoreNamed(Node* node) { |
| + DCHECK(node->opcode() == IrOpcode::kJSStoreNamed); |
| + 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> name = p.name().handle(); |
| + 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 |
| + |
| + Handle<Map> map = maps.first(); |
| + FieldAccess field_access; |
| + if (!GetInObjectFieldAccess(STORE, map, name, &field_access)) { |
| + return NoChange(); |
| + } |
| + |
| + Node* control = NodeProperties::GetControlInput(node); |
| + Node* check_success; |
| + Node* check_failed; |
| + BuildMapCheck(receiver, map, true, effect, control, &check_success, |
| + &check_failed); |
| + |
| + // Build the actual load. |
| + Node* value = node->InputAt(1); |
| + Node* store = graph()->NewNode(simplified()->StoreField(field_access), |
| + receiver, value, effect, check_success); |
| + |
| + // TODO(turbofan): handle slow case instead of deoptimizing. |
| + // TODO(titzer): frame state should be from before the store. |
| + Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); |
| + Node* deopt = graph()->NewNode(common()->Deoptimize(), frame_state, effect, |
| + check_failed); |
| + NodeProperties::MergeControlToEnd(graph(), common(), deopt); |
| + NodeProperties::ReplaceWithValue(node, store, store, check_success); |
| + return Replace(store); |
| +} |
| + |
| + |
| +Reduction JSTypeFeedbackSpecializer::ReduceJSStoreProperty(Node* node) { |
| + return NoChange(); |
| +} |
| + |
| + |
| +void JSTypeFeedbackSpecializer::BuildMapCheck(Node* receiver, Handle<Map> map, |
| + bool smi_check, Node* effect, |
| + Node* control, Node** success, |
| + Node** fail) { |
| + Node* if_smi = nullptr; |
| + if (smi_check) { |
| + Node* branch_smi = graph()->NewNode( |
| + common()->Branch(BranchHint::kFalse), |
| + graph()->NewNode(simplified()->ObjectIsSmi(), receiver), control); |
| + if_smi = graph()->NewNode(common()->IfTrue(), branch_smi); |
| + control = graph()->NewNode(common()->IfFalse(), branch_smi); |
| + } |
| + |
| + FieldAccess map_access = {kTaggedBase, JSObject::kMapOffset, |
|
Michael Starzinger
2015/03/24 09:25:29
Can we use AccessBuilder::ForMap here?
|
| + Handle<Name>::null(), Type::Internal(), |
| + kMachAnyTagged}; |
| + Node* receiver_map = graph()->NewNode(simplified()->LoadField(map_access), |
| + receiver, effect, control); |
| + Node* map_const = jsgraph_->Constant(map); |
| + Node* cmp = graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), |
| + receiver_map, map_const); |
| + Node* branch = |
| + graph()->NewNode(common()->Branch(BranchHint::kTrue), cmp, control); |
| + *success = graph()->NewNode(common()->IfTrue(), branch); |
| + *fail = graph()->NewNode(common()->IfFalse(), branch); |
| + |
| + if (if_smi) { |
| + *fail = graph()->NewNode(common()->Merge(2), *fail, if_smi); |
| + } |
| +} |
| + |
| + |
| +void JSTypeFeedbackSpecializer::GatherReceiverTypes(Node* receiver, |
| + Node* effect, |
| + TypeFeedbackId id, |
| + Handle<Name> 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); |
| +} |
| + |
| + |
| +} // namespace compiler |
| +} // namespace internal |
| +} // namespace v8 |