Index: src/compilation-cache.cc |
=================================================================== |
--- src/compilation-cache.cc (revision 1954) |
+++ src/compilation-cache.cc (working copy) |
@@ -32,12 +32,20 @@ |
namespace v8 { namespace internal { |
enum { |
- NUMBER_OF_ENTRY_KINDS = CompilationCache::LAST_ENTRY + 1 |
+ // The number of script generations tell how many GCs a script can |
+ // survive in the compilation cache, before it will be flushed if it |
+ // hasn't been used. |
+ NUMBER_OF_SCRIPT_GENERATIONS = 5, |
+ |
+ // The compilation cache consists of tables - one for each entry |
+ // kind plus extras for the script generations. |
+ NUMBER_OF_TABLE_ENTRIES = |
+ CompilationCache::LAST_ENTRY + NUMBER_OF_SCRIPT_GENERATIONS |
}; |
// Keep separate tables for the different entry kinds. |
-static Object* tables[NUMBER_OF_ENTRY_KINDS] = { 0, }; |
+static Object* tables[NUMBER_OF_TABLE_ENTRIES] = { 0, }; |
static Handle<CompilationCacheTable> AllocateTable(int size) { |
@@ -121,41 +129,52 @@ |
} |
-static Handle<JSFunction> Lookup(Handle<String> source, |
- CompilationCache::Entry entry) { |
- // Make sure not to leak the table into the surrounding handle |
- // scope. Otherwise, we risk keeping old tables around even after |
- // having cleared the cache. |
- Object* result; |
- { HandleScope scope; |
- Handle<CompilationCacheTable> table = GetTable(entry); |
- result = table->Lookup(*source); |
- } |
- if (result->IsJSFunction()) { |
- return Handle<JSFunction>(JSFunction::cast(result)); |
- } else { |
- return Handle<JSFunction>::null(); |
- } |
-} |
- |
- |
-// TODO(245): Need to allow identical code from different contexts to be |
-// cached. Currently the first use will be cached, but subsequent code |
-// from different source / line won't. |
+// TODO(245): Need to allow identical code from different contexts to |
+// be cached in the same script generation. Currently the first use |
+// will be cached, but subsequent code from different source / line |
+// won't. |
Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source, |
Handle<Object> name, |
int line_offset, |
int column_offset) { |
- Handle<JSFunction> result = Lookup(source, SCRIPT); |
- if (result.is_null()) { |
- Counters::compilation_cache_misses.Increment(); |
- } else if (HasOrigin(result, name, line_offset, column_offset)) { |
+ Object* result = NULL; |
+ Entry generation = SCRIPT; // First generation. |
+ |
+ // Probe the script generation tables. Make sure not to leak handles |
+ // into the caller's handle scope. |
+ { HandleScope scope; |
+ while (generation < SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS) { |
+ Handle<CompilationCacheTable> table = GetTable(generation); |
+ Handle<Object> probe(table->Lookup(*source)); |
+ if (probe->IsJSFunction()) { |
+ Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(probe); |
+ // Break when we've found a suitable boilerplate function that |
+ // matches the origin. |
+ if (HasOrigin(boilerplate, name, line_offset, column_offset)) { |
+ result = *boilerplate; |
+ break; |
+ } |
+ } |
+ // Go to the next generation. |
+ generation = static_cast<Entry>(generation + 1); |
+ } |
+ } |
+ |
+ // Once outside the menacles of the handle scope, we need to recheck |
+ // to see if we actually found a cached script. If so, we return a |
+ // handle created in the caller's handle scope. |
+ if (result != NULL) { |
+ Handle<JSFunction> boilerplate(JSFunction::cast(result)); |
+ ASSERT(HasOrigin(boilerplate, name, line_offset, column_offset)); |
+ // If the script was found in a later generation, we promote it to |
+ // the first generation to let it survive longer in the cache. |
+ if (generation != SCRIPT) PutScript(source, boilerplate); |
Counters::compilation_cache_hits.Increment(); |
+ return boilerplate; |
} else { |
- result = Handle<JSFunction>::null(); |
Counters::compilation_cache_misses.Increment(); |
+ return Handle<JSFunction>::null(); |
} |
- return result; |
} |
@@ -216,15 +235,26 @@ |
void CompilationCache::Clear() { |
- for (int i = 0; i < NUMBER_OF_ENTRY_KINDS; i++) { |
+ for (int i = 0; i < NUMBER_OF_TABLE_ENTRIES; i++) { |
tables[i] = Heap::undefined_value(); |
} |
} |
void CompilationCache::Iterate(ObjectVisitor* v) { |
- v->VisitPointers(&tables[0], &tables[NUMBER_OF_ENTRY_KINDS]); |
+ v->VisitPointers(&tables[0], &tables[NUMBER_OF_TABLE_ENTRIES]); |
} |
+void CompilationCache::MarkCompactPrologue() { |
+ ASSERT(LAST_ENTRY == SCRIPT); |
+ for (int i = NUMBER_OF_TABLE_ENTRIES - 1; i > SCRIPT; i--) { |
+ tables[i] = tables[i - 1]; |
+ } |
+ for (int j = 0; j <= LAST_ENTRY; j++) { |
+ tables[j] = Heap::undefined_value(); |
+ } |
+} |
+ |
+ |
} } // namespace v8::internal |