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