Introduce faster swapping primitives.
authorantonm@chromium.org <antonm@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 29 Apr 2010 15:14:39 +0000 (15:14 +0000)
committerantonm@chromium.org <antonm@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 29 Apr 2010 15:14:39 +0000 (15:14 +0000)
Keyed store stub sits high in sorting profiles.

Swapping allows to save us additional type checks as we could both read and
write elmenets (on fast path) without them.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4551 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

12 files changed:
src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/array.js
src/codegen.h
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/ic.h
src/runtime.cc
src/runtime.h
src/x64/codegen-x64.cc
src/x64/codegen-x64.h
test/mjsunit/fuzz-natives.js

index 2f9a32ff18e6e4239570a2e861efa491856e88c4..90428a79555fb59defab2bb24a44c4d243b4b966 100644 (file)
@@ -4325,6 +4325,20 @@ void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
+  Comment cmnt(masm_, "[ GenerateSwapElements");
+
+  ASSERT_EQ(3, args->length());
+
+  Load(args->at(0));
+  Load(args->at(1));
+  Load(args->at(2));
+
+  frame_->CallRuntime(Runtime::kSwapElements, 3);
+  frame_->EmitPush(r0);
+}
+
+
 void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
   Comment cmnt(masm_, "[ GenerateCallFunction");
 
