From 493c9f072cffbafa412acf1d3136e9ed1b3d2f7e Mon Sep 17 00:00:00 2001 From: "whesse@chromium.org" Date: Mon, 9 Nov 2009 09:56:57 +0000 Subject: [PATCH] Enable writes and reads of context slots in fast compiler. Review URL: http://codereview.chromium.org/360054 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3242 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/fast-codegen-arm.cc | 177 ++++++++++++++++++++++++++-------- src/compiler.cc | 20 +++- src/ia32/fast-codegen-ia32.cc | 169 ++++++++++++++++++++++++-------- src/x64/fast-codegen-x64.cc | 172 +++++++++++++++++++++++++-------- 4 files changed, 418 insertions(+), 120 deletions(-) diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc index 7b84bacae2..56e7ac3671 100644 --- a/src/arm/fast-codegen-arm.cc +++ b/src/arm/fast-codegen-arm.cc @@ -364,6 +364,7 @@ void FastCodeGenerator::VisitDeclaration(Declaration* decl) { __ str(r0, CodeGenerator::ContextOperand(cp, slot->index())); int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; __ mov(r2, Operand(offset)); + // We know that we have written a function, which is not a smi. __ RecordWrite(cp, r2, r0); } break; @@ -421,6 +422,7 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { Comment cmnt(masm_, "[ VariableProxy"); Expression* rewrite = expr->var()->rewrite(); if (rewrite == NULL) { + ASSERT(expr->var()->is_global()); Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in r2 and the global // object on the stack. @@ -431,8 +433,47 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); DropAndMove(expr->context(), r0); } else { - Comment cmnt(masm_, "Stack slot"); - Move(expr->context(), rewrite->AsSlot()); + Slot* slot = rewrite->AsSlot(); + ASSERT_NE(NULL, slot); + switch (slot->type()) { + case Slot::LOCAL: + case Slot::PARAMETER: { + Comment cmnt(masm_, "Stack slot"); + Move(expr->context(), rewrite->AsSlot()); + break; + } + + case Slot::CONTEXT: { + Comment cmnt(masm_, "Context slot"); + int chain_length = + function_->scope()->ContextChainLength(slot->var()->scope()); + if (chain_length > 0) { + // Move up the chain of contexts to the context containing the slot. + __ ldr(r0, CodeGenerator::ContextOperand(cp, Context::CLOSURE_INDEX)); + // Load the function context (which is the incoming, outer context). + __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset)); + for (int i = 1; i < chain_length; i++) { + __ ldr(r0, + CodeGenerator::ContextOperand(r0, Context::CLOSURE_INDEX)); + // Load the function context (which is the incoming, outer context). + __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset)); + } + // The context may be an intermediate context, not a function context. + __ ldr(r0, + CodeGenerator::ContextOperand(r0, Context::FCONTEXT_INDEX)); + } else { // Slot is in the current context. + __ ldr(r0, + CodeGenerator::ContextOperand(cp, Context::FCONTEXT_INDEX)); + } + __ ldr(r0, CodeGenerator::ContextOperand(r0, slot->index())); + Move(expr->context(), r0); + break; + } + + case Slot::LOOKUP: + UNREACHABLE(); + break; + } } } @@ -705,45 +746,103 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { DropAndMove(expr->context(), r0); } else { - switch (expr->context()) { - case Expression::kUninitialized: + Slot* slot = var->slot(); + ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled. + switch (slot->type()) { + case Slot::LOCAL: + case Slot::PARAMETER: { + switch (expr->context()) { + case Expression::kUninitialized: + UNREACHABLE(); + case Expression::kEffect: + // Perform assignment and discard value. + __ pop(r0); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); + break; + case Expression::kValue: + // Perform assignment and preserve value. + __ ldr(r0, MemOperand(sp)); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); + break; + case Expression::kTest: + // Perform assignment and test (and discard) value. + __ pop(r0); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); + TestAndBranch(r0, true_label_, false_label_); + break; + case Expression::kValueTest: { + Label discard; + __ ldr(r0, MemOperand(sp)); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); + TestAndBranch(r0, true_label_, &discard); + __ bind(&discard); + __ pop(); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + __ ldr(r0, MemOperand(sp)); + __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); + TestAndBranch(r0, &discard, false_label_); + __ bind(&discard); + __ pop(); + __ jmp(true_label_); + break; + } + } + break; + } + + case Slot::CONTEXT: { + int chain_length = + function_->scope()->ContextChainLength(slot->var()->scope()); + if (chain_length > 0) { + // Move up the chain of contexts to the context containing the slot. + __ ldr(r0, CodeGenerator::ContextOperand(cp, Context::CLOSURE_INDEX)); + // Load the function context (which is the incoming, outer context). + __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset)); + for (int i = 1; i < chain_length; i++) { + __ ldr(r0, + CodeGenerator::ContextOperand(r0, Context::CLOSURE_INDEX)); + __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset)); + } + } else { // Slot is in the current context. Generate optimized code. + __ mov(r0, cp); + } + // The context may be an intermediate context, not a function context. + __ ldr(r0, CodeGenerator::ContextOperand(r0, Context::FCONTEXT_INDEX)); + __ pop(r1); + __ str(r1, CodeGenerator::ContextOperand(r0, slot->index())); + + // RecordWrite may destroy all its register arguments. + if (expr->context() == Expression::kValue) { + __ push(r1); + } else if (expr->context() != Expression::kEffect) { + __ mov(r3, r1); + } + int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + + // Update the write barrier for the array store with r0 as the scratch + // register. Skip the write barrier if r0 is a smi. + // The smi test is part of RecordWrite on other platforms, not on arm. + Label exit; + __ tst(r0, Operand(kSmiTagMask)); + __ b(eq, &exit); + + __ mov(r2, Operand(offset)); + __ RecordWrite(r0, r2, r1); + __ bind(&exit); + if (expr->context() != Expression::kEffect && + expr->context() != Expression::kValue) { + Move(expr->context(), r3); + } + break; + } + + case Slot::LOOKUP: UNREACHABLE(); - case Expression::kEffect: - // Perform assignment and discard value. - __ pop(r0); - __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); break; - case Expression::kValue: - // Perform assignment and preserve value. - __ ldr(r0, MemOperand(sp)); - __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); - break; - case Expression::kTest: - // Perform assignment and test (and discard) value. - __ pop(r0); - __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); - TestAndBranch(r0, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - __ ldr(r0, MemOperand(sp)); - __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); - TestAndBranch(r0, true_label_, &discard); - __ bind(&discard); - __ pop(); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - __ ldr(r0, MemOperand(sp)); - __ str(r0, MemOperand(fp, SlotOffset(var->slot()))); - TestAndBranch(r0, &discard, false_label_); - __ bind(&discard); - __ pop(); - __ jmp(true_label_); - break; - } } } } diff --git a/src/compiler.cc b/src/compiler.cc index c5c55bb0e7..d1ed33b5dc 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -650,6 +650,16 @@ void CodeGenSelector::VisitDeclaration(Declaration* decl) { if (decl->fun() != NULL) { ProcessExpression(decl->fun(), Expression::kValue); } + Variable* var = decl->proxy()->var(); + ASSERT_NOT_NULL(var); + if ((!var->is_global() && decl->fun() != NULL)) { + BAILOUT("Non-global function declaration"); + } + if ((!var->is_global() && + var->slot() != NULL && + var->slot()->type() == Slot::LOOKUP)) { + BAILOUT("Lookup slot encountered in declaration"); + } } @@ -794,8 +804,10 @@ void CodeGenSelector::VisitVariableProxy(VariableProxy* expr) { } Slot::Type type = slot->type(); - if (type != Slot::PARAMETER && type != Slot::LOCAL) { - BAILOUT("non-parameter/non-local slot reference"); + // When LOOKUP slots are enabled, some currently dead code + // implementing unary typeof will become live. + if (type == Slot::LOOKUP) { + BAILOUT("Lookup slot"); } } } @@ -883,8 +895,8 @@ void CodeGenSelector::VisitAssignment(Assignment* expr) { BAILOUT("non-global/non-slot assignment"); } Slot::Type type = var->slot()->type(); - if (type != Slot::PARAMETER && type != Slot::LOCAL) { - BAILOUT("non-parameter/non-local slot assignment"); + if (type == Slot::LOOKUP) { + BAILOUT("Lookup slot"); } } } else if (prop != NULL) { diff --git a/src/ia32/fast-codegen-ia32.cc b/src/ia32/fast-codegen-ia32.cc index c4d0aa248f..4ea0950765 100644 --- a/src/ia32/fast-codegen-ia32.cc +++ b/src/ia32/fast-codegen-ia32.cc @@ -410,6 +410,7 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { Comment cmnt(masm_, "[ VariableProxy"); Expression* rewrite = expr->var()->rewrite(); if (rewrite == NULL) { + ASSERT(expr->var()->is_global()); Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in ecx and the global // object on the stack. @@ -425,8 +426,48 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { DropAndMove(expr->context(), eax); } else { - Comment cmnt(masm_, "Stack slot"); - Move(expr->context(), rewrite->AsSlot()); + Slot* slot = rewrite->AsSlot(); + ASSERT_NE(NULL, slot); + switch (slot->type()) { + case Slot::LOCAL: + case Slot::PARAMETER: { + Comment cmnt(masm_, "Stack slot"); + Move(expr->context(), slot); + break; + } + + case Slot::CONTEXT: { + Comment cmnt(masm_, "Context slot"); + int chain_length = + function_->scope()->ContextChainLength(slot->var()->scope()); + if (chain_length > 0) { + // Move up the chain of contexts to the context containing the slot. + __ mov(eax, + Operand(esi, Context::SlotOffset(Context::CLOSURE_INDEX))); + // Load the function context (which is the incoming, outer context). + __ mov(eax, FieldOperand(eax, JSFunction::kContextOffset)); + for (int i = 1; i < chain_length; i++) { + __ mov(eax, + Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX))); + __ mov(eax, FieldOperand(eax, JSFunction::kContextOffset)); + } + // The context may be an intermediate context, not a function context. + __ mov(eax, + Operand(eax, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } else { // Slot is in the current function context. + // The context may be an intermediate context, not a function context. + __ mov(eax, + Operand(esi, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } + __ mov(eax, Operand(eax, Context::SlotOffset(slot->index()))); + Move(expr->context(), eax); + break; + } + + case Slot::LOOKUP: + UNREACHABLE(); + break; + } } } @@ -693,44 +734,96 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { DropAndMove(expr->context(), eax); } else { - switch (expr->context()) { - case Expression::kUninitialized: + Slot* slot = var->slot(); + ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled. + switch (slot->type()) { + case Slot::LOCAL: + case Slot::PARAMETER: { + switch (expr->context()) { + case Expression::kUninitialized: + UNREACHABLE(); + case Expression::kEffect: + // Perform assignment and discard value. + __ pop(Operand(ebp, SlotOffset(var->slot()))); + break; + case Expression::kValue: + // Perform assignment and preserve value. + __ mov(eax, Operand(esp, 0)); + __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + break; + case Expression::kTest: + // Perform assignment and test (and discard) value. + __ pop(eax); + __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + TestAndBranch(eax, true_label_, false_label_); + break; + case Expression::kValueTest: { + Label discard; + __ mov(eax, Operand(esp, 0)); + __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + TestAndBranch(eax, true_label_, &discard); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + __ mov(eax, Operand(esp, 0)); + __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + TestAndBranch(eax, &discard, false_label_); + __ bind(&discard); + __ add(Operand(esp), Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } + } + break; + } + + case Slot::CONTEXT: { + int chain_length = + function_->scope()->ContextChainLength(slot->var()->scope()); + if (chain_length > 0) { + // Move up the context chain to the context containing the slot. + __ mov(eax, + Operand(esi, Context::SlotOffset(Context::CLOSURE_INDEX))); + // Load the function context (which is the incoming, outer context). + __ mov(eax, FieldOperand(eax, JSFunction::kContextOffset)); + for (int i = 1; i < chain_length; i++) { + __ mov(eax, + Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX))); + __ mov(eax, FieldOperand(eax, JSFunction::kContextOffset)); + } + } else { // Slot is in the current context. Generate optimized code. + __ mov(eax, esi); // RecordWrite destroys the object register. + } + if (FLAG_debug_code) { + __ cmp(eax, + Operand(eax, Context::SlotOffset(Context::FCONTEXT_INDEX))); + __ Check(equal, "Context Slot chain length wrong."); + } + __ pop(ecx); + __ mov(Operand(eax, Context::SlotOffset(slot->index())), ecx); + + // RecordWrite may destroy all its register arguments. + if (expr->context() == Expression::kValue) { + __ push(ecx); + } else if (expr->context() != Expression::kEffect) { + __ mov(edx, ecx); + } + int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + __ RecordWrite(eax, offset, ecx, ebx); + if (expr->context() != Expression::kEffect && + expr->context() != Expression::kValue) { + Move(expr->context(), edx); + } + break; + } + + case Slot::LOOKUP: UNREACHABLE(); - case Expression::kEffect: - // Perform assignment and discard value. - __ pop(Operand(ebp, SlotOffset(var->slot()))); break; - case Expression::kValue: - // Perform assignment and preserve value. - __ mov(eax, Operand(esp, 0)); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); - break; - case Expression::kTest: - // Perform assignment and test (and discard) value. - __ pop(eax); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); - TestAndBranch(eax, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - __ mov(eax, Operand(esp, 0)); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); - TestAndBranch(eax, true_label_, &discard); - __ bind(&discard); - __ add(Operand(esp), Immediate(kPointerSize)); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - __ mov(eax, Operand(esp, 0)); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); - TestAndBranch(eax, &discard, false_label_); - __ bind(&discard); - __ add(Operand(esp), Immediate(kPointerSize)); - __ jmp(true_label_); - break; - } } } } diff --git a/src/x64/fast-codegen-x64.cc b/src/x64/fast-codegen-x64.cc index 8184b52a9f..cfd8c4fae3 100644 --- a/src/x64/fast-codegen-x64.cc +++ b/src/x64/fast-codegen-x64.cc @@ -418,6 +418,7 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { Comment cmnt(masm_, "[ VariableProxy"); Expression* rewrite = expr->var()->rewrite(); if (rewrite == NULL) { + ASSERT(expr->var()->is_global()); Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in rcx and the global // object on the stack. @@ -425,14 +426,55 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { __ Move(rcx, expr->name()); Handle ic(Builtins::builtin(Builtins::LoadIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); - // A test rax instruction following the call is used by the IC to // indicate that the inobject property case was inlined. Ensure there // is no test rax instruction here. + __ nop(); + DropAndMove(expr->context(), rax); } else { - Comment cmnt(masm_, "Stack slot"); - Move(expr->context(), rewrite->AsSlot()); + Slot* slot = rewrite->AsSlot(); + ASSERT_NE(NULL, slot); + switch (slot->type()) { + case Slot::LOCAL: + case Slot::PARAMETER: { + Comment cmnt(masm_, "Stack slot"); + Move(expr->context(), slot); + break; + } + + case Slot::CONTEXT: { + Comment cmnt(masm_, "Context slot"); + int chain_length = + function_->scope()->ContextChainLength(slot->var()->scope()); + if (chain_length > 0) { + // Move up the chain of contexts to the context containing the slot. + __ movq(rax, + Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX))); + // Load the function context (which is the incoming, outer context). + __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset)); + for (int i = 1; i < chain_length; i++) { + __ movq(rax, + Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX))); + __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset)); + } + // The context may be an intermediate context, not a function context. + __ movq(rax, + Operand(rax, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } else { // Slot is in the current function context. + // The context may be an intermediate context, not a function context. + __ movq(rax, + Operand(rsi, Context::SlotOffset(Context::FCONTEXT_INDEX))); + } + __ movq(rax, Operand(rax, Context::SlotOffset(slot->index()))); + Move(expr->context(), rax); + break; + } + + case Slot::LOOKUP: + UNREACHABLE(); + break; + } } } @@ -695,44 +737,96 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) { DropAndMove(expr->context(), rax); } else { - switch (expr->context()) { - case Expression::kUninitialized: + Slot* slot = var->slot(); + ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled. + switch (slot->type()) { + case Slot::LOCAL: + case Slot::PARAMETER: { + switch (expr->context()) { + case Expression::kUninitialized: + UNREACHABLE(); + case Expression::kEffect: + // Perform assignment and discard value. + __ pop(Operand(rbp, SlotOffset(var->slot()))); + break; + case Expression::kValue: + // Perform assignment and preserve value. + __ movq(rax, Operand(rsp, 0)); + __ movq(Operand(rbp, SlotOffset(var->slot())), rax); + break; + case Expression::kTest: + // Perform assignment and test (and discard) value. + __ pop(rax); + __ movq(Operand(rbp, SlotOffset(var->slot())), rax); + TestAndBranch(rax, true_label_, false_label_); + break; + case Expression::kValueTest: { + Label discard; + __ movq(rax, Operand(rsp, 0)); + __ movq(Operand(rbp, SlotOffset(var->slot())), rax); + TestAndBranch(rax, true_label_, &discard); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(false_label_); + break; + } + case Expression::kTestValue: { + Label discard; + __ movq(rax, Operand(rsp, 0)); + __ movq(Operand(rbp, SlotOffset(var->slot())), rax); + TestAndBranch(rax, &discard, false_label_); + __ bind(&discard); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(true_label_); + break; + } + } + break; + } + + case Slot::CONTEXT: { + int chain_length = + function_->scope()->ContextChainLength(slot->var()->scope()); + if (chain_length > 0) { + // Move up the context chain to the context containing the slot. + __ movq(rax, + Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX))); + // Load the function context (which is the incoming, outer context). + __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset)); + for (int i = 1; i < chain_length; i++) { + __ movq(rax, + Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX))); + __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset)); + } + } else { // Slot is in the current context. Generate optimized code. + __ movq(rax, rsi); // RecordWrite destroys the object register. + } + if (FLAG_debug_code) { + __ cmpq(rax, + Operand(rax, Context::SlotOffset(Context::FCONTEXT_INDEX))); + __ Check(equal, "Context Slot chain length wrong."); + } + __ pop(rcx); + __ movq(Operand(rax, Context::SlotOffset(slot->index())), rcx); + + // RecordWrite may destroy all its register arguments. + if (expr->context() == Expression::kValue) { + __ push(rcx); + } else if (expr->context() != Expression::kEffect) { + __ movq(rdx, rcx); + } + int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + __ RecordWrite(rax, offset, rcx, rbx); + if (expr->context() != Expression::kEffect && + expr->context() != Expression::kValue) { + Move(expr->context(), rdx); + } + break; + } + + case Slot::LOOKUP: UNREACHABLE(); - case Expression::kEffect: - // Perform assignment and discard value. - __ pop(Operand(rbp, SlotOffset(var->slot()))); break; - case Expression::kValue: - // Perform assignment and preserve value. - __ movq(rax, Operand(rsp, 0)); - __ movq(Operand(rbp, SlotOffset(var->slot())), rax); - break; - case Expression::kTest: - // Perform assignment and test (and discard) value. - __ pop(rax); - __ movq(Operand(rbp, SlotOffset(var->slot())), rax); - TestAndBranch(rax, true_label_, false_label_); - break; - case Expression::kValueTest: { - Label discard; - __ movq(rax, Operand(rsp, 0)); - __ movq(Operand(rbp, SlotOffset(var->slot())), rax); - TestAndBranch(rax, true_label_, &discard); - __ bind(&discard); - __ addq(rsp, Immediate(kPointerSize)); - __ jmp(false_label_); - break; - } - case Expression::kTestValue: { - Label discard; - __ movq(rax, Operand(rsp, 0)); - __ movq(Operand(rbp, SlotOffset(var->slot())), rax); - TestAndBranch(rax, &discard, false_label_); - __ bind(&discard); - __ addq(rsp, Immediate(kPointerSize)); - __ jmp(true_label_); - break; - } } } }