| Index: src/compiler/ast-graph-builder.cc
|
| diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc
|
| index fd28e7f624181ae95abe5cd2fe4c45c312351560..32d1eb45f0da8f8e9018286619f0a0b4481a8911 100644
|
| --- a/src/compiler/ast-graph-builder.cc
|
| +++ b/src/compiler/ast-graph-builder.cc
|
| @@ -3062,6 +3062,17 @@ VectorSlotPair AstGraphBuilder::CreateVectorSlotPair(
|
| }
|
|
|
|
|
| +namespace {
|
| +
|
| +// Limit of context chain length to which inline check is possible.
|
| +const int kMaxCheckDepth = 30;
|
| +
|
| +// Sentinel for {TryLoadDynamicVariable} disabling inline checks.
|
| +const uint32_t kFullCheckRequired = -1;
|
| +
|
| +} // namespace
|
| +
|
| +
|
| uint32_t AstGraphBuilder::ComputeBitsetForDynamicGlobal(Variable* variable) {
|
| DCHECK_EQ(DYNAMIC_GLOBAL, variable->mode());
|
| bool found_eval_scope = false;
|
| @@ -3074,9 +3085,7 @@ uint32_t AstGraphBuilder::ComputeBitsetForDynamicGlobal(Variable* variable) {
|
| if (s->is_eval_scope()) found_eval_scope = true;
|
| if (!s->calls_sloppy_eval() && !found_eval_scope) continue;
|
| int depth = current_scope()->ContextChainLength(s);
|
| - if (depth > DynamicGlobalAccess::kMaxCheckDepth) {
|
| - return DynamicGlobalAccess::kFullCheckRequired;
|
| - }
|
| + if (depth > kMaxCheckDepth) return kFullCheckRequired;
|
| check_depths |= 1 << depth;
|
| }
|
| return check_depths;
|
| @@ -3090,9 +3099,7 @@ uint32_t AstGraphBuilder::ComputeBitsetForDynamicContext(Variable* variable) {
|
| if (s->num_heap_slots() <= 0) continue;
|
| if (!s->calls_sloppy_eval() && s != variable->scope()) continue;
|
| int depth = current_scope()->ContextChainLength(s);
|
| - if (depth > DynamicContextAccess::kMaxCheckDepth) {
|
| - return DynamicContextAccess::kFullCheckRequired;
|
| - }
|
| + if (depth > kMaxCheckDepth) return kFullCheckRequired;
|
| check_depths |= 1 << depth;
|
| if (s == variable->scope()) break;
|
| }
|
| @@ -3380,40 +3387,15 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
|
| }
|
| case VariableLocation::LOOKUP: {
|
| // Dynamic lookup of context variable (anywhere in the chain).
|
| - Node* value = jsgraph()->TheHoleConstant();
|
| Handle<String> name = variable->name();
|
| - if (mode == DYNAMIC_GLOBAL) {
|
| - uint32_t check_bitset = ComputeBitsetForDynamicGlobal(variable);
|
| - const Operator* op = javascript()->LoadDynamicGlobal(
|
| - name, check_bitset, feedback, typeof_mode);
|
| - value = NewNode(op, BuildLoadFeedbackVector(), current_context());
|
| - states.AddToNode(value, bailout_id, combine);
|
| - } else if (mode == DYNAMIC_LOCAL) {
|
| - Variable* local = variable->local_if_not_shadowed();
|
| - DCHECK(local->location() ==
|
| - VariableLocation::CONTEXT); // Must be context.
|
| - int depth = current_scope()->ContextChainLength(local->scope());
|
| - uint32_t check_bitset = ComputeBitsetForDynamicContext(variable);
|
| - const Operator* op = javascript()->LoadDynamicContext(
|
| - name, check_bitset, depth, local->index());
|
| - value = NewNode(op, current_context());
|
| - PrepareFrameState(value, bailout_id, combine);
|
| - 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 = BuildHoleCheckThenThrow(value, local, value, bailout_id);
|
| - }
|
| - } else if (mode == DYNAMIC) {
|
| - uint32_t check_bitset = DynamicGlobalAccess::kFullCheckRequired;
|
| - const Operator* op = javascript()->LoadDynamicGlobal(
|
| - name, check_bitset, feedback, typeof_mode);
|
| - value = NewNode(op, BuildLoadFeedbackVector(), current_context());
|
| - states.AddToNode(value, bailout_id, combine);
|
| + if (Node* node =
|
| + TryLoadDynamicVariable(variable, name, bailout_id, states,
|
| + feedback, combine, typeof_mode)) {
|
| + return node;
|
| }
|
| + const Operator* op = javascript()->LoadDynamic(name, typeof_mode);
|
| + Node* value = NewNode(op, BuildLoadFeedbackVector(), current_context());
|
| + states.AddToNode(value, bailout_id, combine);
|
| return value;
|
| }
|
| }
|
| @@ -3907,6 +3889,102 @@ Node* AstGraphBuilder::TryLoadGlobalConstant(Handle<Name> name) {
|
| }
|
|
|
|
|
| +Node* AstGraphBuilder::TryLoadDynamicVariable(
|
| + Variable* variable, Handle<String> name, BailoutId bailout_id,
|
| + FrameStateBeforeAndAfter& states, const VectorSlotPair& feedback,
|
| + OutputFrameStateCombine combine, TypeofMode typeof_mode) {
|
| + VariableMode mode = variable->mode();
|
| +
|
| + if (mode == DYNAMIC_GLOBAL) {
|
| + uint32_t bitset = ComputeBitsetForDynamicGlobal(variable);
|
| + if (bitset == kFullCheckRequired) return nullptr;
|
| +
|
| + // We are using two blocks to model fast and slow cases.
|
| + BlockBuilder fast_block(this);
|
| + BlockBuilder slow_block(this);
|
| + environment()->Push(jsgraph()->TheHoleConstant());
|
| + slow_block.BeginBlock();
|
| + environment()->Pop();
|
| + fast_block.BeginBlock();
|
| +
|
| + // Perform checks whether the fast mode applies, by looking for any
|
| + // extension object which might shadow the optimistic declaration.
|
| + for (int depth = 0; bitset != 0; bitset >>= 1, depth++) {
|
| + if ((bitset & 1) == 0) continue;
|
| + Node* load = NewNode(
|
| + javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false),
|
| + current_context());
|
| + Node* check =
|
| + NewNode(javascript()->CallRuntime(Runtime::kInlineIsSmi, 1), load);
|
| + fast_block.BreakUnless(check, BranchHint::kTrue);
|
| + }
|
| +
|
| + // Fast case, because variable is not shadowed. Perform global slot load.
|
| + Node* fast = BuildGlobalLoad(name, feedback, typeof_mode);
|
| + states.AddToNode(fast, bailout_id, combine);
|
| + environment()->Push(fast);
|
| + slow_block.Break();
|
| + environment()->Pop();
|
| + fast_block.EndBlock();
|
| +
|
| + // Slow case, because variable potentially shadowed. Perform dynamic lookup.
|
| + const Operator* op = javascript()->LoadDynamic(name, typeof_mode);
|
| + Node* slow = NewNode(op, BuildLoadFeedbackVector(), current_context());
|
| + states.AddToNode(slow, bailout_id, combine);
|
| + environment()->Push(slow);
|
| + slow_block.EndBlock();
|
| +
|
| + return environment()->Pop();
|
| + }
|
| +
|
| + if (mode == DYNAMIC_LOCAL) {
|
| + uint32_t bitset = ComputeBitsetForDynamicContext(variable);
|
| + if (bitset == kFullCheckRequired) return nullptr;
|
| +
|
| + // We are using two blocks to model fast and slow cases.
|
| + BlockBuilder fast_block(this);
|
| + BlockBuilder slow_block(this);
|
| + environment()->Push(jsgraph()->TheHoleConstant());
|
| + slow_block.BeginBlock();
|
| + environment()->Pop();
|
| + fast_block.BeginBlock();
|
| +
|
| + // Perform checks whether the fast mode applies, by looking for any
|
| + // extension object which might shadow the optimistic declaration.
|
| + for (int depth = 0; bitset != 0; bitset >>= 1, depth++) {
|
| + if ((bitset & 1) == 0) continue;
|
| + Node* load = NewNode(
|
| + javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false),
|
| + current_context());
|
| + Node* check =
|
| + NewNode(javascript()->CallRuntime(Runtime::kInlineIsSmi, 1), load);
|
| + fast_block.BreakUnless(check, BranchHint::kTrue);
|
| + }
|
| +
|
| + // Fast case, because variable is not shadowed. Perform context slot load.
|
| + Variable* local = variable->local_if_not_shadowed();
|
| + DCHECK(local->location() == VariableLocation::CONTEXT); // Must be context.
|
| + Node* fast = BuildVariableLoad(local, bailout_id, states, feedback, combine,
|
| + typeof_mode);
|
| + environment()->Push(fast);
|
| + slow_block.Break();
|
| + environment()->Pop();
|
| + fast_block.EndBlock();
|
| +
|
| + // Slow case, because variable potentially shadowed. Perform dynamic lookup.
|
| + const Operator* op = javascript()->LoadDynamic(name, typeof_mode);
|
| + Node* slow = NewNode(op, BuildLoadFeedbackVector(), current_context());
|
| + states.AddToNode(slow, bailout_id, combine);
|
| + environment()->Push(slow);
|
| + slow_block.EndBlock();
|
| +
|
| + return environment()->Pop();
|
| + }
|
| +
|
| + return nullptr;
|
| +}
|
| +
|
| +
|
| Node* AstGraphBuilder::TryFastToBoolean(Node* input) {
|
| switch (input->opcode()) {
|
| case IrOpcode::kNumberConstant: {
|
|
|