index 2207ba24b0c4ea5c24355e46312fad80f37a1d1b..ec3e2d6c1be3e4b58cf101b1db05b21eb6b91ff8 100644 (file)
@@ -452,6 +452,9 @@ class CodeGenerator: public AstVisitor {
   // Fast support for number to string.
   void GenerateNumberToString(ZoneList<Expression*>* args);
 
+  // Fast swapping of elements.
+  void GenerateSwapElements(ZoneList<Expression*>* args);
+
   // Fast call for custom callbacks.
   void GenerateCallFunction(ZoneList<Expression*>* args);
 
index 00010de91ac0ec245cee129d5c88497c162f9b6e..216c03b6332c7a5a0fdd2b66816da21db88a6f21 100644 (file)
@@ -684,8 +684,7 @@ function ArraySort(comparefn) {
     var pivot = a[pivot_index];
     // Issue 95: Keep the pivot element out of the comparisons to avoid
     // infinite recursion if comparefn(pivot, pivot) != 0.
-    a[pivot_index] = a[from];
-    a[from] = pivot;
+    %_SwapElements(a, from, pivot_index);
     var low_end = from;   // Upper bound of the elements lower than pivot.
     var high_start = to;  // Lower bound of the elements greater than pivot.
     // From low_end to i are elements equal to pivot.
@@ -694,14 +693,12 @@ function ArraySort(comparefn) {
       var element = a[i];
       var order = %_CallFunction(global_receiver, element, pivot, comparefn);
       if (order < 0) {
-        a[i] = a[low_end];
-        a[low_end] = element;
+        %_SwapElements(a, i, low_end);
         i++;
         low_end++;
       } else if (order > 0) {
         high_start--;
-        a[i] = a[high_start];
-        a[high_start] = element;
+        %_SwapElements(a, i, high_start);
       } else {  // order == 0
         i++;
       }
index a42eb4a8b3ff2da655fc29c6d612f262fc9d9912..a5bb31f1415cfb77cf178185d4b2269fbb4213f1 100644 (file)
@@ -126,6 +126,7 @@ namespace internal {
   F(RegExpConstructResult, 3, 1)                                             \
   F(GetFromCache, 2, 1)                                                      \
   F(NumberToString, 1, 1)                                                    \
+  F(SwapElements, 3, 1)                                                      \
   F(MathPow, 2, 1)                                                           \
   F(MathSin, 1, 1)                                                           \
   F(MathCos, 1, 1)                                                           \
index 83ed1855ec316c8e27865dcf258465ac6b746e5d..6b380f06cf2f3ffad8197ea209abf6cfe0b9c52b 100644 (file)
@@ -6608,6 +6608,121 @@ void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
 }
 
 
+class DeferredSwapElements: public DeferredCode {
+ public:
+  DeferredSwapElements(Register object, Register index1, Register index2)
+      : object_(object), index1_(index1), index2_(index2) {
+    set_comment("[ DeferredSwapElements");
+  }
+
+  virtual void Generate();
+
+ private:
+  Register object_, index1_, index2_;
+};
+
+
+void DeferredSwapElements::Generate() {
+  __ push(object_);
+  __ push(index1_);
+  __ push(index2_);
+  __ CallRuntime(Runtime::kSwapElements, 3);
+}
+
+
+void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
+  // Note: this code assumes that indices are passed are within
+  // elements' bounds and refer to valid (not holes) values.
+  Comment cmnt(masm_, "[ GenerateSwapElements");
+
+  ASSERT_EQ(3, args->length());
+
+  Load(args->at(0));
+  Load(args->at(1));
+  Load(args->at(2));
+
+  Result index2 = frame_->Pop();
+  index2.ToRegister();
+
+  Result index1 = frame_->Pop();
+  index1.ToRegister();
+
+  Result object = frame_->Pop();
+  object.ToRegister();
+
+  Result tmp1 = allocator()->Allocate();
+  tmp1.ToRegister();
+  Result tmp2 = allocator()->Allocate();
+  tmp2.ToRegister();
+
+  frame_->Spill(object.reg());
+  frame_->Spill(index1.reg());
+  frame_->Spill(index2.reg());
+
+  DeferredSwapElements* deferred = new DeferredSwapElements(object.reg(),
+                                                            index1.reg(),
+                                                            index2.reg());
+
+  // Fetch the map and check if array is in fast case.
+  // Check that object doesn't require security checks and
+  // has no indexed interceptor.
+  __ CmpObjectType(object.reg(), FIRST_JS_OBJECT_TYPE, tmp1.reg());
+  deferred->Branch(less);
+  __ movzx_b(tmp1.reg(), FieldOperand(tmp1.reg(), Map::kBitFieldOffset));
+  __ test(tmp1.reg(), Immediate(KeyedLoadIC::kSlowCaseBitFieldMask));
+  deferred->Branch(not_zero);
+
+  // Check the object's elements are in fast case.
+  __ mov(tmp1.reg(), FieldOperand(object.reg(), JSObject::kElementsOffset));
+  __ cmp(FieldOperand(tmp1.reg(), HeapObject::kMapOffset),
+         Immediate(Factory::fixed_array_map()));
+  deferred->Branch(not_equal);
+
+  // Smi-tagging is equivalent to multiplying by 2.
+  STATIC_ASSERT(kSmiTag == 0);
+  STATIC_ASSERT(kSmiTagSize == 1);
+
+  // Check that both indices are smis.
+  __ mov(tmp2.reg(), index1.reg());
+  __ or_(tmp2.reg(), Operand(index2.reg()));
+  __ test(tmp2.reg(), Immediate(kSmiTagMask));
+  deferred->Branch(not_zero);
+
+  // Bring addresses into index1 and index2.
+  __ lea(index1.reg(), FieldOperand(tmp1.reg(),
+                                    index1.reg(),
+                                    times_half_pointer_size,  // index1 is Smi
+                                    FixedArray::kHeaderSize));
+  __ lea(index2.reg(), FieldOperand(tmp1.reg(),
+                                    index2.reg(),
+                                    times_half_pointer_size,  // index2 is Smi
+                                    FixedArray::kHeaderSize));
+
+  // Swap elements.
+  __ mov(object.reg(), Operand(index1.reg(), 0));
+  __ mov(tmp2.reg(),   Operand(index2.reg(), 0));
+  __ mov(Operand(index2.reg(), 0), object.reg());
+  __ mov(Operand(index1.reg(), 0), tmp2.reg());
+
+  Label done;
+  __ InNewSpace(tmp1.reg(), tmp2.reg(), equal, &done);
+  // Possible optimization: do a check that both values are Smis
+  // (or them and test against Smi mask.)
+
+  __ mov(tmp2.reg(), tmp1.reg());
+  RecordWriteStub recordWrite1(tmp2.reg(), index1.reg(), object.reg());
+  __ CallStub(&recordWrite1);
+
+  RecordWriteStub recordWrite2(tmp1.reg(), index2.reg(), object.reg());
+  __ CallStub(&recordWrite2);
+
+  __ bind(&done);
+
+  deferred->BindExit();
+  frame_->Push(Factory::undefined_value());
+}
+
+
 void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
   Comment cmnt(masm_, "[ GenerateCallFunction");
 
index 16e827e89842f70fa76ff9fde61888ddeffb6ccd..983758316e46fa08fa7679713a84c47b06d2b142 100644 (file)
@@ -636,6 +636,9 @@ class CodeGenerator: public AstVisitor {
   // Fast support for number to string.
   void GenerateNumberToString(ZoneList<Expression*>* args);
 
+  // Fast swapping of elements.
+  void GenerateSwapElements(ZoneList<Expression*>* args);
+
   // Fast call for custom callbacks.
   void GenerateCallFunction(ZoneList<Expression*>* args);
 
index 7464a57c6b62e7dde4d0330db1a4566de9dd0cbb..6aae096fc25f6c44e54ab4c3f6c3632a62a35e1a 100644 (file)
--- a/src/ic.h
+++ b/src/ic.h
@@ -301,7 +301,6 @@ class KeyedLoadIC: public IC {
   // Clear the use of the inlined version.
   static void ClearInlinedVersion(Address address);
 
- private:
   // Bit mask to be tested against bit field for the cases when
   // generic stub should go into slow case.
   // Access check is necessary explicitly since generic stub does not perform
@@ -309,6 +308,7 @@ class KeyedLoadIC: public IC {
   static const int kSlowCaseBitFieldMask =
       (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor);
 
+ private:
   // Update the inline cache.
   void UpdateCaches(LookupResult* lookup,
                     State state,
index 90b2229fb6d209bba781965f583d486cb96b6ae1..c35a1fb418e9338d121296f64910ff73afe82b76 100644 (file)
@@ -7773,6 +7773,38 @@ static Object* Runtime_EstimateNumberOfElements(Arguments args) {
 }
 
 
+static Object* Runtime_SwapElements(Arguments args) {
+  HandleScope handle_scope;
+
+  ASSERT_EQ(3, args.length());
+
+  Handle<Object> object = args.at<Object>(0);
+  Handle<Object> key1 = args.at<Object>(1);
+  Handle<Object> key2 = args.at<Object>(2);
+
+  uint32_t index1, index2;
+  // That must be the most common case.
+  if (object->IsJSObject()
+      && Array::IndexFromObject(*key1, &index1)
+      && Array::IndexFromObject(*key2, &index2)) {
+    Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
+    Handle<Object> tmp1 = GetElement(jsobject, index1);
+    Handle<Object> tmp2 = GetElement(jsobject, index2);
+
+    SetElement(jsobject, index1, tmp2);
+    SetElement(jsobject, index2, tmp1);
+  } else {
+    Handle<Object> tmp1 = GetProperty(object, key1);
+    Handle<Object> tmp2 = GetProperty(object, key2);
+
+    SetProperty(object, key1, tmp2, NONE);
+    SetProperty(object, key2, tmp1, NONE);
+  }
+
+  return Heap::undefined_value();
+}
+
+
 // Returns an array that tells you where in the [0, length) interval an array
 // might have elements.  Can either return keys or intervals.  Keys can have
 // gaps in (undefined).  Intervals can also span over some undefined keys.
index d0719f616e08bf19872aad19c076d045332c8fed..a7f0bf37b1b2c94144f869dc83c102c342114883 100644 (file)
@@ -233,6 +233,7 @@ namespace internal {
   F(GetArrayKeys, 2, 1) \
   F(MoveArrayContents, 2, 1) \
   F(EstimateNumberOfElements, 1, 1) \
+  F(SwapElements, 3, 1) \
   \
   /* Getters and Setters */ \
   F(DefineAccessor, -1 /* 4 or 5 */, 1) \
index e15a5fc8582c1c5a00f5b15c3fedcfd5ef82c4c2..9f53ec4aa24261196cc2c529411a699d444ee146 100644 (file)
@@ -4478,6 +4478,20 @@ void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
+  Comment cmnt(masm_, "[ GenerateSwapElements");
+
+  ASSERT_EQ(3, args->length());
+
+  Load(args->at(0));
+  Load(args->at(1));
+  Load(args->at(2));
+
+  Result result = frame_->CallRuntime(Runtime::kSwapElements, 3);
+  frame_->Push(&result);
+}
+
+
 void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
   Comment cmnt(masm_, "[ GenerateCallFunction");
 
index 397419c5c34172d9fe54454e999139c6091a5a52..8416f391ec8a66bbd58bfc7a563b744487e2e2ec 100644 (file)
@@ -594,6 +594,9 @@ class CodeGenerator: public AstVisitor {
   // Fast support for number to string.
   void GenerateNumberToString(ZoneList<Expression*>* args);
 
+  // Fast swapping of elements.
+  void GenerateSwapElements(ZoneList<Expression*>* args);
+
   // Fast call for custom callbacks.
   void GenerateCallFunction(ZoneList<Expression*>* args);
 
index 0d930ca0c2b0972a0b8dc1db75deb17f1af75806..6e925869bdcf8bacbe961a23267760289f7257cf 100644 (file)
@@ -160,6 +160,8 @@ var knownProblems = {
   // That can only be invoked on Array.prototype.
   "FinishArrayPrototypeSetup": true,
 
+  "_SwapElements": true,
+
   // Performance critical function which cannot afford type checks.
   "_CallFunction": true,