Use the property load IC for accessing the array length.
authormmassi@chromium.org <mmassi@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 21 Nov 2012 11:49:15 +0000 (11:49 +0000)
committermmassi@chromium.org <mmassi@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 21 Nov 2012 11:49:15 +0000 (11:49 +0000)
BUG=

Review URL: https://chromiumcodereview.appspot.com/11299004

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

src/arm/lithium-codegen-arm.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/ia32/lithium-codegen-ia32.cc
src/ic.cc
src/mips/lithium-codegen-mips.cc
src/objects-inl.h
src/objects.h
src/x64/lithium-codegen-x64.cc
test/mjsunit/array-length.js

index 204a155..be107d6 100644 (file)
@@ -4942,11 +4942,12 @@ void LCodeGen::DoCheckMaps(LCheckMaps* instr) {
   SmallMapList* map_set = instr->hydrogen()->map_set();
   for (int i = 0; i < map_set->length() - 1; i++) {
     Handle<Map> map = map_set->at(i);
-    __ CompareMap(reg, scratch, map, &success, REQUIRE_EXACT_MAP);
+    __ CompareMap(reg, scratch, map, &success, instr->hydrogen()->mode());
     __ b(eq, &success);
   }
   Handle<Map> map = map_set->last();
-  DoCheckMapCommon(reg, scratch, map, REQUIRE_EXACT_MAP, instr->environment());
+  DoCheckMapCommon(reg, scratch, map, instr->hydrogen()->mode(),
+                   instr->environment());
   __ bind(&success);
 }
 
