From 666b98fb4bf1e030aa4fbf5012a9c942bc86144d Mon Sep 17 00:00:00 2001 From: "ager@chromium.org" Date: Fri, 10 Sep 2010 10:57:07 +0000 Subject: [PATCH] Handle both global and local variables potentially shadowed by eval-introduced variables in full-codegen. Make sure that x64 assembler records source positions for calls. Review URL: http://codereview.chromium.org/3357022 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5441 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/full-codegen-arm.cc | 158 +++++++++++++++++++++++++++++++++++------- src/full-codegen.h | 5 ++ src/ia32/full-codegen-ia32.cc | 143 +++++++++++++++++++++++++++++++++----- src/x64/assembler-x64.cc | 1 + src/x64/full-codegen-x64.cc | 144 +++++++++++++++++++++++++++++++++----- 5 files changed, 393 insertions(+), 58 deletions(-) diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index c76ca8c..999bf3d 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -879,6 +879,89 @@ void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { } +MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( + Slot* slot, + Label* slow) { + ASSERT(slot->type() == Slot::CONTEXT); + Register current = cp; + Register next = r3; + Register temp = r4; + + for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { + if (s->num_heap_slots() > 0) { + if (s->calls_eval()) { + // Check that extension is NULL. + __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); + __ tst(temp, temp); + __ b(ne, slow); + } + __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX)); + __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset)); + // Walk the rest of the chain without clobbering cp. + current = next; + } + } + // Check that last extension is NULL. + __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); + __ tst(temp, temp); + __ b(ne, slow); + __ ldr(temp, ContextOperand(current, Context::FCONTEXT_INDEX)); + return ContextOperand(temp, slot->index()); +} + + +void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( + Slot* slot, + TypeofState typeof_state, + Label* slow, + Label* done) { + // 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, typeof_state, slow); + __ jmp(done); + } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { + Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot(); + Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite(); + if (potential_slot != NULL) { + // Generate fast case for locals that rewrite to slots. + __ ldr(r0, ContextSlotOperandCheckExtensions(potential_slot, slow)); + if (potential_slot->var()->mode() == Variable::CONST) { + __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); + __ cmp(r0, ip); + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); + } + __ jmp(done); + } else if (rewrite != NULL) { + // Generate fast case for calls of an argument function. + Property* property = rewrite->AsProperty(); + if (property != NULL) { + VariableProxy* obj_proxy = property->obj()->AsVariableProxy(); + Literal* key_literal = property->key()->AsLiteral(); + if (obj_proxy != NULL && + key_literal != NULL && + obj_proxy->IsArguments() && + key_literal->handle()->IsSmi()) { + // Load arguments object if there are no eval-introduced + // variables. Then load the argument from the arguments + // object using keyed load. + __ ldr(r1, + ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(), + slow)); + __ mov(r0, Operand(key_literal->handle())); + Handle ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); + __ Call(ic, RelocInfo::CODE_TARGET); + __ jmp(done); + } + } + } + } +} + + void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( Slot* slot, TypeofState typeof_state, @@ -899,8 +982,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( // Load next context in chain. __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX)); __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset)); - // Walk the rest of the chain using a single register without - // clobbering cp. + // Walk the rest of the chain without clobbering cp. current = next; } // If no outer scope calls eval, we do not need to check more @@ -962,25 +1044,19 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, } 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, r0); - __ jmp(&done); - } + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done); __ bind(&slow); Comment cmnt(masm_, "Lookup slot"); __ mov(r1, Operand(var->name())); __ Push(cp, r1); // Context and name. __ CallRuntime(Runtime::kLoadContextSlot, 2); - Apply(context, r0); __ bind(&done); + Apply(context, r0); + } else if (slot != NULL) { Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) ? "Context slot" @@ -988,14 +1064,11 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, if (var->mode() == Variable::CONST) { // Constants may be the hole value if they have not been initialized. // Unhole them. - Label done; MemOperand slot_operand = EmitSlotSearch(slot, r0); __ ldr(r0, slot_operand); __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); __ cmp(r0, ip); - __ b(ne, &done); - __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); - __ bind(&done); + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); Apply(context, r0); } else { Apply(context, slot); @@ -1722,15 +1795,41 @@ void FullCodeGenerator::VisitCall(Call* expr) { EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->slot() != NULL && var->slot()->type() == Slot::LOOKUP) { - // Call to a lookup slot (dynamically introduced variable). Call the - // runtime to find the function to call (returned in eax) and the object - // holding it (returned in edx). + // Call to a lookup slot (dynamically introduced variable). + Label slow, done; + + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLoadFromSlotFastCase(var->slot(), + NOT_INSIDE_TYPEOF, + &slow, + &done); + + __ bind(&slow); + // Call the runtime to find the function to call (returned in eax) + // and the object holding it (returned in edx). __ push(context_register()); __ mov(r2, Operand(var->name())); __ push(r2); __ CallRuntime(Runtime::kLoadContextSlot, 2); - __ push(r0); // Function. - __ push(r1); // Receiver. + __ Push(r0, r1); // Function, receiver. + + // If fast case code has been generated, emit code to push the + // function and receiver and have the slow path jump around this + // code. + if (done.is_linked()) { + Label call; + __ b(&call); + __ bind(&done); + // Push function. + __ push(r0); + // Push global receiver. + __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); + __ push(r1); + __ bind(&call); + } + EmitCallWithStub(expr); } else if (fun->AsProperty() != NULL) { // Call to an object property. @@ -1753,12 +1852,9 @@ void FullCodeGenerator::VisitCall(Call* expr) { Handle ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); - // Push result (function). - __ push(r0); - // Push Global receiver. __ ldr(r1, CodeGenerator::GlobalObject()); __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); - __ push(r1); + __ Push(r0, r1); // Function, receiver. EmitCallWithStub(expr); } else { EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET); @@ -3002,9 +3098,19 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr, Location where) { } else if (proxy != NULL && proxy->var()->slot() != NULL && proxy->var()->slot()->type() == Slot::LOOKUP) { + Label done, slow; + + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + Slot* slot = proxy->var()->slot(); + EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done); + + __ bind(&slow); __ mov(r0, Operand(proxy->name())); __ Push(cp, r0); __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); + __ bind(&done); + if (where == kStack) __ push(r0); } else { // This expression cannot throw a reference error at the top level. diff --git a/src/full-codegen.h b/src/full-codegen.h index bc3589f..ab0fd36 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -384,6 +384,11 @@ class FullCodeGenerator: public AstVisitor { void EmitLoadGlobalSlotCheckExtensions(Slot* slot, TypeofState typeof_state, Label* slow); + MemOperand ContextSlotOperandCheckExtensions(Slot* slot, Label* slow); + void EmitDynamicLoadFromSlotFastCase(Slot* slot, + TypeofState typeof_state, + Label* slow, + Label* done); void EmitVariableLoad(Variable* expr, Expression::Context context); // Platform-specific support for allocating a new closure based on diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index ca0313e..a308941 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -902,8 +902,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( // Load next context in chain. __ mov(temp, ContextOperand(context, Context::CLOSURE_INDEX)); __ mov(temp, FieldOperand(temp, JSFunction::kContextOffset)); - // Walk the rest of the chain using a single register without - // clobbering esi. + // Walk the rest of the chain without clobbering esi. context = temp; } // If no outer scope calls eval, we do not need to check more @@ -947,6 +946,88 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( } +MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( + Slot* slot, + Label* slow) { + ASSERT(slot->type() == Slot::CONTEXT); + Register context = esi; + Register temp = ebx; + + for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { + if (s->num_heap_slots() > 0) { + if (s->calls_eval()) { + // Check that extension is NULL. + __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), + Immediate(0)); + __ j(not_equal, slow); + } + __ mov(temp, ContextOperand(context, Context::CLOSURE_INDEX)); + __ mov(temp, FieldOperand(temp, JSFunction::kContextOffset)); + // Walk the rest of the chain without clobbering esi. + context = temp; + } + } + // Check that last extension is NULL. + __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); + __ j(not_equal, slow); + __ mov(temp, ContextOperand(context, Context::FCONTEXT_INDEX)); + return ContextOperand(temp, slot->index()); +} + + +void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( + Slot* slot, + TypeofState typeof_state, + Label* slow, + Label* done) { + // 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, typeof_state, slow); + __ jmp(done); + } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { + Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot(); + Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite(); + if (potential_slot != NULL) { + // Generate fast case for locals that rewrite to slots. + __ mov(eax, + ContextSlotOperandCheckExtensions(potential_slot, slow)); + if (potential_slot->var()->mode() == Variable::CONST) { + __ cmp(eax, Factory::the_hole_value()); + __ j(not_equal, done); + __ mov(eax, Factory::undefined_value()); + } + __ jmp(done); + } else if (rewrite != NULL) { + // Generate fast case for calls of an argument function. + Property* property = rewrite->AsProperty(); + if (property != NULL) { + VariableProxy* obj_proxy = property->obj()->AsVariableProxy(); + Literal* key_literal = property->key()->AsLiteral(); + if (obj_proxy != NULL && + key_literal != NULL && + obj_proxy->IsArguments() && + key_literal->handle()->IsSmi()) { + // Load arguments object if there are no eval-introduced + // variables. Then load the argument from the arguments + // object using keyed load. + __ mov(edx, + ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(), + slow)); + __ mov(eax, Immediate(key_literal->handle())); + Handle ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + __ jmp(done); + } + } + } + } +} + + void FullCodeGenerator::EmitVariableLoad(Variable* var, Expression::Context context) { // Four cases: non-this global variables, lookup slots, all other @@ -973,25 +1054,19 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, } 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, eax); - __ jmp(&done); - } + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done); __ bind(&slow); Comment cmnt(masm_, "Lookup slot"); __ push(esi); // Context. __ push(Immediate(var->name())); __ CallRuntime(Runtime::kLoadContextSlot, 2); - Apply(context, eax); __ bind(&done); + Apply(context, eax); + } else if (slot != NULL) { Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) ? "Context slot" @@ -2030,14 +2105,40 @@ void FullCodeGenerator::VisitCall(Call* expr) { EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->slot() != NULL && var->slot()->type() == Slot::LOOKUP) { - // Call to a lookup slot (dynamically introduced variable). Call the - // runtime to find the function to call (returned in eax) and the object - // holding it (returned in edx). + // Call to a lookup slot (dynamically introduced variable). + Label slow, done; + + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLoadFromSlotFastCase(var->slot(), + NOT_INSIDE_TYPEOF, + &slow, + &done); + + __ bind(&slow); + // Call the runtime to find the function to call (returned in eax) + // and the object holding it (returned in edx). __ push(context_register()); __ push(Immediate(var->name())); __ CallRuntime(Runtime::kLoadContextSlot, 2); __ push(eax); // Function. __ push(edx); // Receiver. + + // If fast case code has been generated, emit code to push the + // function and receiver and have the slow path jump around this + // code. + if (done.is_linked()) { + Label call; + __ jmp(&call); + __ bind(&done); + // Push function. + __ push(eax); + // Push global receiver. + __ mov(ebx, CodeGenerator::GlobalObject()); + __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); + __ bind(&call); + } + EmitCallWithStub(expr); } else if (fun->AsProperty() != NULL) { // Call to an object property. @@ -3341,9 +3442,19 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr, Location where) { } else if (proxy != NULL && proxy->var()->slot() != NULL && proxy->var()->slot()->type() == Slot::LOOKUP) { + Label done, slow; + + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + Slot* slot = proxy->var()->slot(); + EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done); + + __ bind(&slow); __ push(esi); __ push(Immediate(proxy->name())); __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); + __ bind(&done); + if (where == kStack) __ push(eax); } else { // This expression cannot throw a reference error at the top level. diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 9ad94ce..9318bb8 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -829,6 +829,7 @@ void Assembler::call(Label* L) { void Assembler::call(Handle target, RelocInfo::Mode rmode) { + WriteRecordedPositions(); EnsureSpace ensure_space(this); last_pc_ = pc_; // 1110 1000 #32-bit disp. diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 74699b5..6641a22 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -897,8 +897,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( // 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. + // Walk the rest of the chain without clobbering rsi. context = temp; } // If no outer scope calls eval, we do not need to check more @@ -943,6 +942,88 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( } +MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( + Slot* slot, + Label* slow) { + ASSERT(slot->type() == Slot::CONTEXT); + Register context = rsi; + Register temp = rbx; + + for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { + 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); + } + __ movq(temp, ContextOperand(context, Context::CLOSURE_INDEX)); + __ movq(temp, FieldOperand(temp, JSFunction::kContextOffset)); + // Walk the rest of the chain without clobbering rsi. + context = temp; + } + } + // Check that last extension is NULL. + __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); + __ j(not_equal, slow); + __ movq(temp, ContextOperand(context, Context::FCONTEXT_INDEX)); + return ContextOperand(temp, slot->index()); +} + + +void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( + Slot* slot, + TypeofState typeof_state, + Label* slow, + Label* done) { + // 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, typeof_state, slow); + __ jmp(done); + } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { + Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot(); + Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite(); + if (potential_slot != NULL) { + // Generate fast case for locals that rewrite to slots. + __ movq(rax, + ContextSlotOperandCheckExtensions(potential_slot, slow)); + if (potential_slot->var()->mode() == Variable::CONST) { + __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); + __ j(not_equal, done); + __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); + } + __ jmp(done); + } else if (rewrite != NULL) { + // Generate fast case for calls of an argument function. + Property* property = rewrite->AsProperty(); + if (property != NULL) { + VariableProxy* obj_proxy = property->obj()->AsVariableProxy(); + Literal* key_literal = property->key()->AsLiteral(); + if (obj_proxy != NULL && + key_literal != NULL && + obj_proxy->IsArguments() && + key_literal->handle()->IsSmi()) { + // Load arguments object if there are no eval-introduced + // variables. Then load the argument from the arguments + // object using keyed load. + __ movq(rdx, + ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(), + slow)); + __ Move(rax, key_literal->handle()); + Handle ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + __ jmp(done); + } + } + } + } +} + + void FullCodeGenerator::EmitVariableLoad(Variable* var, Expression::Context context) { // Four cases: non-this global variables, lookup slots, all other @@ -968,25 +1049,19 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, } 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); - } + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done); __ bind(&slow); Comment cmnt(masm_, "Lookup slot"); __ push(rsi); // Context. __ Push(var->name()); __ CallRuntime(Runtime::kLoadContextSlot, 2); - Apply(context, rax); __ bind(&done); + Apply(context, rax); + } else if (slot != NULL) { Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) ? "Context slot" @@ -1790,15 +1865,42 @@ void FullCodeGenerator::VisitCall(Call* expr) { EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->slot() != NULL && var->slot()->type() == Slot::LOOKUP) { - // Call to a lookup slot (dynamically introduced variable). Call - // the runtime to find the function to call (returned in rax) and - // the object holding it (returned in rdx). + // Call to a lookup slot (dynamically introduced variable). + Label slow, done; + + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLoadFromSlotFastCase(var->slot(), + NOT_INSIDE_TYPEOF, + &slow, + &done); + + __ bind(&slow); + // Call the runtime to find the function to call (returned in rax) + // and the object holding it (returned in rdx). __ push(context_register()); __ Push(var->name()); __ CallRuntime(Runtime::kLoadContextSlot, 2); __ push(rax); // Function. __ push(rdx); // Receiver. + + // If fast case code has been generated, emit code to push the + // function and receiver and have the slow path jump around this + // code. + if (done.is_linked()) { + Label call; + __ jmp(&call); + __ bind(&done); + // Push function. + __ push(rax); + // Push global receiver. + __ movq(rbx, CodeGenerator::GlobalObject()); + __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); + __ bind(&call); + } + EmitCallWithStub(expr); + } else if (fun->AsProperty() != NULL) { // Call to an object property. Property* prop = fun->AsProperty(); @@ -3077,9 +3179,19 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr, Location where) { } else if (proxy != NULL && proxy->var()->slot() != NULL && proxy->var()->slot()->type() == Slot::LOOKUP) { + Label done, slow; + + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + Slot* slot = proxy->var()->slot(); + EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done); + + __ bind(&slow); __ push(rsi); __ Push(proxy->name()); __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); + __ bind(&done); + if (where == kStack) __ push(rax); } else { // This expression cannot throw a reference error at the top level. -- 2.7.4