Commit 5ca1f24d authored by mstarzinger's avatar mstarzinger Committed by Commit bot

[turbofan] Optimized lowering of DYNAMIC_LOCAL lookup slot loads.

This adds handling of JSLoadDynamicContext nodes to JSTypedLowering to
perform extension checks and an inline fast path. The fast path is a
context slot load targeting a specific context.

R=bmeurer@chromium.org
BUG=v8:4131
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#28823}
parent 98f45a40
......@@ -2968,7 +2968,7 @@ uint32_t AstGraphBuilder::ComputeBitsetForDynamicContext(Variable* variable) {
EnumSet<int, uint32_t> check_depths;
for (Scope* s = current_scope(); s != nullptr; s = s->outer_scope()) {
if (s->num_heap_slots() <= 0) continue;
if (!s->calls_sloppy_eval()) continue;
if (!s->calls_sloppy_eval() && s != variable->scope()) continue;
int depth = current_scope()->ContextChainLength(s);
if (depth > DynamicContextAccess::kMaxCheckDepth) {
return DynamicContextAccess::kFullCheckRequired;
......@@ -3247,7 +3247,15 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
name, check_bitset, depth, local->index());
value = NewNode(op, current_context());
PrepareFrameState(value, bailout_id, combine);
// TODO(mstarzinger): Hole checks are missing here when optimized.
VariableMode local_mode = local->mode();
if (local_mode == CONST_LEGACY) {
// Perform check for uninitialized legacy const variables.
Node* undefined = jsgraph()->UndefinedConstant();
value = BuildHoleCheckSilent(value, undefined, value);
} else if (local_mode == LET || local_mode == CONST) {
// Perform check for uninitialized let/const variables.
value = BuildHoleCheckThrow(value, local, value, bailout_id);
}
} else if (mode == DYNAMIC) {
uint32_t check_bitset = DynamicGlobalAccess::kFullCheckRequired;
const Operator* op = javascript()->LoadDynamicGlobal(
......
......@@ -992,6 +992,63 @@ Reduction JSTypedLowering::ReduceJSLoadDynamicGlobal(Node* node) {
}
Reduction JSTypedLowering::ReduceJSLoadDynamicContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadDynamicContext, node->opcode());
DynamicContextAccess const& access = DynamicContextAccessOf(node->op());
ContextAccess const& context_access = access.context_access();
Node* const context = NodeProperties::GetContextInput(node);
Node* const state = NodeProperties::GetFrameStateInput(node, 0);
Node* const effect = NodeProperties::GetEffectInput(node);
Node* const control = NodeProperties::GetControlInput(node);
if (access.RequiresFullCheck()) return NoChange();
// Perform checks whether the fast mode applies, by looking for any extension
// object which might shadow the optimistic declaration.
uint32_t bitset = access.check_bitset();
Node* check_true = control;
Node* check_false = graph()->NewNode(common()->Merge(0));
for (int depth = 0; bitset != 0; bitset >>= 1, depth++) {
if ((bitset & 1) == 0) continue;
Node* load = graph()->NewNode(
javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false),
context, context, effect);
Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()),
load, jsgraph()->ZeroConstant());
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check,
check_true);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
check_false->set_op(common()->Merge(check_false->InputCount() + 1));
check_false->AppendInput(graph()->zone(), if_false);
check_true = if_true;
}
// Fast case, because variable is not shadowed. Perform context slot load.
Node* fast =
graph()->NewNode(javascript()->LoadContext(context_access.depth(),
context_access.index(), false),
context, context, effect);
// Slow case, because variable potentially shadowed. Perform dynamic lookup.
uint32_t check_bitset = DynamicContextAccess::kFullCheckRequired;
Node* slow =
graph()->NewNode(javascript()->LoadDynamicContext(
access.name(), check_bitset, context_access.depth(),
context_access.index()),
context, context, state, effect, check_false);
// Replace value, effect and control uses accordingly.
Node* new_control =
graph()->NewNode(common()->Merge(2), check_true, check_false);
Node* new_effect =
graph()->NewNode(common()->EffectPhi(2), fast, slow, new_control);
Node* new_value = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), fast,
slow, new_control);
ReplaceWithValue(node, new_value, new_effect, new_control);
return Changed(new_value);
}
Reduction JSTypedLowering::ReduceJSCreateClosure(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode());
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
......@@ -1501,6 +1558,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSStoreContext(node);
case IrOpcode::kJSLoadDynamicGlobal:
return ReduceJSLoadDynamicGlobal(node);
case IrOpcode::kJSLoadDynamicContext:
return ReduceJSLoadDynamicContext(node);
case IrOpcode::kJSCreateClosure:
return ReduceJSCreateClosure(node);
case IrOpcode::kJSCreateLiteralArray:
......
......@@ -47,6 +47,7 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSStoreContext(Node* node);
Reduction ReduceJSLoadDynamicGlobal(Node* node);
Reduction ReduceJSLoadDynamicContext(Node* node);
Reduction ReduceJSEqual(Node* node, bool invert);
Reduction ReduceJSStrictEqual(Node* node, bool invert);
Reduction ReduceJSUnaryNot(Node* node);
......
......@@ -931,6 +931,40 @@ TEST_F(JSTypedLoweringTest, JSLoadDynamicGlobal) {
}
}
// -----------------------------------------------------------------------------
// JSLoadDynamicContext
TEST_F(JSTypedLoweringTest, JSLoadDynamicContext) {
Node* const context = Parameter(Type::Any());
Node* const frame_state = EmptyFrameState();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<String> name = factory()->object_string();
for (int i = 0; i < DynamicContextAccess::kMaxCheckDepth; ++i) {
uint32_t bitset = 1 << i; // Only single check.
Reduction r = Reduce(
graph()->NewNode(javascript()->LoadDynamicContext(name, bitset, 23, 42),
context, context, frame_state, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsPhi(kMachAnyTagged,
IsLoadContext(ContextAccess(23, 42, false), context), _,
IsMerge(
IsIfTrue(IsBranch(
IsReferenceEqual(
Type::Tagged(),
IsLoadContext(
ContextAccess(i, Context::EXTENSION_INDEX, false),
context),
IsNumberConstant(BitEq(0.0))),
control)),
_)));
}
}
#if V8_TURBOFAN_TARGET
// -----------------------------------------------------------------------------
......
......@@ -1299,6 +1299,15 @@ class IsLoadContextMatcher final : public NodeMatcher {
access_matcher_(access_matcher),
context_matcher_(context_matcher) {}
void DescribeTo(std::ostream* os) const final {
NodeMatcher::DescribeTo(os);
*os << " whose access (";
access_matcher_.DescribeTo(os);
*os << ") and context (";
context_matcher_.DescribeTo(os);
*os << ")";
}
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(OpParameter<ContextAccess>(node), "access",
......
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