Revision: 10138
Author:   [email protected]
Date:     Fri Dec  2 04:42:35 2011
Log:      Optimize Crankshaft array literal initialization from boilerplate.

BUG=none
TEST=test/mjsunit/array-literal-transitions.js

Review URL: http://codereview.chromium.org/8747009
http://code.google.com/p/v8/source/detail?r=10138

Modified:
 /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc
 /branches/bleeding_edge/src/hydrogen-instructions.cc
 /branches/bleeding_edge/src/hydrogen-instructions.h
 /branches/bleeding_edge/src/hydrogen.cc
 /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc
 /branches/bleeding_edge/test/mjsunit/array-literal-transitions.js

=======================================
--- /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Thu Dec 1 08:57:54 2011 +++ /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Fri Dec 2 04:42:35 2011
@@ -4231,15 +4231,31 @@


 void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
- Handle<FixedArray> constant_elements = instr->hydrogen()->constant_elements();
-  ASSERT_EQ(2, constant_elements->length());
-  ElementsKind constant_elements_kind =
- static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value());
+  Heap* heap = isolate()->heap();
+  ElementsKind boilerplate_elements_kind =
+      instr->hydrogen()->boilerplate_elements_kind();
+
+ // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has
+  // already been converted to FAST_ELEMENTS.
+  if (boilerplate_elements_kind != FAST_ELEMENTS) {
+    LoadHeapObject(r1, instr->hydrogen()->boilerplate_object());
+    // Load map into r2.
+    __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
+    // Load the map's "bit field 2".
+    __ ldrb(r2, FieldMemOperand(r2, Map::kBitField2Offset));
+    // Retrieve elements_kind from bit field 2.
+    __ ubfx(r2, r2, Map::kElementsKindShift, Map::kElementsKindBitCount);
+    __ cmp(r2, Operand(boilerplate_elements_kind));
+    DeoptimizeIf(ne, instr->environment());
+  }

   __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
   __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset));
   __ mov(r2, Operand(Smi::FromInt(instr->hydrogen()->literal_index())));
-  __ mov(r1, Operand(constant_elements));
+  // Boilerplate already exists, constant elements are never accessed.
+  // Pass an empty fixed array.
+  __ mov(r1, Operand(Handle<FixedArray>(heap->empty_fixed_array())));
   __ Push(r3, r2, r1);

   // Pick the right runtime function or stub to call.
@@ -4256,9 +4272,9 @@
     CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr);
   } else {
     FastCloneShallowArrayStub::Mode mode =
-        constant_elements_kind == FAST_DOUBLE_ELEMENTS
-        ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
-        : FastCloneShallowArrayStub::CLONE_ELEMENTS;
+        boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
+            ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
+            : FastCloneShallowArrayStub::CLONE_ELEMENTS;
     FastCloneShallowArrayStub stub(mode, length);
     CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
   }
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.cc Thu Nov 24 05:42:52 2011 +++ /branches/bleeding_edge/src/hydrogen-instructions.cc Fri Dec 2 04:42:35 2011
@@ -1227,10 +1227,7 @@


 bool HArrayLiteral::IsCopyOnWrite() const {
-  Handle<FixedArray> constant_elements = this->constant_elements();
-  FixedArrayBase* constant_elements_values =
-      FixedArrayBase::cast(constant_elements->get(1));
-  return constant_elements_values->map() == HEAP->fixed_cow_array_map();
+ return boilerplate_object_->elements()->map() == HEAP->fixed_cow_array_map();
 }


=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Thu Dec 1 08:57:54 2011 +++ /branches/bleeding_edge/src/hydrogen-instructions.h Fri Dec 2 04:42:35 2011
@@ -4167,18 +4167,21 @@
 class HArrayLiteral: public HMaterializedLiteral<1> {
  public:
   HArrayLiteral(HValue* context,
-                Handle<FixedArray> constant_elements,
+                Handle<JSObject> boilerplate_object,
                 int length,
                 int literal_index,
                 int depth)
       : HMaterializedLiteral<1>(literal_index, depth),
         length_(length),
-        constant_elements_(constant_elements) {
+        boilerplate_object_(boilerplate_object) {
     SetOperandAt(0, context);
   }

   HValue* context() { return OperandAt(0); }
- Handle<FixedArray> constant_elements() const { return constant_elements_; }
+  ElementsKind boilerplate_elements_kind() const {
+    return boilerplate_object_->GetElementsKind();
+  }
+ Handle<JSObject> boilerplate_object() const { return boilerplate_object_; }
   int length() const { return length_; }

   bool IsCopyOnWrite() const;
@@ -4192,7 +4195,7 @@

  private:
   int length_;
-  Handle<FixedArray> constant_elements_;
+  Handle<JSObject> boilerplate_object_;
 };


=======================================
--- /branches/bleeding_edge/src/hydrogen.cc     Thu Dec  1 08:57:54 2011
+++ /branches/bleeding_edge/src/hydrogen.cc     Fri Dec  2 04:42:35 2011
@@ -3457,11 +3457,25 @@
   int length = subexprs->length();
   HValue* context = environment()->LookupContext();

-  HArrayLiteral* literal = new(zone()) HArrayLiteral(context,
- expr->constant_elements(),
-                                                     length,
-                                                     expr->literal_index(),
-                                                     expr->depth());
+  FixedArray* literals = environment()->closure()->literals();
+  Handle<Object> raw_boilerplate(literals->get(expr->literal_index()));
+
+  // For now, no boilerplate causes a deopt.
+  if (raw_boilerplate->IsUndefined()) {
+    AddInstruction(new(zone()) HSoftDeoptimize);
+    return ast_context()->ReturnValue(graph()->GetConstantUndefined());
+  }
+
+  Handle<JSObject> boilerplate(Handle<JSObject>::cast(raw_boilerplate));
+  ElementsKind boilerplate_elements_kind = boilerplate->GetElementsKind();
+
+  HArrayLiteral* literal = new(zone()) HArrayLiteral(
+      context,
+      boilerplate,
+      length,
+      expr->literal_index(),
+      expr->depth());
+
   // The array is expected in the bailout environment during computation
   // of the property values and is the value of the entire expression.
   PushAndAdd(literal);
@@ -3484,42 +3498,25 @@
     HValue* key = AddInstruction(
         new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
                               Representation::Integer32()));
-    HInstruction* elements_kind =
-        AddInstruction(new(zone()) HElementsKind(literal));
-    HBasicBlock* store_fast = graph()->CreateBasicBlock();
-    // Two empty blocks to satisfy edge split form.
-    HBasicBlock* store_fast_edgesplit1 = graph()->CreateBasicBlock();
-    HBasicBlock* store_fast_edgesplit2 = graph()->CreateBasicBlock();
-    HBasicBlock* store_generic = graph()->CreateBasicBlock();
-    HBasicBlock* check_smi_only_elements = graph()->CreateBasicBlock();
-    HBasicBlock* join = graph()->CreateBasicBlock();
-
-    HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(value);
-    smicheck->SetSuccessorAt(0, store_fast_edgesplit1);
-    smicheck->SetSuccessorAt(1, check_smi_only_elements);
-    current_block()->Finish(smicheck);
-    store_fast_edgesplit1->Finish(new(zone()) HGoto(store_fast));
-
-    set_current_block(check_smi_only_elements);
-    HCompareConstantEqAndBranch* smi_elements_check =
-        new(zone()) HCompareConstantEqAndBranch(elements_kind,
-                                                FAST_ELEMENTS,
-                                                Token::EQ_STRICT);
-    smi_elements_check->SetSuccessorAt(0, store_fast_edgesplit2);
-    smi_elements_check->SetSuccessorAt(1, store_generic);
-    current_block()->Finish(smi_elements_check);
-    store_fast_edgesplit2->Finish(new(zone()) HGoto(store_fast));
-
-    set_current_block(store_fast);
- AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
-    store_fast->Goto(join);
-
-    set_current_block(store_generic);
-    AddInstruction(BuildStoreKeyedGeneric(literal, key, value));
-    store_generic->Goto(join);
-
-    join->SetJoinId(expr->id());
-    set_current_block(join);
+
+    switch (boilerplate_elements_kind) {
+      case FAST_SMI_ONLY_ELEMENTS:
+      case FAST_ELEMENTS:
+        AddInstruction(new(zone()) HStoreKeyedFastElement(
+            elements,
+            key,
+            value,
+            boilerplate_elements_kind));
+        break;
+      case FAST_DOUBLE_ELEMENTS:
+        AddInstruction(new(zone()) HStoreKeyedFastDoubleElement(elements,
+                                                                key,
+                                                                value));
+        break;
+      default:
+        UNREACHABLE();
+        break;
+    }

     AddSimulate(expr->GetIdForElement(i));
   }
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Fri Dec 2 02:01:50 2011 +++ /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Fri Dec 2 04:42:35 2011
@@ -4107,17 +4107,32 @@

 void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
   ASSERT(ToRegister(instr->context()).is(esi));
