Revision: 6472
Author: [email protected]
Date: Tue Jan 25 07:51:10 2011
Log: Support StringLength in hydrogen (similar to ArrayLength).

To avoid deopts a few extra changes were needed:

o Enable megamorphic state for special property loads on
  primitives. We used to flip between monomorphic stubs.
o Extract pure string (no string wrapper support) version of the
  string length stub.

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

Modified:
 /branches/bleeding_edge/src/arm/ic-arm.cc
 /branches/bleeding_edge/src/arm/stub-cache-arm.cc
 /branches/bleeding_edge/src/ast.cc
 /branches/bleeding_edge/src/ast.h
 /branches/bleeding_edge/src/builtins.cc
 /branches/bleeding_edge/src/builtins.h
 /branches/bleeding_edge/src/hydrogen.cc
 /branches/bleeding_edge/src/ia32/ic-ia32.cc
 /branches/bleeding_edge/src/ia32/stub-cache-ia32.cc
 /branches/bleeding_edge/src/ic.cc
 /branches/bleeding_edge/src/ic.h
 /branches/bleeding_edge/src/stub-cache.h
 /branches/bleeding_edge/src/x64/ic-x64.cc
 /branches/bleeding_edge/src/x64/stub-cache-x64.cc

=======================================
--- /branches/bleeding_edge/src/arm/ic-arm.cc   Tue Jan 25 06:52:35 2011
+++ /branches/bleeding_edge/src/arm/ic-arm.cc   Tue Jan 25 07:51:10 2011
@@ -379,7 +379,7 @@
 }


-void LoadIC::GenerateStringLength(MacroAssembler* masm) {
+void LoadIC::GenerateStringLength(MacroAssembler* masm, bool support_wrappers) {
   // ----------- S t a t e -------------
   //  -- r2    : name
   //  -- lr    : return address
@@ -388,7 +388,8 @@
   // -----------------------------------
   Label miss;

-  StubCompiler::GenerateLoadStringLength(masm, r0, r1, r3, &miss);
+  StubCompiler::GenerateLoadStringLength(masm, r0, r1, r3, &miss,
+                                         support_wrappers);
   // Cache miss: Jump to runtime.
   __ bind(&miss);
   StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
=======================================
--- /branches/bleeding_edge/src/arm/stub-cache-arm.cc Fri Jan 21 15:58:00 2011 +++ /branches/bleeding_edge/src/arm/stub-cache-arm.cc Tue Jan 25 07:51:10 2011
@@ -370,27 +370,31 @@
                                             Register receiver,
                                             Register scratch1,
                                             Register scratch2,
-                                            Label* miss) {
+                                            Label* miss,
+                                            bool support_wrappers) {
   Label check_wrapper;

   // Check if the object is a string leaving the instance type in the
   // scratch1 register.
- GenerateStringCheck(masm, receiver, scratch1, scratch2, miss, &check_wrapper);
+  GenerateStringCheck(masm, receiver, scratch1, scratch2, miss,
+                      support_wrappers ? &check_wrapper : miss);

   // Load length directly from the string.
   __ ldr(r0, FieldMemOperand(receiver, String::kLengthOffset));
   __ Ret();

-  // Check if the object is a JSValue wrapper.
-  __ bind(&check_wrapper);
-  __ cmp(scratch1, Operand(JS_VALUE_TYPE));
-  __ b(ne, miss);
-
-  // Unwrap the value and check if the wrapped value is a string.
-  __ ldr(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset));
-  GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss);
-  __ ldr(r0, FieldMemOperand(scratch1, String::kLengthOffset));
-  __ Ret();
+  if (support_wrappers) {
+    // Check if the object is a JSValue wrapper.
+    __ bind(&check_wrapper);
+    __ cmp(scratch1, Operand(JS_VALUE_TYPE));
+    __ b(ne, miss);
+
+    // Unwrap the value and check if the wrapped value is a string.
+    __ ldr(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset));
+    GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss);
+    __ ldr(r0, FieldMemOperand(scratch1, String::kLengthOffset));
+    __ Ret();
+  }
 }


