| 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();
|
| - }
|
| }
|
|
|
|
|
|
|