Index: src/x64/full-codegen-x64.cc |
=================================================================== |
--- src/x64/full-codegen-x64.cc (revision 5429) |
+++ src/x64/full-codegen-x64.cc (working copy) |
@@ -507,7 +507,7 @@ |
int context_chain_length = |
scope()->ContextChainLength(slot->var()->scope()); |
__ LoadContext(scratch, context_chain_length); |
- return CodeGenerator::ContextOperand(scratch, slot->index()); |
+ return ContextOperand(scratch, slot->index()); |
} |
case Slot::LOOKUP: |
UNREACHABLE(); |
@@ -568,20 +568,17 @@ |
ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); |
if (FLAG_debug_code) { |
// Check if we have the correct context pointer. |
- __ movq(rbx, |
- CodeGenerator::ContextOperand(rsi, Context::FCONTEXT_INDEX)); |
+ __ movq(rbx, ContextOperand(rsi, Context::FCONTEXT_INDEX)); |
__ cmpq(rbx, rsi); |
__ Check(equal, "Unexpected declaration in current context."); |
} |
if (mode == Variable::CONST) { |
__ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); |
- __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), |
- kScratchRegister); |
+ __ movq(ContextOperand(rsi, slot->index()), kScratchRegister); |
// No write barrier since the hole value is in old space. |
} else if (function != NULL) { |
VisitForValue(function, kAccumulator); |
- __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), |
- result_register()); |
+ __ movq(ContextOperand(rsi, slot->index()), result_register()); |
int offset = Context::SlotOffset(slot->index()); |
__ movq(rbx, rsi); |
__ RecordWrite(rbx, offset, result_register(), rcx); |
@@ -881,6 +878,71 @@ |
} |
+void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( |
+ Slot* slot, |
+ TypeofState typeof_state, |
+ Label* slow) { |
+ Register context = rsi; |
+ Register temp = rdx; |
+ |
+ Scope* s = scope(); |
+ while (s != NULL) { |
+ if (s->num_heap_slots() > 0) { |
+ if (s->calls_eval()) { |
+ // Check that extension is NULL. |
+ __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), |
+ Immediate(0)); |
+ __ j(not_equal, slow); |
+ } |
+ // Load next context in chain. |
+ __ movq(temp, ContextOperand(context, Context::CLOSURE_INDEX)); |
+ __ movq(temp, FieldOperand(temp, JSFunction::kContextOffset)); |
+ // Walk the rest of the chain using a single register without |
+ // clobbering rsi. |
+ context = temp; |
+ } |
+ // If no outer scope calls eval, we do not need to check more |
+ // context extensions. If we have reached an eval scope, we check |
+ // all extensions from this point. |
+ if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break; |
+ s = s->outer_scope(); |
+ } |
+ |
+ if (s != NULL && s->is_eval_scope()) { |
+ // Loop up the context chain. There is no frame effect so it is |
+ // safe to use raw labels here. |
+ Label next, fast; |
+ if (!context.is(temp)) { |
+ __ movq(temp, context); |
+ } |
+ // Load map for comparison into register, outside loop. |
+ __ LoadRoot(kScratchRegister, Heap::kGlobalContextMapRootIndex); |
+ __ bind(&next); |
+ // Terminate at global context. |
+ __ cmpq(kScratchRegister, FieldOperand(temp, HeapObject::kMapOffset)); |
+ __ j(equal, &fast); |
+ // Check that extension is NULL. |
+ __ cmpq(ContextOperand(temp, Context::EXTENSION_INDEX), Immediate(0)); |
+ __ j(not_equal, slow); |
+ // Load next context in chain. |
+ __ movq(temp, ContextOperand(temp, Context::CLOSURE_INDEX)); |
+ __ movq(temp, FieldOperand(temp, JSFunction::kContextOffset)); |
+ __ jmp(&next); |
+ __ bind(&fast); |
+ } |
+ |
+ // All extension objects were empty and it is safe to use a global |
+ // load IC call. |
+ __ movq(rax, CodeGenerator::GlobalObject()); |
+ __ Move(rcx, slot->var()->name()); |
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); |
+ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) |
+ ? RelocInfo::CODE_TARGET |
+ : RelocInfo::CODE_TARGET_CONTEXT; |
+ __ call(ic, mode); |
+} |
+ |
+ |
void FullCodeGenerator::EmitVariableLoad(Variable* var, |
Expression::Context context) { |
// Four cases: non-this global variables, lookup slots, all other |
@@ -904,11 +966,26 @@ |
Apply(context, rax); |
} else if (slot != NULL && slot->type() == Slot::LOOKUP) { |
+ Label done, slow; |
+ |
+ // Generate fast-case code for variables that might be shadowed by |
+ // eval-introduced variables. Eval is used a lot without |
+ // introducing variables. In those cases, we do not want to |
+ // perform a runtime call for all variables in the scope |
+ // containing the eval. |
+ if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { |
+ EmitLoadGlobalSlotCheckExtensions(slot, NOT_INSIDE_TYPEOF, &slow); |
+ Apply(context, rax); |
+ __ jmp(&done); |
+ } |
+ |
+ __ bind(&slow); |
Comment cmnt(masm_, "Lookup slot"); |
__ push(rsi); // Context. |
__ Push(var->name()); |
__ CallRuntime(Runtime::kLoadContextSlot, 2); |
Apply(context, rax); |
+ __ bind(&done); |
} else if (slot != NULL) { |
Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) |
@@ -2522,12 +2599,11 @@ |
Register key = rax; |
Register cache = rbx; |
Register tmp = rcx; |
- __ movq(cache, CodeGenerator::ContextOperand(rsi, Context::GLOBAL_INDEX)); |
+ __ movq(cache, ContextOperand(rsi, Context::GLOBAL_INDEX)); |
__ movq(cache, |
FieldOperand(cache, GlobalObject::kGlobalContextOffset)); |
__ movq(cache, |
- CodeGenerator::ContextOperand( |
- cache, Context::JSFUNCTION_RESULT_CACHES_INDEX)); |
+ ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX)); |
__ movq(cache, |
FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id))); |
@@ -3243,7 +3319,7 @@ |
void FullCodeGenerator::LoadContextField(Register dst, int context_index) { |
- __ movq(dst, CodeGenerator::ContextOperand(rsi, context_index)); |
+ __ movq(dst, ContextOperand(rsi, context_index)); |
} |