Home | History | Annotate | Download | only in src
      1 // Copyright 2008 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include "v8.h"
     29 
     30 #include "compilation-cache.h"
     31 #include "serialize.h"
     32 
     33 namespace v8 {
     34 namespace internal {
     35 
     36 
     37 // The number of sub caches covering the different types to cache.
     38 static const int kSubCacheCount = 4;
     39 
     40 // The number of generations for each sub cache.
     41 // The number of ScriptGenerations is carefully chosen based on histograms.
     42 // See issue 458: http://code.google.com/p/v8/issues/detail?id=458
     43 static const int kScriptGenerations = 5;
     44 static const int kEvalGlobalGenerations = 2;
     45 static const int kEvalContextualGenerations = 2;
     46 static const int kRegExpGenerations = 2;
     47 
     48 // Initial size of each compilation cache table allocated.
     49 static const int kInitialCacheSize = 64;
     50 
     51 // The compilation cache consists of several generational sub-caches which uses
     52 // this class as a base class. A sub-cache contains a compilation cache tables
     53 // for each generation of the sub-cache. Since the same source code string has
     54 // different compiled code for scripts and evals, we use separate sub-caches
     55 // for different compilation modes, to avoid retrieving the wrong result.
     56 class CompilationSubCache {
     57  public:
     58   explicit CompilationSubCache(int generations): generations_(generations) {
     59     tables_ = NewArray<Object*>(generations);
     60   }
     61 
     62   ~CompilationSubCache() { DeleteArray(tables_); }
     63 
     64   // Get the compilation cache tables for a specific generation.
     65   Handle<CompilationCacheTable> GetTable(int generation);
     66 
     67   // Age the sub-cache by evicting the oldest generation and creating a new
     68   // young generation.
     69   void Age();
     70 
     71   // GC support.
     72   void Iterate(ObjectVisitor* v);
     73 
     74   // Clear this sub-cache evicting all its content.
     75   void Clear();
     76 
     77   // Number of generations in this sub-cache.
     78   inline int generations() { return generations_; }
     79 
     80  private:
     81   int generations_;  // Number of generations.
     82   Object** tables_;  // Compilation cache tables - one for each generation.
     83 
     84   DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationSubCache);
     85 };
     86 
     87 
     88 // Sub-cache for scripts.
     89 class CompilationCacheScript : public CompilationSubCache {
     90  public:
     91   explicit CompilationCacheScript(int generations)
     92       : CompilationSubCache(generations) { }
     93 
     94   Handle<JSFunction> Lookup(Handle<String> source,
     95                             Handle<Object> name,
     96                             int line_offset,
     97                             int column_offset);
     98   void Put(Handle<String> source, Handle<JSFunction> boilerplate);
     99 
    100  private:
    101   bool HasOrigin(Handle<JSFunction> boilerplate,
    102                  Handle<Object> name,
    103                  int line_offset,
    104                  int column_offset);
    105 
    106   DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheScript);
    107 };
    108 
    109 
    110 // Sub-cache for eval scripts.
    111 class CompilationCacheEval: public CompilationSubCache {
    112  public:
    113   explicit CompilationCacheEval(int generations)
    114       : CompilationSubCache(generations) { }
    115 
    116   Handle<JSFunction> Lookup(Handle<String> source, Handle<Context> context);
    117 
    118   void Put(Handle<String> source,
    119            Handle<Context> context,
    120            Handle<JSFunction> boilerplate);
    121 
    122   DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEval);
    123 };
    124 
    125 
    126 // Sub-cache for regular expressions.
    127 class CompilationCacheRegExp: public CompilationSubCache {
    128  public:
    129   explicit CompilationCacheRegExp(int generations)
    130       : CompilationSubCache(generations) { }
    131 
    132   Handle<FixedArray> Lookup(Handle<String> source, JSRegExp::Flags flags);
    133 
    134   void Put(Handle<String> source,
    135            JSRegExp::Flags flags,
    136            Handle<FixedArray> data);
    137 
    138   DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheRegExp);
    139 };
    140 
    141 
    142 // Statically allocate all the sub-caches.
    143 static CompilationCacheScript script(kScriptGenerations);
    144 static CompilationCacheEval eval_global(kEvalGlobalGenerations);
    145 static CompilationCacheEval eval_contextual(kEvalContextualGenerations);
    146 static CompilationCacheRegExp reg_exp(kRegExpGenerations);
    147 static CompilationSubCache* subcaches[kSubCacheCount] =
    148     {&script, &eval_global, &eval_contextual, &reg_exp};
    149 
    150 
    151 // Current enable state of the compilation cache.
    152 static bool enabled = true;
    153 static inline bool IsEnabled() {
    154   return FLAG_compilation_cache && enabled;
    155 }
    156 
    157 
    158 static Handle<CompilationCacheTable> AllocateTable(int size) {
    159   CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size),
    160                      CompilationCacheTable);
    161 }
    162 
    163 
    164 Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
    165   ASSERT(generation < generations_);
    166   Handle<CompilationCacheTable> result;
    167   if (tables_[generation]->IsUndefined()) {
    168     result = AllocateTable(kInitialCacheSize);
    169     tables_[generation] = *result;
    170   } else {
    171     CompilationCacheTable* table =
    172         CompilationCacheTable::cast(tables_[generation]);
    173     result = Handle<CompilationCacheTable>(table);
    174   }
    175   return result;
    176 }
    177 
    178 
    179 void CompilationSubCache::Age() {
    180   // Age the generations implicitly killing off the oldest.
    181   for (int i = generations_ - 1; i > 0; i--) {
    182     tables_[i] = tables_[i - 1];
    183   }
    184 
    185   // Set the first generation as unborn.
    186   tables_[0] = Heap::undefined_value();
    187 }
    188 
    189 
    190 void CompilationSubCache::Iterate(ObjectVisitor* v) {
    191   v->VisitPointers(&tables_[0], &tables_[generations_]);
    192 }
    193 
    194 
    195 void CompilationSubCache::Clear() {
    196   for (int i = 0; i < generations_; i++) {
    197     tables_[i] = Heap::undefined_value();
    198   }
    199 }
    200 
    201 
    202 // We only re-use a cached function for some script source code if the
    203 // script originates from the same place. This is to avoid issues
    204 // when reporting errors, etc.
    205 bool CompilationCacheScript::HasOrigin(Handle<JSFunction> boilerplate,
    206                                        Handle<Object> name,
    207                                        int line_offset,
    208                                        int column_offset) {
    209   Handle<Script> script =
    210       Handle<Script>(Script::cast(boilerplate->shared()->script()));
    211   // If the script name isn't set, the boilerplate script should have
    212   // an undefined name to have the same origin.
    213   if (name.is_null()) {
    214     return script->name()->IsUndefined();
    215   }
    216   // Do the fast bailout checks first.
    217   if (line_offset != script->line_offset()->value()) return false;
    218   if (column_offset != script->column_offset()->value()) return false;
    219   // Check that both names are strings. If not, no match.
    220   if (!name->IsString() || !script->name()->IsString()) return false;
    221   // Compare the two name strings for equality.
    222   return String::cast(*name)->Equals(String::cast(script->name()));
    223 }
    224 
    225 
    226 // TODO(245): Need to allow identical code from different contexts to
    227 // be cached in the same script generation. Currently the first use
    228 // will be cached, but subsequent code from different source / line
    229 // won't.
    230 Handle<JSFunction> CompilationCacheScript::Lookup(Handle<String> source,
    231                                                   Handle<Object> name,
    232                                                   int line_offset,
    233                                                   int column_offset) {
    234   Object* result = NULL;
    235   int generation;
    236 
    237   // Probe the script generation tables. Make sure not to leak handles
    238   // into the caller's handle scope.
    239   { HandleScope scope;
    240     for (generation = 0; generation < generations(); generation++) {
    241       Handle<CompilationCacheTable> table = GetTable(generation);
    242       Handle<Object> probe(table->Lookup(*source));
    243       if (probe->IsJSFunction()) {
    244         Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(probe);
    245         // Break when we've found a suitable boilerplate function that
    246         // matches the origin.
    247         if (HasOrigin(boilerplate, name, line_offset, column_offset)) {
    248           result = *boilerplate;
    249           break;
    250         }
    251       }
    252     }
    253   }
    254 
    255   static void* script_histogram = StatsTable::CreateHistogram(
    256       "V8.ScriptCache",
    257       0,
    258       kScriptGenerations,
    259       kScriptGenerations + 1);
    260 
    261   if (script_histogram != NULL) {
    262     // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss.
    263     StatsTable::AddHistogramSample(script_histogram, generation);
    264   }
    265 
    266   // Once outside the manacles of the handle scope, we need to recheck
    267   // to see if we actually found a cached script. If so, we return a
    268   // handle created in the caller's handle scope.
    269   if (result != NULL) {
    270     Handle<JSFunction> boilerplate(JSFunction::cast(result));
    271     ASSERT(HasOrigin(boilerplate, name, line_offset, column_offset));
    272     // If the script was found in a later generation, we promote it to
    273     // the first generation to let it survive longer in the cache.
    274     if (generation != 0) Put(source, boilerplate);
    275     Counters::compilation_cache_hits.Increment();
    276     return boilerplate;
    277   } else {
    278     Counters::compilation_cache_misses.Increment();
    279     return Handle<JSFunction>::null();
    280   }
    281 }
    282 
    283 
    284 void CompilationCacheScript::Put(Handle<String> source,
    285                                  Handle<JSFunction> boilerplate) {
    286   HandleScope scope;
    287   ASSERT(boilerplate->IsBoilerplate());
    288   Handle<CompilationCacheTable> table = GetTable(0);
    289   CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate));
    290 }
    291 
    292 
    293 Handle<JSFunction> CompilationCacheEval::Lookup(Handle<String> source,
    294                                                 Handle<Context> context) {
    295   // Make sure not to leak the table into the surrounding handle
    296   // scope. Otherwise, we risk keeping old tables around even after
    297   // having cleared the cache.
    298   Object* result = NULL;
    299   int generation;
    300   { HandleScope scope;
    301     for (generation = 0; generation < generations(); generation++) {
    302       Handle<CompilationCacheTable> table = GetTable(generation);
    303       result = table->LookupEval(*source, *context);
    304       if (result->IsJSFunction()) {
    305         break;
    306       }
    307     }
    308   }
    309   if (result->IsJSFunction()) {
    310     Handle<JSFunction> boilerplate(JSFunction::cast(result));
    311     if (generation != 0) {
    312       Put(source, context, boilerplate);
    313     }
    314     Counters::compilation_cache_hits.Increment();
    315     return boilerplate;
    316   } else {
    317     Counters::compilation_cache_misses.Increment();
    318     return Handle<JSFunction>::null();
    319   }
    320 }
    321 
    322 
    323 void CompilationCacheEval::Put(Handle<String> source,
    324                                Handle<Context> context,
    325                                Handle<JSFunction> boilerplate) {
    326   HandleScope scope;
    327   ASSERT(boilerplate->IsBoilerplate());
    328   Handle<CompilationCacheTable> table = GetTable(0);
    329   CALL_HEAP_FUNCTION_VOID(table->PutEval(*source, *context, *boilerplate));
    330 }
    331 
    332 
    333 Handle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source,
    334                                                   JSRegExp::Flags flags) {
    335   // Make sure not to leak the table into the surrounding handle
    336   // scope. Otherwise, we risk keeping old tables around even after
    337   // having cleared the cache.
    338   Object* result = NULL;
    339   int generation;
    340   { HandleScope scope;
    341     for (generation = 0; generation < generations(); generation++) {
    342       Handle<CompilationCacheTable> table = GetTable(generation);
    343       result = table->LookupRegExp(*source, flags);
    344       if (result->IsFixedArray()) {
    345         break;
    346       }
    347     }
    348   }
    349   if (result->IsFixedArray()) {
    350     Handle<FixedArray> data(FixedArray::cast(result));
    351     if (generation != 0) {
    352       Put(source, flags, data);
    353     }
    354     Counters::compilation_cache_hits.Increment();
    355     return data;
    356   } else {
    357     Counters::compilation_cache_misses.Increment();
    358     return Handle<FixedArray>::null();
    359   }
    360 }
    361 
    362 
    363 void CompilationCacheRegExp::Put(Handle<String> source,
    364                                  JSRegExp::Flags flags,
    365                                  Handle<FixedArray> data) {
    366   HandleScope scope;
    367   Handle<CompilationCacheTable> table = GetTable(0);
    368   CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data));
    369 }
    370 
    371 
    372 Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source,
    373                                                   Handle<Object> name,
    374                                                   int line_offset,
    375                                                   int column_offset) {
    376   if (!IsEnabled()) {
    377     return Handle<JSFunction>::null();
    378   }
    379 
    380   return script.Lookup(source, name, line_offset, column_offset);
    381 }
    382 
    383 
    384 Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source,
    385                                                 Handle<Context> context,
    386                                                 bool is_global) {
    387   if (!IsEnabled()) {
    388     return Handle<JSFunction>::null();
    389   }
    390 
    391   Handle<JSFunction> result;
    392   if (is_global) {
    393     result = eval_global.Lookup(source, context);
    394   } else {
    395     result = eval_contextual.Lookup(source, context);
    396   }
    397   return result;
    398 }
    399 
    400 
    401 Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
    402                                                   JSRegExp::Flags flags) {
    403   if (!IsEnabled()) {
    404     return Handle<FixedArray>::null();
    405   }
    406 
    407   return reg_exp.Lookup(source, flags);
    408 }
    409 
    410 
    411 void CompilationCache::PutScript(Handle<String> source,
    412                                  Handle<JSFunction> boilerplate) {
    413   if (!IsEnabled()) {
    414     return;
    415   }
    416 
    417   ASSERT(boilerplate->IsBoilerplate());
    418   script.Put(source, boilerplate);
    419 }
    420 
    421 
    422 void CompilationCache::PutEval(Handle<String> source,
    423                                Handle<Context> context,
    424                                bool is_global,
    425                                Handle<JSFunction> boilerplate) {
    426   if (!IsEnabled()) {
    427     return;
    428   }
    429 
    430   HandleScope scope;
    431   ASSERT(boilerplate->IsBoilerplate());
    432   if (is_global) {
    433     eval_global.Put(source, context, boilerplate);
    434   } else {
    435     eval_contextual.Put(source, context, boilerplate);
    436   }
    437 }
    438 
    439 
    440 
    441 void CompilationCache::PutRegExp(Handle<String> source,
    442                                  JSRegExp::Flags flags,
    443                                  Handle<FixedArray> data) {
    444   if (!IsEnabled()) {
    445     return;
    446   }
    447 
    448   reg_exp.Put(source, flags, data);
    449 }
    450 
    451 
    452 void CompilationCache::Clear() {
    453   for (int i = 0; i < kSubCacheCount; i++) {
    454     subcaches[i]->Clear();
    455   }
    456 }
    457 
    458 
    459 void CompilationCache::Iterate(ObjectVisitor* v) {
    460   for (int i = 0; i < kSubCacheCount; i++) {
    461     subcaches[i]->Iterate(v);
    462   }
    463 }
    464 
    465 
    466 void CompilationCache::MarkCompactPrologue() {
    467   for (int i = 0; i < kSubCacheCount; i++) {
    468     subcaches[i]->Age();
    469   }
    470 }
    471 
    472 
    473 void CompilationCache::Enable() {
    474   enabled = true;
    475 }
    476 
    477 
    478 void CompilationCache::Disable() {
    479   enabled = false;
    480   Clear();
    481 }
    482 
    483 
    484 } }  // namespace v8::internal
    485