-
- Handle<FixedArray> constant_elements = instr->hydrogen()->constant_elements();
-  ASSERT_EQ(2, constant_elements->length());
-  ElementsKind constant_elements_kind =
- static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value());
+  Heap* heap = isolate()->heap();
+  ElementsKind boilerplate_elements_kind =
+      instr->hydrogen()->boilerplate_elements_kind();
+
+ // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has
+  // already been converted to FAST_ELEMENTS.
+  if (boilerplate_elements_kind != FAST_ELEMENTS) {
+    LoadHeapObject(eax, instr->hydrogen()->boilerplate_object());
+    __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+    // Load the map's "bit field 2". We only need the first byte,
+    // but the following masking takes care of that anyway.
+    __ mov(ebx, FieldOperand(ebx, Map::kBitField2Offset));
+    // Retrieve elements_kind from bit field 2.
+    __ and_(ebx, Map::kElementsKindMask);
+    __ cmp(ebx, boilerplate_elements_kind << Map::kElementsKindShift);
+    DeoptimizeIf(not_equal, instr->environment());
+  }

   // Setup the parameters to the stub/runtime call.
   __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
   __ push(FieldOperand(eax, JSFunction::kLiteralsOffset));
   __ push(Immediate(Smi::FromInt(instr->hydrogen()->literal_index())));
-  __ push(Immediate(constant_elements));
+  // Boilerplate already exists, constant elements are never accessed.
+  // Pass an empty fixed array.
+  __ push(Immediate(Handle<FixedArray>(heap->empty_fixed_array())));

   // Pick the right runtime function or stub to call.
   int length = instr->hydrogen()->length();
@@ -4133,9 +4148,9 @@
     CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr);
   } else {
     FastCloneShallowArrayStub::Mode mode =
-        constant_elements_kind == FAST_DOUBLE_ELEMENTS
-        ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
-        : FastCloneShallowArrayStub::CLONE_ELEMENTS;
+        boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
+            ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
+            : FastCloneShallowArrayStub::CLONE_ELEMENTS;
     FastCloneShallowArrayStub stub(mode, length);
     CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
   }
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Tue Nov 29 00:43:14 2011
+++ /branches/bleeding_edge/src/runtime.cc      Fri Dec  2 04:42:35 2011
@@ -625,6 +625,7 @@
   // Check if boilerplate exists. If not, create it first.
   Handle<Object> boilerplate(literals->get(literals_index), isolate);
   if (*boilerplate == isolate->heap()->undefined_value()) {
+    ASSERT(*elements != isolate->heap()->empty_fixed_array());
boilerplate = CreateArrayLiteralBoilerplate(isolate, literals, elements);
     if (boilerplate.is_null()) return Failure::Exception();
     // Update the functions literal and return the boilerplate.
@@ -4651,6 +4652,7 @@
   if (value->IsNumber()) {
     ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS);
     TransitionElementsKind(object, FAST_DOUBLE_ELEMENTS);
+    TransitionElementsKind(boilerplate_object, FAST_DOUBLE_ELEMENTS);
     ASSERT(object->GetElementsKind() == FAST_DOUBLE_ELEMENTS);
     FixedDoubleArray* double_array =
         FixedDoubleArray::cast(object->elements());
@@ -4660,6 +4662,7 @@
     ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS ||
            elements_kind == FAST_DOUBLE_ELEMENTS);
     TransitionElementsKind(object, FAST_ELEMENTS);
+    TransitionElementsKind(boilerplate_object, FAST_ELEMENTS);
     FixedArray* object_array =
         FixedArray::cast(object->elements());
     object_array->set(store_index, *value);
=======================================
--- /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Thu Dec 1 08:57:54 2011 +++ /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Fri Dec 2 04:42:35 2011
@@ -3836,16 +3836,32 @@


 void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
- Handle<FixedArray> constant_elements = instr->hydrogen()->constant_elements();
-  ASSERT_EQ(2, constant_elements->length());
-  ElementsKind constant_elements_kind =
- static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value());
+  Heap* heap = isolate()->heap();
+  ElementsKind boilerplate_elements_kind =
+      instr->hydrogen()->boilerplate_elements_kind();
+
+ // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has
+  // already been converted to FAST_ELEMENTS.
+  if (boilerplate_elements_kind != FAST_ELEMENTS) {
+    LoadHeapObject(rax, instr->hydrogen()->boilerplate_object());
+    __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+    // Load the map's "bit field 2".
+    __ movb(rbx, FieldOperand(rbx, Map::kBitField2Offset));
+    // Retrieve elements_kind from bit field 2.
+    __ and_(rbx, Immediate(Map::kElementsKindMask));
+    __ cmpb(rbx, Immediate(boilerplate_elements_kind <<
+                           Map::kElementsKindShift));
+    DeoptimizeIf(not_equal, instr->environment());
+  }

   // Setup the parameters to the stub/runtime call.
   __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
   __ push(FieldOperand(rax, JSFunction::kLiteralsOffset));
   __ Push(Smi::FromInt(instr->hydrogen()->literal_index()));
