Index: src/compilation-cache.cc |
=================================================================== |
--- src/compilation-cache.cc (revision 2226) |
+++ src/compilation-cache.cc (working copy) |
@@ -32,94 +32,88 @@ |
namespace v8 { |
namespace internal { |
-enum { |
- // 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 |
-}; |
+// The number of sub caches covering the different types to cache. |
+#define NUMBER_OF_SUB_CACHES 4 |
+// The number of generations for each sub cache. |
+#define NUMBER_OF_SCRIPT_GENERATIONS 5 |
+#define NUMBER_OF_EVAL_GLOBAL_GENERATIONS 2 |
+#define NUMBER_OF_EVAL_CONTEXTUAL_GENERATIONS 2 |
+#define NUMBER_OF_REGEXP_GENERATIONS 2 |
+ |
+// Statically allocate all the sub-caches. |
+static CompilationCacheScript script(NUMBER_OF_SCRIPT_GENERATIONS); |
+static CompilationCacheEval eval_global(NUMBER_OF_EVAL_GLOBAL_GENERATIONS); |
+static CompilationCacheEval |
+ eval_contextual(NUMBER_OF_EVAL_CONTEXTUAL_GENERATIONS); |
+static CompilationCacheRegExp reg_exp(NUMBER_OF_REGEXP_GENERATIONS); |
+static CompilationSubCache* entry[NUMBER_OF_SUB_CACHES] = |
+ {&script, &eval_global, &eval_contextual, ®_exp}; |
+ |
+ |
// Current enable state of the compilation cache. |
static bool enabled = true; |
static inline bool IsEnabled() { |
return FLAG_compilation_cache && enabled; |
} |
-// Keep separate tables for the different entry kinds. |
-static Object* tables[NUMBER_OF_TABLE_ENTRIES] = { 0, }; |
- |
static Handle<CompilationCacheTable> AllocateTable(int size) { |
CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size), |
CompilationCacheTable); |
} |
-static Handle<CompilationCacheTable> GetTable(int index) { |
- ASSERT(index >= 0 && index < NUMBER_OF_TABLE_ENTRIES); |
+Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) { |
+ ASSERT(generation < generations_); |
Handle<CompilationCacheTable> result; |
- if (tables[index]->IsUndefined()) { |
+ if (tables_[generation]->IsUndefined()) { |
static const int kInitialCacheSize = 64; |
result = AllocateTable(kInitialCacheSize); |
- tables[index] = *result; |
+ tables_[generation] = *result; |
} else { |
- CompilationCacheTable* table = CompilationCacheTable::cast(tables[index]); |
+ CompilationCacheTable* table = |
+ CompilationCacheTable::cast(tables_[generation]); |
result = Handle<CompilationCacheTable>(table); |
} |
return result; |
} |
-static Handle<JSFunction> Lookup(Handle<String> source, |
- Handle<Context> context, |
- 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->LookupEval(*source, *context); |
+void CompilationSubCache::Age() { |
+ // Age the generations implicitly killing off the oldest. |
+ for (int generation = generations_ - 1; |
+ generation > 0; |
+ generation--) { |
+ tables_[generation] = tables_[generation - 1]; |
} |
- if (result->IsJSFunction()) { |
- return Handle<JSFunction>(JSFunction::cast(result)); |
- } else { |
- return Handle<JSFunction>::null(); |
- } |
+ |
+ // Set the first generation as unborn. |
+ tables_[0] = Heap::undefined_value(); |
} |
-static Handle<FixedArray> Lookup(Handle<String> source, |
- JSRegExp::Flags flags) { |
- // 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(CompilationCache::REGEXP); |
- result = table->LookupRegExp(*source, flags); |
+void CompilationSubCache::Iterate(ObjectVisitor* v) { |
+ v->VisitPointers(&tables_[0], &tables_[generations_]); |
+} |
+ |
+ |
+void CompilationSubCache::Clear() { |
+ for (int i = 0; i < generations_; i++) { |
+ tables_[i] = Heap::undefined_value(); |
} |
- if (result->IsFixedArray()) { |
- return Handle<FixedArray>(FixedArray::cast(result)); |
- } else { |
- return Handle<FixedArray>::null(); |
- } |
} |
// We only re-use a cached function for some script source code if the |
// script originates from the same place. This is to avoid issues |
// when reporting errors, etc. |
-static bool HasOrigin(Handle<JSFunction> boilerplate, |
- Handle<Object> name, |
- int line_offset, |
- int column_offset) { |
+bool CompilationCacheScript::HasOrigin(Handle<JSFunction> boilerplate, |
+ Handle<Object> name, |
+ int line_offset, |
+ int column_offset) { |
Handle<Script> script = |
Handle<Script>(Script::cast(boilerplate->shared()->script())); |
// If the script name isn't set, the boilerplate script should have |
@@ -141,24 +135,17 @@ |
// 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<JSFunction> CompilationCacheScript::Lookup(Handle<String> source, |
Handle<Object> name, |
int line_offset, |
int column_offset) { |
- if (!IsEnabled()) { |
- return Handle<JSFunction>::null(); |
- } |
- |
- // Use an int for the generation index, so value range propagation |
- // in gcc 4.3+ won't assume it can only go up to LAST_ENTRY when in |
- // fact it can go up to SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS. |
- int generation = SCRIPT; |
Object* result = NULL; |
+ int 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) { |
+ for (generation = 0; generation < generations(); generation++) { |
Handle<CompilationCacheTable> table = GetTable(generation); |
Handle<Object> probe(table->Lookup(*source)); |
if (probe->IsJSFunction()) { |
@@ -170,8 +157,6 @@ |
break; |
} |
} |
- // Go to the next generation. |
- generation++; |
} |
} |
@@ -183,7 +168,7 @@ |
if (script_histogram != NULL) { |
// The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss. |
- StatsTable::AddHistogramSample(script_histogram, generation - SCRIPT); |
+ StatsTable::AddHistogramSample(script_histogram, generation); |
} |
// Once outside the manacles of the handle scope, we need to recheck |
@@ -194,7 +179,7 @@ |
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); |
+ if (generation != 0) Put(source, boilerplate); |
Counters::compilation_cache_hits.Increment(); |
return boilerplate; |
} else { |
@@ -204,19 +189,118 @@ |
} |
+void CompilationCacheScript::Put(Handle<String> source, |
+ Handle<JSFunction> boilerplate) { |
+ HandleScope scope; |
+ ASSERT(boilerplate->IsBoilerplate()); |
+ Handle<CompilationCacheTable> table = GetTable(0); |
+ CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate)); |
+} |
+ |
+ |
+Handle<JSFunction> CompilationCacheEval::Lookup(Handle<String> source, |
+ Handle<Context> context) { |
+ // 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 = NULL; |
+ int generation; |
+ { HandleScope scope; |
+ for (generation = 0; generation < generations(); generation++) { |
+ Handle<CompilationCacheTable> table = GetTable(generation); |
+ result = table->LookupEval(*source, *context); |
+ if (result->IsJSFunction()) { |
+ break; |
+ } |
+ } |
+ } |
+ if (result->IsJSFunction()) { |
+ Handle<JSFunction> boilerplate(JSFunction::cast(result)); |
+ if (generation != 0) { |
+ Put(source, context, boilerplate); |
+ } |
+ Counters::compilation_cache_hits.Increment(); |
+ return boilerplate; |
+ } else { |
+ Counters::compilation_cache_misses.Increment(); |
+ return Handle<JSFunction>::null(); |
+ } |
+} |
+ |
+ |
+void CompilationCacheEval::Put(Handle<String> source, |
+ Handle<Context> context, |
+ Handle<JSFunction> boilerplate) { |
+ HandleScope scope; |
+ ASSERT(boilerplate->IsBoilerplate()); |
+ Handle<CompilationCacheTable> table = GetTable(0); |
+ CALL_HEAP_FUNCTION_VOID(table->PutEval(*source, *context, *boilerplate)); |
+} |
+ |
+ |
+Handle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source, |
+ JSRegExp::Flags flags) { |
+ // 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 = NULL; |
+ int generation; |
+ { HandleScope scope; |
+ for (generation = 0; generation < generations(); generation++) { |
+ Handle<CompilationCacheTable> table = GetTable(generation); |
+ result = table->LookupRegExp(*source, flags); |
+ if (result->IsFixedArray()) { |
+ break; |
+ } |
+ } |
+ } |
+ if (result->IsFixedArray()) { |
+ Handle<FixedArray> data(FixedArray::cast(result)); |
+ if (generation != 0) { |
+ Put(source, flags, data); |
+ } |
+ Counters::compilation_cache_hits.Increment(); |
+ return data; |
+ } else { |
+ Counters::compilation_cache_misses.Increment(); |
+ return Handle<FixedArray>::null(); |
+ } |
+} |
+ |
+ |
+void CompilationCacheRegExp::Put(Handle<String> source, |
+ JSRegExp::Flags flags, |
+ Handle<FixedArray> data) { |
+ HandleScope scope; |
+ Handle<CompilationCacheTable> table = GetTable(0); |
+ CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data)); |
+} |
+ |
+ |
+Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source, |
+ Handle<Object> name, |
+ int line_offset, |
+ int column_offset) { |
+ if (!IsEnabled()) { |
+ return Handle<JSFunction>::null(); |
+ } |
+ |
+ return script.Lookup(source, name, line_offset, column_offset); |
+} |
+ |
+ |
Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source, |
Handle<Context> context, |
- Entry entry) { |
+ bool is_global) { |
if (!IsEnabled()) { |
return Handle<JSFunction>::null(); |
} |
- ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL); |
- Handle<JSFunction> result = Lookup(source, context, entry); |
- if (result.is_null()) { |
- Counters::compilation_cache_misses.Increment(); |
+ Handle<JSFunction> result; |
+ if (is_global) { |
+ result = eval_global.Lookup(source, context); |
} else { |
- Counters::compilation_cache_hits.Increment(); |
+ result = eval_contextual.Lookup(source, context); |
} |
return result; |
} |
@@ -228,13 +312,7 @@ |
return Handle<FixedArray>::null(); |
} |
- Handle<FixedArray> result = Lookup(source, flags); |
- if (result.is_null()) { |
- Counters::compilation_cache_misses.Increment(); |
- } else { |
- Counters::compilation_cache_hits.Increment(); |
- } |
- return result; |
+ return reg_exp.Lookup(source, flags); |
} |
@@ -244,16 +322,14 @@ |
return; |
} |
- HandleScope scope; |
ASSERT(boilerplate->IsBoilerplate()); |
- Handle<CompilationCacheTable> table = GetTable(SCRIPT); |
- CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate)); |
+ script.Put(source, boilerplate); |
} |
void CompilationCache::PutEval(Handle<String> source, |
Handle<Context> context, |
- Entry entry, |
+ bool is_global, |
Handle<JSFunction> boilerplate) { |
if (!IsEnabled()) { |
return; |
@@ -261,8 +337,11 @@ |
HandleScope scope; |
ASSERT(boilerplate->IsBoilerplate()); |
- Handle<CompilationCacheTable> table = GetTable(entry); |
- CALL_HEAP_FUNCTION_VOID(table->PutEval(*source, *context, *boilerplate)); |
+ if (is_global) { |
+ eval_global.Put(source, context, boilerplate); |
+ } else { |
+ eval_contextual.Put(source, context, boilerplate); |
+ } |
} |
@@ -274,32 +353,28 @@ |
return; |
} |
- HandleScope scope; |
- Handle<CompilationCacheTable> table = GetTable(REGEXP); |
- CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data)); |
+ reg_exp.Put(source, flags, data); |
} |
void CompilationCache::Clear() { |
- for (int i = 0; i < NUMBER_OF_TABLE_ENTRIES; i++) { |
- tables[i] = Heap::undefined_value(); |
+ for (int i = 0; i < NUMBER_OF_SUB_CACHES; i++) { |
+ entry[i]->Clear(); |
} |
} |
void CompilationCache::Iterate(ObjectVisitor* v) { |
- v->VisitPointers(&tables[0], &tables[NUMBER_OF_TABLE_ENTRIES]); |
+ for (int i = 0; i < NUMBER_OF_SUB_CACHES; i++) { |
+ entry[i]->Iterate(v); |
+ } |
} |
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 i = 0; i < NUMBER_OF_SUB_CACHES; i++) { |
+ entry[i]->Age(); |
} |
- for (int j = 0; j <= LAST_ENTRY; j++) { |
- tables[j] = Heap::undefined_value(); |
- } |
} |