Revision: 3530 Author: [email protected] Date: Mon Jan 4 05:56:31 2010 Log: 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 http://code.google.com/p/v8/source/detail?r=3530 Added: /branches/bleeding_edge/test/mjsunit/compiler/countoperation.js Modified: /branches/bleeding_edge/src/arm/fast-codegen-arm.cc /branches/bleeding_edge/src/compiler.cc /branches/bleeding_edge/src/fast-codegen.cc /branches/bleeding_edge/src/fast-codegen.h /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc /branches/bleeding_edge/src/x64/fast-codegen-x64.cc ======================================= --- /dev/null +++ /branches/bleeding_edge/test/mjsunit/compiler/countoperation.js Mon Jan 4 05:56:31 2010 @@ -0,0 +1,111 @@ +// 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]); ======================================= --- /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Tue Dec 22 04:41:45 2009 +++ /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Mon Jan 4 05:56:31 2010 @@ -245,6 +245,40 @@ } } } + + +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 <> @@ -839,6 +873,7 @@ 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::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::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 @@ 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 @@ 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 @@ __ 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 @@ __ 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 @@ // 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 @@ // 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::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); - 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(r0); - break; - case Expression::kEffect: - // Do not save result. - break; - } + // 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(r0); + break; + 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 @@ } 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. + + // 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::kTest: - __ pop(r0); - TestAndBranch(r0, true_label_, 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::kValueTest: { - Label discard; - __ ldr(r0, MemOperand(sp)); - TestAndBranch(r0, true_label_, &discard); - __ bind(&discard); - __ add(sp, sp, Operand(kPointerSize)); - __ b(false_label_); - 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; } } ======================================= --- /branches/bleeding_edge/src/compiler.cc Fri Dec 18 05:38:28 2009 +++ /branches/bleeding_edge/src/compiler.cc Mon Jan 4 05:56:31 2010 @@ -649,12 +649,6 @@ 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 @@ } } } 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 @@ 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::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"); + } } ======================================= --- /branches/bleeding_edge/src/fast-codegen.cc Fri Dec 18 05:38:28 2009 +++ /branches/bleeding_edge/src/fast-codegen.cc Mon Jan 4 05:56:31 2010 @@ -676,7 +676,7 @@ EmitNamedPropertyLoad(prop, Expression::kValue); break; case KEYED_PROPERTY: - EmitKeyedPropertyLoad(Expression::kValue); + EmitKeyedPropertyLoad(prop, Expression::kValue); break; } } @@ -694,7 +694,8 @@ // Store the value. switch (assign_type) { case VARIABLE: - EmitVariableAssignment(expr); + EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), + expr->context()); break; case NAMED_PROPERTY: EmitNamedPropertyAssignment(expr); ======================================= --- /branches/bleeding_edge/src/fast-codegen.h Fri Dec 18 05:38:28 2009 +++ /branches/bleeding_edge/src/fast-codegen.h Mon Jan 4 05:56:31 2010 @@ -213,6 +213,7 @@ 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 @@ // 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 @@ // 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. ======================================= --- /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Tue Dec 22 04:41:45 2009 +++ /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Mon Jan 4 05:56:31 2010 @@ -225,6 +225,40 @@ } } } + + +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 <> @@ -828,6 +862,7 @@ 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::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::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 @@ 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 @@ 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 @@ __ 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::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); - 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(eax); - break; - case Expression::kEffect: - // Do not save result. - break; - } + // 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(eax); + break; + 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 @@ } 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. + + // 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::kTest: - __ pop(eax); - TestAndBranch(eax, true_label_, 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::kValueTest: { - Label discard; - __ mov(eax, Operand(esp, 0)); - 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)); - 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; } } ======================================= --- /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Mon Dec 28 01:21:23 2009 +++ /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Mon Jan 4 05:56:31 2010 @@ -234,6 +234,40 @@ } } } + + +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 <> @@ -837,6 +871,7 @@ 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::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::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 @@ 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 @@ 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 @@ __ 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::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::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()) { -- v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev
