Commit fd8cebb1 authored by neis's avatar neis Committed by Commit bot

[compiler] Generalize JSContextSpecialization.

With this CL, context loads and stores are "strengthened" by reducing
the incoming context chain and decreasing the depth accordingly,
whenever possible.  This enables more opportunities for specialization
and will let us easily add module context specialization later.

BUG=

Review-Url: https://codereview.chromium.org/2559173003
Cr-Commit-Position: refs/heads/master@{#42334}
parent a6fe748d
...@@ -28,50 +28,81 @@ Reduction JSContextSpecialization::Reduce(Node* node) { ...@@ -28,50 +28,81 @@ Reduction JSContextSpecialization::Reduce(Node* node) {
return NoChange(); return NoChange();
} }
Reduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
Node* new_context,
size_t new_depth) {
DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
const ContextAccess& access = ContextAccessOf(node->op());
DCHECK_LE(new_depth, access.depth());
if (new_depth == access.depth() &&
new_context == NodeProperties::GetContextInput(node)) {
return NoChange();
}
MaybeHandle<Context> JSContextSpecialization::GetSpecializationContext( const Operator* op = jsgraph_->javascript()->LoadContext(
Node* node) { new_depth, access.index(), access.immutable());
DCHECK(node->opcode() == IrOpcode::kJSLoadContext || NodeProperties::ReplaceContextInput(node, new_context);
node->opcode() == IrOpcode::kJSStoreContext); NodeProperties::ChangeOp(node, op);
Node* const object = NodeProperties::GetContextInput(node); return Changed(node);
return NodeProperties::GetSpecializationContext(object, context());
} }
Reduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
Node* new_context,
size_t new_depth) {
DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
const ContextAccess& access = ContextAccessOf(node->op());
DCHECK_LE(new_depth, access.depth());
if (new_depth == access.depth() &&
new_context == NodeProperties::GetContextInput(node)) {
return NoChange();
}
const Operator* op =
jsgraph_->javascript()->StoreContext(new_depth, access.index());
NodeProperties::ReplaceContextInput(node, new_context);
NodeProperties::ChangeOp(node, op);
return Changed(node);
}
Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) { Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
// Get the specialization context from the node.
Handle<Context> context;
if (!GetSpecializationContext(node).ToHandle(&context)) return NoChange();
// Find the right parent context.
const ContextAccess& access = ContextAccessOf(node->op()); const ContextAccess& access = ContextAccessOf(node->op());
for (size_t i = access.depth(); i > 0; --i) { size_t depth = access.depth();
context = handle(context->previous(), isolate());
// First walk up the context chain in the graph as far as possible.
Node* outer = NodeProperties::GetOuterContext(node, &depth);
Handle<Context> concrete;
if (!NodeProperties::GetSpecializationContext(outer, context())
.ToHandle(&concrete)) {
// We do not have a concrete context object, so we can only partially reduce
// the load by folding-in the outer context node.
return SimplifyJSLoadContext(node, outer, depth);
} }
// If the access itself is mutable, only fold-in the parent. // Now walk up the concrete context chain for the remaining depth.
if (!access.immutable()) { for (; depth > 0; --depth) {
// The access does not have to look up a parent, nothing to fold. concrete = handle(concrete->previous(), isolate());
if (access.depth() == 0) {
return NoChange();
} }
const Operator* op = jsgraph_->javascript()->LoadContext(
0, access.index(), access.immutable()); if (!access.immutable()) {
NodeProperties::ReplaceContextInput(node, jsgraph_->Constant(context)); // We found the requested context object but since the context slot is
NodeProperties::ChangeOp(node, op); // mutable we can only partially reduce the load.
return Changed(node); return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
} }
Handle<Object> value =
handle(context->get(static_cast<int>(access.index())), isolate());
// Even though the context slot is immutable, the context might have escaped // Even though the context slot is immutable, the context might have escaped
// before the function to which it belongs has initialized the slot. // before the function to which it belongs has initialized the slot.
// We must be conservative and check if the value in the slot is currently the // We must be conservative and check if the value in the slot is currently
// hole or undefined. If it is neither of these, then it must be initialized. // the hole or undefined. Only if it is neither of these, can we be sure that
// it won't change anymore.
Handle<Object> value(concrete->get(static_cast<int>(access.index())),
isolate());
if (value->IsUndefined(isolate()) || value->IsTheHole(isolate())) { if (value->IsUndefined(isolate()) || value->IsTheHole(isolate())) {
return NoChange(); return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
} }
// Success. The context load can be replaced with the constant. // Success. The context load can be replaced with the constant.
...@@ -86,24 +117,27 @@ Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) { ...@@ -86,24 +117,27 @@ Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) { Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode()); DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
// Get the specialization context from the node.
Handle<Context> context;
if (!GetSpecializationContext(node).ToHandle(&context)) return NoChange();
// The access does not have to look up a parent, nothing to fold.
const ContextAccess& access = ContextAccessOf(node->op()); const ContextAccess& access = ContextAccessOf(node->op());
if (access.depth() == 0) { size_t depth = access.depth();
return NoChange();
// First walk up the context chain in the graph until we reduce the depth to 0
// or hit a node that does not have a CreateXYZContext operator.
Node* outer = NodeProperties::GetOuterContext(node, &depth);
Handle<Context> concrete;
if (!NodeProperties::GetSpecializationContext(outer, context())
.ToHandle(&concrete)) {
// We do not have a concrete context object, so we can only partially reduce
// the load by folding-in the outer context node.
return SimplifyJSStoreContext(node, outer, depth);
} }
// Find the right parent context. // Now walk up the concrete context chain for the remaining depth.
for (size_t i = access.depth(); i > 0; --i) { for (; depth > 0; --depth) {
context = handle(context->previous(), isolate()); concrete = handle(concrete->previous(), isolate());
} }
NodeProperties::ReplaceContextInput(node, jsgraph_->Constant(context)); return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
NodeProperties::ChangeOp(node, javascript()->StoreContext(0, access.index()));
return Changed(node);
} }
......
...@@ -30,8 +30,10 @@ class JSContextSpecialization final : public AdvancedReducer { ...@@ -30,8 +30,10 @@ class JSContextSpecialization final : public AdvancedReducer {
Reduction ReduceJSLoadContext(Node* node); Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSStoreContext(Node* node); Reduction ReduceJSStoreContext(Node* node);
// Returns the {Context} to specialize {node} to (if any). Reduction SimplifyJSStoreContext(Node* node, Node* new_context,
MaybeHandle<Context> GetSpecializationContext(Node* node); size_t new_depth);
Reduction SimplifyJSLoadContext(Node* node, Node* new_context,
size_t new_depth);
Isolate* isolate() const; Isolate* isolate() const;
JSOperatorBuilder* javascript() const; JSOperatorBuilder* javascript() const;
......
...@@ -338,6 +338,17 @@ MaybeHandle<Context> NodeProperties::GetSpecializationContext( ...@@ -338,6 +338,17 @@ MaybeHandle<Context> NodeProperties::GetSpecializationContext(
} }
// static
Node* NodeProperties::GetOuterContext(Node* node, size_t* depth) {
Node* context = NodeProperties::GetContextInput(node);
while (*depth > 0 &&
IrOpcode::IsContextChainExtendingOpcode(context->opcode())) {
context = NodeProperties::GetContextInput(context);
(*depth)--;
}
return context;
}
// static // static
Type* NodeProperties::GetTypeOrAny(Node* node) { Type* NodeProperties::GetTypeOrAny(Node* node) {
return IsTyped(node) ? node->type() : Type::Any(); return IsTyped(node) ? node->type() : Type::Any();
......
...@@ -132,6 +132,11 @@ class V8_EXPORT_PRIVATE NodeProperties final { ...@@ -132,6 +132,11 @@ class V8_EXPORT_PRIVATE NodeProperties final {
static MaybeHandle<Context> GetSpecializationContext( static MaybeHandle<Context> GetSpecializationContext(
Node* node, MaybeHandle<Context> context = MaybeHandle<Context>()); Node* node, MaybeHandle<Context> context = MaybeHandle<Context>());
// Walk up the context chain from the given {node} until we reduce the {depth}
// to 0 or hit a node that does not extend the context chain ({depth} will be
// updated accordingly).
static Node* GetOuterContext(Node* node, size_t* depth);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Type. // Type.
......
...@@ -800,6 +800,10 @@ class V8_EXPORT_PRIVATE IrOpcode { ...@@ -800,6 +800,10 @@ class V8_EXPORT_PRIVATE IrOpcode {
(kNumberEqual <= value && value <= kStringLessThanOrEqual) || (kNumberEqual <= value && value <= kStringLessThanOrEqual) ||
(kWord32Equal <= value && value <= kFloat64LessThanOrEqual); (kWord32Equal <= value && value <= kFloat64LessThanOrEqual);
} }
static bool IsContextChainExtendingOpcode(Value value) {
return kJSCreateFunctionContext <= value && value <= kJSCreateScriptContext;
}
}; };
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&, IrOpcode::Value); V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&, IrOpcode::Value);
......
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