| 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..bdd61df87e8a4c2b868a7abc9f18da430d18b1f1
|
| --- /dev/null
|
| +++ b/src/compiler/js-type-feedback.cc
|
| @@ -0,0 +1,256 @@
|
| +// 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/access-builder.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)) {}
|
| +
|
| +
|
| +void JSTypeFeedbackTable::Record(Node* node, TypeFeedbackId id) {
|
| + map_.insert(std::make_pair(node->id(), id));
|
| +}
|
| +
|
| +
|
| +Reduction JSTypeFeedbackSpecializer::Reduce(Node* node) {
|
| + // TODO(turbofan): type feedback currently requires deoptimization.
|
| + if (!FLAG_turbo_deoptimization) return NoChange();
|
| + 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 (property_details.type() != DATA) {
|
| + // TODO(turbofan): constant loads and stores.
|
| + return false;
|
| + }
|
| +
|
| + 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;
|
| + }
|
| + } else {
|
| + // Check property details for loads.
|
| + 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 = AccessBuilder::ForMap();
|
| + 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
|
|
|