Commit 6fbf7903 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add initial support for global specialization.

Introduce a new JSGlobalSpecialization advanced reducer that runs
during the initial inlining and context specialization, and specializes
the graph to the globals of the native context.  Currently we assume
that we do not inline cross native context, but long-term we will grab
the global object from the JSLoadGlobal/JSStoreGlobal feedback (with the
new global load/store ICs that are currently in the workings), and then
this whole specialization will be fully compositional even across
cross-context inlining.

Note that we cannot really handle most of the stores to global object
property cells because TurboFan doesn't have a mechanism to enforce
certain representations.  Also note that we cannot yet fully benefit
from the type feedback collected on the global object property cells,
because the type system cannot deal with maps in a reasonable way.

CQ_INCLUDE_TRYBOTS=tryserver.v8:v8_linux_nosnap_rel
R=jarin@chromium.org
BUG=v8:4470
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#31139}
parent 4979b8e4
......@@ -755,6 +755,8 @@ source_set("v8_base") {
"src/compiler/js-frame-specialization.h",
"src/compiler/js-generic-lowering.cc",
"src/compiler/js-generic-lowering.h",
"src/compiler/js-global-specialization.cc",
"src/compiler/js-global-specialization.h",
"src/compiler/js-graph.cc",
"src/compiler/js-graph.h",
"src/compiler/js-inlining.cc",
......
......@@ -445,6 +445,9 @@ OptimizedCompileJob::Status OptimizedCompileJob::CreateGraph() {
FLAG_turbo_asm_deoptimization) {
info()->MarkAsDeoptimizationEnabled();
}
if (info()->has_global_object() && FLAG_native_context_specialization) {
info()->MarkAsNativeContextSpecializing();
}
Timer t(this, &time_taken_to_create_graph_);
compiler::Pipeline pipeline(info());
......@@ -772,7 +775,8 @@ static void InsertCodeIntoOptimizedCodeMap(CompilationInfo* info) {
Handle<Code> code = info->code();
if (code->kind() != Code::OPTIMIZED_FUNCTION) return; // Nothing to do.
// Context specialization folds-in the context, so no sharing can occur.
// Function context specialization folds-in the function context,
// so no sharing can occur.
if (info->is_function_context_specializing()) return;
// Frame specialization implies function context specialization.
DCHECK(!info->is_frame_specializing());
......@@ -790,11 +794,12 @@ static void InsertCodeIntoOptimizedCodeMap(CompilationInfo* info) {
literals, info->osr_ast_id());
}
// Do not cache context-independent code compiled for OSR.
// Do not cache (native) context-independent code compiled for OSR.
if (code->is_turbofanned() && info->is_osr()) return;
// Cache optimized context-independent code.
if (FLAG_turbo_cache_shared_code && code->is_turbofanned()) {
// Cache optimized (native) context-independent code.
if (FLAG_turbo_cache_shared_code && code->is_turbofanned() &&
!info->is_native_context_specializing()) {
DCHECK(!info->is_function_context_specializing());
DCHECK(info->osr_ast_id().IsNone());
Handle<SharedFunctionInfo> shared(function->shared());
......
......@@ -117,14 +117,15 @@ class CompilationInfo {
kSerializing = 1 << 7,
kFunctionContextSpecializing = 1 << 8,
kFrameSpecializing = 1 << 9,
kInliningEnabled = 1 << 10,
kTypingEnabled = 1 << 11,
kDisableFutureOptimization = 1 << 12,
kSplittingEnabled = 1 << 13,
kTypeFeedbackEnabled = 1 << 14,
kDeoptimizationEnabled = 1 << 15,
kSourcePositionsEnabled = 1 << 16,
kFirstCompile = 1 << 17,
kNativeContextSpecializing = 1 << 10,
kInliningEnabled = 1 << 11,
kTypingEnabled = 1 << 12,
kDisableFutureOptimization = 1 << 13,
kSplittingEnabled = 1 << 14,
kTypeFeedbackEnabled = 1 << 15,
kDeoptimizationEnabled = 1 << 16,
kSourcePositionsEnabled = 1 << 17,
kFirstCompile = 1 << 18,
};
explicit CompilationInfo(ParseInfo* parse_info);
......@@ -227,6 +228,14 @@ class CompilationInfo {
bool is_frame_specializing() const { return GetFlag(kFrameSpecializing); }
void MarkAsNativeContextSpecializing() {
SetFlag(kNativeContextSpecializing);
}
bool is_native_context_specializing() const {
return GetFlag(kNativeContextSpecializing);
}
void MarkAsTypeFeedbackEnabled() { SetFlag(kTypeFeedbackEnabled); }
bool is_type_feedback_enabled() const {
......
......@@ -151,8 +151,14 @@ FieldAccess AccessBuilder::ForContextSlot(size_t index) {
// static
FieldAccess AccessBuilder::ForPropertyCellValue() {
return ForPropertyCellValue(Type::Tagged());
}
// static
FieldAccess AccessBuilder::ForPropertyCellValue(Type* type) {
FieldAccess access = {kTaggedBase, PropertyCell::kValueOffset, Handle<Name>(),
Type::Any(), kMachAnyTagged};
type, kMachAnyTagged};
return access;
}
......
......@@ -69,6 +69,7 @@ class AccessBuilder final : public AllStatic {
// Provides access to PropertyCell::value() field.
static FieldAccess ForPropertyCellValue();
static FieldAccess ForPropertyCellValue(Type* type);
// Provides access to SharedFunctionInfo::feedback_vector() field.
static FieldAccess ForSharedFunctionInfoTypeFeedbackVector();
......
......@@ -95,6 +95,9 @@ Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
// Success. The context load can be replaced with the constant.
// TODO(titzer): record the specialization for sharing code across multiple
// contexts that have the same value in the corresponding context slot.
if (value->IsConsString()) {
value = String::Flatten(Handle<String>::cast(value), TENURED);
}
Node* constant = jsgraph_->Constant(value);
ReplaceWithValue(node, constant);
return Replace(constant);
......
// 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-specialization.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/contexts.h"
#include "src/lookup.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
JSGlobalSpecialization::JSGlobalSpecialization(
Editor* editor, JSGraph* jsgraph, Flags flags,
Handle<GlobalObject> global_object, CompilationDependencies* dependencies)
: AdvancedReducer(editor),
jsgraph_(jsgraph),
flags_(flags),
global_object_(global_object),
dependencies_(dependencies),
simplified_(graph()->zone()) {}
Reduction JSGlobalSpecialization::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSLoadGlobal:
return ReduceJSLoadGlobal(node);
case IrOpcode::kJSStoreGlobal:
return ReduceJSStoreGlobal(node);
default:
break;
}
return NoChange();
}
Reduction JSGlobalSpecialization::ReduceJSLoadGlobal(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
Node* effect = NodeProperties::GetEffectInput(node);
// Try to lookup the name on the script context table first (lexical scoping).
if (name->IsString()) {
Handle<ScriptContextTable> script_context_table(
global_object()->native_context()->script_context_table());
ScriptContextTable::LookupResult result;
if (ScriptContextTable::Lookup(script_context_table,
Handle<String>::cast(name), &result)) {
Handle<Context> script_context = ScriptContextTable::GetContext(
script_context_table, result.context_index);
if (script_context->is_the_hole(result.slot_index)) {
// TODO(bmeurer): Is this relevant in practice?
return NoChange();
}
Node* context = jsgraph()->Constant(script_context);
Node* value = effect = graph()->NewNode(
javascript()->LoadContext(0, result.slot_index,
IsImmutableVariableMode(result.mode)),
context, context, effect);
return Replace(node, value, effect);
}
}
// Lookup on the global object instead.
LookupIterator it(global_object(), name, LookupIterator::OWN);
if (it.state() == LookupIterator::DATA) {
return ReduceLoadFromPropertyCell(node, it.GetPropertyCell());
}
return NoChange();
}
Reduction JSGlobalSpecialization::ReduceJSStoreGlobal(Node* node) {
DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
Node* value = NodeProperties::GetValueInput(node, 2);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Try to lookup the name on the script context table first (lexical scoping).
if (name->IsString()) {
Handle<ScriptContextTable> script_context_table(
global_object()->native_context()->script_context_table());
ScriptContextTable::LookupResult result;
if (ScriptContextTable::Lookup(script_context_table,
Handle<String>::cast(name), &result)) {
if (IsImmutableVariableMode(result.mode)) return NoChange();
Handle<Context> script_context = ScriptContextTable::GetContext(
script_context_table, result.context_index);
if (script_context->is_the_hole(result.slot_index)) {
// TODO(bmeurer): Is this relevant in practice?
return NoChange();
}
Node* context = jsgraph()->Constant(script_context);
effect =
graph()->NewNode(javascript()->StoreContext(0, result.slot_index),
context, value, context, effect, control);
return Replace(node, value, effect, control);
}
}
// Lookup on the global object instead.
LookupIterator it(global_object(), name, LookupIterator::OWN);
if (it.state() == LookupIterator::DATA) {
return ReduceStoreToPropertyCell(node, it.GetPropertyCell());
}
return NoChange();
}
Reduction JSGlobalSpecialization::ReduceLoadFromPropertyCell(
Node* node, Handle<PropertyCell> property_cell) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// We only specialize global data property access.
PropertyDetails property_details = property_cell->property_details();
DCHECK_EQ(kData, property_details.kind());
Handle<Object> property_cell_value(property_cell->value(), isolate());
DCHECK(!property_cell_value->IsTheHole());
// 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()) {
return Replace(node, property_cell_value);
}
// Load from constant/undefined global property can be constant-folded
// with deoptimization support, by adding a code dependency on the cell.
if ((property_details.cell_type() == PropertyCellType::kConstant ||
property_details.cell_type() == PropertyCellType::kUndefined) &&
(flags() & kDeoptimizationEnabled)) {
dependencies()->AssumePropertyCell(property_cell);
return Replace(node, property_cell_value);
}
// Not much we can do if we run the generic pipeline here.
if (!(flags() & kTypingEnabled)) return NoChange();
// Load from constant type global property can benefit from representation
// (and map) feedback with deoptimization support (requires code dependency).
if (property_details.cell_type() == PropertyCellType::kConstantType &&
(flags() & kDeoptimizationEnabled)) {
dependencies()->AssumePropertyCell(property_cell);
Type* property_cell_value_type = Type::Any();
switch (property_cell->GetConstantType()) {
case PropertyCellConstantType::kSmi:
property_cell_value_type = Type::Intersect(
Type::SignedSmall(), Type::TaggedSigned(), graph()->zone());
break;
case PropertyCellConstantType::kStableMap: {
// TODO(bmeurer): Determine type based on the map's instance type.
property_cell_value_type = Type::TaggedPointer();
break;
}
}
Node* value = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForPropertyCellValue(property_cell_value_type)),
jsgraph()->Constant(property_cell), effect, control);
return Replace(node, value, effect);
}
// 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.
if (property_details.IsConfigurable()) {
// With deoptimization support, we can lower loads even from configurable
// data properties on the global object, by adding a code dependency on
// the cell.
if (!(flags() & kDeoptimizationEnabled)) return NoChange();
dependencies()->AssumePropertyCell(property_cell);
}
Node* value = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForPropertyCellValue()),
jsgraph()->Constant(property_cell), effect, control);
return Replace(node, value, effect);
}
Reduction JSGlobalSpecialization::ReduceStoreToPropertyCell(
Node* node, Handle<PropertyCell> property_cell) {
Node* value = NodeProperties::GetValueInput(node, 2);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// We only specialize global data property access.
PropertyDetails property_details = property_cell->property_details();
DCHECK_EQ(kData, property_details.kind());
Handle<Object> property_cell_value(property_cell->value(), isolate());
DCHECK(!property_cell_value->IsTheHole());
// Don't even bother trying to lower stores to read-only data properties.
if (property_details.IsReadOnly()) return NoChange();
// Not much we can do if we run the generic pipeline here.
if (!(flags() & kTypingEnabled)) return NoChange();
// TODO(bmeurer): For now we deal only with cells in mutable state.
if (property_details.cell_type() != PropertyCellType::kMutable) {
return NoChange();
}
// 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()->Constant(property_cell), value, effect, control);
return Replace(node, value, effect, control);
}
Reduction JSGlobalSpecialization::Replace(Node* node, Handle<Object> value) {
if (value->IsConsString()) {
value = String::Flatten(Handle<String>::cast(value), TENURED);
}
return Replace(node, jsgraph()->Constant(value));
}
Graph* JSGlobalSpecialization::graph() const { return jsgraph()->graph(); }
Isolate* JSGlobalSpecialization::isolate() const {
return jsgraph()->isolate();
}
JSOperatorBuilder* JSGlobalSpecialization::javascript() const {
return jsgraph()->javascript();
}
} // namespace compiler
} // namespace internal
} // namespace v8
// 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_GLOBAL_SPECIALIZATION_H_
#define V8_COMPILER_JS_GLOBAL_SPECIALIZATION_H_
#include "src/base/flags.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/simplified-operator.h"
namespace v8 {
namespace internal {
// Forward declarations.
class CompilationDependencies;
namespace compiler {
// Forward declarations.
class JSGraph;
class JSOperatorBuilder;
// Specializes a given JSGraph to a given GlobalObject, potentially constant
// folding some {LoadGlobal} nodes or strength reducing some {StoreGlobal}
// nodes.
class JSGlobalSpecialization final : public AdvancedReducer {
public:
// Flags that control the mode of operation.
enum Flag {
kNoFlags = 0u,
kDeoptimizationEnabled = 1u << 0,
kTypingEnabled = 1u << 1
};
typedef base::Flags<Flag> Flags;
JSGlobalSpecialization(Editor* editor, JSGraph* jsgraph, Flags flags,
Handle<GlobalObject> global_object,
CompilationDependencies* dependencies);
Reduction Reduce(Node* node) final;
private:
Reduction ReduceJSLoadGlobal(Node* node);
Reduction ReduceJSStoreGlobal(Node* node);
Reduction ReduceLoadFromPropertyCell(Node* node,
Handle<PropertyCell> property_cell);
Reduction ReduceStoreToPropertyCell(Node* node,
Handle<PropertyCell> property_cell);
Reduction Replace(Node* node, Node* value, Node* effect = nullptr,
Node* control = nullptr) {
ReplaceWithValue(node, value, effect, control);
return Changed(value);
}
Reduction Replace(Node* node, Handle<Object> value);
Graph* graph() const;
JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const;
JSOperatorBuilder* javascript() const;
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
Flags flags() const { return flags_; }
Handle<GlobalObject> global_object() const { return global_object_; }
CompilationDependencies* dependencies() const { return dependencies_; }
JSGraph* const jsgraph_;
Flags const flags_;
Handle<GlobalObject> global_object_;
CompilationDependencies* const dependencies_;
SimplifiedOperatorBuilder simplified_;
DISALLOW_COPY_AND_ASSIGN(JSGlobalSpecialization);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_JS_GLOBAL_SPECIALIZATION_H_
......@@ -1401,7 +1401,7 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) {
function, effect, control);
NodeProperties::ReplaceContextInput(node, context);
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
if (is_strict(p.language_mode())) {
if (p.AllowTailCalls()) {
flags |= CallDescriptor::kSupportsTailCalls;
}
NodeProperties::ChangeOp(node,
......
......@@ -30,6 +30,7 @@
#include "src/compiler/js-context-specialization.h"
#include "src/compiler/js-frame-specialization.h"
#include "src/compiler/js-generic-lowering.h"
#include "src/compiler/js-global-specialization.h"
#include "src/compiler/js-inlining.h"
#include "src/compiler/js-intrinsic-lowering.h"
#include "src/compiler/js-type-feedback.h"
......@@ -511,6 +512,20 @@ struct InliningPhase {
: MaybeHandle<Context>());
JSFrameSpecialization frame_specialization(data->info()->osr_frame(),
data->jsgraph());
JSGlobalSpecialization::Flags global_flags =
JSGlobalSpecialization::kNoFlags;
if (data->info()->is_deoptimization_enabled()) {
global_flags |= JSGlobalSpecialization::kDeoptimizationEnabled;
}
if (data->info()->is_typing_enabled()) {
global_flags |= JSGlobalSpecialization::kTypingEnabled;
}
JSGlobalSpecialization global_specialization(
&graph_reducer, data->jsgraph(), global_flags,
data->info()->has_global_object()
? handle(data->info()->global_object())
: Handle<GlobalObject>(),
data->info()->dependencies());
JSInliner inliner(&graph_reducer, data->info()->is_inlining_enabled()
? JSInliner::kGeneralInlining
: JSInliner::kRestrictedInlining,
......@@ -520,6 +535,9 @@ struct InliningPhase {
if (data->info()->is_frame_specializing()) {
AddReducer(data, &graph_reducer, &frame_specialization);
}
if (data->info()->is_native_context_specializing()) {
AddReducer(data, &graph_reducer, &global_specialization);
}
AddReducer(data, &graph_reducer, &context_specialization);
AddReducer(data, &graph_reducer, &inliner);
graph_reducer.ReduceGraph();
......
......@@ -432,6 +432,8 @@ DEFINE_BOOL(turbo_source_positions, false,
DEFINE_IMPLICATION(trace_turbo, turbo_source_positions)
DEFINE_BOOL(function_context_specialization, false,
"enable function context specialization in TurboFan")
DEFINE_BOOL(native_context_specialization, true,
"enable native context specialization in TurboFan")
DEFINE_BOOL(turbo_inlining, false, "enable inlining in TurboFan")
DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining")
DEFINE_BOOL(loop_assignment_analysis, true, "perform loop assignment analysis")
......
......@@ -400,6 +400,7 @@ TEST(OptimizedCodeSharing2) {
if (FLAG_stress_compaction) return;
FLAG_allow_natives_syntax = true;
FLAG_cache_optimized_code = true;
FLAG_native_context_specialization = false;
FLAG_turbo_cache_shared_code = true;
const char* flag = "--turbo-filter=*";
FlagList::SetFlagsFromString(flag, StrLength(flag));
......@@ -455,6 +456,7 @@ TEST(OptimizedCodeSharing3) {
if (FLAG_stress_compaction) return;
FLAG_allow_natives_syntax = true;
FLAG_cache_optimized_code = true;
FLAG_native_context_specialization = false;
FLAG_turbo_cache_shared_code = true;
const char* flag = "--turbo-filter=*";
FlagList::SetFlagsFromString(flag, StrLength(flag));
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --nostress-opt --turbo
// Flags: --nonative-context-specialization
var p0 = new Object();
var p1 = new Object();
......
......@@ -520,6 +520,8 @@
'../../src/compiler/js-frame-specialization.h',
'../../src/compiler/js-generic-lowering.cc',
'../../src/compiler/js-generic-lowering.h',
'../../src/compiler/js-global-specialization.cc',
'../../src/compiler/js-global-specialization.h',
'../../src/compiler/js-graph.cc',
'../../src/compiler/js-graph.h',
'../../src/compiler/js-inlining.cc',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment