From 3ce873c351a54d2d699df9e2ed4692732184adca Mon Sep 17 00:00:00 2001 From: "sgjesse@chromium.org" Date: Mon, 22 Jun 2009 11:12:51 +0000 Subject: [PATCH] Refactor the handling of generations in the compilation cache. Add generations to the compilation cache for eval and regexp. The number of generations for these are set to two. BUG=none TEST=none Review URL: http://codereview.chromium.org/140056 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2233 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/compilation-cache.cc | 360 ++++++++++++++++++++++++++++----------- src/compilation-cache.h | 17 +- src/compiler.cc | 7 +- 3 files changed, 266 insertions(+), 118 deletions(-) diff --git a/src/compilation-cache.cc b/src/compilation-cache.cc index 421b6766f..fd706af88 100644 --- a/src/compilation-cache.cc +++ b/src/compilation-cache.cc @@ -32,28 +32,123 @@ 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. +static const int kSubCacheCount = 4; + +// The number of generations for each sub cache. +static const int kScriptGenerations = 5; +static const int kEvalGlobalGenerations = 2; +static const int kEvalContextualGenerations = 2; +static const int kRegExpGenerations = 2; + +// Initial of each compilation cache table allocated. +static const int kInitialCacheSize = 64; + +// The compilation cache consists of several generational sub-caches which uses +// this class as a base class. A sub-cache contains a compilation cache tables +// for each generation of the sub-cache. As the same source code string has +// different compiled code for scripts and evals. Internally, we use separate +// sub-caches to avoid getting the wrong kind of result when looking up. +class CompilationSubCache { + public: + explicit CompilationSubCache(int generations): generations_(generations) { + tables_ = NewArray(generations); + } + + // Get the compilation cache tables for a specific generation. + Handle GetTable(int generation); + + // Age the sub-cache by evicting the oldest generation and creating a new + // young generation. + void Age(); + + // GC support. + void Iterate(ObjectVisitor* v); + + // Clear this sub-cache evicting all its content. + void Clear(); + + // Number of generations in this sub-cache. + inline int generations() { return generations_; } + + private: + int generations_; // Number of generations. + Object** tables_; // Compilation cache tables - one for each generation. + + DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationSubCache); }; +// Sub-cache for scripts. +class CompilationCacheScript : public CompilationSubCache { + public: + explicit CompilationCacheScript(int generations) + : CompilationSubCache(generations) { } + + Handle Lookup(Handle source, + Handle name, + int line_offset, + int column_offset); + void Put(Handle source, Handle boilerplate); + + private: + bool HasOrigin(Handle boilerplate, + Handle name, + int line_offset, + int column_offset); + + DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheScript); +}; + + +// Sub-cache for eval scripts. +class CompilationCacheEval: public CompilationSubCache { + public: + explicit CompilationCacheEval(int generations) + : CompilationSubCache(generations) { } + + Handle Lookup(Handle source, Handle context); + + void Put(Handle source, + Handle context, + Handle boilerplate); + + DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEval); +}; + + +// Sub-cache for regular expressions. +class CompilationCacheRegExp: public CompilationSubCache { + public: + explicit CompilationCacheRegExp(int generations) + : CompilationSubCache(generations) { } + + Handle Lookup(Handle source, JSRegExp::Flags flags); + + void Put(Handle source, + JSRegExp::Flags flags, + Handle data); + + DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheRegExp); +}; + + +// Statically allocate all the sub-caches. +static CompilationCacheScript script(kScriptGenerations); +static CompilationCacheEval eval_global(kEvalGlobalGenerations); +static CompilationCacheEval eval_contextual(kEvalContextualGenerations); +static CompilationCacheRegExp reg_exp(kRegExpGenerations); +static CompilationSubCache* subcaches[kSubCacheCount] = + {&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 AllocateTable(int size) { CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size), @@ -61,54 +156,40 @@ static Handle AllocateTable(int size) { } -static Handle GetTable(int index) { - ASSERT(index >= 0 && index < NUMBER_OF_TABLE_ENTRIES); +Handle CompilationSubCache::GetTable(int generation) { + ASSERT(generation < generations_); Handle result; - if (tables[index]->IsUndefined()) { - static const int kInitialCacheSize = 64; + if (tables_[generation]->IsUndefined()) { result = AllocateTable(kInitialCacheSize); - tables[index] = *result; + tables_[generation] = *result; } else { - CompilationCacheTable* table = CompilationCacheTable::cast(tables[index]); + CompilationCacheTable* table = + CompilationCacheTable::cast(tables_[generation]); result = Handle(table); } return result; } -static Handle Lookup(Handle source, - Handle 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 table = GetTable(entry); - result = table->LookupEval(*source, *context); - } - if (result->IsJSFunction()) { - return Handle(JSFunction::cast(result)); - } else { - return Handle::null(); +void CompilationSubCache::Age() { + // Age the generations implicitly killing off the oldest. + for (int i = generations_ - 1; i > 0; i--) { + tables_[i] = tables_[i - 1]; } + + // Set the first generation as unborn. + tables_[0] = Heap::undefined_value(); } -static Handle Lookup(Handle 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 table = GetTable(CompilationCache::REGEXP); - result = table->LookupRegExp(*source, flags); - } - if (result->IsFixedArray()) { - return Handle(FixedArray::cast(result)); - } else { - return Handle::null(); +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(); } } @@ -116,10 +197,10 @@ static Handle Lookup(Handle source, // 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 boilerplate, - Handle name, - int line_offset, - int column_offset) { +bool CompilationCacheScript::HasOrigin(Handle boilerplate, + Handle name, + int line_offset, + int column_offset) { Handle