Revision: 4418
Author: [email protected]
Date: Wed Apr 14 07:46:15 2010
Log: Introduce fast native caches and use it in String.search.

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

Modified:
 /branches/bleeding_edge/src/arm/codegen-arm.cc
 /branches/bleeding_edge/src/arm/codegen-arm.h
 /branches/bleeding_edge/src/bootstrapper.cc
 /branches/bleeding_edge/src/codegen.h
 /branches/bleeding_edge/src/contexts.h
 /branches/bleeding_edge/src/factory.cc
 /branches/bleeding_edge/src/factory.h
 /branches/bleeding_edge/src/heap.cc
 /branches/bleeding_edge/src/heap.h
 /branches/bleeding_edge/src/ia32/codegen-ia32.cc
 /branches/bleeding_edge/src/ia32/codegen-ia32.h
 /branches/bleeding_edge/src/macros.py
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/src/string.js
 /branches/bleeding_edge/src/x64/codegen-x64.cc
 /branches/bleeding_edge/src/x64/codegen-x64.h
 /branches/bleeding_edge/test/mjsunit/fuzz-natives.js

=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.cc      Wed Apr 14 02:25:33 2010
+++ /branches/bleeding_edge/src/arm/codegen-arm.cc      Wed Apr 14 07:46:15 2010
@@ -4116,6 +4116,72 @@
   frame_->Forget(3);
   frame_->EmitPush(r0);
 }
+
+
+class DeferredSearchCache: public DeferredCode {
+ public:
+  DeferredSearchCache(Register dst, Register cache, Register key)
+      : dst_(dst), cache_(cache), key_(key) {
+    set_comment("[ DeferredSearchCache");
+  }
+
+  virtual void Generate();
+
+ private:
+  Register dst_, cache_, key_;
+};
+
+
+void DeferredSearchCache::Generate() {
+  __ push(cache_);
+  __ push(key_);
+  __ CallRuntime(Runtime::kGetFromCache, 2);
+  if (!dst_.is(r0)) {
+    __ mov(dst_, r0);
+  }
+}
+
+
+void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
+  ASSERT_EQ(2, args->length());
+
+  ASSERT_NE(NULL, args->at(0)->AsLiteral());
+  int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
+
+  Handle<FixedArray> jsfunction_result_caches(
+      Top::global_context()->jsfunction_result_caches());
+  if (jsfunction_result_caches->length() <= cache_id) {
+    __ Abort("Attempt to use undefined cache.");
+    __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+    frame_->EmitPush(r0);
+    return;
+  }
+  Handle<FixedArray> cache_obj(
+      FixedArray::cast(jsfunction_result_caches->get(cache_id)));
+
+  Load(args->at(1));
+  frame_->EmitPop(r2);
+
+  DeferredSearchCache* deferred = new DeferredSearchCache(r0, r1, r2);
+
+  const int kFingerOffset =
+      FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
+  ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+  __ mov(r1, Operand(cache_obj));
+  __ ldr(r0, FieldMemOperand(r1, kFingerOffset));
+  // r0 now holds finger offset as a smi.
+  __ add(r3, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+  // r3 now points to the start of fixed array elements.
+ __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize, PreIndex));
+  // Note side effect of PreIndex: r3 now points to the key of the pair.
+  __ cmp(r2, r0);
+  deferred->Branch(ne);
+
+  __ ldr(r0, MemOperand(r3, kPointerSize));
+
+  deferred->BindExit();
+  frame_->EmitPush(r0);
+}


 void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.h       Tue Apr 13 02:31:03 2010
+++ /branches/bleeding_edge/src/arm/codegen-arm.h       Wed Apr 14 07:46:15 2010
@@ -409,6 +409,9 @@

   void GenerateRegExpConstructResult(ZoneList<Expression*>* args);

+  // Support for fast native caches.
+  void GenerateGetFromCache(ZoneList<Expression*>* args);
+
   // Fast support for number to string.
   void GenerateNumberToString(ZoneList<Expression*>* args);

