[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}
This commit is contained in:
parent
98f45a409c
commit
5ca1f24dd5
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user