| Index: src/compiler/js-global-object-specialization.cc
|
| diff --git a/src/compiler/js-global-object-specialization.cc b/src/compiler/js-global-object-specialization.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..59f0401f66ec06fb0583d6321e9ec382c7e38448
|
| --- /dev/null
|
| +++ b/src/compiler/js-global-object-specialization.cc
|
| @@ -0,0 +1,294 @@
|
| +// 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-global-object-specialization.h"
|
| +
|
| +#include "src/compilation-dependencies.h"
|
| +#include "src/compiler/access-builder.h"
|
| +#include "src/compiler/common-operator.h"
|
| +#include "src/compiler/js-graph.h"
|
| +#include "src/compiler/js-operator.h"
|
| +#include "src/compiler/node-properties.h"
|
| +#include "src/compiler/simplified-operator.h"
|
| +#include "src/lookup.h"
|
| +#include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker!
|
| +#include "src/type-cache.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +namespace compiler {
|
| +
|
| +struct JSGlobalObjectSpecialization::ScriptContextTableLookupResult {
|
| + Handle<Context> context;
|
| + bool immutable;
|
| + int index;
|
| +};
|
| +
|
| +
|
| +JSGlobalObjectSpecialization::JSGlobalObjectSpecialization(
|
| + Editor* editor, JSGraph* jsgraph, Flags flags,
|
| + Handle<JSGlobalObject> global_object, CompilationDependencies* dependencies)
|
| + : AdvancedReducer(editor),
|
| + jsgraph_(jsgraph),
|
| + flags_(flags),
|
| + global_object_(global_object),
|
| + script_context_table_(
|
| + global_object->native_context()->script_context_table(), isolate()),
|
| + dependencies_(dependencies),
|
| + type_cache_(TypeCache::Get()) {}
|
| +
|
| +
|
| +Reduction JSGlobalObjectSpecialization::Reduce(Node* node) {
|
| + switch (node->opcode()) {
|
| + case IrOpcode::kJSLoadGlobal:
|
| + return ReduceJSLoadGlobal(node);
|
| + case IrOpcode::kJSStoreGlobal:
|
| + return ReduceJSStoreGlobal(node);
|
| + default:
|
| + break;
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| +
|
| +Reduction JSGlobalObjectSpecialization::ReduceJSLoadGlobal(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
|
| + Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| +
|
| + // Try to lookup the name on the script context table first (lexical scoping).
|
| + ScriptContextTableLookupResult result;
|
| + if (LookupInScriptContextTable(name, &result)) {
|
| + if (result.context->is_the_hole(result.index)) return NoChange();
|
| + Node* context = jsgraph()->HeapConstant(result.context);
|
| + Node* value = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, result.index, result.immutable), context,
|
| + context, effect);
|
| + ReplaceWithValue(node, value, effect);
|
| + return Replace(value);
|
| + }
|
| +
|
| + // Lookup on the global object instead. We only deal with own data
|
| + // properties of the global object here (represented as PropertyCell).
|
| + LookupIterator it(global_object(), name, LookupIterator::OWN);
|
| + if (it.state() != LookupIterator::DATA) return NoChange();
|
| + Handle<PropertyCell> property_cell = it.GetPropertyCell();
|
| + PropertyDetails property_details = property_cell->property_details();
|
| + Handle<Object> property_cell_value(property_cell->value(), isolate());
|
| +
|
| + // Load from non-configurable, read-only data property on the global
|
| + // object can be constant-folded, even without deoptimization support.
|
| + if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
|
| + Node* value = jsgraph()->Constant(property_cell_value);
|
| + ReplaceWithValue(node, value);
|
| + return Replace(value);
|
| + }
|
| +
|
| + // Load from non-configurable, data property on the global can be lowered to
|
| + // a field load, even without deoptimization, because the property cannot be
|
| + // deleted or reconfigured to an accessor/interceptor property. Yet, if
|
| + // deoptimization support is available, we can constant-fold certain global
|
| + // properties or at least lower them to field loads annotated with more
|
| + // precise type feedback.
|
| + Type* property_cell_value_type = Type::Tagged();
|
| + if (flags() & kDeoptimizationEnabled) {
|
| + // Record a code dependency on the cell if we can benefit from the
|
| + // additional feedback, or the global property is configurable (i.e.
|
| + // can be deleted or reconfigured to an accessor property).
|
| + if (property_details.cell_type() != PropertyCellType::kMutable ||
|
| + property_details.IsConfigurable()) {
|
| + dependencies()->AssumePropertyCell(property_cell);
|
| + }
|
| +
|
| + // Load from constant/undefined global property can be constant-folded.
|
| + if ((property_details.cell_type() == PropertyCellType::kConstant ||
|
| + property_details.cell_type() == PropertyCellType::kUndefined)) {
|
| + Node* value = jsgraph()->Constant(property_cell_value);
|
| + ReplaceWithValue(node, value);
|
| + return Replace(value);
|
| + }
|
| +
|
| + // Load from constant type cell can benefit from type feedback.
|
| + if (property_details.cell_type() == PropertyCellType::kConstantType) {
|
| + // Compute proper type based on the current value in the cell.
|
| + if (property_cell_value->IsSmi()) {
|
| + property_cell_value_type = type_cache_.kSmi;
|
| + } else if (property_cell_value->IsNumber()) {
|
| + property_cell_value_type = type_cache_.kHeapNumber;
|
| + } else {
|
| + Handle<Map> property_cell_value_map(
|
| + Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
|
| + property_cell_value_type =
|
| + Type::Class(property_cell_value_map, graph()->zone());
|
| + }
|
| + }
|
| + } else if (property_details.IsConfigurable()) {
|
| + // Access to configurable global properties requires deoptimization support.
|
| + return NoChange();
|
| + }
|
| + Node* value = effect = graph()->NewNode(
|
| + simplified()->LoadField(
|
| + AccessBuilder::ForPropertyCellValue(property_cell_value_type)),
|
| + jsgraph()->HeapConstant(property_cell), effect, control);
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| +}
|
| +
|
| +
|
| +Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
|
| + Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
|
| + Node* value = NodeProperties::GetValueInput(node, 0);
|
| + Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| +
|
| + // Try to lookup the name on the script context table first (lexical scoping).
|
| + ScriptContextTableLookupResult result;
|
| + if (LookupInScriptContextTable(name, &result)) {
|
| + if (result.context->is_the_hole(result.index)) return NoChange();
|
| + if (result.immutable) return NoChange();
|
| + Node* context = jsgraph()->HeapConstant(result.context);
|
| + effect = graph()->NewNode(javascript()->StoreContext(0, result.index),
|
| + context, value, context, effect, control);
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| + }
|
| +
|
| + // Lookup on the global object instead. We only deal with own data
|
| + // properties of the global object here (represented as PropertyCell).
|
| + LookupIterator it(global_object(), name, LookupIterator::OWN);
|
| + if (it.state() != LookupIterator::DATA) return NoChange();
|
| + Handle<PropertyCell> property_cell = it.GetPropertyCell();
|
| + PropertyDetails property_details = property_cell->property_details();
|
| + Handle<Object> property_cell_value(property_cell->value(), isolate());
|
| +
|
| + // Don't even bother trying to lower stores to read-only data properties.
|
| + if (property_details.IsReadOnly()) return NoChange();
|
| + switch (property_details.cell_type()) {
|
| + case PropertyCellType::kUndefined: {
|
| + return NoChange();
|
| + }
|
| + case PropertyCellType::kConstant: {
|
| + // Store to constant property cell requires deoptimization support,
|
| + // because we might even need to eager deoptimize for mismatch.
|
| + if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| + dependencies()->AssumePropertyCell(property_cell);
|
| + Node* check =
|
| + graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value,
|
| + jsgraph()->Constant(property_cell_value));
|
| + Node* branch =
|
| + graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
|
| + Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
| + Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
|
| + effect, if_false);
|
| + // TODO(bmeurer): This should be on the AdvancedReducer somehow.
|
| + NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
|
| + control = graph()->NewNode(common()->IfTrue(), branch);
|
| + break;
|
| + }
|
| + case PropertyCellType::kConstantType: {
|
| + // Store to constant-type property cell requires deoptimization support,
|
| + // because we might even need to eager deoptimize for mismatch.
|
| + if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| + dependencies()->AssumePropertyCell(property_cell);
|
| + Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
|
| + if (property_cell_value->IsHeapObject()) {
|
| + Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
|
| + check, control);
|
| + Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
|
| + Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
|
| + effect, if_true);
|
| + // TODO(bmeurer): This should be on the AdvancedReducer somehow.
|
| + NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
|
| + control = graph()->NewNode(common()->IfFalse(), branch);
|
| + Node* value_map =
|
| + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
|
| + value, effect, control);
|
| + Handle<Map> property_cell_value_map(
|
| + Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
|
| + check = graph()->NewNode(
|
| + simplified()->ReferenceEqual(Type::Any()), value_map,
|
| + jsgraph()->HeapConstant(property_cell_value_map));
|
| + }
|
| + Node* branch =
|
| + graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
|
| + Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
| + Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
|
| + effect, if_false);
|
| + // TODO(bmeurer): This should be on the AdvancedReducer somehow.
|
| + NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
|
| + control = graph()->NewNode(common()->IfTrue(), branch);
|
| + effect = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForPropertyCellValue()),
|
| + jsgraph()->HeapConstant(property_cell), value, effect, control);
|
| + break;
|
| + }
|
| + case PropertyCellType::kMutable: {
|
| + // Store to non-configurable, data property on the global can be lowered
|
| + // to a field store, even without deoptimization, because the property
|
| + // cannot be deleted or reconfigured to an accessor/interceptor property.
|
| + if (property_details.IsConfigurable()) {
|
| + // With deoptimization support, we can lower stores even to configurable
|
| + // data properties on the global object, by adding a code dependency on
|
| + // the cell.
|
| + if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| + dependencies()->AssumePropertyCell(property_cell);
|
| + }
|
| + effect = graph()->NewNode(
|
| + simplified()->StoreField(AccessBuilder::ForPropertyCellValue()),
|
| + jsgraph()->HeapConstant(property_cell), value, effect, control);
|
| + break;
|
| + }
|
| + }
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| +}
|
| +
|
| +
|
| +bool JSGlobalObjectSpecialization::LookupInScriptContextTable(
|
| + Handle<Name> name, ScriptContextTableLookupResult* result) {
|
| + if (!name->IsString()) return false;
|
| + ScriptContextTable::LookupResult lookup_result;
|
| + if (!ScriptContextTable::Lookup(script_context_table(),
|
| + Handle<String>::cast(name), &lookup_result)) {
|
| + return false;
|
| + }
|
| + Handle<Context> script_context = ScriptContextTable::GetContext(
|
| + script_context_table(), lookup_result.context_index);
|
| + result->context = script_context;
|
| + result->immutable = IsImmutableVariableMode(lookup_result.mode);
|
| + result->index = lookup_result.slot_index;
|
| + return true;
|
| +}
|
| +
|
| +
|
| +Graph* JSGlobalObjectSpecialization::graph() const {
|
| + return jsgraph()->graph();
|
| +}
|
| +
|
| +
|
| +Isolate* JSGlobalObjectSpecialization::isolate() const {
|
| + return jsgraph()->isolate();
|
| +}
|
| +
|
| +
|
| +CommonOperatorBuilder* JSGlobalObjectSpecialization::common() const {
|
| + return jsgraph()->common();
|
| +}
|
| +
|
| +
|
| +JSOperatorBuilder* JSGlobalObjectSpecialization::javascript() const {
|
| + return jsgraph()->javascript();
|
| +}
|
| +
|
| +
|
| +SimplifiedOperatorBuilder* JSGlobalObjectSpecialization::simplified() const {
|
| + return jsgraph()->simplified();
|
| +}
|
| +
|
| +} // namespace compiler
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|