@@ -2995,7 +2999,7 @@
   __ cmp(r0, Operand(Handle<String>(name)));
   __ b(ne, &miss);

-  GenerateLoadStringLength(masm(), r1, r2, r3, &miss);
+  GenerateLoadStringLength(masm(), r1, r2, r3, &miss, true);
   __ bind(&miss);
   __ DecrementCounter(&Counters::keyed_load_string_length, 1, r2, r3);

=======================================
--- /branches/bleeding_edge/src/ast.cc  Tue Jan 25 04:21:03 2011
+++ /branches/bleeding_edge/src/ast.cc  Tue Jan 25 07:51:10 2011
@@ -521,6 +521,8 @@
   if (key()->IsPropertyName()) {
     if (oracle->LoadIsBuiltin(this, Builtins::LoadIC_ArrayLength)) {
       is_array_length_ = true;
+ } else if (oracle->LoadIsBuiltin(this, Builtins::LoadIC_StringLength)) {
+      is_string_length_ = true;
     } else if (oracle->LoadIsBuiltin(this,
                                      Builtins::LoadIC_FunctionPrototype)) {
       is_function_prototype_ = true;
=======================================
--- /branches/bleeding_edge/src/ast.h   Thu Jan 20 10:51:47 2011
+++ /branches/bleeding_edge/src/ast.h   Tue Jan 25 07:51:10 2011
@@ -1205,9 +1205,10 @@
         key_(key),
         pos_(pos),
         type_(type),
-        is_monomorphic_(false),
         receiver_types_(NULL),
+        is_monomorphic_(false),
         is_array_length_(false),
+        is_string_length_(false),
         is_function_prototype_(false),
         is_arguments_access_(false) { }

@@ -1221,6 +1222,7 @@
   int position() const { return pos_; }
   bool is_synthetic() const { return type_ == SYNTHETIC; }

+  bool IsStringLength() const { return is_string_length_; }
   bool IsFunctionPrototype() const { return is_function_prototype_; }

   // Marks that this is actually an argument rewritten to a keyed property
@@ -1249,11 +1251,12 @@
   int pos_;
   Type type_;

-  bool is_monomorphic_;
   ZoneMapList* receiver_types_;
-  bool is_array_length_;
-  bool is_function_prototype_;
-  bool is_arguments_access_;
+  bool is_monomorphic_ : 1;
+  bool is_array_length_ : 1;
+  bool is_string_length_ : 1;
+  bool is_function_prototype_ : 1;
+  bool is_arguments_access_ : 1;
   Handle<Map> monomorphic_receiver_type_;

   // Dummy property used during preparsing.
=======================================
--- /branches/bleeding_edge/src/builtins.cc     Fri Jan 21 15:58:00 2011
+++ /branches/bleeding_edge/src/builtins.cc     Tue Jan 25 07:51:10 2011
@@ -1228,7 +1228,12 @@


 static void Generate_LoadIC_StringLength(MacroAssembler* masm) {
-  LoadIC::GenerateStringLength(masm);
+  LoadIC::GenerateStringLength(masm, false);
+}
+
+
+static void Generate_LoadIC_StringWrapperLength(MacroAssembler* masm) {
+  LoadIC::GenerateStringLength(masm, true);
 }


=======================================
--- /branches/bleeding_edge/src/builtins.h      Fri Jan 21 15:58:00 2011
+++ /branches/bleeding_edge/src/builtins.h      Tue Jan 25 07:51:10 2011
@@ -86,6 +86,7 @@
   V(LoadIC_Normal,              LOAD_IC, MONOMORPHIC)                     \
   V(LoadIC_ArrayLength,         LOAD_IC, MONOMORPHIC)                     \
   V(LoadIC_StringLength,        LOAD_IC, MONOMORPHIC)                     \
+  V(LoadIC_StringWrapperLength, LOAD_IC, MONOMORPHIC)                     \
   V(LoadIC_FunctionPrototype,   LOAD_IC, MONOMORPHIC)                     \
   V(LoadIC_Megamorphic,         LOAD_IC, MEGAMORPHIC)                     \
                                                                           \
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc     Thu Jan 20 04:56:34 2011
+++ /branches/bleeding_edge/src/hydrogen.cc     Tue Jan 25 07:51:10 2011
@@ -3766,6 +3766,14 @@
AddInstruction(new HCheckInstanceType(array, JS_ARRAY_TYPE, JS_ARRAY_TYPE));
     instr = new HJSArrayLength(array);

+  } else if (expr->IsStringLength()) {
+    HValue* string = Pop();
+    AddInstruction(new HCheckNonSmi(string));
+    AddInstruction(new HCheckInstanceType(string,
+                                          FIRST_STRING_TYPE,
+                                          LAST_STRING_TYPE));
+    instr = new HStringLength(string);
+
   } else if (expr->IsFunctionPrototype()) {
     HValue* function = Pop();
     AddInstruction(new HCheckNonSmi(function));
=======================================
--- /branches/bleeding_edge/src/ia32/ic-ia32.cc Fri Jan 21 15:58:00 2011
+++ /branches/bleeding_edge/src/ia32/ic-ia32.cc Tue Jan 25 07:51:10 2011
@@ -388,7 +388,8 @@
 }


-void LoadIC::GenerateStringLength(MacroAssembler* masm) {
+void LoadIC::GenerateStringLength(MacroAssembler* masm,
+                                  bool support_wrappers) {
   // ----------- S t a t e -------------
   //  -- eax    : receiver
   //  -- ecx    : name
@@ -396,7 +397,8 @@
   // -----------------------------------
   Label miss;

-  StubCompiler::GenerateLoadStringLength(masm, eax, edx, ebx, &miss);
+  StubCompiler::GenerateLoadStringLength(masm, eax, edx, ebx, &miss,
+                                         support_wrappers);
   __ bind(&miss);
   StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
 }
=======================================
--- /branches/bleeding_edge/src/ia32/stub-cache-ia32.cc Fri Jan 21 15:58:00 2011 +++ /branches/bleeding_edge/src/ia32/stub-cache-ia32.cc Tue Jan 25 07:51:10 2011
@@ -327,28 +327,32 @@
                                             Register receiver,
                                             Register scratch1,
                                             Register scratch2,
-                                            Label* miss) {
+                                            Label* miss,
+                                            bool support_wrappers) {
   Label check_wrapper;

   // Check if the object is a string leaving the instance type in the
   // scratch register.
-  GenerateStringCheck(masm, receiver, scratch1, miss, &check_wrapper);
+  GenerateStringCheck(masm, receiver, scratch1, miss,
+                      support_wrappers ? &check_wrapper : miss);

   // Load length from the string and convert to a smi.
   __ mov(eax, FieldOperand(receiver, String::kLengthOffset));
   __ ret(0);

-  // Check if the object is a JSValue wrapper.
-  __ bind(&check_wrapper);
-  __ cmp(scratch1, JS_VALUE_TYPE);
-  __ j(not_equal, miss, not_taken);
-
-  // Check if the wrapped value is a string and load the length
-  // directly if it is.
-  __ mov(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
-  GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
-  __ mov(eax, FieldOperand(scratch2, String::kLengthOffset));
-  __ ret(0);
+  if (support_wrappers) {
+    // Check if the object is a JSValue wrapper.
+    __ bind(&check_wrapper);
+    __ cmp(scratch1, JS_VALUE_TYPE);
+    __ j(not_equal, miss, not_taken);
+
+    // Check if the wrapped value is a string and load the length
+    // directly if it is.
+    __ mov(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
+    GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
+    __ mov(eax, FieldOperand(scratch2, String::kLengthOffset));
+    __ ret(0);
+  }
 }


@@ -3089,7 +3093,7 @@
   __ cmp(Operand(eax), Immediate(Handle<String>(name)));
   __ j(not_equal, &miss, not_taken);

-  GenerateLoadStringLength(masm(), edx, ecx, ebx, &miss);
+  GenerateLoadStringLength(masm(), edx, ecx, ebx, &miss, true);
   __ bind(&miss);
   __ DecrementCounter(&Counters::keyed_load_string_length, 1);
   GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
=======================================
--- /branches/bleeding_edge/src/ic.cc   Tue Jan 25 06:52:35 2011
+++ /branches/bleeding_edge/src/ic.cc   Tue Jan 25 07:51:10 2011
@@ -822,6 +822,9 @@
   }

   if (FLAG_use_ic) {
+    Code* non_monomorphic_stub =
+ (state == UNINITIALIZED) ? pre_monomorphic_stub() : megamorphic_stub();
+
     // Use specialized code for getting the length of strings and
     // string wrapper objects.  The length property of string wrapper
     // objects is read-only and therefore always returns the length of
@@ -829,22 +832,27 @@
     if ((object->IsString() || object->IsStringWrapper()) &&
         name->Equals(Heap::length_symbol())) {
       HandleScope scope;
-      // Get the string if we have a string wrapper object.
-      if (object->IsJSValue()) {
-        object = Handle<Object>(Handle<JSValue>::cast(object)->value());
-      }
 #ifdef DEBUG
       if (FLAG_trace_ic) PrintF("[LoadIC : +#length /string]\n");
 #endif
-      Map* map = HeapObject::cast(*object)->map();
-      if (object->IsString()) {
-        const int offset = String::kLengthOffset;
-        PatchInlinedLoad(address(), map, offset);
-      }
-
-      Code* target = NULL;
-      target = Builtins::builtin(Builtins::LoadIC_StringLength);
-      set_target(target);
+      if (state == PREMONOMORPHIC) {
+        if (object->IsString()) {
+          Map* map = HeapObject::cast(*object)->map();
+          const int offset = String::kLengthOffset;
+          PatchInlinedLoad(address(), map, offset);
+          set_target(Builtins::builtin(Builtins::LoadIC_StringLength));
+        } else {
+ set_target(Builtins::builtin(Builtins::LoadIC_StringWrapperLength));
+        }
+      } else if (state == MONOMORPHIC && object->IsStringWrapper()) {
+ set_target(Builtins::builtin(Builtins::LoadIC_StringWrapperLength));
+      } else {
+        set_target(non_monomorphic_stub);
+      }
+      // Get the string if we have a string wrapper object.
+      if (object->IsJSValue()) {
+        object = Handle<Object>(Handle<JSValue>::cast(object)->value());
+      }
       return Smi::FromInt(String::cast(*object)->length());
     }

@@ -853,12 +861,14 @@
 #ifdef DEBUG
       if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
 #endif
-      Map* map = HeapObject::cast(*object)->map();
-      const int offset = JSArray::kLengthOffset;
-      PatchInlinedLoad(address(), map, offset);
-
-      Code* target = Builtins::builtin(Builtins::LoadIC_ArrayLength);
-      set_target(target);
+      if (state == PREMONOMORPHIC) {
+        Map* map = HeapObject::cast(*object)->map();
+        const int offset = JSArray::kLengthOffset;
+        PatchInlinedLoad(address(), map, offset);
+        set_target(Builtins::builtin(Builtins::LoadIC_ArrayLength));
+      } else {
+        set_target(non_monomorphic_stub);
+      }
       return JSArray::cast(*object)->length();
     }

@@ -868,8 +878,11 @@
 #ifdef DEBUG
       if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
 #endif
-      Code* target = Builtins::builtin(Builtins::LoadIC_FunctionPrototype);
-      set_target(target);
+      if (state == PREMONOMORPHIC) {
+        set_target(Builtins::builtin(Builtins::LoadIC_FunctionPrototype));
+      } else {
+        set_target(non_monomorphic_stub);
+      }
       return Accessors::FunctionGetPrototype(*object, 0);
     }
   }
