Speed up ArrayBuffer/typed array/DataView properties.
authordslomov@chromium.org <dslomov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 25 Sep 2013 08:19:35 +0000 (08:19 +0000)
committerdslomov@chromium.org <dslomov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 25 Sep 2013 08:19:35 +0000 (08:19 +0000)
Optimizes byteLength, byteOffset, buffer and other properties on
ArrayBuffer, typed arrays and DataView into simple field loads. Some
unification with the way Array.length and String.length are treated.

R=verwaest@chromium.org

Review URL: https://codereview.chromium.org/24360019

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

src/accessors.cc
src/accessors.h
src/heap.h
src/hydrogen-instructions.h
src/hydrogen.cc
src/hydrogen.h
src/ic.cc

index 669c02b..5023266 100644 (file)
@@ -78,6 +78,61 @@ MaybeObject* Accessors::ReadOnlySetAccessor(Isolate* isolate,
 }
 
 
+static V8_INLINE bool CheckForName(Handle<String> name,
+                                   String* property_name,
+                                   int offset,
+                                   int* object_offset) {
+  if (name->Equals(property_name)) {
+    *object_offset = offset;
+    return true;
+  }
+  return false;
+}
+
+
+bool Accessors::IsJSObjectFieldAccessor(
+      Handle<Map> map, Handle<String> name,
+      int* object_offset) {
+  Isolate* isolate = map->GetIsolate();
+  switch (map->instance_type()) {
+    case JS_ARRAY_TYPE:
+      return
+        CheckForName(name, isolate->heap()->length_string(),
+                     JSArray::kLengthOffset, object_offset);
+    case JS_TYPED_ARRAY_TYPE:
+      return
+        CheckForName(name, isolate->heap()->length_string(),
+                     JSTypedArray::kLengthOffset, object_offset) ||
+        CheckForName(name, isolate->heap()->byte_length_string(),
+                     JSTypedArray::kByteLengthOffset, object_offset) ||
+        CheckForName(name, isolate->heap()->byte_offset_string(),
+                     JSTypedArray::kByteOffsetOffset, object_offset) ||
+        CheckForName(name, isolate->heap()->buffer_string(),
+                     JSTypedArray::kBufferOffset, object_offset);
+    case JS_ARRAY_BUFFER_TYPE:
+      return
+        CheckForName(name, isolate->heap()->byte_length_string(),
+                     JSArrayBuffer::kByteLengthOffset, object_offset);
+    case JS_DATA_VIEW_TYPE:
+      return
+        CheckForName(name, isolate->heap()->byte_length_string(),
+                     JSDataView::kByteLengthOffset, object_offset) ||
+        CheckForName(name, isolate->heap()->byte_offset_string(),
+                     JSDataView::kByteOffsetOffset, object_offset) ||
+        CheckForName(name, isolate->heap()->buffer_string(),
+                     JSDataView::kBufferOffset, object_offset);
+    default: {
+      if (map->instance_type() < FIRST_NONSTRING_TYPE) {
+        return
+          CheckForName(name, isolate->heap()->length_string(),
+                       String::kLengthOffset, object_offset);
+      }
+      return false;
+    }
+  }
+}
+
+
 //
 // Accessors::ArrayLength
 //
