Added general pre- and postfix count operations to top-level compiler.

Until now we only supported postfix operations on global variables.
This change add generic count operations to the top-level compiler.

I tried to re-use code from the code generator used for assignment expressions
where possible.

Review URL: http://codereview.chromium.org/496009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3530 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 88ba93d9
......@@ -247,6 +247,40 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) {
}
void FastCodeGenerator::MoveTOS(Expression::Context context) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
__ Drop(1);
break;
case Expression::kValue:
break;
case Expression::kTest:
__ pop(r0);
TestAndBranch(r0, true_label_, false_label_);
break;
case Expression::kValueTest: {
Label discard;
__ ldr(r0, MemOperand(sp, 0));
TestAndBranch(r0, true_label_, &discard);
__ bind(&discard);
__ Drop(1);
__ jmp(false_label_);
break;
}
case Expression::kTestValue: {
Label discard;
__ ldr(r0, MemOperand(sp, 0));
TestAndBranch(r0, &discard, false_label_);
__ bind(&discard);
__ Drop(1);
__ jmp(true_label_);
}
}
}
template <>
MemOperand FastCodeGenerator::CreateSlotOperand<MemOperand>(
Slot* source,
......@@ -839,6 +873,7 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
void FastCodeGenerator::EmitNamedPropertyLoad(Property* prop,
Expression::Context context) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
__ mov(r2, Operand(key->handle()));
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
......@@ -847,7 +882,9 @@ void FastCodeGenerator::EmitNamedPropertyLoad(Property* prop,
}
void FastCodeGenerator::EmitKeyedPropertyLoad(Expression::Context context) {
void FastCodeGenerator::EmitKeyedPropertyLoad(Property* prop,
Expression::Context context) {
SetSourcePosition(prop->position());
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
Move(context, r0);
......@@ -865,8 +902,8 @@ void FastCodeGenerator::EmitCompoundAssignmentOp(Token::Value op,
}
void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
Variable* var = expr->target()->AsVariableProxy()->AsVariable();
void FastCodeGenerator::EmitVariableAssignment(Variable* var,
Expression::Context context) {
ASSERT(var != NULL);
ASSERT(var->is_global() || var->slot() != NULL);
if (var->is_global()) {
......@@ -880,7 +917,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
// Overwrite the global object on the stack with the result if needed.
DropAndMove(expr->context(), r0);
DropAndMove(context, r0);
} else if (var->slot()) {
Slot* slot = var->slot();
......@@ -888,7 +925,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
switch (slot->type()) {
case Slot::LOCAL:
case Slot::PARAMETER: {
switch (expr->context()) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
......@@ -953,9 +990,9 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
__ str(r1, CodeGenerator::ContextOperand(r0, slot->index()));
// RecordWrite may destroy all its register arguments.
if (expr->context() == Expression::kValue) {
if (context == Expression::kValue) {
__ push(r1);
} else if (expr->context() != Expression::kEffect) {
} else if (context != Expression::kEffect) {
__ mov(r3, r1);
}
int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
......@@ -970,9 +1007,9 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
__ mov(r2, Operand(offset));
__ RecordWrite(r0, r2, r1);
__ bind(&exit);
if (expr->context() != Expression::kEffect &&
expr->context() != Expression::kValue) {
Move(expr->context(), r3);
if (context != Expression::kEffect &&
context != Expression::kValue) {
Move(context, r3);
}
break;
}
......@@ -1025,7 +1062,7 @@ void FastCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
// change to slow case to avoid the quadratic behavior of repeatedly
// adding fast properties.
if (expr->starts_initialization_block()) {
// Reciever is under the key and value.
// Receiver is under the key and value.
__ ldr(ip, MemOperand(sp, 2 * kPointerSize));
__ push(ip);
__ CallRuntime(Runtime::kToSlowProperties, 1);
......@@ -1219,7 +1256,7 @@ void FastCodeGenerator::VisitCallNew(CallNew* expr) {
// Load function, arg_count into r1 and r0.
__ mov(r0, Operand(arg_count));
// Function is in esp[arg_count + 1].
// Function is in sp[arg_count + 1].
__ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall));
......@@ -1402,27 +1439,76 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
VariableProxy* proxy = expr->expression()->AsVariableProxy();
ASSERT(proxy->AsVariable() != NULL);
ASSERT(proxy->AsVariable()->is_global());
Visit(proxy);
// Expression can only be a property, a global or a (parameter or local)
// slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
LhsKind assign_type = VARIABLE;
Property* prop = expr->expression()->AsProperty();
// In case of a property we use the uninitialized expression context
// of the key to detect a named property.
if (prop != NULL) {
assign_type = (prop->key()->context() == Expression::kUninitialized)
? NAMED_PROPERTY
: KEYED_PROPERTY;
}
// Evaluate expression and get value.
if (assign_type == VARIABLE) {
ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
EmitVariableLoad(expr->expression()->AsVariableProxy()->var(),
Expression::kValue);
} else {
// Reserve space for result of postfix operation.
if (expr->is_postfix() && expr->context() != Expression::kEffect) {
ASSERT(expr->context() != Expression::kUninitialized);
__ mov(ip, Operand(Smi::FromInt(0)));
__ push(ip);
}
Visit(prop->obj());
ASSERT_EQ(Expression::kValue, prop->obj()->context());
if (assign_type == NAMED_PROPERTY) {
EmitNamedPropertyLoad(prop, Expression::kValue);
} else {
Visit(prop->key());
ASSERT_EQ(Expression::kValue, prop->key()->context());
EmitKeyedPropertyLoad(prop, Expression::kValue);
}
}
// Convert to number.
__ InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS);
// Save result for postfix expressions.
if (expr->is_postfix()) {
switch (expr->context()) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
// Do not save result.
break;
case Expression::kValue: // Fall through
case Expression::kTest: // Fall through
case Expression::kTestValue: // Fall through
case Expression::kValueTest:
// Duplicate the result on the stack.
// Save the result on the stack. If we have a named or keyed property
// we store the result under the receiver that is currently on top
// of the stack.
switch (assign_type) {
case VARIABLE:
__ push(r0);
break;
case Expression::kEffect:
// Do not save result.
case NAMED_PROPERTY:
__ str(r0, MemOperand(sp, kPointerSize));
break;
case KEYED_PROPERTY:
__ str(r0, MemOperand(sp, 2 * kPointerSize));
break;
}
break;
}
}
// Call runtime for +1/-1.
__ push(r0);
__ mov(ip, Operand(Smi::FromInt(1)));
......@@ -1432,43 +1518,49 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
} else {
__ CallRuntime(Runtime::kNumberSub, 2);
}
// Call Store IC.
__ mov(r2, Operand(proxy->AsVariable()->name()));
__ ldr(ip, CodeGenerator::GlobalObject());
__ push(ip);
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
// Restore up stack after store IC.
__ add(sp, sp, Operand(kPointerSize));
switch (expr->context()) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect: // Fall through
case Expression::kValue:
// Do nothing. Result in either on the stack for value context
// or discarded for effect context.
break;
case Expression::kTest:
__ pop(r0);
TestAndBranch(r0, true_label_, false_label_);
// Store the value returned in r0.
switch (assign_type) {
case VARIABLE:
__ push(r0);
if (expr->is_postfix()) {
EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
Expression::kEffect);
// For all contexts except kEffect: We have the result on
// top of the stack.
if (expr->context() != Expression::kEffect) {
MoveTOS(expr->context());
}
} else {
EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
expr->context());
}
break;
case Expression::kValueTest: {
Label discard;
__ ldr(r0, MemOperand(sp));
TestAndBranch(r0, true_label_, &discard);
__ bind(&discard);
__ add(sp, sp, Operand(kPointerSize));
__ b(false_label_);
case NAMED_PROPERTY: {
__ mov(r2, Operand(prop->key()->AsLiteral()->handle()));
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
if (expr->is_postfix()) {
__ Drop(1); // Result is on the stack under the receiver.
if (expr->context() != Expression::kEffect) {
MoveTOS(expr->context());
}
} else {
DropAndMove(expr->context(), r0);
}
break;
}
case Expression::kTestValue: {
Label discard;
__ ldr(r0, MemOperand(sp));
TestAndBranch(r0, &discard, false_label_);
__ bind(&discard);
__ add(sp, sp, Operand(kPointerSize));
__ b(true_label_);
case KEYED_PROPERTY: {
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
if (expr->is_postfix()) {
__ Drop(2); // Result is on the stack under the key and the receiver.
if (expr->context() != Expression::kEffect) {
MoveTOS(expr->context());
}
} else {
DropAndMove(expr->context(), r0, 2);
}
break;
}
}
......
......@@ -649,12 +649,6 @@ void CodeGenSelector::VisitStatements(ZoneList<Statement*>* stmts) {
void CodeGenSelector::VisitDeclaration(Declaration* decl) {
Property* prop = decl->proxy()->AsProperty();
if (prop != NULL) {
// Property rewrites are shared, ensure we are not changing its
// expression context state.
ASSERT(prop->obj()->context() == Expression::kUninitialized ||
prop->obj()->context() == Expression::kValue);
ASSERT(prop->key()->context() == Expression::kUninitialized ||
prop->key()->context() == Expression::kValue);
ProcessExpression(prop->obj(), Expression::kValue);
ProcessExpression(prop->key(), Expression::kValue);
}
......@@ -903,8 +897,6 @@ void CodeGenSelector::VisitAssignment(Assignment* expr) {
}
}
} else if (prop != NULL) {
ASSERT(prop->obj()->context() == Expression::kUninitialized ||
prop->obj()->context() == Expression::kValue);
ProcessExpression(prop->obj(), Expression::kValue);
CHECK_BAILOUT;
// We will only visit the key during code generation for keyed property
......@@ -915,8 +907,6 @@ void CodeGenSelector::VisitAssignment(Assignment* expr) {
if (lit == NULL ||
!lit->handle()->IsSymbol() ||
String::cast(*(lit->handle()))->AsArrayIndex(&ignored)) {
ASSERT(prop->key()->context() == Expression::kUninitialized ||
prop->key()->context() == Expression::kValue);
ProcessExpression(prop->key(), Expression::kValue);
CHECK_BAILOUT;
}
......@@ -1022,11 +1012,36 @@ void CodeGenSelector::VisitUnaryOperation(UnaryOperation* expr) {
void CodeGenSelector::VisitCountOperation(CountOperation* expr) {
// We support postfix count operations on global variables.
if (expr->is_prefix()) BAILOUT("Prefix CountOperation");
Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
if (var == NULL || !var->is_global()) BAILOUT("non-global postincrement");
ProcessExpression(expr->expression(), Expression::kValue);
Property* prop = expr->expression()->AsProperty();
ASSERT(var == NULL || prop == NULL);
if (var != NULL) {
// All global variables are supported.
if (!var->is_global()) {
ASSERT(var->slot() != NULL);
Slot::Type type = var->slot()->type();
if (type == Slot::LOOKUP) {
BAILOUT("CountOperation with lookup slot");
}
}
} else if (prop != NULL) {
ProcessExpression(prop->obj(), Expression::kValue);
CHECK_BAILOUT;
// We will only visit the key during code generation for keyed property
// stores. Leave its expression context uninitialized for named
// property stores.
Literal* lit = prop->key()->AsLiteral();
uint32_t ignored;
if (lit == NULL ||
!lit->handle()->IsSymbol() ||
String::cast(*(lit->handle()))->AsArrayIndex(&ignored)) {
ProcessExpression(prop->key(), Expression::kValue);
CHECK_BAILOUT;
}
} else {
// This is a throw reference error.
BAILOUT("CountOperation non-variable/non-property expression");
}
}
......
......@@ -676,7 +676,7 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
EmitNamedPropertyLoad(prop, Expression::kValue);
break;
case KEYED_PROPERTY:
EmitKeyedPropertyLoad(Expression::kValue);
EmitKeyedPropertyLoad(prop, Expression::kValue);
break;
}
}
......@@ -694,7 +694,8 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
// Store the value.
switch (assign_type) {
case VARIABLE:
EmitVariableAssignment(expr);
EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
expr->context());
break;
case NAMED_PROPERTY:
EmitNamedPropertyAssignment(expr);
......
......@@ -213,6 +213,7 @@ class FastCodeGenerator: public AstVisitor {
int SlotOffset(Slot* slot);
void Move(Expression::Context destination, Register source);
void MoveTOS(Expression::Context destination);
void Move(Expression::Context destination, Slot* source, Register scratch);
void Move(Expression::Context destination, Literal* source);
void Move(Slot* dst, Register source, Register scratch1, Register scratch2);
......@@ -247,13 +248,13 @@ class FastCodeGenerator: public AstVisitor {
// Platform-specific support for compiling assignments.
// Load a value from a named property and push the result on the stack.
// Load a value from a named property.
// The receiver is left on the stack by the IC.
void EmitNamedPropertyLoad(Property* expr, Expression::Context context);
// Load a value from a named property and push the result on the stack.
// Load a value from a keyed property.
// The receiver and the key is left on the stack by the IC.
void EmitKeyedPropertyLoad(Expression::Context context);
void EmitKeyedPropertyLoad(Property* expr, Expression::Context context);
// Apply the compound assignment operator. Expects both operands on top
// of the stack.
......@@ -261,7 +262,7 @@ class FastCodeGenerator: public AstVisitor {
// Complete a variable assignment. The right-hand-side value is expected
// on top of the stack.
void EmitVariableAssignment(Assignment* expr);
void EmitVariableAssignment(Variable* var, Expression::Context context);
// Complete a named property assignment. The receiver and right-hand-side
// value are expected on top of the stack.
......
......@@ -227,6 +227,40 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) {
}
void FastCodeGenerator::MoveTOS(Expression::Context context) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
__ Drop(1);
break;
case Expression::kValue:
break;
case Expression::kTest:
__ pop(eax);
TestAndBranch(eax, true_label_, false_label_);
break;
case Expression::kValueTest: {
Label discard;
__ mov(eax, Operand(esp, 0));
TestAndBranch(eax, true_label_, &discard);
__ bind(&discard);
__ Drop(1);
__ jmp(false_label_);
break;
}
case Expression::kTestValue: {
Label discard;
__ mov(eax, Operand(esp, 0));
TestAndBranch(eax, &discard, false_label_);
__ bind(&discard);
__ Drop(1);
__ jmp(true_label_);
}
}
}
template <>
Operand FastCodeGenerator::CreateSlotOperand<Operand>(Slot* source,
Register scratch) {
......@@ -828,6 +862,7 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
void FastCodeGenerator::EmitNamedPropertyLoad(Property* prop,
Expression::Context context) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
__ mov(ecx, Immediate(key->handle()));
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
......@@ -836,7 +871,9 @@ void FastCodeGenerator::EmitNamedPropertyLoad(Property* prop,
}
void FastCodeGenerator::EmitKeyedPropertyLoad(Expression::Context context) {
void FastCodeGenerator::EmitKeyedPropertyLoad(Property* prop,
Expression::Context context) {
SetSourcePosition(prop->position());
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
Move(context, eax);
......@@ -853,8 +890,8 @@ void FastCodeGenerator::EmitCompoundAssignmentOp(Token::Value op,
}
void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
Variable* var = expr->target()->AsVariableProxy()->AsVariable();
void FastCodeGenerator::EmitVariableAssignment(Variable* var,
Expression::Context context) {
ASSERT(var != NULL);
ASSERT(var->is_global() || var->slot() != NULL);
if (var->is_global()) {
......@@ -867,7 +904,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// Overwrite the receiver on the stack with the result if needed.
DropAndMove(expr->context(), eax);
DropAndMove(context, eax);
} else if (var->slot() != NULL) {
Slot* slot = var->slot();
......@@ -875,7 +912,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
case Slot::LOCAL:
case Slot::PARAMETER: {
Operand target = Operand(ebp, SlotOffset(var->slot()));
switch (expr->context()) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
......@@ -943,16 +980,16 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
__ mov(Operand(eax, Context::SlotOffset(slot->index())), ecx);
// RecordWrite may destroy all its register arguments.
if (expr->context() == Expression::kValue) {
if (context == Expression::kValue) {
__ push(ecx);
} else if (expr->context() != Expression::kEffect) {
} else if (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);
if (context != Expression::kEffect &&
context != Expression::kValue) {
Move(context, edx);
}
break;
}
......@@ -1377,27 +1414,75 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
VariableProxy* proxy = expr->expression()->AsVariableProxy();
ASSERT(proxy->AsVariable() != NULL);
ASSERT(proxy->AsVariable()->is_global());
Visit(proxy);
// Expression can only be a property, a global or a (parameter or local)
// slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
LhsKind assign_type = VARIABLE;
Property* prop = expr->expression()->AsProperty();
// In case of a property we use the uninitialized expression context
// of the key to detect a named property.
if (prop != NULL) {
assign_type = (prop->key()->context() == Expression::kUninitialized)
? NAMED_PROPERTY
: KEYED_PROPERTY;
}
// Evaluate expression and get value.
if (assign_type == VARIABLE) {
ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
EmitVariableLoad(expr->expression()->AsVariableProxy()->var(),
Expression::kValue);
} else {
// Reserve space for result of postfix operation.
if (expr->is_postfix() && expr->context() != Expression::kEffect) {
ASSERT(expr->context() != Expression::kUninitialized);
__ push(Immediate(Smi::FromInt(0)));
}
Visit(prop->obj());
ASSERT_EQ(Expression::kValue, prop->obj()->context());
if (assign_type == NAMED_PROPERTY) {
EmitNamedPropertyLoad(prop, Expression::kValue);
} else {
Visit(prop->key());
ASSERT_EQ(Expression::kValue, prop->key()->context());
EmitKeyedPropertyLoad(prop, Expression::kValue);
}
}
// Convert to number.
__ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
// Save result for postfix expressions.
if (expr->is_postfix()) {
switch (expr->context()) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
// Do not save result.
break;
case Expression::kValue: // Fall through
case Expression::kTest: // Fall through
case Expression::kTestValue: // Fall through
case Expression::kValueTest:
// Duplicate the result on the stack.
// Save the result on the stack. If we have a named or keyed property
// we store the result under the receiver that is currently on top
// of the stack.
switch (assign_type) {
case VARIABLE:
__ push(eax);
break;
case Expression::kEffect:
// Do not save result.
case NAMED_PROPERTY:
__ mov(Operand(esp, kPointerSize), eax);
break;
case KEYED_PROPERTY:
__ mov(Operand(esp, 2 * kPointerSize), eax);
break;
}
break;
}
}
// Call runtime for +1/-1.
__ push(eax);
__ push(Immediate(Smi::FromInt(1)));
......@@ -1406,42 +1491,55 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
} else {
__ CallRuntime(Runtime::kNumberSub, 2);
}
// Call Store IC.
__ mov(ecx, proxy->AsVariable()->name());
__ push(CodeGenerator::GlobalObject());
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// Restore up stack after store IC.
__ add(Operand(esp), Immediate(kPointerSize));
switch (expr->context()) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect: // Fall through
case Expression::kValue:
// Do nothing. Result in either on the stack for value context
// or discarded for effect context.
break;
case Expression::kTest:
__ pop(eax);
TestAndBranch(eax, true_label_, false_label_);
// Store the value returned in eax.
switch (assign_type) {
case VARIABLE:
__ push(eax);
if (expr->is_postfix()) {
EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
Expression::kEffect);
// For all contexts except kEffect: We have the result on
// top of the stack.
if (expr->context() != Expression::kEffect) {
MoveTOS(expr->context());
}
} else {
EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
expr->context());
}
break;
case Expression::kValueTest: {
Label discard;
__ mov(eax, Operand(esp, 0));
TestAndBranch(eax, true_label_, &discard);
__ bind(&discard);
__ add(Operand(esp), Immediate(kPointerSize));
__ jmp(false_label_);
case NAMED_PROPERTY: {
__ mov(ecx, prop->key()->AsLiteral()->handle());
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// This nop signals to the IC that there is no inlined code at the call
// site for it to patch.
__ nop();
if (expr->is_postfix()) {
__ Drop(1); // Result is on the stack under the receiver.
if (expr->context() != Expression::kEffect) {
MoveTOS(expr->context());
}
} else {
DropAndMove(expr->context(), eax);
}
break;
}
case Expression::kTestValue: {
Label discard;
__ mov(eax, Operand(esp, 0));
TestAndBranch(eax, &discard, false_label_);
__ bind(&discard);
__ add(Operand(esp), Immediate(kPointerSize));
__ jmp(true_label_);
case KEYED_PROPERTY: {
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// This nop signals to the IC that there is no inlined code at the call
// site for it to patch.
__ nop();
if (expr->is_postfix()) {
__ Drop(2); // Result is on the stack under the key and the receiver.
if (expr->context() != Expression::kEffect) {
MoveTOS(expr->context());
}
} else {
DropAndMove(expr->context(), eax, 2);
}
break;
}
}
......
......@@ -236,6 +236,40 @@ void FastCodeGenerator::Move(Expression::Context context, Register source) {
}
void FastCodeGenerator::MoveTOS(Expression::Context context) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
__ Drop(1);
break;
case Expression::kValue:
break;
case Expression::kTest:
__ pop(rax);
TestAndBranch(rax, true_label_, false_label_);
break;
case Expression::kValueTest: {
Label discard;
__ movq(rax, Operand(rsp, 0));
TestAndBranch(rax, true_label_, &discard);
__ bind(&discard);
__ Drop(1);
__ jmp(false_label_);
break;
}
case Expression::kTestValue: {
Label discard;
__ movq(rax, Operand(rsp, 0));
TestAndBranch(rax, &discard, false_label_);
__ bind(&discard);
__ Drop(1);
__ jmp(true_label_);
}
}
}
template <>
Operand FastCodeGenerator::CreateSlotOperand<Operand>(Slot* source,
Register scratch) {
......@@ -837,6 +871,7 @@ void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
void FastCodeGenerator::EmitNamedPropertyLoad(Property* prop,
Expression::Context context) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
__ Move(rcx, key->handle());
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
......@@ -845,7 +880,9 @@ void FastCodeGenerator::EmitNamedPropertyLoad(Property* prop,
}
void FastCodeGenerator::EmitKeyedPropertyLoad(Expression::Context context) {
void FastCodeGenerator::EmitKeyedPropertyLoad(Property* prop,
Expression::Context context) {
SetSourcePosition(prop->position());
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
Move(context, rax);
......@@ -862,8 +899,8 @@ void FastCodeGenerator::EmitCompoundAssignmentOp(Token::Value op,
}
void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
Variable* var = expr->target()->AsVariableProxy()->AsVariable();
void FastCodeGenerator::EmitVariableAssignment(Variable* var,
Expression::Context context) {
ASSERT(var != NULL);
ASSERT(var->is_global() || var->slot() != NULL);
if (var->is_global()) {
......@@ -876,7 +913,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
// Overwrite the global object on the stack with the result if needed.
DropAndMove(expr->context(), rax);
DropAndMove(context, rax);
} else if (var->slot()) {
Slot* slot = var->slot();
......@@ -884,7 +921,7 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
switch (slot->type()) {
case Slot::LOCAL:
case Slot::PARAMETER: {
switch (expr->context()) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
......@@ -952,16 +989,16 @@ void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
__ movq(Operand(rax, Context::SlotOffset(slot->index())), rcx);
// RecordWrite may destroy all its register arguments.
if (expr->context() == Expression::kValue) {
if (context == Expression::kValue) {
__ push(rcx);
} else if (expr->context() != Expression::kEffect) {
} else if (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);
if (context != Expression::kEffect &&
context != Expression::kValue) {
Move(context, rdx);
}
break;
}
......@@ -1263,78 +1300,6 @@ void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
}
}
void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
VariableProxy* proxy = expr->expression()->AsVariableProxy();
ASSERT(proxy->AsVariable() != NULL);
ASSERT(proxy->AsVariable()->is_global());
Visit(proxy);
__ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
switch (expr->context()) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kValue: // Fall through
case Expression::kTest: // Fall through
case Expression::kTestValue: // Fall through
case Expression::kValueTest:
// Duplicate the result on the stack.
__ push(rax);
break;
case Expression::kEffect:
// Do not save result.
break;
}
// Call runtime for +1/-1.
__ push(rax);
__ Push(Smi::FromInt(1));
if (expr->op() == Token::INC) {
__ CallRuntime(Runtime::kNumberAdd, 2);
} else {
__ CallRuntime(Runtime::kNumberSub, 2);
}
// Call Store IC.
__ Move(rcx, proxy->AsVariable()->name());
__ push(CodeGenerator::GlobalObject());
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// Restore up stack after store IC
__ addq(rsp, Immediate(kPointerSize));
switch (expr->context()) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect: // Fall through
case Expression::kValue:
// Do nothing. Result in either on the stack for value context
// or discarded for effect context.
break;
case Expression::kTest:
__ pop(rax);
TestAndBranch(rax, true_label_, false_label_);
break;
case Expression::kValueTest: {
Label discard;
__ movq(rax, Operand(rsp, 0));
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));
TestAndBranch(rax, &discard, false_label_);
__ bind(&discard);
__ addq(rsp, Immediate(kPointerSize));
__ jmp(true_label_);
break;
}
}
}
void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
switch (expr->op()) {
......@@ -1464,6 +1429,139 @@ void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
}
void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
// Expression can only be a property, a global or a (parameter or local)
// slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
LhsKind assign_type = VARIABLE;
Property* prop = expr->expression()->AsProperty();
// In case of a property we use the uninitialized expression context
// of the key to detect a named property.
if (prop != NULL) {
assign_type = (prop->key()->context() == Expression::kUninitialized)
? NAMED_PROPERTY
: KEYED_PROPERTY;
}
// Evaluate expression and get value.
if (assign_type == VARIABLE) {
ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
EmitVariableLoad(expr->expression()->AsVariableProxy()->var(),
Expression::kValue);
} else {
// Reserve space for result of postfix operation.
if (expr->is_postfix() && expr->context() != Expression::kEffect) {
ASSERT(expr->context() != Expression::kUninitialized);
__ Push(Smi::FromInt(0));
}
Visit(prop->obj());
ASSERT_EQ(Expression::kValue, prop->obj()->context());
if (assign_type == NAMED_PROPERTY) {
EmitNamedPropertyLoad(prop, Expression::kValue);
} else {
Visit(prop->key());
ASSERT_EQ(Expression::kValue, prop->key()->context());
EmitKeyedPropertyLoad(prop, Expression::kValue);
}
}
// Convert to number.
__ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
// Save result for postfix expressions.
if (expr->is_postfix()) {
switch (expr->context()) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
// Do not save result.
break;
case Expression::kValue: // Fall through
case Expression::kTest: // Fall through
case Expression::kTestValue: // Fall through
case Expression::kValueTest:
// Save the result on the stack. If we have a named or keyed property
// we store the result under the receiver that is currently on top
// of the stack.
switch (assign_type) {
case VARIABLE:
__ push(rax);
break;
case NAMED_PROPERTY:
__ movq(Operand(rsp, kPointerSize), rax);
break;
case KEYED_PROPERTY:
__ movq(Operand(rsp, 2 * kPointerSize), rax);
break;
}
break;
}
}
// Call runtime for +1/-1.
__ push(rax);
__ Push(Smi::FromInt(1));
if (expr->op() == Token::INC) {
__ CallRuntime(Runtime::kNumberAdd, 2);
} else {
__ CallRuntime(Runtime::kNumberSub, 2);
}
// Store the value returned in rax.
switch (assign_type) {
case VARIABLE:
__ push(rax);
if (expr->is_postfix()) {
EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
Expression::kEffect);
// For all contexts except kEffect: We have the result on
// top of the stack.
if (expr->context() != Expression::kEffect) {
MoveTOS(expr->context());
}
} else {
EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
expr->context());
}
break;
case NAMED_PROPERTY: {
__ Move(rcx, prop->key()->AsLiteral()->handle());
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// This nop signals to the IC that there is no inlined code at the call
// site for it to patch.
__ nop();
if (expr->is_postfix()) {
__ Drop(1); // Result is on the stack under the receiver.
if (expr->context() != Expression::kEffect) {
MoveTOS(expr->context());
}
} else {
DropAndMove(expr->context(), rax);
}
break;
}
case KEYED_PROPERTY: {
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// This nop signals to the IC that there is no inlined code at the call
// site for it to patch.
__ nop();
if (expr->is_postfix()) {
__ Drop(2); // Result is on the stack under the key and the receiver.
if (expr->context() != Expression::kEffect) {
MoveTOS(expr->context());
}
} else {
DropAndMove(expr->context(), rax, 2);
}
break;
}
}
}
void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
Comment cmnt(masm_, "[ BinaryOperation");
switch (expr->op()) {
......
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Test pre- and postfix count operations.
// Test value context.
var a = 42;
var b = {x:42};
var c = "x";
assertEquals(43, ++a);
assertEquals(43, a);
assertEquals(43, a++);
assertEquals(44, a);
assertEquals(43, ++b.x);
assertEquals(43, b.x);
assertEquals(43, b.x++);
assertEquals(44, b.x);
assertEquals(45, ++b[c]);
assertEquals(45, b[c]);
assertEquals(45, b[c]++);
assertEquals(46, b[c]);
// Test effect context.
a = 42;
b = {x:42};
c = "x";
assertEquals(1, eval("++a; 1"));
assertEquals(43, a);
assertEquals(1, eval("a++; 1"));
assertEquals(44, a);
assertEquals(1, eval("++b.x; 1"));
assertEquals(43, b.x);
assertEquals(1, eval("b.x++; 1"));
assertEquals(44, b.x);
assertEquals(1, eval("++b[c]; 1"));
assertEquals(45, b[c]);
assertEquals(1, eval("b[c]++; 1"));
assertEquals(46, b[c]);
// Test test context.
a = 42;
b = {x:42};
c = "x";
assertEquals(1, (++a) ? 1 : 0);
assertEquals(43, a);
assertEquals(1, (a++) ? 1 : 0);
assertEquals(44, a);
assertEquals(1, (++b.x) ? 1 : 0);
assertEquals(43, b.x);
assertEquals(1, (b.x++) ? 1 : 0);
assertEquals(44, b.x);
assertEquals(1, (++b[c]) ? 1 : 0);
assertEquals(45, b[c]);
assertEquals(1, (b[c]++) ? 1 : 0);
assertEquals(46, b[c]);
// Test value/test and test/value contexts.
a = 42;
b = {x:42};
c = "x";
assertEquals(43, ++a || 1);
assertEquals(43, a);
assertEquals(43, a++ || 1);
assertEquals(44, a);
assertEquals(43, ++b.x || 1);
assertEquals(43, b.x);
assertEquals(43, (b.x++) || 1);
assertEquals(44, b.x);
assertEquals(45, ++b[c] || 1);
assertEquals(45, b[c]);
assertEquals(45, b[c]++ || 1);
assertEquals(46, b[c]);
a = 42;
b = {x:42};
c = "x";
assertEquals(1, ++a && 1);
assertEquals(43, a);
assertEquals(1, a++ && 1);
assertEquals(44, a);
assertEquals(1, ++b.x && 1);
assertEquals(43, b.x);
assertEquals(1, (b.x++) && 1);
assertEquals(44, b.x);
assertEquals(1, ++b[c] && 1);
assertEquals(45, b[c]);
assertEquals(1, b[c]++ && 1);
assertEquals(46, b[c]);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment