Add Crankshaft support for smi-only elements
authorjkummerow@chromium.org <jkummerow@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 26 Sep 2011 12:09:04 +0000 (12:09 +0000)
committerjkummerow@chromium.org <jkummerow@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 26 Sep 2011 12:09:04 +0000 (12:09 +0000)
Review URL: http://codereview.chromium.org/8002019

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

src/arm/lithium-codegen-arm.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/hydrogen.h
src/ia32/lithium-codegen-ia32.cc
src/ic.cc
src/runtime.cc
src/x64/lithium-codegen-x64.cc
test/mjsunit/element-kind.js

index a29a43c..218742c 100644 (file)
@@ -3346,6 +3346,13 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
   Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
   Register scratch = scratch0();
 
+  // This instruction cannot handle the FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS
+  // conversion, so it deopts in that case.
+  if (instr->hydrogen()->ValueNeedsSmiCheck()) {
+    __ tst(value, Operand(kSmiTagMask));
+    DeoptimizeIf(ne, instr->environment());
+  }
+
   // Do the store.
   if (instr->key()->IsConstantOperand()) {
     ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
index 4a25da0..c4b44e8 100644 (file)
@@ -908,8 +908,8 @@ class HDeoptimize: public HControlInstruction {
 class HGoto: public HTemplateControlInstruction<1, 0> {
  public:
   explicit HGoto(HBasicBlock* target) {
-        SetSuccessorAt(0, target);
-      }
+    SetSuccessorAt(0, target);
+  }
 
   virtual Representation RequiredInputRepresentation(int index) const {
     return Representation::None();
@@ -3740,7 +3740,9 @@ class HStoreNamedGeneric: public HTemplateInstruction<3> {
 
 class HStoreKeyedFastElement: public HTemplateInstruction<3> {
  public:
-  HStoreKeyedFastElement(HValue* obj, HValue* key, HValue* val) {
+  HStoreKeyedFastElement(HValue* obj, HValue* key, HValue* val,
+                         ElementsKind elements_kind = FAST_ELEMENTS)
+      : elements_kind_(elements_kind) {
     SetOperandAt(0, obj);
     SetOperandAt(1, key);
     SetOperandAt(2, val);
@@ -3757,14 +3759,28 @@ class HStoreKeyedFastElement: public HTemplateInstruction<3> {
   HValue* object() { return OperandAt(0); }
   HValue* key() { return OperandAt(1); }
   HValue* value() { return OperandAt(2); }
+  bool value_is_smi() {
+    return elements_kind_ == FAST_SMI_ONLY_ELEMENTS;
+  }
 
   bool NeedsWriteBarrier() {
-    return StoringValueNeedsWriteBarrier(value());
+    if (value_is_smi()) {
+      return false;
+    } else {
+      return StoringValueNeedsWriteBarrier(value());
+    }
+  }
+
+  bool ValueNeedsSmiCheck() {
+    return value_is_smi();
   }
 
   virtual void PrintDataTo(StringStream* stream);
 
   DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement)
+
+ private:
+  ElementsKind elements_kind_;
 };
 
 
index 9d75bfa..554e1f7 100644 (file)
@@ -3347,7 +3347,42 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
         new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
                               Representation::Integer32()));
     if (FLAG_smi_only_arrays) {
+      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_SMI_ONLY_ELEMENTS,
+                                                  Token::EQ_STRICT);
+      smi_elements_check->SetSuccessorAt(0, store_generic);
+      smi_elements_check->SetSuccessorAt(1, store_fast_edgesplit2);
+      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);
     } else {
       AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
     }
@@ -3979,6 +4014,30 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
 }
 
 
+HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
+                                                    HValue* checked_key,
+                                                    HValue* val,
+                                                    ElementsKind elements_kind,
+                                                    bool is_store) {
+  if (is_store) {
+    ASSERT(val != NULL);
+    if (elements_kind == FAST_DOUBLE_ELEMENTS) {
+      return new(zone()) HStoreKeyedFastDoubleElement(
+          elements, checked_key, val);
+    } else {  // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS.
+      return new(zone()) HStoreKeyedFastElement(
+          elements, checked_key, val, elements_kind);
+    }
+  }
+  // It's an element load (!is_store).
+  if (elements_kind == FAST_DOUBLE_ELEMENTS) {
+    return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key);
+  } else {  // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS.
+    return new(zone()) HLoadKeyedFastElement(elements, checked_key);
+  }
+}
+
+
 HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
                                                            HValue* key,
                                                            HValue* val,
@@ -3988,15 +4047,18 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
   Handle<Map> map = expr->GetMonomorphicReceiverType();
   AddInstruction(new(zone()) HCheckNonSmi(object));
   HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map));
-  if (!map->has_fast_elements() &&
-      !map->has_fast_double_elements() &&
+  bool fast_smi_only_elements = map->has_fast_smi_only_elements();
+  bool fast_elements = map->has_fast_elements();
+  bool fast_double_elements = map->has_fast_double_elements();
+  if (!fast_smi_only_elements &&
+      !fast_elements &&
+      !fast_double_elements &&
       !map->has_external_array_elements()) {
     return is_store ? BuildStoreKeyedGeneric(object, key, val)
                     : BuildLoadKeyedGeneric(object, key);
   }
   HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
-  bool fast_double_elements = map->has_fast_double_elements();
-  if (is_store && map->has_fast_elements()) {
+  if (is_store && (fast_elements || fast_smi_only_elements)) {
     AddInstruction(new(zone()) HCheckMap(
         elements, isolate()->factory()->fixed_array_map()));
   }
@@ -4011,28 +4073,15 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
     return BuildExternalArrayElementAccess(external_elements, checked_key,
                                            val, map->elements_kind(), is_store);
   }
-  ASSERT(map->has_fast_elements() || fast_double_elements);
+  ASSERT(fast_smi_only_elements || fast_elements || fast_double_elements);
   if (map->instance_type() == JS_ARRAY_TYPE) {
     length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck));
   } else {
     length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
   }
   checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
-  if (is_store) {
-    if (fast_double_elements) {
-      return new(zone()) HStoreKeyedFastDoubleElement(elements,
-                                                      checked_key,
-                                                      val);
-    } else {
-      return new(zone()) HStoreKeyedFastElement(elements, checked_key, val);
-    }
-  } else {
-    if (fast_double_elements) {
-      return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key);
-    } else {
-      return new(zone()) HLoadKeyedFastElement(elements, checked_key);
-    }
-  }
+  return BuildFastElementAccess(elements, checked_key, val,
+                                map->elements_kind(), is_store);
 }
 
 
