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
This commit is contained in:
whesse@chromium.org 2009-11-09 09:56:57 +00:00
parent 0afb1e1697
commit 493c9f072c
4 changed files with 418 additions and 120 deletions

View File

@ -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;
}
}
}
}

View File

@ -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) {

View File

@ -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;
}
}
}
}

View File

@ -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<Code> 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;
}
}
}
}