@@ -1092,6 +1105,8 @@
     }

     if (FLAG_use_ic) {
+      // TODO(1073): don't ignore the current stub state.
+
       // Use specialized code for getting the length of strings.
       if (object->IsString() && name->Equals(Heap::length_symbol())) {
         Handle<String> string = Handle<String>::cast(object);
=======================================
--- /branches/bleeding_edge/src/ic.h    Fri Jan 21 15:58:00 2011
+++ /branches/bleeding_edge/src/ic.h    Tue Jan 25 07:51:10 2011
@@ -284,7 +284,8 @@

   // Specialized code generator routines.
   static void GenerateArrayLength(MacroAssembler* masm);
-  static void GenerateStringLength(MacroAssembler* masm);
+  static void GenerateStringLength(MacroAssembler* masm,
+                                   bool support_wrappers);
   static void GenerateFunctionPrototype(MacroAssembler* masm);

   // Clear the use of the inlined version.
=======================================
--- /branches/bleeding_edge/src/stub-cache.h    Fri Jan 21 15:58:00 2011
+++ /branches/bleeding_edge/src/stub-cache.h    Tue Jan 25 07:51:10 2011
@@ -427,7 +427,8 @@
                                        Register receiver,
                                        Register scratch1,
                                        Register scratch2,
-                                       Label* miss_label);
+                                       Label* miss_label,
+                                       bool support_wrappers);

   static void GenerateLoadFunctionPrototype(MacroAssembler* masm,
                                             Register receiver,
=======================================
--- /branches/bleeding_edge/src/x64/ic-x64.cc   Fri Jan 21 15:58:00 2011
+++ /branches/bleeding_edge/src/x64/ic-x64.cc   Tue Jan 25 07:51:10 2011
@@ -397,7 +397,7 @@
 }


