| Index: src/ia32/full-codegen-ia32.cc
|
| ===================================================================
|
| --- src/ia32/full-codegen-ia32.cc (revision 7006)
|
| +++ src/ia32/full-codegen-ia32.cc (working copy)
|
| @@ -206,45 +206,48 @@
|
| Move(dot_arguments_slot, ecx, ebx, edx);
|
| }
|
|
|
| - { Comment cmnt(masm_, "[ Declarations");
|
| - // For named function expressions, declare the function name as a
|
| - // constant.
|
| - if (scope()->is_function_scope() && scope()->function() != NULL) {
|
| - EmitDeclaration(scope()->function(), Variable::CONST, NULL);
|
| - }
|
| - // Visit all the explicit declarations unless there is an illegal
|
| - // redeclaration.
|
| - if (scope()->HasIllegalRedeclaration()) {
|
| - scope()->VisitIllegalRedeclaration(this);
|
| - } else {
|
| - VisitDeclarations(scope()->declarations());
|
| - }
|
| - }
|
| -
|
| if (FLAG_trace) {
|
| __ CallRuntime(Runtime::kTraceEnter, 0);
|
| }
|
|
|
| - { Comment cmnt(masm_, "[ Stack check");
|
| - PrepareForBailout(info->function(), NO_REGISTERS);
|
| - NearLabel ok;
|
| - ExternalReference stack_limit =
|
| - ExternalReference::address_of_stack_limit();
|
| - __ cmp(esp, Operand::StaticVariable(stack_limit));
|
| - __ j(above_equal, &ok, taken);
|
| - StackCheckStub stub;
|
| - __ CallStub(&stub);
|
| - __ bind(&ok);
|
| - }
|
| + // Visit the declarations and body unless there is an illegal
|
| + // redeclaration.
|
| + if (scope()->HasIllegalRedeclaration()) {
|
| + Comment cmnt(masm_, "[ Declarations");
|
| + scope()->VisitIllegalRedeclaration(this);
|
|
|
| - { Comment cmnt(masm_, "[ Body");
|
| - ASSERT(loop_depth() == 0);
|
| - VisitStatements(function()->body());
|
| - ASSERT(loop_depth() == 0);
|
| + } else {
|
| + { Comment cmnt(masm_, "[ Declarations");
|
| + // For named function expressions, declare the function name as a
|
| + // constant.
|
| + if (scope()->is_function_scope() && scope()->function() != NULL) {
|
| + EmitDeclaration(scope()->function(), Variable::CONST, NULL);
|
| + }
|
| + VisitDeclarations(scope()->declarations());
|
| + }
|
| +
|
| + { Comment cmnt(masm_, "[ Stack check");
|
| + PrepareForBailout(info->function(), NO_REGISTERS);
|
| + NearLabel ok;
|
| + ExternalReference stack_limit =
|
| + ExternalReference::address_of_stack_limit();
|
| + __ cmp(esp, Operand::StaticVariable(stack_limit));
|
| + __ j(above_equal, &ok, taken);
|
| + StackCheckStub stub;
|
| + __ CallStub(&stub);
|
| + __ bind(&ok);
|
| + }
|
| +
|
| + { Comment cmnt(masm_, "[ Body");
|
| + ASSERT(loop_depth() == 0);
|
| + VisitStatements(function()->body());
|
| + ASSERT(loop_depth() == 0);
|
| + }
|
| }
|
|
|
| + // Always emit a 'return undefined' in case control fell off the end of
|
| + // the body.
|
| { Comment cmnt(masm_, "[ return <undefined>;");
|
| - // Emit a 'return undefined' in case control fell off the end of the body.
|
| __ mov(eax, isolate()->factory()->undefined_value());
|
| EmitReturnSequence();
|
| }
|
| @@ -307,12 +310,14 @@
|
| // patch with the code required by the debugger.
|
| __ mov(esp, ebp);
|
| __ pop(ebp);
|
| - __ ret((scope()->num_parameters() + 1) * kPointerSize);
|
| +
|
| + int arguments_bytes = (scope()->num_parameters() + 1) * kPointerSize;
|
| + __ Ret(arguments_bytes, ecx);
|
| #ifdef ENABLE_DEBUGGER_SUPPORT
|
| - // Check that the size of the code used for returning matches what is
|
| - // expected by the debugger.
|
| - ASSERT_EQ(Assembler::kJSReturnSequenceLength,
|
| - masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
|
| + // Check that the size of the code used for returning is large enough
|
| + // for the debugger's requirements.
|
| + ASSERT(Assembler::kJSReturnSequenceLength <=
|
| + masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
|
| #endif
|
| }
|
| }
|
| @@ -612,7 +617,7 @@
|
| __ mov(location, src);
|
| // Emit the write barrier code if the location is in the heap.
|
| if (dst->type() == Slot::CONTEXT) {
|
| - int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize;
|
| + int offset = Context::SlotOffset(dst->index());
|
| __ RecordWrite(scratch1, offset, src, scratch2);
|
| }
|
| }
|
| @@ -668,10 +673,11 @@
|
| // We bypass the general EmitSlotSearch because we know more about
|
| // this specific context.
|
|
|
| - // The variable in the decl always resides in the current context.
|
| + // The variable in the decl always resides in the current function
|
| + // context.
|
| ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
|
| if (FLAG_debug_code) {
|
| - // Check if we have the correct context pointer.
|
| + // Check that we're not inside a 'with'.
|
| __ mov(ebx, ContextOperand(esi, Context::FCONTEXT_INDEX));
|
| __ cmp(ebx, Operand(esi));
|
| __ Check(equal, "Unexpected declaration in current context.");
|
| @@ -715,18 +721,25 @@
|
| } else if (prop != NULL) {
|
| if (function != NULL || mode == Variable::CONST) {
|
| // We are declaring a function or constant that rewrites to a
|
| - // property. Use (keyed) IC to set the initial value.
|
| - VisitForStackValue(prop->obj());
|
| + // property. Use (keyed) IC to set the initial value. We cannot
|
| + // visit the rewrite because it's shared and we risk recording
|
| + // duplicate AST IDs for bailouts from optimized code.
|
| + ASSERT(prop->obj()->AsVariableProxy() != NULL);
|
| + { AccumulatorValueContext for_object(this);
|
| + EmitVariableLoad(prop->obj()->AsVariableProxy()->var());
|
| + }
|
| +
|
| if (function != NULL) {
|
| - VisitForStackValue(prop->key());
|
| + __ push(eax);
|
| VisitForAccumulatorValue(function);
|
| - __ pop(ecx);
|
| + __ pop(edx);
|
| } else {
|
| - VisitForAccumulatorValue(prop->key());
|
| - __ mov(ecx, result_register());
|
| - __ mov(result_register(), isolate()->factory()->the_hole_value());
|
| + __ mov(edx, eax);
|
| + __ mov(eax, isolate()->factory()->the_hole_value());
|
| }
|
| - __ pop(edx);
|
| + ASSERT(prop->key()->AsLiteral() != NULL &&
|
| + prop->key()->AsLiteral()->handle()->IsSmi());
|
| + __ Set(ecx, Immediate(prop->key()->AsLiteral()->handle()));
|
|
|
| Handle<Code> ic(isolate()->builtins()->builtin(
|
| Builtins::KeyedStoreIC_Initialize));
|
| @@ -1129,8 +1142,11 @@
|
| // 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());
|
| +
|
| + // This function is used only for loads, not stores, so it's safe to
|
| + // return an esi-based operand (the write barrier cannot be allowed to
|
| + // destroy the esi register).
|
| + return ContextOperand(context, slot->index());
|
| }
|
|
|
|
|
| @@ -2015,57 +2031,75 @@
|
| Builtins::StoreIC_Initialize));
|
| EmitCallIC(ic, RelocInfo::CODE_TARGET);
|
|
|
| - } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) {
|
| - // Perform the assignment for non-const variables and for initialization
|
| - // of const variables. Const assignments are simply skipped.
|
| - Label done;
|
| + } else if (op == Token::INIT_CONST) {
|
| + // Like var declarations, const declarations are hoisted to function
|
| + // scope. However, unlike var initializers, const initializers are able
|
| + // to drill a hole to that function context, even from inside a 'with'
|
| + // context. We thus bypass the normal static scope lookup.
|
| Slot* slot = var->AsSlot();
|
| + Label skip;
|
| switch (slot->type()) {
|
| case Slot::PARAMETER:
|
| + // No const parameters.
|
| + UNREACHABLE();
|
| + break;
|
| case Slot::LOCAL:
|
| - if (op == Token::INIT_CONST) {
|
| - // Detect const reinitialization by checking for the hole value.
|
| - __ mov(edx, Operand(ebp, SlotOffset(slot)));
|
| - __ cmp(edx, isolate()->factory()->the_hole_value());
|
| - __ j(not_equal, &done);
|
| - }
|
| + __ mov(edx, Operand(ebp, SlotOffset(slot)));
|
| + __ cmp(edx, isolate()->factory()->the_hole_value());
|
| + __ j(not_equal, &skip);
|
| + __ mov(Operand(ebp, SlotOffset(slot)), eax);
|
| + break;
|
| + case Slot::CONTEXT: {
|
| + __ mov(ecx, ContextOperand(esi, Context::FCONTEXT_INDEX));
|
| + __ mov(edx, ContextOperand(ecx, slot->index()));
|
| + __ cmp(edx, isolate()->factory()->the_hole_value());
|
| + __ j(not_equal, &skip);
|
| + __ mov(ContextOperand(ecx, slot->index()), eax);
|
| + int offset = Context::SlotOffset(slot->index());
|
| + __ mov(edx, eax); // Preserve the stored value in eax.
|
| + __ RecordWrite(ecx, offset, edx, ebx);
|
| + break;
|
| + }
|
| + case Slot::LOOKUP:
|
| + __ push(eax);
|
| + __ push(esi);
|
| + __ push(Immediate(var->name()));
|
| + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
|
| + break;
|
| + }
|
| + __ bind(&skip);
|
| +
|
| + } else if (var->mode() != Variable::CONST) {
|
| + // Perform the assignment for non-const variables. Const assignments
|
| + // are simply skipped.
|
| + Slot* slot = var->AsSlot();
|
| + switch (slot->type()) {
|
| + case Slot::PARAMETER:
|
| + case Slot::LOCAL:
|
| // Perform the assignment.
|
| __ mov(Operand(ebp, SlotOffset(slot)), eax);
|
| break;
|
|
|
| case Slot::CONTEXT: {
|
| MemOperand target = EmitSlotSearch(slot, ecx);
|
| - if (op == Token::INIT_CONST) {
|
| - // Detect const reinitialization by checking for the hole value.
|
| - __ mov(edx, target);
|
| - __ cmp(edx, isolate()->factory()->the_hole_value());
|
| - __ j(not_equal, &done);
|
| - }
|
| // Perform the assignment and issue the write barrier.
|
| __ mov(target, eax);
|
| // The value of the assignment is in eax. RecordWrite clobbers its
|
| // register arguments.
|
| __ mov(edx, eax);
|
| - int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
|
| + int offset = Context::SlotOffset(slot->index());
|
| __ RecordWrite(ecx, offset, edx, ebx);
|
| break;
|
| }
|
|
|
| case Slot::LOOKUP:
|
| - // Call the runtime for the assignment. The runtime will ignore
|
| - // const reinitialization.
|
| + // Call the runtime for the assignment.
|
| __ push(eax); // Value.
|
| __ push(esi); // Context.
|
| __ push(Immediate(var->name()));
|
| - if (op == Token::INIT_CONST) {
|
| - // The runtime will ignore const redeclaration.
|
| - __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
|
| - } else {
|
| - __ CallRuntime(Runtime::kStoreContextSlot, 3);
|
| - }
|
| + __ CallRuntime(Runtime::kStoreContextSlot, 3);
|
| break;
|
| }
|
| - __ bind(&done);
|
| }
|
| }
|
|
|
| @@ -2289,7 +2323,9 @@
|
|
|
| // Push the receiver of the enclosing function and do runtime call.
|
| __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize));
|
| - __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
|
| + // Push the strict mode flag.
|
| + __ push(Immediate(Smi::FromInt(strict_mode_flag())));
|
| + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4);
|
|
|
| // The runtime call returns a pair of values in eax (function) and
|
| // edx (receiver). Touch up the stack with the right values.
|
| @@ -3770,8 +3806,8 @@
|
| Label no_conversion;
|
| __ test(result_register(), Immediate(kSmiTagMask));
|
| __ j(zero, &no_conversion);
|
| - __ push(result_register());
|
| - __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
|
| + ToNumberStub convert_stub;
|
| + __ CallStub(&convert_stub);
|
| __ bind(&no_conversion);
|
| context()->Plug(result_register());
|
| break;
|
| @@ -3891,8 +3927,8 @@
|
| __ test(eax, Immediate(kSmiTagMask));
|
| __ j(zero, &no_conversion);
|
| }
|
| - __ push(eax);
|
| - __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
|
| + ToNumberStub convert_stub;
|
| + __ CallStub(&convert_stub);
|
| __ bind(&no_conversion);
|
|
|
| // Save result for postfix expressions.
|
| @@ -3945,8 +3981,7 @@
|
| // Call stub for +1/-1.
|
| __ mov(edx, eax);
|
| __ mov(eax, Immediate(Smi::FromInt(1)));
|
| - TypeRecordingBinaryOpStub stub(expr->binary_op(),
|
| - NO_OVERWRITE);
|
| + TypeRecordingBinaryOpStub stub(expr->binary_op(), NO_OVERWRITE);
|
| EmitCallIC(stub.GetCode(), &patch_site);
|
| __ bind(&done);
|
|
|
|
|