-  __ Push(instr->hydrogen()->constant_elements());
+  // Boilerplate already exists, constant elements are never accessed.
+  // Pass an empty fixed array.
+  __ Push(Handle<FixedArray>(heap->empty_fixed_array()));

   // Pick the right runtime function or stub to call.
   int length = instr->hydrogen()->length();
@@ -3861,9 +3877,9 @@
     CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr);
   } else {
     FastCloneShallowArrayStub::Mode mode =
-        constant_elements_kind == FAST_DOUBLE_ELEMENTS
-        ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
-        : FastCloneShallowArrayStub::CLONE_ELEMENTS;
+        boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
+            ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
+            : FastCloneShallowArrayStub::CLONE_ELEMENTS;
     FastCloneShallowArrayStub stub(mode, length);
     CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
   }
=======================================
--- /branches/bleeding_edge/test/mjsunit/array-literal-transitions.js Wed Oct 19 04:36:55 2011 +++ /branches/bleeding_edge/test/mjsunit/array-literal-transitions.js Fri Dec 2 04:42:35 2011
@@ -122,4 +122,76 @@
   }
   %OptimizeFunctionOnNextCall(test_large_literal);
   test_large_literal();
-}
+
+  function deopt_array(use_literal) {
+    if (use_literal) {
+      return [.5, 3, 4];
+    }  else {
+      return new Array();
+    }
+  }
+
+  deopt_array(false);
+  deopt_array(false);
+  deopt_array(false);
+  %OptimizeFunctionOnNextCall(deopt_array);
+  var array = deopt_array(false);
+  assertTrue(2 != %GetOptimizationStatus(deopt_array));
+  deopt_array(true);
+  assertTrue(1 != %GetOptimizationStatus(deopt_array));
+  array = deopt_array(false);
+  assertTrue(1 != %GetOptimizationStatus(deopt_array));
+
+ // Check that unexpected changes in the objects stored into the boilerplate
+  // also force a deopt.
+  function deopt_array_literal_all_smis(a) {
+    return [0, 1, a];
+  }
+
+  deopt_array_literal_all_smis(2);
+  deopt_array_literal_all_smis(3);
+  deopt_array_literal_all_smis(4);
+  array = deopt_array_literal_all_smis(4);
+  assertEquals(0, array[0]);
+  assertEquals(1, array[1]);
+  assertEquals(4, array[2]);
+  %OptimizeFunctionOnNextCall(deopt_array_literal_all_smis);
+  array = deopt_array_literal_all_smis(5);
+  array = deopt_array_literal_all_smis(6);
+  assertTrue(2 != %GetOptimizationStatus(deopt_array_literal_all_smis));
+  assertEquals(0, array[0]);
+  assertEquals(1, array[1]);
+  assertEquals(6, array[2]);
+
+  array = deopt_array_literal_all_smis(.5);
+  assertTrue(1 != %GetOptimizationStatus(deopt_array_literal_all_smis));
+  assertEquals(0, array[0]);
+  assertEquals(1, array[1]);
+  assertEquals(.5, array[2]);
+
+  function deopt_array_literal_all_doubles(a) {
+    return [0.5, 1, a];
+  }
+
+  deopt_array_literal_all_doubles(.5);
+  deopt_array_literal_all_doubles(.5);
+  deopt_array_literal_all_doubles(.5);
+  array = deopt_array_literal_all_doubles(0.5);
+  assertEquals(0.5, array[0]);
+  assertEquals(1, array[1]);
+  assertEquals(0.5, array[2]);
+  %OptimizeFunctionOnNextCall(deopt_array_literal_all_doubles);
+  array = deopt_array_literal_all_doubles(5);
+  array = deopt_array_literal_all_doubles(6);
+  assertTrue(2 != %GetOptimizationStatus(deopt_array_literal_all_doubles));
+  assertEquals(0.5, array[0]);
+  assertEquals(1, array[1]);
+  assertEquals(6, array[2]);
+
+  var foo = new Object();
+  array = deopt_array_literal_all_doubles(foo);
+  assertTrue(1 != %GetOptimizationStatus(deopt_array_literal_all_doubles));
+  assertEquals(0.5, array[0]);
+  assertEquals(1, array[1]);
+  assertEquals(foo, array[2]);
+}

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to