-void LoadIC::GenerateStringLength(MacroAssembler* masm) {
+void LoadIC::GenerateStringLength(MacroAssembler* masm, bool support_wrappers) {
   // ----------- S t a t e -------------
   //  -- rax    : receiver
   //  -- rcx    : name
@@ -405,7 +405,8 @@
   // -----------------------------------
   Label miss;

-  StubCompiler::GenerateLoadStringLength(masm, rax, rdx, rbx, &miss);
+  StubCompiler::GenerateLoadStringLength(masm, rax, rdx, rbx, &miss,
+                                         support_wrappers);
   __ bind(&miss);
   StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
 }
=======================================
--- /branches/bleeding_edge/src/x64/stub-cache-x64.cc Fri Jan 21 15:58:00 2011 +++ /branches/bleeding_edge/src/x64/stub-cache-x64.cc Tue Jan 25 07:51:10 2011
@@ -307,28 +307,32 @@
                                             Register receiver,
                                             Register scratch1,
                                             Register scratch2,
-                                            Label* miss) {
+                                            Label* miss,
+                                            bool support_wrappers) {
   Label check_wrapper;

   // Check if the object is a string leaving the instance type in the
   // scratch register.
-  GenerateStringCheck(masm, receiver, scratch1, miss, &check_wrapper);
+  GenerateStringCheck(masm, receiver, scratch1, miss,
+                      support_wrappers ? &check_wrapper : miss);

   // Load length directly from the string.
   __ movq(rax, FieldOperand(receiver, String::kLengthOffset));
   __ ret(0);

-  // Check if the object is a JSValue wrapper.
-  __ bind(&check_wrapper);
-  __ cmpl(scratch1, Immediate(JS_VALUE_TYPE));
-  __ j(not_equal, miss);
-
-  // Check if the wrapped value is a string and load the length
-  // directly if it is.
-  __ movq(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
-  GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
-  __ movq(rax, FieldOperand(scratch2, String::kLengthOffset));
-  __ ret(0);
+  if (support_wrappers) {
+    // Check if the object is a JSValue wrapper.
+    __ bind(&check_wrapper);
+    __ cmpl(scratch1, Immediate(JS_VALUE_TYPE));
+    __ j(not_equal, miss);
+
+    // Check if the wrapped value is a string and load the length
+    // directly if it is.
+    __ movq(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
+    GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
+    __ movq(rax, FieldOperand(scratch2, String::kLengthOffset));
+    __ ret(0);
+  }
 }


@@ -2933,7 +2937,7 @@
   __ Cmp(rax, Handle<String>(name));
   __ j(not_equal, &miss);

-  GenerateLoadStringLength(masm(), rdx, rcx, rbx, &miss);
+  GenerateLoadStringLength(masm(), rdx, rcx, rbx, &miss, true);
   __ bind(&miss);
   __ DecrementCounter(&Counters::keyed_load_string_length, 1);
   GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);

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

Reply via email to