index e529078..79e4a5d 100644 (file)
@@ -2201,7 +2201,8 @@ class HLoadExternalArrayPointer: public HUnaryOperation {
 class HCheckMaps: public HTemplateInstruction<2> {
  public:
   HCheckMaps(HValue* value, Handle<Map> map, Zone* zone,
-             HValue* typecheck = NULL) {
+             HValue* typecheck = NULL,
+             CompareMapMode mode = REQUIRE_EXACT_MAP) : mode_(mode) {
     SetOperandAt(0, value);
     // If callers don't depend on a typecheck, they can pass in NULL. In that
     // case we use a copy of the |value| argument as a dummy value.
@@ -2212,7 +2213,8 @@ class HCheckMaps: public HTemplateInstruction<2> {
     SetGVNFlag(kDependsOnElementsKind);
     map_set()->Add(map, zone);
   }
-  HCheckMaps(HValue* value, SmallMapList* maps, Zone* zone) {
+  HCheckMaps(HValue* value, SmallMapList* maps, Zone* zone,
+             CompareMapMode mode = REQUIRE_EXACT_MAP) : mode_(mode) {
     SetOperandAt(0, value);
     SetOperandAt(1, value);
     set_representation(Representation::Tagged());
@@ -2257,6 +2259,7 @@ class HCheckMaps: public HTemplateInstruction<2> {
 
   HValue* value() { return OperandAt(0); }
   SmallMapList* map_set() { return &map_set_; }
+  CompareMapMode mode() { return mode_; }
 
   DECLARE_CONCRETE_INSTRUCTION(CheckMaps)
 
@@ -2273,6 +2276,7 @@ class HCheckMaps: public HTemplateInstruction<2> {
 
  private:
   SmallMapList map_set_;
+  CompareMapMode mode_;
 };
 
 
index ff7550c..a719aa9 100644 (file)
@@ -6591,6 +6591,8 @@ void HGraphBuilder::VisitProperty(Property* expr) {
 
   HInstruction* instr = NULL;
   if (expr->AsProperty()->IsArrayLength()) {
+    // Note that in the monomorphic case IsArrayLength() is false because we
+    // handle that it with a regular property load IC.
     HValue* array = Pop();
     AddInstruction(new(zone()) HCheckNonSmi(array));
     HInstruction* mapcheck =
@@ -6626,23 +6628,74 @@ void HGraphBuilder::VisitProperty(Property* expr) {
       map = types->first();
       if (map->is_dictionary_map()) monomorphic = false;
     }
-    if (monomorphic) {
-      Handle<JSFunction> getter;
-      Handle<JSObject> holder;
-      if (LookupGetter(map, name, &getter, &holder)) {
-        AddCheckConstantFunction(holder, Top(), map);
-        if (FLAG_inline_accessors && TryInlineGetter(getter, expr)) return;
-        AddInstruction(new(zone()) HPushArgument(Pop()));
-        instr = new(zone()) HCallConstantFunction(getter, 1);
+
+    // Try to see if this is an array length access.
+    if (name->Equals(isolate()->heap()->length_symbol())) {
+      bool is_array = false;
+      bool fast_mode = false;
+      bool map_mode = false;
+      HInstruction* mapcheck = NULL;
+
+      if (expr->IsMonomorphic()) {
+        // Even if there is more than one map they all must be element
+        // transition maps, so checking just one is ok.
+        if (map->instance_type() == JS_ARRAY_TYPE) {
+          is_array = true;
+          map_mode = true;
+          fast_mode = IsFastElementsKind(map->elements_kind());
+        }
       } else {
-        instr = BuildLoadNamedMonomorphic(Pop(), name, expr, map);
+        // Since we will emit an instance check we have to conservatively
+        // assume that some arrays will contain slow elements, so we set
+        // fast_mode to false.
+        map_mode = false;
+        fast_mode = false;
+        is_array = true;
+        for (int i = 0; i < types->length(); i++) {
+          Handle<Map> current_map = types->at(i);
+          if (!current_map->instance_type() == JS_ARRAY_TYPE) {
+            is_array = false;
+            break;
+          }
+        }
+      }
+
+      // It is an array length access so we produce a HJSArrayLength.
+      if (is_array) {
+        HValue* array = Pop();
+        AddInstruction(new(zone()) HCheckNonSmi(array));
+        mapcheck = map_mode ?
+            HInstruction::cast(
+                new(zone()) HCheckMaps(array, map, zone(), NULL,
+                                       ALLOW_ELEMENT_TRANSITION_MAPS)) :
+            HInstruction::cast(
+                HCheckInstanceType::NewIsJSArray(array, zone()));
+        AddInstruction(mapcheck);
+        instr = new(zone()) HJSArrayLength(
+            array, mapcheck, fast_mode ? HType::Smi() : HType::Tagged());
       }
-    } else if (types != NULL && types->length() > 1) {
-      return HandlePolymorphicLoadNamedField(expr, Pop(), types, name);
-    } else {
-      instr = BuildLoadNamedGeneric(Pop(), name, expr);
     }
 
+    // We cannot know if it was an array length access so we handle it as
+    // a regular property access.
+    if (instr == NULL) {
+      if (monomorphic) {
+        Handle<JSFunction> getter;
+        Handle<JSObject> holder;
+        if (LookupGetter(map, name, &getter, &holder)) {
+          AddCheckConstantFunction(holder, Top(), map);
+          if (FLAG_inline_accessors && TryInlineGetter(getter, expr)) return;
+          AddInstruction(new(zone()) HPushArgument(Pop()));
+          instr = new(zone()) HCallConstantFunction(getter, 1);
+        } else {
+          instr = BuildLoadNamedMonomorphic(Pop(), name, expr, map);
+        }
+      } else if (types != NULL && types->length() > 1) {
+        return HandlePolymorphicLoadNamedField(expr, Pop(), types, name);
+      } else {
+        instr = BuildLoadNamedGeneric(Pop(), name, expr);
+      }
+    }
   } else {
     CHECK_ALIVE(VisitForValue(expr->key()));
 
index 3e19c39..2b2cafb 100644 (file)
@@ -4770,11 +4770,11 @@ void LCodeGen::DoCheckMaps(LCheckMaps* instr) {
   SmallMapList* map_set = instr->hydrogen()->map_set();
   for (int i = 0; i < map_set->length() - 1; i++) {
     Handle<Map> map = map_set->at(i);
-    __ CompareMap(reg, map, &success, REQUIRE_EXACT_MAP);
+    __ CompareMap(reg, map, &success, instr->hydrogen()->mode());
     __ j(equal, &success);
   }
   Handle<Map> map = map_set->last();
-  DoCheckMapCommon(reg, map, REQUIRE_EXACT_MAP, instr->environment());
+  DoCheckMapCommon(reg, map, instr->hydrogen()->mode(), instr->environment());
   __ bind(&success);
 }
 
index 5212004..07facd8 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -855,26 +855,6 @@ MaybeObject* LoadIC::Load(State state,
       return Smi::FromInt(String::cast(*string)->length());
     }
 
-    // Use specialized code for getting the length of arrays.
-    if (object->IsJSArray() &&
-        name->Equals(isolate()->heap()->length_symbol())) {
-      Handle<Code> stub;
-      if (state == UNINITIALIZED) {
-        stub = pre_monomorphic_stub();
-      } else if (state == PREMONOMORPHIC) {
-        stub = isolate()->builtins()->LoadIC_ArrayLength();
-      } else if (state != MEGAMORPHIC) {
-        stub = megamorphic_stub();
-      }
-      if (!stub.is_null()) {
-        set_target(*stub);
-#ifdef DEBUG
-        if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
-#endif
-      }
-      return JSArray::cast(*object)->length();
-    }
-
     // Use specialized code for getting prototype of functions.
     if (object->IsJSFunction() &&
         name->Equals(isolate()->heap()->prototype_symbol()) &&
@@ -1006,6 +986,17 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
           if (!holder->HasFastProperties()) return;
           code = isolate()->stub_cache()->ComputeLoadViaGetter(
               name, receiver, holder, Handle<JSFunction>::cast(getter));
+        } else if (holder->IsJSArray() &&
+            name->Equals(isolate()->heap()->length_symbol())) {
+          ASSERT(callback->IsForeign());
+          ASSERT(reinterpret_cast<AccessorDescriptor*>(
+              Handle<Foreign>::cast(callback)->foreign_address())
+              == &Accessors::ArrayLength);
+          // Use a "load field" IC for getting the array length.
+          // Note that the resulting code object is marked as "Code::FIELD"
+          // and not as "Code::CALLBACKS".
+          code = isolate()->stub_cache()->ComputeLoadField(
+              name, receiver, holder, JSArray::ArrayLengthIndex());
         } else {
           ASSERT(callback->IsForeign());
           // No IC support for old-style native accessors.
index e19caf3..f398441 100644 (file)
@@ -4639,10 +4639,11 @@ void LCodeGen::DoCheckMaps(LCheckMaps* instr) {
   for (int i = 0; i < map_set->length() - 1; i++) {
     Handle<Map> map = map_set->at(i);
     __ CompareMapAndBranch(
-        reg, scratch, map, &success, eq, &success, REQUIRE_EXACT_MAP);
+        reg, scratch, map, &success, eq, &success, instr->hydrogen()->mode());
   }
   Handle<Map> map = map_set->last();
-  DoCheckMapCommon(reg, scratch, map, REQUIRE_EXACT_MAP, instr->environment());
+  DoCheckMapCommon(reg, scratch, map, instr->hydrogen()->mode(),
+                   instr->environment());
   __ bind(&success);
 }
 
index 6e8d148..ec92e76 100644 (file)
@@ -4669,6 +4669,11 @@ bool Code::contains(byte* inner_pointer) {
 ACCESSORS(JSArray, length, Object, kLengthOffset)
 
 
+PropertyIndex JSArray::ArrayLengthIndex() {
+  return PropertyIndex::NewHeaderIndex(kLengthOffset / kPointerSize);
+}
+
+
 ACCESSORS(JSRegExp, data, Object, kDataOffset)
 
 
index 12753dd..275c706 100644 (file)
@@ -8323,6 +8323,9 @@ class Foreign: public HeapObject {
 };
 
 
+class PropertyIndex;
+
+
 // The JSArray describes JavaScript Arrays
 //  Such an array can be in one of two modes:
 //    - fast, backing storage is a FixedArray and length <= elements.length();
@@ -8376,6 +8379,9 @@ class JSArray: public JSObject {
   static const int kLengthOffset = JSObject::kHeaderSize;
   static const int kSize = kLengthOffset + kPointerSize;
 
+  static inline PropertyIndex ArrayLengthIndex();
+  STATIC_ASSERT(kLengthOffset % kPointerSize == 0);
+
  private:
   // Expand the fixed array backing of a fast-case JSArray to at least
   // the requested size.
index 13c5fa5..9feef1b 100644 (file)
@@ -4537,11 +4537,11 @@ void LCodeGen::DoCheckMaps(LCheckMaps* instr) {
   SmallMapList* map_set = instr->hydrogen()->map_set();
   for (int i = 0; i < map_set->length() - 1; i++) {
     Handle<Map> map = map_set->at(i);
-    __ CompareMap(reg, map, &success, REQUIRE_EXACT_MAP);
+    __ CompareMap(reg, map, &success, instr->hydrogen()->mode());
     __ j(equal, &success);
   }
   Handle<Map> map = map_set->last();
-  DoCheckMapCommon(reg, map, REQUIRE_EXACT_MAP, instr->environment());
+  DoCheckMapCommon(reg, map, instr->hydrogen()->mode(), instr->environment());
   __ bind(&success);
 }
 
index 16867db..7c3063e 100644 (file)
@@ -25,6 +25,8 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+// Flags: --allow-natives-syntax
+
 var a = [0,1,2,3];
 assertEquals(0, a.length = 0);
 
@@ -119,3 +121,42 @@ for (var i = 0; i < 7; i++) {
   t = a.length = 7;
   assertEquals(7, t);
 }
+
+function TestLengthAccess() {
+  var TestGetLength = function(a, l, getLength, getKeyedLength) {
+    assertEquals(l, getLength(a));
+    assertEquals(l, getKeyedLength(a));
+  }
+
+  var Test = function(a, l, getLength, getKeyedLength) {
+    // Make the ICs reach the state we want.
+    TestGetLength(a, l, getLength, getKeyedLength);
+    TestGetLength(a, l, getLength, getKeyedLength);
+    TestGetLength(a, l, getLength, getKeyedLength);
+    // Also test crankshaft.
+    %OptimizeFunctionOnNextCall(getLength);
+    %OptimizeFunctionOnNextCall(getKeyedLength);
+    TestGetLength(a, l, getLength, getKeyedLength);
+  }
+
+  var getLength = function(a) { return a.length; }
+  var getKeyedLength = function(a) { return a['length']; }
+  Test(new Array(4), 4, getLength, getKeyedLength);
+
+  getLength = function(a) { return a.length; }
+  getKeyedLength = function(a) { return a['length']; }
+  Test([1, 2, 3, 4], 4, getLength, getKeyedLength);
+
+  getLength = function(a) { return a.length; }
+  getKeyedLength = function(a) { return a['length']; }
+  Test(Object.create(new Array(4)), 4, getLength, getKeyedLength);
+
+  getLength = function(a) { return a.length; }
+  getKeyedLength = function(a) { return a['length']; }
+  // Set the 'length' of the global object.
+  length = 4;
+  var globalObject = (1,eval)("this");
+  Test(globalObject, 4, getLength, getKeyedLength);
+}
+TestLengthAccess();
+