=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Tue Apr 13 02:31:03 2010
+++ /branches/bleeding_edge/src/bootstrapper.cc Wed Apr 14 07:46:15 2010
@@ -228,6 +228,7 @@
   // Used for creating a context from scratch.
   void InstallNativeFunctions();
   bool InstallNatives();
+  void InstallJSFunctionResultCaches();
// Used both for deserialized and from-scratch contexts to add the extensions
   // provided.
   static bool InstallExtensions(Handle<Context> global_context,
@@ -1299,6 +1300,44 @@

   return true;
 }
+
+
+// Do not forget to update macros.py with named constant
+// of cache id.
+#define JSFUNCTION_RESULT_CACHE_LIST(F) \
+  F(16, global_context()->regexp_function())
+
+
+static FixedArray* CreateCache(int size, JSFunction* factory) {
+  // Caches are supposed to live for a long time, allocate in old space.
+  int array_size = JSFunctionResultCache::kEntriesIndex + 2 * size;
+  Handle<FixedArray> cache =
+      Factory::NewFixedArrayWithHoles(array_size, TENURED);
+  cache->set(JSFunctionResultCache::kFactoryIndex, factory);
+  cache->set(JSFunctionResultCache::kFingerIndex,
+      Smi::FromInt(JSFunctionResultCache::kEntriesIndex));
+  cache->set(JSFunctionResultCache::kCacheSizeIndex,
+      Smi::FromInt(JSFunctionResultCache::kEntriesIndex));
+  return *cache;
+}
+
+
+void Genesis::InstallJSFunctionResultCaches() {
+  const int kNumberOfCaches = 0 +
+#define F(size, func) + 1
+    JSFUNCTION_RESULT_CACHE_LIST(F)
+#undef F
+  ;
+
+ Handle<FixedArray> caches = Factory::NewFixedArray(kNumberOfCaches, TENURED);
+
+  int index = 0;
+#define F(size, func) caches->set(index++, CreateCache(size, func));
+    JSFUNCTION_RESULT_CACHE_LIST(F)
+#undef F
+
+  global_context()->set_jsfunction_result_caches(*caches);
+}


 int BootstrapperActive::nesting_ = 0;
@@ -1664,6 +1703,7 @@
     HookUpGlobalProxy(inner_global, global_proxy);
     InitializeGlobal(inner_global, empty_function);
     if (!InstallNatives()) return;
+    InstallJSFunctionResultCaches();

     MakeFunctionInstancePrototypeWritable();

=======================================
--- /branches/bleeding_edge/src/codegen.h       Tue Apr 13 02:31:03 2010
+++ /branches/bleeding_edge/src/codegen.h       Wed Apr 14 07:46:15 2010
@@ -124,6 +124,7 @@
F(StringCompare, 2, 1) \ F(RegExpExec, 4, 1) \ F(RegExpConstructResult, 3, 1) \ + F(GetFromCache, 2, 1) \ F(NumberToString, 1, 1) \ F(MathPow, 2, 1) \ F(MathSin, 1, 1) \
=======================================
--- /branches/bleeding_edge/src/contexts.h      Tue Apr 13 02:31:03 2010
+++ /branches/bleeding_edge/src/contexts.h      Wed Apr 14 07:46:15 2010
@@ -83,6 +83,7 @@
   V(GET_STACK_TRACE_LINE_INDEX, JSFunction, get_stack_trace_line_fun) \
   V(CONFIGURE_GLOBAL_INDEX, JSFunction, configure_global_fun) \
   V(FUNCTION_CACHE_INDEX, JSObject, function_cache) \
+  V(JSFUNCTION_RESULT_CACHES_INDEX, FixedArray, jsfunction_result_caches) \
   V(RUNTIME_CONTEXT_INDEX, Context, runtime_context) \
