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

Reply via email to