@@ -4085,7 +4134,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
   for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND;
        elements_kind <= LAST_ELEMENTS_KIND;
        elements_kind = ElementsKind(elements_kind + 1)) {
-    // After having handled FAST_ELEMENTS, FAST_SMI_ELEMENTS,
+    // After having handled FAST_ELEMENTS, FAST_SMI_ONLY_ELEMENTS,
     // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code
     // that's executed for all external array cases.
     STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
@@ -4112,13 +4161,22 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
       if (elements_kind == FAST_SMI_ONLY_ELEMENTS ||
           elements_kind == FAST_ELEMENTS ||
           elements_kind == FAST_DOUBLE_ELEMENTS) {
-        bool fast_double_elements =
-            elements_kind == FAST_DOUBLE_ELEMENTS;
-        if (is_store && !fast_double_elements) {
+        if (is_store && elements_kind == FAST_SMI_ONLY_ELEMENTS) {
+          AddInstruction(new(zone()) HCheckSmi(val));
+        }
+        if (is_store && elements_kind != FAST_DOUBLE_ELEMENTS) {
           AddInstruction(new(zone()) HCheckMap(
               elements, isolate()->factory()->fixed_array_map(),
               elements_kind_branch));
         }
+        // TODO(jkummerow): The need for these two blocks could be avoided
+        // in one of two ways:
+        // (1) Introduce ElementsKinds for JSArrays that are distinct from
+        //     those for fast objects.
+        // (2) Put the common instructions into a third "join" block. This
+        //     requires additional AST IDs that we can deopt to from inside
+        //     that join block. They must be added to the Property class (when
+        //     it's a keyed property) and registered in the full codegen.
         HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
         HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
         HHasInstanceTypeAndBranch* typecheck =
@@ -4129,37 +4187,12 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
 
         set_current_block(if_jsarray);
         HInstruction* length;
-        if (is_store && elements_kind == FAST_SMI_ONLY_ELEMENTS) {
-          // For now, fall back to the generic stub for
-          // FAST_SMI_ONLY_ELEMENTS
-          access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
-        } else {
-          length = new(zone()) HJSArrayLength(object, typecheck);
-          AddInstruction(length);
-          checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
-          if (is_store) {
-            if (fast_double_elements) {
-              access = AddInstruction(
-                  new(zone()) HStoreKeyedFastDoubleElement(elements,
-                                                           checked_key,
-                                                           val));
-            } else {
-              access = AddInstruction(
-                  new(zone()) HStoreKeyedFastElement(elements,
-                                                     checked_key,
-                                                     val));
-            }
-          } else {
-            if (fast_double_elements) {
-              access = AddInstruction(
-                  new(zone()) HLoadKeyedFastDoubleElement(elements,
-                                                          checked_key));
-            } else {
-              access = AddInstruction(
-                  new(zone()) HLoadKeyedFastElement(elements, checked_key));
-            }
-            Push(access);
-          }
+        length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck));
+        checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
+        access = AddInstruction(BuildFastElementAccess(
+            elements, checked_key, val, elements_kind, is_store));
+        if (!is_store) {
+          Push(access);
         }
 
         *has_side_effects |= access->HasSideEffects();
@@ -4171,25 +4204,8 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
         set_current_block(if_fastobject);
         length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
         checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
-        if (is_store) {
-          if (fast_double_elements) {
-            access = AddInstruction(
-                new(zone()) HStoreKeyedFastDoubleElement(elements,
-                                                         checked_key,
-                                                         val));
-          } else {
-            access = AddInstruction(
-                new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
-          }
-        } else {
-          if (fast_double_elements) {
-            access = AddInstruction(
-                new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key));
-          } else {
-            access = AddInstruction(
-                new(zone()) HLoadKeyedFastElement(elements, checked_key));
-          }
-        }
+        access = AddInstruction(BuildFastElementAccess(
+            elements, checked_key, val, elements_kind, is_store));
       } else if (elements_kind == DICTIONARY_ELEMENTS) {
         if (is_store) {
           access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
index ca6da72..8b507c2 100644 (file)
@@ -942,6 +942,11 @@ class HGraphBuilder: public AstVisitor {
       HValue* val,
       ElementsKind elements_kind,
       bool is_store);
+  HInstruction* BuildFastElementAccess(HValue* elements,
+                                       HValue* checked_key,
+                                       HValue* val,
+                                       ElementsKind elements_kind,
+                                       bool is_store);
 
   HInstruction* BuildMonomorphicElementAccess(HValue* object,
                                               HValue* key,
index 5c9c134..76383cc 100644 (file)
@@ -3238,6 +3238,13 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
   Register elements = ToRegister(instr->object());
   Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
 
+  // This instruction cannot handle the FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS
+  // conversion, so it deopts in that case.
+  if (instr->hydrogen()->ValueNeedsSmiCheck()) {
+    __ test(value, Immediate(kSmiTagMask));
+    DeoptimizeIf(not_zero, instr->environment());
+  }
+
   // Do the store.
   if (instr->key()->IsConstantOperand()) {
     ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
index 1457d75..50b1cde 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -1689,6 +1689,7 @@ MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver,
                                              Code* generic_stub) {
   Code* result = NULL;
   if (receiver->HasFastElements() ||
+      receiver->HasFastSmiOnlyElements() ||
       receiver->HasExternalArrayElements() ||
       receiver->HasFastDoubleElements() ||
       receiver->HasDictionaryElements()) {
index 235755b..5afaf2f 100644 (file)
@@ -443,7 +443,7 @@ static Handle<Object> CreateArrayLiteralBoilerplate(
       for (int i = 0; i < content->length(); i++) {
         Object* current = content->get(i);
         ASSERT(!current->IsFixedArray());
-        if (!current->IsSmi()) {
+        if (!current->IsSmi() && !current->IsTheHole()) {
           has_non_smi = true;
         }
       }
@@ -467,7 +467,7 @@ static Handle<Object> CreateArrayLiteralBoilerplate(
         content->set(i, *result);
         has_non_smi = true;
       } else {
-        if (!current->IsSmi()) {
+        if (!current->IsSmi() && !current->IsTheHole()) {
           has_non_smi = true;
         }
       }
@@ -8250,6 +8250,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_OptimizeFunctionOnNextCall) {
 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOptimizationStatus) {
   HandleScope scope(isolate);
   ASSERT(args.length() == 1);
+  // The least significant bit (after untagging) indicates whether the
+  // function is currently optimized, regardless of reason.
   if (!V8::UseCrankshaft()) {
     return Smi::FromInt(4);  // 4 == "never".
   }
index 7ac743f..5d45a46 100644 (file)
@@ -3161,6 +3161,13 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
   Register elements = ToRegister(instr->object());
   Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
 
+  // This instruction cannot handle the FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS
+  // conversion, so it deopts in that case.
+  if (instr->hydrogen()->ValueNeedsSmiCheck()) {
+    Condition cc = masm()->CheckSmi(value);
+    DeoptimizeIf(NegateCondition(cc), instr->environment());
+  }
+
   // Do the store.
   if (instr->key()->IsConstantOperand()) {
     ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
index 0159f5a..8c51210 100644 (file)
@@ -125,3 +125,70 @@ assertKind(element_kind.external_unsigned_int_elements,   new Uint32Array(23));
 assertKind(element_kind.external_float_elements,          new Float32Array(7));
 assertKind(element_kind.external_double_elements,         new Float64Array(0));
 assertKind(element_kind.external_pixel_elements,          new PixelArray(512));
+
+// Crankshaft support for smi-only array elements.
+function monomorphic(array) {
+  for (var i = 0; i < 3; i++) {
+    array[i] = i + 10;
+  }
+  assertKind(element_kind.fast_smi_only_elements, array);
+  for (var i = 0; i < 3; i++) {
+    var a = array[i];
+    assertEquals(i + 10, a);
+  }
+}
+var smi_only = [1, 2, 3];
+for (var i = 0; i < 3; i++) monomorphic(smi_only);
+%OptimizeFunctionOnNextCall(monomorphic);
+monomorphic(smi_only);
+function polymorphic(array, expected_kind) {
+  array[1] = 42;
+  assertKind(expected_kind, array);
+  var a = array[1];
+  assertEquals(42, a);
+}
+var smis = [1, 2, 3];
+var strings = ["one", "two", "three"];
+var doubles = [0, 0, 0]; doubles[0] = 1.5; doubles[1] = 2.5; doubles[2] = 3.5;
+assertKind(support_smi_only_arrays
+               ? element_kind.fast_double_elements
+               : element_kind.fast_elements,
+           doubles);
+for (var i = 0; i < 3; i++) {
+  polymorphic(smis, element_kind.fast_smi_only_elements);
+  polymorphic(strings, element_kind.fast_elements);
+  polymorphic(doubles, support_smi_only_arrays
+                           ? element_kind.fast_double_elements
+                           : element_kind.fast_elements);
+}
+%OptimizeFunctionOnNextCall(polymorphic);
+polymorphic(smis, element_kind.fast_smi_only_elements);
+polymorphic(strings, element_kind.fast_elements);
+polymorphic(doubles, support_smi_only_arrays
+    ? element_kind.fast_double_elements
+    : element_kind.fast_elements);
+
+// Crankshaft support for smi-only elements in dynamic array literals.
+function get(foo) { return foo; }  // Used to generate dynamic values.
+
+//function crankshaft_test(expected_kind) {
+function crankshaft_test() {
+  var a = [get(1), get(2), get(3)];
+  assertKind(element_kind.fast_smi_only_elements, a);
+  var b = [get(1), get(2), get("three")];
+  assertKind(element_kind.fast_elements, b);
+  var c = [get(1), get(2), get(3.5)];
+  // The full code generator doesn't support conversion to fast_double_elements
+  // yet. Crankshaft does, but only with --smi-only-arrays support.
+  if ((%GetOptimizationStatus(crankshaft_test) & 1) &&
+      support_smi_only_arrays) {
+    assertKind(element_kind.fast_double_elements, c);
+  } else {
+    assertKind(element_kind.fast_elements, c);
+  }
+}
+for (var i = 0; i < 3; i++) {
+  crankshaft_test();
+}
+%OptimizeFunctionOnNextCall(crankshaft_test);
+crankshaft_test();