V(CALL_AS_FUNCTION_DELEGATE_INDEX, JSFunction, call_as_function_delegate) \
   V(CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, JSFunction, \
@@ -205,6 +206,7 @@
     GET_STACK_TRACE_LINE_INDEX,
     CONFIGURE_GLOBAL_INDEX,
     FUNCTION_CACHE_INDEX,
+    JSFUNCTION_RESULT_CACHES_INDEX,
     RUNTIME_CONTEXT_INDEX,
     CALL_AS_FUNCTION_DELEGATE_INDEX,
     CALL_AS_CONSTRUCTOR_DELEGATE_INDEX,
=======================================
--- /branches/bleeding_edge/src/factory.cc      Mon Mar 22 23:04:44 2010
+++ /branches/bleeding_edge/src/factory.cc      Wed Apr 14 07:46:15 2010
@@ -43,9 +43,11 @@
 }


-Handle<FixedArray> Factory::NewFixedArrayWithHoles(int size) {
+Handle<FixedArray> Factory::NewFixedArrayWithHoles(int size,
+ PretenureFlag pretenure) {
   ASSERT(0 <= size);
-  CALL_HEAP_FUNCTION(Heap::AllocateFixedArrayWithHoles(size), FixedArray);
+  CALL_HEAP_FUNCTION(Heap::AllocateFixedArrayWithHoles(size, pretenure),
+                     FixedArray);
 }


=======================================
--- /branches/bleeding_edge/src/factory.h       Mon Mar 22 23:04:44 2010
+++ /branches/bleeding_edge/src/factory.h       Wed Apr 14 07:46:15 2010
@@ -47,7 +47,9 @@
       PretenureFlag pretenure = NOT_TENURED);

   // Allocate a new fixed array with non-existing entries (the hole).
-  static Handle<FixedArray> NewFixedArrayWithHoles(int size);
+  static Handle<FixedArray> NewFixedArrayWithHoles(
+      int size,
+      PretenureFlag pretenure = NOT_TENURED);

static Handle<NumberDictionary> NewNumberDictionary(int at_least_space_for);

=======================================
--- /branches/bleeding_edge/src/heap.cc Wed Apr 14 00:26:20 2010
+++ /branches/bleeding_edge/src/heap.cc Wed Apr 14 07:46:15 2010
@@ -1670,8 +1670,8 @@

   if (InitializeNumberStringCache()->IsFailure()) return false;

-  // Allocate cache for single character strings.
-  obj = AllocateFixedArray(String::kMaxAsciiCharCode+1, TENURED);
+  // Allocate cache for single character ASCII strings.
+  obj = AllocateFixedArray(String::kMaxAsciiCharCode + 1, TENURED);
   if (obj->IsFailure()) return false;
   set_single_character_string_cache(FixedArray::cast(obj));

@@ -3013,13 +3013,10 @@
 }