index d9a2130..b2dee27 100644 (file)
@@ -86,6 +86,13 @@ class Accessors : public AllStatic {
   static Handle<AccessorInfo> MakeModuleExport(
       Handle<String> name, int index, PropertyAttributes attributes);
 
+  // Returns true for properties that are accessors to object fields.
+  // If true, *object_offset contains offset of object field.
+  static bool IsJSObjectFieldAccessor(
+      Handle<Map> map, Handle<String> name,
+      int* object_offset);
+
+
  private:
   // Accessor functions only used through the descriptor.
   static MaybeObject* FunctionSetPrototype(Isolate* isolate,
index cb979a6..e059434 100644 (file)
@@ -292,7 +292,10 @@ namespace internal {
   V(throw_string, "throw")                                               \
   V(done_string, "done")                                                 \
   V(value_string, "value")                                               \
-  V(next_string, "next")
+  V(next_string, "next")                                                 \
+  V(byte_length_string, "byteLength")                                    \
+  V(byte_offset_string, "byteOffset")                                    \
+  V(buffer_string, "buffer")
 
 // Forward declarations.
 class GCTracer;
index 71bdb33..7e5272e 100644 (file)
@@ -5626,13 +5626,6 @@ class HObjectAccess V8_FINAL {
                 ? Representation::Smi() : Representation::Tagged());
   }
 
-  static HObjectAccess ForTypedArrayLength() {
-    return HObjectAccess(
-        kInobject,
-        JSTypedArray::kLengthOffset,
-        Representation::Tagged());
-  }
-
   static HObjectAccess ForAllocationSiteTransitionInfo() {
     return HObjectAccess(kInobject, AllocationSite::kTransitionInfoOffset);
   }
index 0a7e9f0..cd852f5 100644 (file)
@@ -4766,7 +4766,7 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() {
 
 bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadMonomorphic() {
   if (!CanInlinePropertyAccess(*map_)) return IsStringLength();
-  if (IsArrayLength()) return true;
+  if (IsJSObjectFieldAccessor()) return true;
   if (!LookupDescriptor()) return false;
   if (lookup_.IsFound()) return true;
   return LookupInPrototypes();
@@ -4798,9 +4798,10 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic(
     return true;
   }
 
-  if (IsTypedArrayLength()) {
+  if (IsJSObjectFieldAccessor()) {
+    InstanceType instance_type = map_->instance_type();
     for (int i = 1; i < types->length(); ++i) {
-      if (types->at(i)->instance_type() != JS_TYPED_ARRAY_TYPE) return false;
+      if (types->at(i)->instance_type() != instance_type) return false;
     }
     return true;
   }
@@ -4821,20 +4822,10 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic(
     BailoutId ast_id,
     BailoutId return_id,
     bool can_inline_accessor) {
-  if (info->IsStringLength()) {
-    return New<HLoadNamedField>(
-        checked_object, HObjectAccess::ForStringLength());
-  }
-
-  if (info->IsArrayLength()) {
-    return New<HLoadNamedField>(
-        checked_object, HObjectAccess::ForArrayLength(
-            info->map()->elements_kind()));
-  }
 
-  if (info->IsTypedArrayLength()) {
-    return New<HLoadNamedField>(
-        checked_object, HObjectAccess::ForTypedArrayLength());
+  HObjectAccess access = HObjectAccess::ForMap();  // bogus default
+  if (info->GetJSObjectFieldAccess(&access)) {
+    return New<HLoadNamedField>(checked_object, access);
   }
 
   HValue* checked_holder = checked_object;
@@ -5537,16 +5528,6 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedGeneric(
 }
 
 
-HInstruction* HOptimizedGraphBuilder::BuildCallGetter(
-    HValue* object,
-    Handle<Map> map,
-    Handle<JSFunction> getter,
-    Handle<JSObject> holder) {
-  AddCheckConstantFunction(holder, object, map);
-  Add<HPushArgument>(object);
-  return new(zone()) HCallConstantFunction(getter, 1);
-}
-
 
 HInstruction* HOptimizedGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
                                                             HValue* key) {
index 6aa8217..be23fa8 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "v8.h"
 
+#include "accessors.h"
 #include "allocation.h"
 #include "ast.h"
 #include "compiler.h"
@@ -2046,19 +2047,26 @@ class HOptimizedGraphBuilder V8_FINAL
     // PropertyAccessInfo is built for types->first().
     bool CanLoadAsMonomorphic(SmallMapList* types);
 
-    bool IsStringLength() {
-      return map_->instance_type() < FIRST_NONSTRING_TYPE &&
-          name_->Equals(isolate()->heap()->length_string());
+    bool IsJSObjectFieldAccessor() {
+      int offset;  // unused
+      return Accessors::IsJSObjectFieldAccessor(map_, name_, &offset);
     }
 
-    bool IsArrayLength() {
-      return map_->instance_type() == JS_ARRAY_TYPE &&
-          name_->Equals(isolate()->heap()->length_string());
-    }
-
-    bool IsTypedArrayLength() {
-      return map_->instance_type() == JS_TYPED_ARRAY_TYPE &&
-          name_->Equals(isolate()->heap()->length_string());
+    bool GetJSObjectFieldAccess(HObjectAccess* access) {
+      if (IsStringLength()) {
+        *access = HObjectAccess::ForStringLength();
+        return true;
+      } else if (IsArrayLength()) {
+        *access = HObjectAccess::ForArrayLength(map_->elements_kind());
+        return true;
+      } else {
+        int offset;
+        if (Accessors::IsJSObjectFieldAccessor(map_, name_, &offset)) {
+          *access = HObjectAccess::ForJSObjectOffset(offset);
+          return true;
+        }
+        return false;
+      }
     }
 
     bool has_holder() { return !holder_.is_null(); }
@@ -2073,6 +2081,16 @@ class HOptimizedGraphBuilder V8_FINAL
    private:
     Isolate* isolate() { return lookup_.isolate(); }
 
+    bool IsStringLength() {
+      return map_->instance_type() < FIRST_NONSTRING_TYPE &&
+          name_->Equals(isolate()->heap()->length_string());
+    }
+
+    bool IsArrayLength() {
+      return map_->instance_type() == JS_ARRAY_TYPE &&
+          name_->Equals(isolate()->heap()->length_string());
+    }
+
     bool LoadResult(Handle<Map> map);
     bool LookupDescriptor();
     bool LookupInPrototypes();
@@ -2173,10 +2191,6 @@ class HOptimizedGraphBuilder V8_FINAL
   HInstruction* BuildLoadNamedGeneric(HValue* object,
                                       Handle<String> name,
                                       Property* expr);
-  HInstruction* BuildCallGetter(HValue* object,
-                                Handle<Map> map,
-                                Handle<JSFunction> getter,
-                                Handle<JSObject> holder);
 
   HCheckMaps* AddCheckMap(HValue* object, Handle<Map> map);
 
index 84e65ac..f42b9f4 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -1348,20 +1348,19 @@ Handle<Code> LoadIC::ComputeLoadHandler(LookupResult* lookup,
       if (!holder.is_identical_to(receiver)) break;
       return isolate()->stub_cache()->ComputeLoadNormal(name, receiver);
     case CALLBACKS: {
-      Handle<Object> callback(lookup->GetCallbackObject(), isolate());
-      if (name->Equals(isolate()->heap()->length_string())) {
-        if (receiver->IsJSArray()) {
-          PropertyIndex lengthIndex = PropertyIndex::NewHeaderIndex(
-              JSArray::kLengthOffset / kPointerSize);
-          return isolate()->stub_cache()->ComputeLoadField(
-              name, receiver, receiver, lengthIndex, Representation::Tagged());
-        } else if (receiver->IsJSTypedArray()) {
-          PropertyIndex lengthIndex = PropertyIndex::NewHeaderIndex(
-              JSTypedArray::kLengthOffset / kPointerSize);
+      {
+        // Use simple field loads for some well-known callback properties.
+        int object_offset;
+        Handle<Map> map(receiver->map());
+        if (Accessors::IsJSObjectFieldAccessor(map, name, &object_offset)) {
+          PropertyIndex index =
+              PropertyIndex::NewHeaderIndex(object_offset / kPointerSize);
           return isolate()->stub_cache()->ComputeLoadField(
-              name, receiver, receiver, lengthIndex, Representation::Tagged());
+              name, receiver, receiver, index, Representation::Tagged());
         }
       }
+
+      Handle<Object> callback(lookup->GetCallbackObject(), isolate());
       if (callback->IsExecutableAccessorInfo()) {
         Handle<ExecutableAccessorInfo> info =
             Handle<ExecutableAccessorInfo>::cast(callback);