Author: [email protected]
Date: Fri Apr 3 06:27:14 2009
New Revision: 1677
Added:
branches/bleeding_edge/test/mjsunit/constant-folding.js
Removed:
branches/bleeding_edge/test/mjsunit/compare-constants.js
Modified:
branches/bleeding_edge/src/builtins.h
branches/bleeding_edge/src/codegen-arm.cc
branches/bleeding_edge/src/codegen-ia32.cc
branches/bleeding_edge/src/runtime.js
branches/bleeding_edge/src/virtual-frame-ia32.h
Log:
Rewrite of VisitCountOperation that should speed it up
Review URL: http://codereview.chromium.org/56151
Modified: branches/bleeding_edge/src/builtins.h
==============================================================================
--- branches/bleeding_edge/src/builtins.h (original)
+++ branches/bleeding_edge/src/builtins.h Fri Apr 3 06:27:14 2009
@@ -105,8 +105,6 @@
V(MUL, 1) \
V(DIV, 1) \
V(MOD, 1) \
- V(INC, 0) \
- V(DEC, 0) \
V(BIT_OR, 1) \
V(BIT_AND, 1) \
V(BIT_XOR, 1) \
Modified: branches/bleeding_edge/src/codegen-arm.cc
==============================================================================
--- branches/bleeding_edge/src/codegen-arm.cc (original)
+++ branches/bleeding_edge/src/codegen-arm.cc Fri Apr 3 06:27:14 2009
@@ -708,29 +708,6 @@
};
-class InvokeBuiltinStub : public CodeStub {
- public:
- enum Kind { Inc, Dec, ToNumber };
- InvokeBuiltinStub(Kind kind, int argc) : kind_(kind), argc_(argc) { }
-
- private:
- Kind kind_;
- int argc_;
-
- Major MajorKey() { return InvokeBuiltin; }
- int MinorKey() { return (argc_ << 3) | static_cast<int>(kind_); }
- void Generate(MacroAssembler* masm);
-
-#ifdef DEBUG
- void Print() {
- PrintF("InvokeBuiltinStub (kind %d, argc, %d)\n",
- static_cast<int>(kind_),
- argc_);
- }
-#endif
-};
-
-
void CodeGenerator::GenericBinaryOperation(Token::Value op) {
VirtualFrame::SpilledScope spilled_scope(this);
// sp[0] : y
@@ -3696,22 +3673,27 @@
// Slow case: Convert to number.
slow.Bind();
-
- // Postfix: Convert the operand to a number and store it as the result.
+ {
+ // Convert the operand to a number.
+ frame_->EmitPush(r0);
+ Result arg_count = allocator_->Allocate(r0);
+ ASSERT(arg_count.is_valid());
+ __ mov(arg_count.reg(), Operand(0));
+ frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, &arg_count, 1);
+ }
if (is_postfix) {
- InvokeBuiltinStub stub(InvokeBuiltinStub::ToNumber, 2);
- frame_->CallStub(&stub, 0);
- // Store to result (on the stack).
+ // Postfix: store to result (on the stack).
__ str(r0, frame_->ElementAt(target.size()));
}
- // Compute the new value by calling the right JavaScript native.
+ // Compute the new value.
+ __ mov(r1, Operand(Smi::FromInt(1)));
+ frame_->EmitPush(r0);
+ frame_->EmitPush(r1);
if (is_increment) {
- InvokeBuiltinStub stub(InvokeBuiltinStub::Inc, 1);
- frame_->CallStub(&stub, 0);
+ frame_->CallRuntime(Runtime::kNumberAdd, 2);
} else {
- InvokeBuiltinStub stub(InvokeBuiltinStub::Dec, 1);
- frame_->CallStub(&stub, 0);
+ frame_->CallRuntime(Runtime::kNumberSub, 2);
}
// Store the new value in the target if not const.
@@ -4715,19 +4697,6 @@
__ bind(&done);
__ StubReturn(1);
-}
-
-
-void InvokeBuiltinStub::Generate(MacroAssembler* masm) {
- __ push(r0);
- __ mov(r0, Operand(0)); // set number of arguments
- switch (kind_) {
- case ToNumber: __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_JS); break;
- case Inc: __ InvokeBuiltin(Builtins::INC, JUMP_JS); break;
- case Dec: __ InvokeBuiltin(Builtins::DEC, JUMP_JS); break;
- default: UNREACHABLE();
- }
- __ StubReturn(argc_);
}
Modified: branches/bleeding_edge/src/codegen-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/codegen-ia32.cc (original)
+++ branches/bleeding_edge/src/codegen-ia32.cc Fri Apr 3 06:27:14 2009
@@ -4724,11 +4724,11 @@
DeferredCountOperation(CodeGenerator* generator,
bool is_postfix,
bool is_increment,
- int result_offset)
+ int target_size)
: DeferredCode(generator),
is_postfix_(is_postfix),
is_increment_(is_increment),
- result_offset_(result_offset) {
+ target_size_(target_size) {
set_comment("[ DeferredCountOperation");
}
@@ -4737,75 +4737,38 @@
private:
bool is_postfix_;
bool is_increment_;
- int result_offset_;
-};
-
-
-class RevertToNumberStub: public CodeStub {
- public:
- explicit RevertToNumberStub(bool is_increment)
- : is_increment_(is_increment) { }
-
- private:
- bool is_increment_;
-
- Major MajorKey() { return RevertToNumber; }
- int MinorKey() { return is_increment_ ? 1 : 0; }
- void Generate(MacroAssembler* masm);
-
-#ifdef DEBUG
- void Print() {
- PrintF("RevertToNumberStub (is_increment %s)\n",
- is_increment_ ? "true" : "false");
- }
-#endif
-};
-
-
-class CounterOpStub: public CodeStub {
- public:
- CounterOpStub(int result_offset, bool is_postfix, bool is_increment)
- : result_offset_(result_offset),
- is_postfix_(is_postfix),
- is_increment_(is_increment) { }
-
- private:
- int result_offset_;
- bool is_postfix_;
- bool is_increment_;
-
- Major MajorKey() { return CounterOp; }
- int MinorKey() {
- return ((result_offset_ << 2) |
- (is_postfix_ ? 2 : 0) |
- (is_increment_ ? 1 : 0));
- }
- void Generate(MacroAssembler* masm);
-
-#ifdef DEBUG
- void Print() {
- PrintF("CounterOpStub (result_offset %d), (is_postfix %s),"
- " (is_increment %s)\n",
- result_offset_,
- is_postfix_ ? "true" : "false",
- is_increment_ ? "true" : "false");
- }
-#endif
+ int target_size_;
};
void DeferredCountOperation::Generate() {
CodeGenerator* cgen = generator();
-
Result value(cgen);
enter()->Bind(&value);
- if (is_postfix_) {
- RevertToNumberStub to_number_stub(is_increment_);
- value = generator()->frame()->CallStub(&to_number_stub, &value);
+ VirtualFrame* frame = cgen->frame();
+ // Undo the optimistic smi operation.
+ value.ToRegister();
+ frame->Spill(value.reg());
+ if (is_increment_) {
+ __ sub(Operand(value.reg()), Immediate(Smi::FromInt(1)));
+ } else {
+ __ add(Operand(value.reg()), Immediate(Smi::FromInt(1)));
+ }
+ frame->Push(&value);
+ value = frame->InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION, 1);
+ frame->Push(&value);
+ if (is_postfix_) { // Fix up copy of old value with ToNumber(value).
+ // This is only safe because VisitCountOperation makes this frame slot
+ // beneath the reference a register, which is spilled at the above
call.
+ // We cannot safely write to constants or copies below the water line.
+ frame->StoreToElementAt(target_size_ + 1);
+ }
+ frame->Push(Smi::FromInt(1));
+ if (is_increment_) {
+ value = frame->CallRuntime(Runtime::kNumberAdd, 2);
+ } else {
+ value = frame->CallRuntime(Runtime::kNumberSub, 2);
}
-
- CounterOpStub stub(result_offset_, is_postfix_, is_increment_);
- value = generator()->frame()->CallStub(&stub, &value);
exit_.Jump(&value);
}
@@ -4819,7 +4782,8 @@
Variable* var = node->expression()->AsVariableProxy()->AsVariable();
bool is_const = (var != NULL && var->mode() == Variable::CONST);
- // Postfix: Make room for the result.
+ // Postfix operators need a stack slot under the reference to hold
+ // the old value while the new one is being stored.
if (is_postfix) {
frame_->Push(Smi::FromInt(0));
}
@@ -4836,16 +4800,21 @@
target.TakeValue(NOT_INSIDE_TYPEOF);
DeferredCountOperation* deferred =
- new DeferredCountOperation(this, is_postfix, is_increment,
- target.size() * kPointerSize);
+ new DeferredCountOperation(this, is_postfix,
+ is_increment, target.size());
Result value = frame_->Pop();
value.ToRegister();
- ASSERT(value.is_valid());
// Postfix: Store the old value as the result.
if (is_postfix) {
- Result old_value = value;
+ // Explicitly back the slot for the old value with a new register.
+ // This improves performance in some cases.
+ Result old_value = allocator_->Allocate();
+ ASSERT(old_value.is_valid());
+ __ mov(old_value.reg(), value.reg());
+ // SetElement must not create a constant element or a copy in this
slot,
+ // since we will write to it, below the waterline, in deferred code.
frame_->SetElementAt(target.size(), &old_value);
}
@@ -4885,7 +4854,7 @@
tmp.Unuse();
__ test(value.reg(), Immediate(kSmiTagMask));
deferred->enter()->Branch(not_zero, &value, not_taken);
- } else {
+ } else { // Otherwise we test separately for overflow and smi check.
deferred->enter()->Branch(overflow, &value, not_taken);
__ test(value.reg(), Immediate(kSmiTagMask));
deferred->enter()->Branch(not_zero, &value, not_taken);
@@ -6776,49 +6745,6 @@
__ jmp(adaptor, RelocInfo::CODE_TARGET);
}
-
-void RevertToNumberStub::Generate(MacroAssembler* masm) {
- // Revert optimistic increment/decrement.
- if (is_increment_) {
- __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
- } else {
- __ add(Operand(eax), Immediate(Smi::FromInt(1)));
- }
-
- __ pop(ecx);
- __ push(eax);
- __ push(ecx);
- __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
- // Code never returns due to JUMP_FUNCTION.
-}
-
-
-void CounterOpStub::Generate(MacroAssembler* masm) {
- // Store to the result on the stack (skip return address) before
- // performing the count operation.
- if (is_postfix_) {
- __ mov(Operand(esp, result_offset_ + kPointerSize), eax);
- }
-
- // Revert optimistic increment/decrement but only for prefix
- // counts. For postfix counts it has already been reverted before
- // the conversion to numbers.
- if (!is_postfix_) {
- if (is_increment_) {
- __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
- } else {
- __ add(Operand(eax), Immediate(Smi::FromInt(1)));
- }
- }
-
- // Compute the new value by calling the right JavaScript native.
- __ pop(ecx);
- __ push(eax);
- __ push(ecx);
- Builtins::JavaScript builtin = is_increment_ ? Builtins::INC :
Builtins::DEC;
- __ InvokeBuiltin(builtin, JUMP_FUNCTION);
- // Code never returns due to JUMP_FUNCTION.
-}
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
Modified: branches/bleeding_edge/src/runtime.js
==============================================================================
--- branches/bleeding_edge/src/runtime.js (original)
+++ branches/bleeding_edge/src/runtime.js Fri Apr 3 06:27:14 2009
@@ -197,18 +197,6 @@
}
-// ECMA-262, section 11.4.4, page 47.
-function INC() {
- return %NumberAdd(%ToNumber(this), 1);
-}
-
-
-// ECMA-262, section 11.4.5, page 48.
-function DEC() {
- return %NumberSub(%ToNumber(this), 1);
-}
-
-
/* -------------------------------------------
- - - B i t o p e r a t i o n s - - -
Modified: branches/bleeding_edge/src/virtual-frame-ia32.h
==============================================================================
--- branches/bleeding_edge/src/virtual-frame-ia32.h (original)
+++ branches/bleeding_edge/src/virtual-frame-ia32.h Fri Apr 3 06:27:14 2009
@@ -172,6 +172,10 @@
PushFrameSlotAt(elements_.length() - index - 1);
}
+ void StoreToElementAt(int index) {
+ StoreToFrameSlotAt(elements_.length() - index - 1);
+ }
+
// A frame-allocated local as an assembly operand.
Operand LocalAt(int index) const {
ASSERT(0 <= index);
Added: branches/bleeding_edge/test/mjsunit/constant-folding.js
==============================================================================
--- (empty file)
+++ branches/bleeding_edge/test/mjsunit/constant-folding.js Fri Apr 3
06:27:14 2009
@@ -0,0 +1,171 @@
+// 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 operations that involve one or more constants.
+// The code generator now handles compile-time constants specially.
+// Test the code generated when operands are known at compile time
+
+// Test count operations involving constants
+function test_count() {
+ var x = "foo";
+ var y = "3";
+
+ x += x++; // ++ and -- apply ToNumber to their operand, even for
postfix.
+ assertEquals(x, "fooNaN", "fooNaN test");
+ x = "luft";
+ x += ++x;
+ assertEquals(x, "luftNaN", "luftNaN test");
+
+ assertTrue(y++ === 3, "y++ === 3, where y = \"3\"");
+ y = 3;
+ assertEquals(y++, 3, "y++ == 3, where y = 3");
+ y = "7.1";
+ assertTrue(y++ === 7.1, "y++ === 7.1, where y = \"7.1\"");
+ var z = y = x = "9";
+ assertEquals( z++ + (++y) + x++, 28, "z++ + (++y) + x++ == 28");
+ z = y = x = 13;
+ assertEquals( z++ + (++y) + x++, 40, "z++ + (++y) + x++ == 40");
+ z = y = x = -5.5;
+ assertEquals( z++ + (++y) + x++, -15.5, "z++ + (++y) + x++ == -15.5");
+
+ assertEquals(y, -4.5);
+ z = y;
+ z++;
+ assertEquals(y, -4.5);
+ z = y;
+ y++;
+ assertEquals(z, -4.5);
+
+ y = 20;
+ z = y;
+ z++;
+ assertEquals(y, 20);
+ z = y;
+ y++;
+ assertEquals(z, 20);
+
+ const w = 30;
+ assertEquals(w++, 30);
+ assertEquals(++w, 31);
+ assertEquals(++w, 31);
+}
+
+test_count();
+
+// Test comparison operations that involve one or two constant smis.
+
+function test() {
+ var i = 5;
+ var j = 3;
+
+ assertTrue( j < i );
+ i = 5; j = 3;
+ assertTrue( j <= i );
+ i = 5; j = 3;
+ assertTrue( i > j );
+ i = 5; j = 3;
+ assertTrue( i >= j );
+ i = 5; j = 3;
+ assertTrue( i != j );
+ i = 5; j = 3;
+ assertTrue( i == i );
+ i = 5; j = 3;
+ assertFalse( i < j );
+ i = 5; j = 3;
+ assertFalse( i <= j );
+ i = 5; j = 3;
+ assertFalse( j > i );
+ i = 5; j = 3;
+ assertFalse(j >= i );
+ i = 5; j = 3;
+ assertFalse( j == i);
+ i = 5; j = 3;
+ assertFalse( i != i);
+
+ i = 10 * 10;
+ while ( i < 107 ) {
+ ++i;
+ }
+ j = 21;
+
+ assertTrue( j < i );
+ j = 21;
+ assertTrue( j <= i );
+ j = 21;
+ assertTrue( i > j );
+ j = 21;
+ assertTrue( i >= j );
+ j = 21;
+ assertTrue( i != j );
+ j = 21;
+ assertTrue( i == i );
+ j = 21;
+ assertFalse( i < j );
+ j = 21;
+ assertFalse( i <= j );
+ j = 21;
+ assertFalse( j > i );
+ j = 21;
+ assertFalse(j >= i );
+ j = 21;
+ assertFalse( j == i);
+ j = 21;
+ assertFalse( i != i);
+ j = 21;
+ assertTrue( j == j );
+ j = 21;
+ assertFalse( j != j );
+
+ assertTrue( 100 > 99 );
+ assertTrue( 101 >= 90 );
+ assertTrue( 11111 > -234 );
+ assertTrue( -888 <= -20 );
+
+ while ( 234 > 456 ) {
+ i = i + 1;
+ }
+
+ switch(3) {
+ case 5:
+ assertUnreachable();
+ break;
+ case 3:
+ j = 13;
+ default:
+ i = 2;
+ case 7:
+ j = 17;
+ break;
+ case 9:
+ j = 19;
+ assertUnreachable();
+ break;
+ }
+ assertEquals(17, j, "switch with constant value");
+}
+
+test();
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---