-Object* Heap::AllocateFixedArray(int length, PretenureFlag pretenure) {
-  ASSERT(length >= 0);
-  ASSERT(empty_fixed_array()->IsFixedArray());
+Object* Heap::AllocateRawFixedArray(int length, PretenureFlag pretenure) {
   if (length < 0 || length > FixedArray::kMaxLength) {
     return Failure::OutOfMemoryException();
   }
-  if (length == 0) return empty_fixed_array();

   AllocationSpace space =
       (pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
@@ -3053,16 +3050,37 @@
     ASSERT(space == LO_SPACE);
     result = lo_space_->AllocateRawFixedArray(size);
   }
+  return result;
+}
+
+
+static Object* AllocateFixedArrayWithFiller(int length,
+                                            PretenureFlag pretenure,
+                                            Object* filler) {
+  ASSERT(length >= 0);
+  ASSERT(Heap::empty_fixed_array()->IsFixedArray());
+  if (length == 0) return Heap::empty_fixed_array();
+
+  ASSERT(!Heap::InNewSpace(filler));
+  Object* result = Heap::AllocateRawFixedArray(length, pretenure);
   if (result->IsFailure()) return result;

-  // Initialize the object.
-  reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
+  HeapObject::cast(result)->set_map(Heap::fixed_array_map());
   FixedArray* array = FixedArray::cast(result);
   array->set_length(length);
-  ASSERT(!Heap::InNewSpace(undefined_value()));
-  MemsetPointer(array->data_start(), undefined_value(), length);
+  MemsetPointer(array->data_start(), filler, length);
   return array;
 }
+
+
+Object* Heap::AllocateFixedArray(int length, PretenureFlag pretenure) {
+ return AllocateFixedArrayWithFiller(length, pretenure, undefined_value());
+}
+
+
+Object* Heap::AllocateFixedArrayWithHoles(int length, PretenureFlag pretenure) {
+  return AllocateFixedArrayWithFiller(length, pretenure, the_hole_value());
+}


 Object* Heap::AllocateUninitializedFixedArray(int length) {
@@ -3075,22 +3093,6 @@
   FixedArray::cast(obj)->set_length(length);
   return obj;
 }
-
-
-Object* Heap::AllocateFixedArrayWithHoles(int length) {
-  if (length == 0) return empty_fixed_array();
-  Object* result = AllocateRawFixedArray(length);
-  if (!result->IsFailure()) {
-    // Initialize header.
-    reinterpret_cast<Array*>(result)->set_map(fixed_array_map());
-    FixedArray* array = FixedArray::cast(result);
-    array->set_length(length);
-    // Initialize body.
-    ASSERT(!Heap::InNewSpace(the_hole_value()));
-    MemsetPointer(array->data_start(), the_hole_value(), length);
-  }
-  return result;
-}


 Object* Heap::AllocateHashTable(int length, PretenureFlag pretenure) {
=======================================
--- /branches/bleeding_edge/src/heap.h  Wed Apr 14 04:30:34 2010
+++ /branches/bleeding_edge/src/heap.h  Wed Apr 14 07:46:15 2010
@@ -484,7 +484,9 @@
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
   // failed.
   // Please note this does not perform a garbage collection.
-  static Object* AllocateFixedArrayWithHoles(int length);
+  static Object* AllocateFixedArrayWithHoles(
+      int length,
+      PretenureFlag pretenure = NOT_TENURED);

   // AllocateHashTable is identical to AllocateFixedArray except
   // that the resulting object has hash_table_map as map.
@@ -896,8 +898,10 @@
   // Returns the adjusted value.
static inline int AdjustAmountOfExternalAllocatedMemory(int change_in_bytes);

-  // Allocate unitialized fixed array (pretenure == NON_TENURE).
+  // Allocate uninitialized fixed array.
   static Object* AllocateRawFixedArray(int length);
+  static Object* AllocateRawFixedArray(int length,
+                                       PretenureFlag pretenure);

// True if we have reached the allocation limit in the old generation that
   // should force the next GC (caused normally) to be a full one.
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Wed Apr 14 02:25:33 2010 +++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Wed Apr 14 07:46:15 2010
@@ -6626,6 +6626,80 @@
   frame_->Forget(3);
   frame_->Push(eax);
 }
+
+
+class DeferredSearchCache: public DeferredCode {
+ public:
+  DeferredSearchCache(Register dst, Register cache, Register key)
+      : dst_(dst), cache_(cache), key_(key) {
+    set_comment("[ DeferredSearchCache");
+  }
+
+  virtual void Generate();
+
+ private:
+  Register dst_, cache_, key_;
+};
+
+
+void DeferredSearchCache::Generate() {
+  __ push(cache_);
+  __ push(key_);
+  __ CallRuntime(Runtime::kGetFromCache, 2);
+  if (!dst_.is(eax)) {
+    __ mov(dst_, eax);
+  }
+}
+
+
+void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
+  ASSERT_EQ(2, args->length());
+
+  ASSERT_NE(NULL, args->at(0)->AsLiteral());
+  int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
+
+  Handle<FixedArray> jsfunction_result_caches(
+      Top::global_context()->jsfunction_result_caches());
+  if (jsfunction_result_caches->length() <= cache_id) {
+    __ Abort("Attempt to use undefined cache.");
+    frame_->Push(Factory::undefined_value());
+    return;
+  }
+  Handle<FixedArray> cache_obj(
+      FixedArray::cast(jsfunction_result_caches->get(cache_id)));
+
+  Load(args->at(1));
+  Result key = frame_->Pop();
+  key.ToRegister();
+
+  Result cache = allocator()->Allocate();
+  __ mov(cache.reg(), cache_obj);
+
+  Result tmp = allocator()->Allocate();
+
+  DeferredSearchCache* deferred = new DeferredSearchCache(tmp.reg(),
+                                                          cache.reg(),
+                                                          key.reg());
+
+  const int kFingerOffset =
+      FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
+  // tmp.reg() now holds finger offset as a smi.
+  ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+  __ mov(tmp.reg(), FieldOperand(cache.reg(), kFingerOffset));
+  __ cmp(key.reg(), FieldOperand(cache.reg(),
+                                 tmp.reg(),  // as smi
+                                 times_half_pointer_size,
+                                 FixedArray::kHeaderSize));
+  deferred->Branch(not_equal);
+
+  __ mov(tmp.reg(), FieldOperand(cache.reg(),
+                                 tmp.reg(),  // as smi
+                                 times_half_pointer_size,
+                                 kPointerSize + FixedArray::kHeaderSize));
+
+  deferred->BindExit();
+  frame_->Push(&tmp);
+}


 void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.h     Tue Apr 13 02:31:03 2010
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.h     Wed Apr 14 07:46:15 2010
@@ -630,6 +630,9 @@

   void GenerateRegExpConstructResult(ZoneList<Expression*>* args);

+  // Support for fast native caches.
+  void GenerateGetFromCache(ZoneList<Expression*>* args);
+
   // Fast support for number to string.
   void GenerateNumberToString(ZoneList<Expression*>* args);

=======================================
--- /branches/bleeding_edge/src/macros.py       Tue Mar 23 07:47:02 2010
+++ /branches/bleeding_edge/src/macros.py       Wed Apr 14 07:46:15 2010
@@ -83,6 +83,9 @@
 const kMinDate  = -100000000;
 const kMaxDate  = 100000000;

+# Native cache ids.
+const STRING_TO_REGEXP_CACHE_ID = 0;
+
 # Type query macros.
 #
 # Note: We have special support for typeof(foo) === 'bar' in the compiler.
=======================================
--- /branches/bleeding_edge/src/objects.h       Tue Apr 13 02:31:03 2010
+++ /branches/bleeding_edge/src/objects.h       Wed Apr 14 07:46:15 2010
@@ -76,6 +76,7 @@
 //             - MapCache
 //           - Context
 //           - GlobalContext
+//           - JSFunctionResultCache
 //       - String
 //         - SeqString
 //           - SeqAsciiString
@@ -2307,6 +2308,23 @@
 };


+// JSFunctionResultCache caches results of some JSFunction invocation.
+// It is a fixed array with fixed structure:
+//   [0]: factory function
+//   [1]: finger index
+//   [2]: current cache size
+//   [3]: dummy field.
+// The rest of array are key/value pairs.
+class JSFunctionResultCache: public FixedArray {
+ public:
+  static const int kFactoryIndex = 0;
+  static const int kFingerIndex = kFactoryIndex + 1;
+  static const int kCacheSizeIndex = kFingerIndex + 1;
+  static const int kDummyIndex = kCacheSizeIndex + 1;
+  static const int kEntriesIndex = kDummyIndex + 1;
+};
+
+
// ByteArray represents fixed sized byte arrays. Used by the outside world,
 // such as PCRE, and also by the memory allocator and garbage collector to
 // fill in free blocks in the heap.
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Tue Apr 13 04:59:37 2010
+++ /branches/bleeding_edge/src/runtime.cc      Wed Apr 14 07:46:15 2010
@@ -10007,6 +10007,89 @@
 }


+static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
+  ASSERT(index % 2 == 0);  // index of the key
+  ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
+  ASSERT(index < cache_obj->length());
+
+  HandleScope scope;
+
+  Handle<FixedArray> cache(cache_obj);
+  Handle<Object> key(key_obj);
+  Handle<JSFunction> factory(JSFunction::cast(
+        cache->get(JSFunctionResultCache::kFactoryIndex)));
+  // TODO(antonm): consider passing a receiver when constructing a cache.
+  Handle<Object> receiver(Top::global_context()->global());
+
+  Handle<Object> value;
+  {
+    // This handle is nor shared, nor used later, so it's safe.
+    Object** argv[] = { key.location() };
+    bool pending_exception = false;
+    value = Execution::Call(factory,
+                            receiver,
+                            1,
+                            argv,
+                            &pending_exception);
+    if (pending_exception) return Failure::Exception();
+  }
+
+  cache->set(index, *key);
+  cache->set(index + 1, *value);
+  cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
+
+  return *value;
+}
+
+
+static Object* Runtime_GetFromCache(Arguments args) {
+  // This is only called from codegen, so checks might be more lax.
+  CONVERT_CHECKED(FixedArray, cache, args[0]);
+  Object* key = args[1];
+
+  const int finger_index =
+      Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
+
+  Object* o = cache->get(finger_index);
+  if (o == key) {
+    // The fastest case: hit the same place again.
+    return cache->get(finger_index + 1);
+  }
+
+  for (int i = finger_index - 2;
+       i >= JSFunctionResultCache::kEntriesIndex;
+       i -= 2) {
+    o = cache->get(i);
+    if (o == key) {
+      cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
+      return cache->get(i + 1);
+    }
+  }
+
+  const int size =
+ Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
+  ASSERT(size <= cache->length());
+
+  for (int i = size - 2; i > finger_index; i -= 2) {
+    o = cache->get(i);
+    if (o == key) {
+      cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
+      return cache->get(i + 1);
+    }
+  }
+
+  // Cache miss.  If we have spare room, put new data into it, otherwise
+  // evict post finger entry which must be least recently used.
+  if (size < cache->length()) {
+ cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
+    return CacheMiss(cache, size, key);
+  } else {
+    int target_index = (finger_index < cache->length()) ?
+        finger_index + 2 : JSFunctionResultCache::kEntriesIndex;
+    return CacheMiss(cache, target_index, key);
+  }
+}
+
 #ifdef DEBUG
 // ListNatives is ONLY used by the fuzz-natives.js in debug mode
 // Exclude the code in release mode.
=======================================
--- /branches/bleeding_edge/src/runtime.h       Tue Apr 13 02:31:03 2010
+++ /branches/bleeding_edge/src/runtime.h       Wed Apr 14 07:46:15 2010
@@ -288,6 +288,8 @@
   F(LocalKeys, 1, 1) \
   /* Handle scopes */ \
   F(DeleteHandleScopeExtensions, 0, 1) \
+  /* Cache suport */ \
+  F(GetFromCache, 2, 1) \
   \
   /* Pseudo functions - handled as macros by parser */ \
   F(IS_VAR, 1, 1)
=======================================
--- /branches/bleeding_edge/src/string.js       Tue Apr 13 02:31:03 2010
+++ /branches/bleeding_edge/src/string.js       Wed Apr 14 07:46:15 2010
@@ -528,11 +528,17 @@
   parameters[j + 1] = subject;
   return replace.apply(null, parameters);
 }
-

 // ECMA-262 section 15.5.4.12
 function StringSearch(re) {
-  var regexp = new $RegExp(re);
+  var regexp;
+  if (IS_STRING(re)) {
+    regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
+  } else if (IS_REGEXP(re)) {
+    regexp = re;
+  } else {
+    regexp = new $RegExp(re);
+  }
   var s = TO_STRING_INLINE(this);
   var match = DoRegExpExec(regexp, s, 0);
   if (match) {
=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.cc      Wed Apr 14 02:25:33 2010
+++ /branches/bleeding_edge/src/x64/codegen-x64.cc      Wed Apr 14 07:46:15 2010
@@ -4257,6 +4257,82 @@
   frame_->Forget(3);
   frame_->Push(rax);
 }
+
+
+class DeferredSearchCache: public DeferredCode {
+ public:
+  DeferredSearchCache(Register dst, Register cache, Register key)
+      : dst_(dst), cache_(cache), key_(key) {
+    set_comment("[ DeferredSearchCache");
+  }
+
+  virtual void Generate();
+
+ private:
+  Register dst_, cache_, key_;
+};
+
+
+void DeferredSearchCache::Generate() {
+  __ push(cache_);
+  __ push(key_);
+  __ CallRuntime(Runtime::kGetFromCache, 2);
+  if (!dst_.is(rax)) {
+    __ movq(dst_, rax);
+  }
+}
+
+
+void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
+  ASSERT_EQ(2, args->length());
+
+  ASSERT_NE(NULL, args->at(0)->AsLiteral());
+  int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
+
+  Handle<FixedArray> jsfunction_result_caches(
+      Top::global_context()->jsfunction_result_caches());
+  if (jsfunction_result_caches->length() <= cache_id) {
+    __ Abort("Attempt to use undefined cache.");
+    frame_->Push(Factory::undefined_value());
+    return;
+  }
+  Handle<FixedArray> cache_obj(
+      FixedArray::cast(jsfunction_result_caches->get(cache_id)));
+
+  Load(args->at(1));
+  Result key = frame_->Pop();
+  key.ToRegister();
+
+  Result cache = allocator()->Allocate();
+  __ movq(cache.reg(), cache_obj, RelocInfo::EMBEDDED_OBJECT);
+
+  Result tmp = allocator()->Allocate();
+
+  DeferredSearchCache* deferred = new DeferredSearchCache(tmp.reg(),
+                                                          cache.reg(),
+                                                          key.reg());
+
+  const int kFingerOffset =
+      FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
+  // tmp.reg() now holds finger offset as a smi.
+  ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+  __ movq(tmp.reg(), FieldOperand(cache.reg(), kFingerOffset));
+  SmiIndex index =
+      masm()->SmiToIndex(kScratchRegister, tmp.reg(), kPointerSizeLog2);
+  __ cmpq(key.reg(), FieldOperand(cache.reg(),
+                                  index.reg,
+                                  index.scale,
+                                  FixedArray::kHeaderSize));
+  deferred->Branch(not_equal);
+
+  __ movq(tmp.reg(), FieldOperand(cache.reg(),
+                                  index.reg,
+                                  index.scale,
+                                  kPointerSize + FixedArray::kHeaderSize));
+
+  deferred->BindExit();
+  frame_->Push(&tmp);
+}


 void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.h       Tue Apr 13 04:34:14 2010
+++ /branches/bleeding_edge/src/x64/codegen-x64.h       Wed Apr 14 07:46:15 2010
@@ -584,6 +584,9 @@

   void GenerateRegExpConstructResult(ZoneList<Expression*>* args);

+  // Support for fast native caches.
+  void GenerateGetFromCache(ZoneList<Expression*>* args);
+
   // Fast support for number to string.
   void GenerateNumberToString(ZoneList<Expression*>* args);

=======================================
--- /branches/bleeding_edge/test/mjsunit/fuzz-natives.js Tue Apr 13 02:31:03 2010 +++ /branches/bleeding_edge/test/mjsunit/fuzz-natives.js Wed Apr 14 07:46:15 2010
@@ -169,6 +169,10 @@
   "RegExpConstructResult": true,
   "_RegExpConstructResult": true,

+  // This function performs some checks compile time (it requires its first
+  // argument to be a compile time smi).
+  "_GetFromCache": true,
+
   // LiveEdit feature is under development currently and has fragile input.
   "LiveEditFindSharedFunctionInfosForScript": true,
   "LiveEditGatherCompileInfo": true,

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

To unsubscribe, reply using "remove me" as the subject.

Reply via email to