[turbofan]: Integrate basic type feedback for property accesses.

BUG=

Review URL: https://codereview.chromium.org/1021713005

Cr-Commit-Position: refs/heads/master@{#27470}
This commit is contained in:
titzer 2015-03-26 02:38:11 -07:00 committed by Commit bot
parent 1d81d82a74
commit 78abf9d9d9
16 changed files with 506 additions and 56 deletions

View File

@ -593,6 +593,8 @@ source_set("v8_base") {
"src/compiler/js-intrinsic-lowering.h",
"src/compiler/js-operator.cc",
"src/compiler/js-operator.h",
"src/compiler/js-type-feedback.cc",
"src/compiler/js-type-feedback.h",
"src/compiler/js-typed-lowering.cc",
"src/compiler/js-typed-lowering.h",
"src/compiler/jump-threading.cc",

View File

@ -440,6 +440,8 @@ OptimizedCompileJob::Status OptimizedCompileJob::CreateGraph() {
if (info()->shared_info()->asm_function()) {
info()->MarkAsContextSpecializing();
} else if (FLAG_turbo_type_feedback) {
info()->MarkAsTypeFeedbackEnabled();
}
Timer t(this, &time_taken_to_create_graph_);

View File

@ -124,7 +124,8 @@ class CompilationInfo {
kTypingEnabled = 1 << 11,
kDisableFutureOptimization = 1 << 12,
kSplittingEnabled = 1 << 13,
kBuiltinInliningEnabled = 1 << 14
kBuiltinInliningEnabled = 1 << 14,
kTypeFeedbackEnabled = 1 << 15
};
explicit CompilationInfo(ParseInfo* parse_info);
@ -209,6 +210,12 @@ class CompilationInfo {
bool is_context_specializing() const { return GetFlag(kContextSpecializing); }
void MarkAsTypeFeedbackEnabled() { SetFlag(kTypeFeedbackEnabled); }
bool is_type_feedback_enabled() const {
return GetFlag(kTypeFeedbackEnabled);
}
void MarkAsInliningEnabled() { SetFlag(kInliningEnabled); }
bool is_inlining_enabled() const { return GetFlag(kInliningEnabled); }

View File

@ -12,7 +12,7 @@ namespace internal {
namespace compiler {
// This access builder provides a set of static methods constructing commonly
// used FieldAccess and ElementAccess descriptors. These descriptors server as
// used FieldAccess and ElementAccess descriptors. These descriptors serve as
// parameters to simplified load/store operators.
class AccessBuilder FINAL : public AllStatic {
public:

View File

@ -7,6 +7,7 @@
#include "src/compiler.h"
#include "src/compiler/ast-loop-assignment-analyzer.h"
#include "src/compiler/control-builders.h"
#include "src/compiler/js-type-feedback.h"
#include "src/compiler/linkage.h"
#include "src/compiler/liveness-analyzer.h"
#include "src/compiler/machine-operator.h"
@ -381,7 +382,8 @@ class AstGraphBuilder::ControlScopeForFinally : public ControlScope {
AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info,
JSGraph* jsgraph, LoopAssignmentAnalysis* loop)
JSGraph* jsgraph, LoopAssignmentAnalysis* loop,
JSTypeFeedbackTable* js_type_feedback)
: local_zone_(local_zone),
info_(info),
jsgraph_(jsgraph),
@ -397,7 +399,8 @@ AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info,
loop_assignment_analysis_(loop),
state_values_cache_(jsgraph),
liveness_analyzer_(static_cast<size_t>(info->scope()->num_stack_slots()),
local_zone) {
local_zone),
js_type_feedback_(js_type_feedback) {
InitializeAstVisitor(info->isolate(), local_zone);
}
@ -1663,7 +1666,8 @@ void AstGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
VisitForValue(property->value());
Node* value = environment()->Pop();
Handle<Name> name = key->AsPropertyName();
Node* store = BuildNamedStore(literal, name, value);
Node* store =
BuildNamedStore(literal, name, value, TypeFeedbackId::None());
PrepareFrameState(store, key->id());
BuildSetHomeObject(value, literal, property->value());
} else {
@ -1838,7 +1842,8 @@ void AstGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
subexpr->id(), OutputFrameStateCombine::PokeAt(0));
Node* value = environment()->Pop();
Node* index = jsgraph()->Constant(i);
Node* store = BuildKeyedStore(literal, index, value);
Node* store =
BuildKeyedStore(literal, index, value, TypeFeedbackId::None());
PrepareFrameStateAfterAndBefore(store, expr->GetIdForElement(i),
OutputFrameStateCombine::Ignore(),
frame_state_before);
@ -1870,7 +1875,8 @@ void AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value,
Node* object = environment()->Pop();
value = environment()->Pop();
Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
Node* store = BuildNamedStore(object, name, value);
Node* store =
BuildNamedStore(object, name, value, TypeFeedbackId::None());
PrepareFrameState(store, bailout_id);
break;
}
@ -1881,7 +1887,7 @@ void AstGraphBuilder::VisitForInAssignment(Expression* expr, Node* value,
Node* key = environment()->Pop();
Node* object = environment()->Pop();
value = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value);
Node* store = BuildKeyedStore(object, key, value, TypeFeedbackId::None());
// TODO(jarin) Provide a real frame state before.
PrepareFrameStateAfterAndBefore(store, bailout_id,
OutputFrameStateCombine::Ignore(),
@ -1933,7 +1939,8 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) {
Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
VectorSlotPair pair =
CreateVectorSlotPair(property->PropertyFeedbackSlot());
old_value = BuildNamedLoad(object, name, pair);
old_value =
BuildNamedLoad(object, name, pair, property->PropertyFeedbackId());
PrepareFrameState(old_value, property->LoadId(),
OutputFrameStateCombine::Push());
break;
@ -1943,7 +1950,8 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) {
Node* object = environment()->Peek(1);
VectorSlotPair pair =
CreateVectorSlotPair(property->PropertyFeedbackSlot());
old_value = BuildKeyedLoad(object, key, pair);
old_value =
BuildKeyedLoad(object, key, pair, property->PropertyFeedbackId());
PrepareFrameState(old_value, property->LoadId(),
OutputFrameStateCombine::Push());
break;
@ -1988,14 +1996,16 @@ void AstGraphBuilder::VisitAssignment(Assignment* expr) {
case NAMED_PROPERTY: {
Node* object = environment()->Pop();
Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
Node* store = BuildNamedStore(object, name, value);
Node* store =
BuildNamedStore(object, name, value, expr->AssignmentFeedbackId());
PrepareFrameState(store, expr->id(), ast_context()->GetStateCombine());
break;
}
case KEYED_PROPERTY: {
Node* key = environment()->Pop();
Node* object = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value);
Node* store =
BuildKeyedStore(object, key, value, expr->AssignmentFeedbackId());
PrepareFrameStateAfterAndBefore(store, expr->id(),
ast_context()->GetStateCombine(),
frame_state_before_store);
@ -2029,13 +2039,13 @@ void AstGraphBuilder::VisitProperty(Property* expr) {
VisitForValue(expr->obj());
Node* object = environment()->Pop();
Handle<Name> name = expr->key()->AsLiteral()->AsPropertyName();
value = BuildNamedLoad(object, name, pair);
value = BuildNamedLoad(object, name, pair, expr->PropertyFeedbackId());
} else {
VisitForValue(expr->obj());
VisitForValue(expr->key());
Node* key = environment()->Pop();
Node* object = environment()->Pop();
value = BuildKeyedLoad(object, key, pair);
value = BuildKeyedLoad(object, key, pair, expr->PropertyFeedbackId());
}
PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
@ -2083,11 +2093,13 @@ void AstGraphBuilder::VisitCall(Call* expr) {
CreateVectorSlotPair(property->PropertyFeedbackSlot());
if (property->key()->IsPropertyName()) {
Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
callee_value = BuildNamedLoad(object, name, pair);
callee_value =
BuildNamedLoad(object, name, pair, property->PropertyFeedbackId());
} else {
VisitForValue(property->key());
Node* key = environment()->Pop();
callee_value = BuildKeyedLoad(object, key, pair);
callee_value =
BuildKeyedLoad(object, key, pair, property->PropertyFeedbackId());
}
PrepareFrameState(callee_value, property->LoadId(),
OutputFrameStateCombine::Push());
@ -2183,7 +2195,8 @@ void AstGraphBuilder::VisitCallJSRuntime(CallRuntime* expr) {
CallFunctionFlags flags = NO_CALL_FUNCTION_FLAGS;
Node* receiver_value = BuildLoadBuiltinsObject();
VectorSlotPair pair = CreateVectorSlotPair(expr->CallRuntimeFeedbackSlot());
Node* callee_value = BuildNamedLoad(receiver_value, name, pair);
Node* callee_value =
BuildNamedLoad(receiver_value, name, pair, expr->CallRuntimeFeedbackId());
// TODO(jarin): Find/create a bailout id to deoptimize to (crankshaft
// refuses to optimize functions with jsruntime calls).
PrepareFrameState(callee_value, BailoutId::None(),
@ -2271,7 +2284,8 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
VectorSlotPair pair =
CreateVectorSlotPair(property->PropertyFeedbackSlot());
old_value = BuildNamedLoad(object, name, pair);
old_value =
BuildNamedLoad(object, name, pair, property->PropertyFeedbackId());
PrepareFrameState(old_value, property->LoadId(),
OutputFrameStateCombine::Push());
stack_depth = 1;
@ -2284,7 +2298,8 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
Node* object = environment()->Peek(1);
VectorSlotPair pair =
CreateVectorSlotPair(property->PropertyFeedbackSlot());
old_value = BuildKeyedLoad(object, key, pair);
old_value =
BuildKeyedLoad(object, key, pair, property->PropertyFeedbackId());
PrepareFrameState(old_value, property->LoadId(),
OutputFrameStateCombine::Push());
stack_depth = 2;
@ -2327,7 +2342,8 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
case NAMED_PROPERTY: {
Node* object = environment()->Pop();
Handle<Name> name = property->key()->AsLiteral()->AsPropertyName();
Node* store = BuildNamedStore(object, name, value);
Node* store =
BuildNamedStore(object, name, value, expr->CountStoreFeedbackId());
environment()->Push(value);
PrepareFrameState(store, expr->AssignmentId());
environment()->Pop();
@ -2336,7 +2352,8 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
case KEYED_PROPERTY: {
Node* key = environment()->Pop();
Node* object = environment()->Pop();
Node* store = BuildKeyedStore(object, key, value);
Node* store =
BuildKeyedStore(object, key, value, expr->CountStoreFeedbackId());
environment()->Push(value);
PrepareFrameStateAfterAndBefore(store, expr->AssignmentId(),
OutputFrameStateCombine::Ignore(),
@ -2745,7 +2762,8 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
// Global var, const, or let variable.
Node* global = BuildLoadGlobalObject();
Handle<Name> name = variable->name();
Node* node = BuildNamedLoad(global, name, feedback, contextual_mode);
Node* node = BuildNamedLoad(global, name, feedback,
TypeFeedbackId::None(), contextual_mode);
PrepareFrameState(node, bailout_id, OutputFrameStateCombine::Push());
return node;
}
@ -2852,7 +2870,8 @@ Node* AstGraphBuilder::BuildVariableAssignment(
// Global var, const, or let variable.
Node* global = BuildLoadGlobalObject();
Handle<Name> name = variable->name();
Node* store = BuildNamedStore(global, name, value);
Node* store =
BuildNamedStore(global, name, value, TypeFeedbackId::None());
PrepareFrameState(store, bailout_id, combine);
return store;
}
@ -2948,33 +2967,42 @@ 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);
return node;
}
Node* AstGraphBuilder::BuildKeyedLoad(Node* object, Node* key,
const VectorSlotPair& feedback) {
const VectorSlotPair& feedback,
TypeFeedbackId id) {
const Operator* op = javascript()->LoadProperty(feedback);
return NewNode(op, object, key);
return Record(js_type_feedback_, NewNode(op, object, key), id);
}
Node* AstGraphBuilder::BuildNamedLoad(Node* object, Handle<Name> name,
const VectorSlotPair& feedback,
ContextualMode mode) {
TypeFeedbackId id, ContextualMode mode) {
const Operator* op =
javascript()->LoadNamed(MakeUnique(name), feedback, mode);
return NewNode(op, object);
return Record(js_type_feedback_, NewNode(op, object), id);
}
Node* AstGraphBuilder::BuildKeyedStore(Node* object, Node* key, Node* value) {
Node* AstGraphBuilder::BuildKeyedStore(Node* object, Node* key, Node* value,
TypeFeedbackId id) {
const Operator* op = javascript()->StoreProperty(language_mode());
return NewNode(op, object, key, value);
return Record(js_type_feedback_, NewNode(op, object, key, value), id);
}
Node* AstGraphBuilder::BuildNamedStore(Node* object, Handle<Name> name,
Node* value) {
Node* value, TypeFeedbackId id) {
const Operator* op =
javascript()->StoreNamed(language_mode(), MakeUnique(name));
return NewNode(op, object, value);
return Record(js_type_feedback_, NewNode(op, object, value), id);
}
@ -3063,7 +3091,8 @@ Node* AstGraphBuilder::BuildSetHomeObject(Node* value, Node* home_object,
Expression* expr) {
if (!FunctionLiteral::NeedsHomeObject(expr)) return value;
Handle<Name> name = isolate()->factory()->home_object_symbol();
Node* store = BuildNamedStore(value, name, home_object);
Node* store =
BuildNamedStore(value, name, home_object, TypeFeedbackId::None());
PrepareFrameState(store, BailoutId::None());
return store;
}

View File

@ -19,6 +19,7 @@ namespace compiler {
class ControlBuilder;
class Graph;
class JSTypeFeedbackTable;
class LoopAssignmentAnalysis;
class LoopBuilder;
class Node;
@ -30,7 +31,8 @@ class Node;
class AstGraphBuilder : public AstVisitor {
public:
AstGraphBuilder(Zone* local_zone, CompilationInfo* info, JSGraph* jsgraph,
LoopAssignmentAnalysis* loop_assignment = NULL);
LoopAssignmentAnalysis* loop_assignment = NULL,
JSTypeFeedbackTable* js_type_feedback = NULL);
// Creates a graph by visiting the entire AST.
bool CreateGraph(bool constant_context, bool stack_check = true);
@ -105,6 +107,9 @@ class AstGraphBuilder : public AstVisitor {
// Analyzer of local variable liveness.
LivenessAnalyzer liveness_analyzer_;
// Type feedback table.
JSTypeFeedbackTable* js_type_feedback_;
// Growth increment for the temporary buffer used to construct input lists to
// new nodes.
static const int kInputBufferSizeIncrement = 64;
@ -263,12 +268,14 @@ class AstGraphBuilder : public AstVisitor {
// Builders for property loads and stores.
Node* BuildKeyedLoad(Node* receiver, Node* key,
const VectorSlotPair& feedback);
const VectorSlotPair& feedback, TypeFeedbackId id);
Node* BuildNamedLoad(Node* receiver, Handle<Name> name,
const VectorSlotPair& feedback,
const VectorSlotPair& feedback, TypeFeedbackId id,
ContextualMode mode = NOT_CONTEXTUAL);
Node* BuildKeyedStore(Node* receiver, Node* key, Node* value);
Node* BuildNamedStore(Node* receiver, Handle<Name>, Node* value);
Node* BuildKeyedStore(Node* receiver, Node* key, Node* value,
TypeFeedbackId id);
Node* BuildNamedStore(Node* receiver, Handle<Name>, Node* value,
TypeFeedbackId id);
// Builders for accessing the function context.
Node* BuildLoadBuiltinsObject();

View File

@ -111,15 +111,7 @@ Reduction JSIntrinsicLowering::ReduceDeoptimizeNow(Node* node) {
graph()->NewNode(common()->Deoptimize(), frame_state, effect, if_true);
// Connect the deopt to the merge exiting the graph.
Node* end_pred = NodeProperties::GetControlInput(graph()->end());
if (end_pred->opcode() == IrOpcode::kMerge) {
int inputs = end_pred->op()->ControlInputCount() + 1;
end_pred->AppendInput(graph()->zone(), deopt);
end_pred->set_op(common()->Merge(inputs));
} else {
Node* merge = graph()->NewNode(common()->Merge(2), end_pred, deopt);
NodeProperties::ReplaceControlInput(graph()->end(), merge);
}
NodeProperties::MergeControlToEnd(graph(), common(), deopt);
return Changed(deopt);
}

View File

@ -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

View File

@ -0,0 +1,91 @@
// 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.
#ifndef V8_COMPILER_JS_TYPE_FEEDBACK_H_
#define V8_COMPILER_JS_TYPE_FEEDBACK_H_
#include "src/utils.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-aux-data.h"
#include "src/compiler/simplified-operator.h"
namespace v8 {
namespace internal {
class TypeFeedbackOracle;
class SmallMapList;
namespace compiler {
// Stores type feedback information for nodes in the graph in a separate
// data structure.
class JSTypeFeedbackTable : public ZoneObject {
public:
explicit JSTypeFeedbackTable(Zone* zone);
// TODO(titzer): support recording the feedback vector slot.
void Record(Node* node, TypeFeedbackId id);
private:
friend class JSTypeFeedbackSpecializer;
typedef std::map<NodeId, TypeFeedbackId, std::less<NodeId>,
zone_allocator<TypeFeedbackId> > TypeFeedbackIdMap;
TypeFeedbackIdMap map_;
TypeFeedbackId find(Node* node) {
TypeFeedbackIdMap::const_iterator it = map_.find(node->id());
return it == map_.end() ? TypeFeedbackId::None() : it->second;
}
};
// Specializes a graph to the type feedback recorded in the
// {js_type_feedback} provided to the constructor.
class JSTypeFeedbackSpecializer : public Reducer {
public:
JSTypeFeedbackSpecializer(JSGraph* jsgraph,
JSTypeFeedbackTable* js_type_feedback,
TypeFeedbackOracle* oracle)
: jsgraph_(jsgraph),
simplified_(jsgraph->graph()->zone()),
js_type_feedback_(js_type_feedback),
oracle_(oracle) {
CHECK(js_type_feedback);
}
Reduction Reduce(Node* node) OVERRIDE;
// Visible for unit testing.
Reduction ReduceJSLoadNamed(Node* node);
Reduction ReduceJSLoadProperty(Node* node);
Reduction ReduceJSStoreNamed(Node* node);
Reduction ReduceJSStoreProperty(Node* node);
private:
JSGraph* jsgraph_;
SimplifiedOperatorBuilder simplified_;
JSTypeFeedbackTable* js_type_feedback_;
TypeFeedbackOracle* oracle_;
TypeFeedbackOracle* oracle() { return oracle_; }
Graph* graph() { return jsgraph_->graph(); }
CommonOperatorBuilder* common() { return jsgraph_->common(); }
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
void BuildMapCheck(Node* receiver, Handle<Map> map, bool smi_check,
Node* effect, Node* control, Node** success, Node** fail);
void GatherReceiverTypes(Node* receiver, Node* effect, TypeFeedbackId id,
Handle<Name> property, SmallMapList* maps);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif

View File

@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/node-properties.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/graph.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
namespace v8 {
@ -151,6 +151,22 @@ void NodeProperties::RemoveNonValueInputs(Node* node) {
}
void NodeProperties::MergeControlToEnd(Graph* graph,
CommonOperatorBuilder* common,
Node* node) {
// Connect the node to the merge exiting the graph.
Node* end_pred = NodeProperties::GetControlInput(graph->end());
if (end_pred->opcode() == IrOpcode::kMerge) {
int inputs = end_pred->op()->ControlInputCount() + 1;
end_pred->AppendInput(graph->zone(), node);
end_pred->set_op(common->Merge(inputs));
} else {
Node* merge = graph->NewNode(common->Merge(2), end_pred, node);
NodeProperties::ReplaceControlInput(graph->end(), merge);
}
}
// static
void NodeProperties::ReplaceWithValue(Node* node, Node* value, Node* effect,
Node* control) {

View File

@ -12,7 +12,9 @@ namespace v8 {
namespace internal {
namespace compiler {
class Graph;
class Operator;
class CommonOperatorBuilder;
// A facade that simplifies access to the different kinds of inputs to a node.
class NodeProperties FINAL {
@ -80,6 +82,11 @@ class NodeProperties FINAL {
static void ReplaceFrameStateInput(Node* node, int index, Node* frame_state);
static void RemoveNonValueInputs(Node* node);
// Merge the control node {node} into the end of the graph, introducing a
// merge node or expanding an existing merge node if necessary.
static void MergeControlToEnd(Graph* graph, CommonOperatorBuilder* common,
Node* node);
// Replace value uses of {node} with {value} and effect uses of {node} with
// {effect}. If {effect == NULL}, then use the effect input to {node}. All
// control uses will be relaxed assuming {node} cannot throw.

View File

@ -25,6 +25,7 @@
#include "src/compiler/js-generic-lowering.h"
#include "src/compiler/js-inlining.h"
#include "src/compiler/js-intrinsic-lowering.h"
#include "src/compiler/js-type-feedback.h"
#include "src/compiler/js-typed-lowering.h"
#include "src/compiler/jump-threading.h"
#include "src/compiler/load-elimination.h"
@ -46,6 +47,7 @@
#include "src/compiler/verifier.h"
#include "src/compiler/zone-pool.h"
#include "src/ostreams.h"
#include "src/type-info.h"
#include "src/utils.h"
namespace v8 {
@ -72,6 +74,7 @@ class PipelineData {
common_(nullptr),
javascript_(nullptr),
jsgraph_(nullptr),
js_type_feedback_(nullptr),
typer_(nullptr),
schedule_(nullptr),
instruction_zone_scope_(zone_pool_),
@ -111,6 +114,7 @@ class PipelineData {
common_(nullptr),
javascript_(nullptr),
jsgraph_(nullptr),
js_type_feedback_(nullptr),
typer_(nullptr),
schedule_(schedule),
instruction_zone_scope_(zone_pool_),
@ -137,6 +141,7 @@ class PipelineData {
common_(nullptr),
javascript_(nullptr),
jsgraph_(nullptr),
js_type_feedback_(nullptr),
typer_(nullptr),
schedule_(nullptr),
instruction_zone_scope_(zone_pool_),
@ -174,6 +179,10 @@ class PipelineData {
CommonOperatorBuilder* common() const { return common_; }
JSOperatorBuilder* javascript() const { return javascript_; }
JSGraph* jsgraph() const { return jsgraph_; }
JSTypeFeedbackTable* js_type_feedback() { return js_type_feedback_; }
void set_js_type_feedback(JSTypeFeedbackTable* js_type_feedback) {
js_type_feedback_ = js_type_feedback;
}
Typer* typer() const { return typer_.get(); }
LoopAssignmentAnalysis* loop_assignment() const { return loop_assignment_; }
@ -207,6 +216,7 @@ class PipelineData {
common_ = nullptr;
javascript_ = nullptr;
jsgraph_ = nullptr;
js_type_feedback_ = nullptr;
schedule_ = nullptr;
}
@ -259,6 +269,7 @@ class PipelineData {
CommonOperatorBuilder* common_;
JSOperatorBuilder* javascript_;
JSGraph* jsgraph_;
JSTypeFeedbackTable* js_type_feedback_;
// TODO(dcarney): make this into a ZoneObject.
SmartPointer<Typer> typer_;
Schedule* schedule_;
@ -310,8 +321,10 @@ class AstGraphBuilderWithPositions : public AstGraphBuilder {
AstGraphBuilderWithPositions(Zone* local_zone, CompilationInfo* info,
JSGraph* jsgraph,
LoopAssignmentAnalysis* loop_assignment,
JSTypeFeedbackTable* js_type_feedback,
SourcePositionTable* source_positions)
: AstGraphBuilder(local_zone, info, jsgraph, loop_assignment),
: AstGraphBuilder(local_zone, info, jsgraph, loop_assignment,
js_type_feedback),
source_positions_(source_positions),
start_position_(info->shared_info()->start_position()) {}
@ -419,7 +432,7 @@ struct GraphBuilderPhase {
void Run(PipelineData* data, Zone* temp_zone, bool constant_context) {
AstGraphBuilderWithPositions graph_builder(
temp_zone, data->info(), data->jsgraph(), data->loop_assignment(),
data->source_positions());
data->js_type_feedback(), data->source_positions());
bool stack_check = !data->info()->IsStub();
if (!graph_builder.CreateGraph(constant_context, stack_check)) {
data->set_compilation_failed();
@ -480,6 +493,25 @@ struct OsrDeconstructionPhase {
};
struct JSTypeFeedbackPhase {
static const char* phase_name() { return "type feedback specializing"; }
void Run(PipelineData* data, Zone* temp_zone) {
SourcePositionTable::Scope pos(data->source_positions(),
SourcePosition::Unknown());
Handle<Context> native_context(data->info()->context()->native_context());
TypeFeedbackOracle oracle(data->isolate(), temp_zone,
data->info()->unoptimized_code(),
data->info()->feedback_vector(), native_context);
GraphReducer graph_reducer(data->graph(), temp_zone);
JSTypeFeedbackSpecializer specializer(data->jsgraph(),
data->js_type_feedback(), &oracle);
AddReducer(data, &graph_reducer, &specializer);
graph_reducer.ReduceGraph();
}
};
struct TypedLoweringPhase {
static const char* phase_name() { return "typed lowering"; }
@ -884,6 +916,11 @@ Handle<Code> Pipeline::GenerateCode() {
PipelineData data(&zone_pool, info(), pipeline_statistics.get());
this->data_ = &data;
if (info()->is_type_feedback_enabled()) {
data.set_js_type_feedback(new (data.graph_zone())
JSTypeFeedbackTable(data.graph_zone()));
}
BeginPhaseKind("graph creation");
if (FLAG_trace_turbo) {
@ -951,6 +988,11 @@ Handle<Code> Pipeline::GenerateCode() {
RunPrintAndVerify("OSR deconstruction");
}
if (info()->is_type_feedback_enabled()) {
Run<JSTypeFeedbackPhase>();
RunPrintAndVerify("JSType feedback");
}
// Lower simplified operators and insert changes.
Run<SimplifiedLoweringPhase>();
RunPrintAndVerify("Lowered simplified");
@ -963,7 +1005,7 @@ Handle<Code> Pipeline::GenerateCode() {
// Lower changes that have been inserted before.
Run<ChangeLoweringPhase>();
// // TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
// TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
RunPrintAndVerify("Lowered changes", true);
Run<LateControlReductionPhase>();

View File

@ -618,10 +618,6 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kReferenceEqual: {
// (Unique, Any) -> Boolean and
// (Any, Unique) -> Boolean
if (typing == TYPED) {
CHECK(bounds(ValueInput(node, 0)).upper->Is(Type::Unique()) ||
bounds(ValueInput(node, 1)).upper->Is(Type::Unique()));
}
CheckUpperIs(node, Type::Boolean());
break;
}

View File

@ -398,6 +398,7 @@ DEFINE_BOOL(turbo_verify, DEBUG_BOOL, "verify TurboFan graphs at each phase")
DEFINE_BOOL(turbo_stats, false, "print TurboFan statistics")
DEFINE_BOOL(turbo_splitting, true, "split nodes during scheduling in TurboFan")
DEFINE_BOOL(turbo_types, true, "use typed lowering in TurboFan")
DEFINE_BOOL(turbo_type_feedback, false, "use type feedback in TurboFan")
DEFINE_BOOL(turbo_source_positions, false,
"track source code positions when building TurboFan IR")
DEFINE_IMPLICATION(trace_turbo, turbo_source_positions)

View File

@ -267,7 +267,7 @@ TypeImpl<Config>::BitsetType::Lub(i::Map* map) {
// Also, it doesn't apply elsewhere. 8-(
// We ought to find a cleaner solution for compiling stubs parameterised
// over type or class variables, esp ones with bounds...
return kDetectable;
return kDetectable & kTaggedPointer;
case DECLARED_ACCESSOR_INFO_TYPE:
case EXECUTABLE_ACCESSOR_INFO_TYPE:
case SHARED_FUNCTION_INFO_TYPE:

View File

@ -471,6 +471,8 @@
'../../src/compiler/js-intrinsic-lowering.h',
'../../src/compiler/js-operator.cc',
'../../src/compiler/js-operator.h',
'../../src/compiler/js-type-feedback.cc',
'../../src/compiler/js-type-feedback.h',
'../../src/compiler/js-typed-lowering.cc',
'../../src/compiler/js-typed-lowering.h',
'../../src/compiler/jump-threading.cc',