Revision: 4118
Author: [email protected]
Date: Fri Mar 12 05:45:31 2010
Log: Implement a custom call compiler for Array.pop.
Review URL: http://codereview.chromium.org/870007
http://code.google.com/p/v8/source/detail?r=4118
Added:
/branches/bleeding_edge/test/mjsunit/array-pop.js
Modified:
/branches/bleeding_edge/src/arm/stub-cache-arm.cc
/branches/bleeding_edge/src/ia32/stub-cache-ia32.cc
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/stub-cache.h
/branches/bleeding_edge/src/x64/stub-cache-x64.cc
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/array-pop.js Fri Mar 12 05:45:31
2010
@@ -0,0 +1,61 @@
+// Copyright 2010 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.
+
+// Check pops with various number of arguments.
+(function() {
+ var a = [];
+ for (var i = 0; i < 7; i++) {
+ a = [7, 6, 5, 4, 3, 2, 1];
+
+ assertEquals(1, a.pop(), "1st pop");
+ assertEquals(6, a.length, "length 1st pop");
+
+ assertEquals(2, a.pop(1), "2nd pop");
+ assertEquals(5, a.length, "length 2nd pop");
+
+ assertEquals(3, a.pop(1, 2), "3rd pop");
+ assertEquals(4, a.length, "length 3rd pop");
+
+ assertEquals(4, a.pop(1, 2, 3), "4th pop");
+ assertEquals(3, a.length, "length 4th pop");
+
+ assertEquals(5, a.pop(), "5th pop");
+ assertEquals(2, a.length, "length 5th pop");
+
+ assertEquals(6, a.pop(), "6th pop");
+ assertEquals(1, a.length, "length 6th pop");
+
+ assertEquals(7, a.pop(), "7th pop");
+ assertEquals(0, a.length, "length 7th pop");
+
+ assertEquals(undefined, a.pop(), "8th pop");
+ assertEquals(0, a.length, "length 8th pop");
+
+ assertEquals(undefined, a.pop(1, 2, 3), "9th pop");
+ assertEquals(0, a.length, "length 9th pop");
+ }
+})();
=======================================
--- /branches/bleeding_edge/src/arm/stub-cache-arm.cc Thu Mar 11 08:24:31
2010
+++ /branches/bleeding_edge/src/arm/stub-cache-arm.cc Fri Mar 12 05:45:31
2010
@@ -862,6 +862,55 @@
}
return GetCode(CONSTANT_FUNCTION, function_name);
}
+
+
+Object* CallStubCompiler::CompileArrayPopCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ // TODO(642): faster implementation.
+ ASSERT(check == RECEIVER_MAP_CHECK);
+
+ Label miss;
+
+ // Get the receiver from the stack
+ const int argc = arguments().immediate();
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ tst(r1, Operand(kSmiTagMask));
+ __ b(eq, &miss);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss);
+
+ if (object->IsGlobalObject()) {
+ __ ldr(r3, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
+ __ str(r3, MemOperand(sp, argc * kPointerSize));
+ }
+
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop),
+ argc + 1,
+ 1);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ Handle<Code> ic = ComputeCallMiss(arguments().immediate());
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ String* function_name = NULL;
+ if (function->shared()->name()->IsString()) {
+ function_name = String::cast(function->shared()->name());
+ }
+ return GetCode(CONSTANT_FUNCTION, function_name);
+}
Object* CallStubCompiler::CompileCallConstant(Object* object,
=======================================
--- /branches/bleeding_edge/src/ia32/stub-cache-ia32.cc Thu Mar 11 08:24:31
2010
+++ /branches/bleeding_edge/src/ia32/stub-cache-ia32.cc Fri Mar 12 05:45:31
2010
@@ -1221,6 +1221,8 @@
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
+ ASSERT(check == RECEIVER_MAP_CHECK);
+
Label miss;
// Get the receiver from the stack.
@@ -1229,7 +1231,7 @@
// Check that the receiver isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
CheckPrototypes(JSObject::cast(object), edx,
holder, ebx,
@@ -1246,13 +1248,15 @@
// Check that the elements are in fast mode (not dictionary).
__ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
if (argc == 1) { // Otherwise fall through to call builtin.
Label call_builtin, exit, with_rset_update;
// Get the array's length into eax and calculate new length.
__ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
__ add(Operand(eax), Immediate(argc << 1));
// Get the element's length into ecx.
@@ -1308,6 +1312,90 @@
}
return GetCode(CONSTANT_FUNCTION, function_name);
}
+
+
+Object* CallStubCompiler::CompileArrayPopCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+ ASSERT(check == RECEIVER_MAP_CHECK);
+
+ Label miss, empty_array, call_builtin;
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ test(edx, Immediate(kSmiTagMask));
+ __ j(zero, &miss);
+
+ CheckPrototypes(JSObject::cast(object), edx,
+ holder, ebx,
+ eax, name, &miss);
+
+ // Get the elements array of the object.
+ __ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset));
+
+ // Check that the elements are in fast mode (not dictionary).
+ __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(Factory::fixed_array_map()));
+ __ j(not_equal, &miss);
+
+ // Get the array's length into ecx and calculate new length.
+ __ mov(ecx, FieldOperand(edx, JSArray::kLengthOffset));
+ __ sub(Operand(ecx), Immediate(Smi::FromInt(1)));
+ __ j(negative, &empty_array);
+
+ // Get the last element.
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(eax, FieldOperand(ebx,
+ ecx, times_half_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(Operand(eax), Immediate(Factory::the_hole_value()));
+ __ j(equal, &call_builtin);
+
+ // Set the array's length.
+ __ mov(FieldOperand(edx, JSArray::kLengthOffset), ecx);
+
+ // Fill with the hole.
+ __ mov(FieldOperand(ebx,
+ ecx, times_half_pointer_size,
+ FixedArray::kHeaderSize),
+ Immediate(Factory::the_hole_value()));
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&empty_array);
+ __ mov(eax, Immediate(Factory::undefined_value()));
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&call_builtin);
+
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop),
+ argc + 1,
+ 1);
+
+ __ bind(&miss);
+
+ Handle<Code> ic = ComputeCallMiss(arguments().immediate());
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ String* function_name = NULL;
+ if (function->shared()->name()->IsString()) {
+ function_name = String::cast(function->shared()->name());
+ }
+ return GetCode(CONSTANT_FUNCTION, function_name);
+}
Object* CallStubCompiler::CompileCallConstant(Object* object,
=======================================
--- /branches/bleeding_edge/src/runtime.cc Fri Mar 12 05:01:32 2010
+++ /branches/bleeding_edge/src/runtime.cc Fri Mar 12 05:45:31 2010
@@ -1274,6 +1274,16 @@
StubCompiler::CheckType check) {
return compiler->CompileArrayPushCall(object, holder, function, name,
check);
}
+
+
+static Object* CompileArrayPopCall(CallStubCompiler* compiler,
+ Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ StubCompiler::CheckType check) {
+ return compiler->CompileArrayPopCall(object, holder, function, name,
check);
+}
static Object* Runtime_SpecialArrayFunctions(Arguments args) {
@@ -1281,7 +1291,7 @@
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, holder, 0);
- InstallBuiltin(holder, "pop", Builtins::ArrayPop);
+ InstallBuiltin(holder, "pop", Builtins::ArrayPop, CompileArrayPopCall);
InstallBuiltin(holder, "push", Builtins::ArrayPush,
CompileArrayPushCall);
InstallBuiltin(holder, "shift", Builtins::ArrayShift);
InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
=======================================
--- /branches/bleeding_edge/src/stub-cache.h Thu Mar 11 08:24:31 2010
+++ /branches/bleeding_edge/src/stub-cache.h Fri Mar 12 05:45:31 2010
@@ -575,6 +575,12 @@
String* name,
CheckType check);
+ Object* CompileArrayPopCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check);
+
private:
const ParameterCount arguments_;
const InLoopFlag in_loop_;
=======================================
--- /branches/bleeding_edge/src/x64/stub-cache-x64.cc Thu Mar 11 08:24:31
2010
+++ /branches/bleeding_edge/src/x64/stub-cache-x64.cc Fri Mar 12 05:45:31
2010
@@ -697,6 +697,61 @@
argc + 1,
1);
+ // Handle call cache miss.
+ __ bind(&miss);
+ Handle<Code> ic = ComputeCallMiss(arguments().immediate());
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ String* function_name = NULL;
+ if (function->shared()->name()->IsString()) {
+ function_name = String::cast(function->shared()->name());
+ }
+ return GetCode(CONSTANT_FUNCTION, function_name);
+}
+
+
+Object* CallStubCompiler::CompileArrayPopCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+
+ // TODO(642): faster implementation.
+ ASSERT(check == RECEIVER_MAP_CHECK);
+
+ Label miss;
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rdx, &miss);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(JSObject::cast(object), rdx, holder,
+ rbx, rax, name, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
+ }
+
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop),
+ argc + 1,
+ 1);
// Handle call cache miss.
__ bind(&miss);
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev