diff options
Diffstat (limited to 'deps/v8/src/compilation-cache.cc')
-rw-r--r-- | deps/v8/src/compilation-cache.cc | 159 |
1 files changed, 123 insertions, 36 deletions
diff --git a/deps/v8/src/compilation-cache.cc b/deps/v8/src/compilation-cache.cc index 4c02d86ce2..421b6766fe 100644 --- a/deps/v8/src/compilation-cache.cc +++ b/deps/v8/src/compilation-cache.cc @@ -29,15 +29,30 @@ #include "compilation-cache.h" -namespace v8 { namespace internal { +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 }; +// 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_ENTRY_KINDS] = { 0, }; +static Object* tables[NUMBER_OF_TABLE_ENTRIES] = { 0, }; static Handle<CompilationCacheTable> AllocateTable(int size) { @@ -46,14 +61,15 @@ static Handle<CompilationCacheTable> AllocateTable(int size) { } -static Handle<CompilationCacheTable> GetTable(CompilationCache::Entry entry) { +static Handle<CompilationCacheTable> GetTable(int index) { + ASSERT(index >= 0 && index < NUMBER_OF_TABLE_ENTRIES); Handle<CompilationCacheTable> result; - if (tables[entry]->IsUndefined()) { + if (tables[index]->IsUndefined()) { static const int kInitialCacheSize = 64; result = AllocateTable(kInitialCacheSize); - tables[entry] = *result; + tables[index] = *result; } else { - CompilationCacheTable* table = CompilationCacheTable::cast(tables[entry]); + CompilationCacheTable* table = CompilationCacheTable::cast(tables[index]); result = Handle<CompilationCacheTable>(table); } return result; @@ -121,47 +137,80 @@ static bool HasOrigin(Handle<JSFunction> boilerplate, } -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)) { + 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; + + // 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 void* script_histogram = StatsTable::CreateHistogram( + "V8.ScriptCache", + 0, + NUMBER_OF_SCRIPT_GENERATIONS, + NUMBER_OF_SCRIPT_GENERATIONS + 1); + + if (script_histogram != NULL) { + // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss. + StatsTable::AddHistogramSample(script_histogram, generation - SCRIPT); + } + + // Once outside the manacles 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; } Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source, Handle<Context> context, Entry entry) { + if (!IsEnabled()) { + return Handle<JSFunction>::null(); + } + ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL); Handle<JSFunction> result = Lookup(source, context, entry); if (result.is_null()) { @@ -175,6 +224,10 @@ Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source, Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source, JSRegExp::Flags flags) { + if (!IsEnabled()) { + return Handle<FixedArray>::null(); + } + Handle<FixedArray> result = Lookup(source, flags); if (result.is_null()) { Counters::compilation_cache_misses.Increment(); @@ -187,6 +240,10 @@ Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source, void CompilationCache::PutScript(Handle<String> source, Handle<JSFunction> boilerplate) { + if (!IsEnabled()) { + return; + } + HandleScope scope; ASSERT(boilerplate->IsBoilerplate()); Handle<CompilationCacheTable> table = GetTable(SCRIPT); @@ -198,6 +255,10 @@ void CompilationCache::PutEval(Handle<String> source, Handle<Context> context, Entry entry, Handle<JSFunction> boilerplate) { + if (!IsEnabled()) { + return; + } + HandleScope scope; ASSERT(boilerplate->IsBoilerplate()); Handle<CompilationCacheTable> table = GetTable(entry); @@ -209,6 +270,10 @@ void CompilationCache::PutEval(Handle<String> source, void CompilationCache::PutRegExp(Handle<String> source, JSRegExp::Flags flags, Handle<FixedArray> data) { + if (!IsEnabled()) { + return; + } + HandleScope scope; Handle<CompilationCacheTable> table = GetTable(REGEXP); CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data)); @@ -216,14 +281,36 @@ void CompilationCache::PutRegExp(Handle<String> source, 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(); + } +} + + +void CompilationCache::Enable() { + enabled = true; +} + + +void CompilationCache::Disable() { + enabled = false; + Clear(); } |