Home | History | Annotate | Download | only in src
      1 // Copyright 2011 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 "assembler.h"
     31 #include "compilation-cache.h"
     32 #include "serialize.h"
     33 
     34 namespace v8 {
     35 namespace internal {
     36 
     37 
     38 // The number of generations for each sub cache.
     39 // The number of ScriptGenerations is carefully chosen based on histograms.
     40 // See issue 458: http://code.google.com/p/v8/issues/detail?id=458
     41 static const int kScriptGenerations = 5;
     42 static const int kEvalGlobalGenerations = 2;
     43 static const int kEvalContextualGenerations = 2;
     44 static const int kRegExpGenerations = 2;
     45 
     46 // Initial size of each compilation cache table allocated.
     47 static const int kInitialCacheSize = 64;
     48 
     49 
     50 CompilationCache::CompilationCache(Isolate* isolate)
     51     : isolate_(isolate),
     52       script_(isolate, kScriptGenerations),
     53       eval_global_(isolate, kEvalGlobalGenerations),
     54       eval_contextual_(isolate, kEvalContextualGenerations),
     55       reg_exp_(isolate, kRegExpGenerations),
     56       enabled_(true) {
     57   CompilationSubCache* subcaches[kSubCacheCount] =
     58     {&script_, &eval_global_, &eval_contextual_, &reg_exp_};
     59   for (int i = 0; i < kSubCacheCount; ++i) {
     60     subcaches_[i] = subcaches[i];
     61   }
     62 }
     63 
     64 
     65 CompilationCache::~CompilationCache() {}
     66 
     67 
     68 static Handle<CompilationCacheTable> AllocateTable(Isolate* isolate, int size) {
     69   CALL_HEAP_FUNCTION(isolate,
     70                      CompilationCacheTable::Allocate(size),
     71                      CompilationCacheTable);
     72 }
     73 
     74 
     75 Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
     76   ASSERT(generation < generations_);
     77   Handle<CompilationCacheTable> result;
     78   if (tables_[generation]->IsUndefined()) {
     79     result = AllocateTable(isolate(), kInitialCacheSize);
     80     tables_[generation] = *result;
     81   } else {
     82     CompilationCacheTable* table =
     83         CompilationCacheTable::cast(tables_[generation]);
     84     result = Handle<CompilationCacheTable>(table, isolate());
     85   }
     86   return result;
     87 }
     88 
     89 void CompilationSubCache::Age() {
     90   // Age the generations implicitly killing off the oldest.
     91   for (int i = generations_ - 1; i > 0; i--) {
     92     tables_[i] = tables_[i - 1];
     93   }
     94 
     95   // Set the first generation as unborn.
     96   tables_[0] = isolate()->heap()->undefined_value();
     97 }
     98 
     99 
    100 void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
    101   Object* undefined = isolate()->heap()->raw_unchecked_undefined_value();
    102   for (int i = 0; i < generations_; i++) {
    103     if (tables_[i] != undefined) {
    104       reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
    105     }
    106   }
    107 }
    108 
    109 
    110 void CompilationSubCache::Iterate(ObjectVisitor* v) {
    111   v->VisitPointers(&tables_[0], &tables_[generations_]);
    112 }
    113 
    114 
    115 void CompilationSubCache::Clear() {
    116   MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
    117 }
    118 
    119 
    120 void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
    121   // Probe the script generation tables. Make sure not to leak handles
    122   // into the caller's handle scope.
    123   { HandleScope scope(isolate());
    124     for (int generation = 0; generation < generations(); generation++) {
    125       Handle<CompilationCacheTable> table = GetTable(generation);
    126       table->Remove(*function_info);
    127     }
    128   }
    129 }
    130 
    131 
    132 CompilationCacheScript::CompilationCacheScript(Isolate* isolate,
    133                                                int generations)
    134     : CompilationSubCache(isolate, generations),
    135       script_histogram_(NULL),
    136       script_histogram_initialized_(false) { }
    137 
    138 
    139 // We only re-use a cached function for some script source code if the
    140 // script originates from the same place. This is to avoid issues
    141 // when reporting errors, etc.
    142 bool CompilationCacheScript::HasOrigin(
    143     Handle<SharedFunctionInfo> function_info,
    144     Handle<Object> name,
    145     int line_offset,
    146     int column_offset) {
    147   Handle<Script> script =
    148       Handle<Script>(Script::cast(function_info->script()), isolate());
    149   // If the script name isn't set, the boilerplate script should have
    150   // an undefined name to have the same origin.
    151   if (name.is_null()) {
    152     return script->name()->IsUndefined();
    153   }
    154   // Do the fast bailout checks first.
    155   if (line_offset != script->line_offset()->value()) return false;
    156   if (column_offset != script->column_offset()->value()) return false;
    157   // Check that both names are strings. If not, no match.
    158   if (!name->IsString() || !script->name()->IsString()) return false;
    159   // Compare the two name strings for equality.
    160   return String::cast(*name)->Equals(String::cast(script->name()));
    161 }
    162 
    163 
    164 // TODO(245): Need to allow identical code from different contexts to
    165 // be cached in the same script generation. Currently the first use
    166 // will be cached, but subsequent code from different source / line
    167 // won't.
    168 Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(Handle<String> source,
    169                                                           Handle<Object> name,
    170                                                           int line_offset,
    171                                                           int column_offset) {
    172   Object* result = NULL;
    173   int generation;
    174 
    175   // Probe the script generation tables. Make sure not to leak handles
    176   // into the caller's handle scope.
    177   { HandleScope scope(isolate());
    178     for (generation = 0; generation < generations(); generation++) {
    179       Handle<CompilationCacheTable> table = GetTable(generation);
    180       Handle<Object> probe(table->Lookup(*source), isolate());
    181       if (probe->IsSharedFunctionInfo()) {
    182         Handle<SharedFunctionInfo> function_info =
    183             Handle<SharedFunctionInfo>::cast(probe);
    184         // Break when we've found a suitable shared function info that
    185         // matches the origin.
    186         if (HasOrigin(function_info, name, line_offset, column_offset)) {
    187           result = *function_info;
    188           break;
    189         }
    190       }
    191     }
    192   }
    193 
    194   if (!script_histogram_initialized_) {
    195     script_histogram_ = isolate()->stats_table()->CreateHistogram(
    196         "V8.ScriptCache",
    197         0,
    198         kScriptGenerations,
    199         kScriptGenerations + 1);
    200     script_histogram_initialized_ = true;
    201   }
    202 
    203   if (script_histogram_ != NULL) {
    204     // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss.
    205     isolate()->stats_table()->AddHistogramSample(script_histogram_, generation);
    206   }
    207 
    208   // Once outside the manacles of the handle scope, we need to recheck
    209   // to see if we actually found a cached script. If so, we return a
    210   // handle created in the caller's handle scope.
    211   if (result != NULL) {
    212     Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result),
    213                                       isolate());
    214     ASSERT(HasOrigin(shared, name, line_offset, column_offset));
    215     // If the script was found in a later generation, we promote it to
    216     // the first generation to let it survive longer in the cache.
    217     if (generation != 0) Put(source, shared);
    218     isolate()->counters()->compilation_cache_hits()->Increment();
    219     return shared;
    220   } else {
    221     isolate()->counters()->compilation_cache_misses()->Increment();
    222     return Handle<SharedFunctionInfo>::null();
    223   }
    224 }
    225 
    226 
    227 MaybeObject* CompilationCacheScript::TryTablePut(
    228     Handle<String> source,
    229     Handle<SharedFunctionInfo> function_info) {
    230   Handle<CompilationCacheTable> table = GetFirstTable();
    231   return table->Put(*source, *function_info);
    232 }
    233 
    234 
    235 Handle<CompilationCacheTable> CompilationCacheScript::TablePut(
    236     Handle<String> source,
    237     Handle<SharedFunctionInfo> function_info) {
    238   CALL_HEAP_FUNCTION(isolate(),
    239                      TryTablePut(source, function_info),
    240                      CompilationCacheTable);
    241 }
    242 
    243 
    244 void CompilationCacheScript::Put(Handle<String> source,
    245                                  Handle<SharedFunctionInfo> function_info) {
    246   HandleScope scope(isolate());
    247   SetFirstTable(TablePut(source, function_info));
    248 }
    249 
    250 
    251 Handle<SharedFunctionInfo> CompilationCacheEval::Lookup(
    252     Handle<String> source,
    253     Handle<Context> context,
    254     LanguageMode language_mode,
    255     int scope_position) {
    256   // Make sure not to leak the table into the surrounding handle
    257   // scope. Otherwise, we risk keeping old tables around even after
    258   // having cleared the cache.
    259   Object* result = NULL;
    260   int generation;
    261   { HandleScope scope(isolate());
    262     for (generation = 0; generation < generations(); generation++) {
    263       Handle<CompilationCacheTable> table = GetTable(generation);
    264       result = table->LookupEval(
    265           *source, *context, language_mode, scope_position);
    266       if (result->IsSharedFunctionInfo()) {
    267         break;
    268       }
    269     }
    270   }
    271   if (result->IsSharedFunctionInfo()) {
    272     Handle<SharedFunctionInfo>
    273         function_info(SharedFunctionInfo::cast(result), isolate());
    274     if (generation != 0) {
    275       Put(source, context, function_info, scope_position);
    276     }
    277     isolate()->counters()->compilation_cache_hits()->Increment();
    278     return function_info;
    279   } else {
    280     isolate()->counters()->compilation_cache_misses()->Increment();
    281     return Handle<SharedFunctionInfo>::null();
    282   }
    283 }
    284 
    285 
    286 MaybeObject* CompilationCacheEval::TryTablePut(
    287     Handle<String> source,
    288     Handle<Context> context,
    289     Handle<SharedFunctionInfo> function_info,
    290     int scope_position) {
    291   Handle<CompilationCacheTable> table = GetFirstTable();
    292   return table->PutEval(*source, *context, *function_info, scope_position);
    293 }
    294 
    295 
    296 Handle<CompilationCacheTable> CompilationCacheEval::TablePut(
    297     Handle<String> source,
    298     Handle<Context> context,
    299     Handle<SharedFunctionInfo> function_info,
    300     int scope_position) {
    301   CALL_HEAP_FUNCTION(isolate(),
    302                      TryTablePut(
    303                          source, context, function_info, scope_position),
    304                      CompilationCacheTable);
    305 }
    306 
    307 
    308 void CompilationCacheEval::Put(Handle<String> source,
    309                                Handle<Context> context,
    310                                Handle<SharedFunctionInfo> function_info,
    311                                int scope_position) {
    312   HandleScope scope(isolate());
    313   SetFirstTable(TablePut(source, context, function_info, scope_position));
    314 }
    315 
    316 
    317 Handle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source,
    318                                                   JSRegExp::Flags flags) {
    319   // Make sure not to leak the table into the surrounding handle
    320   // scope. Otherwise, we risk keeping old tables around even after
    321   // having cleared the cache.
    322   Object* result = NULL;
    323   int generation;
    324   { HandleScope scope(isolate());
    325     for (generation = 0; generation < generations(); generation++) {
    326       Handle<CompilationCacheTable> table = GetTable(generation);
    327       result = table->LookupRegExp(*source, flags);
    328       if (result->IsFixedArray()) {
    329         break;
    330       }
    331     }
    332   }
    333   if (result->IsFixedArray()) {
    334     Handle<FixedArray> data(FixedArray::cast(result), isolate());
    335     if (generation != 0) {
    336       Put(source, flags, data);
    337     }
    338     isolate()->counters()->compilation_cache_hits()->Increment();
    339     return data;
    340   } else {
    341     isolate()->counters()->compilation_cache_misses()->Increment();
    342     return Handle<FixedArray>::null();
    343   }
    344 }
    345 
    346 
    347 MaybeObject* CompilationCacheRegExp::TryTablePut(
    348     Handle<String> source,
    349     JSRegExp::Flags flags,
    350     Handle<FixedArray> data) {
    351   Handle<CompilationCacheTable> table = GetFirstTable();
    352   return table->PutRegExp(*source, flags, *data);
    353 }
    354 
    355 
    356 Handle<CompilationCacheTable> CompilationCacheRegExp::TablePut(
    357     Handle<String> source,
    358     JSRegExp::Flags flags,
    359     Handle<FixedArray> data) {
    360   CALL_HEAP_FUNCTION(isolate(),
    361                      TryTablePut(source, flags, data),
    362                      CompilationCacheTable);
    363 }
    364 
    365 
    366 void CompilationCacheRegExp::Put(Handle<String> source,
    367                                  JSRegExp::Flags flags,
    368                                  Handle<FixedArray> data) {
    369   HandleScope scope(isolate());
    370   SetFirstTable(TablePut(source, flags, data));
    371 }
    372 
    373 
    374 void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
    375   if (!IsEnabled()) return;
    376 
    377   eval_global_.Remove(function_info);
    378   eval_contextual_.Remove(function_info);
    379   script_.Remove(function_info);
    380 }
    381 
    382 
    383 Handle<SharedFunctionInfo> CompilationCache::LookupScript(Handle<String> source,
    384                                                           Handle<Object> name,
    385                                                           int line_offset,
    386                                                           int column_offset) {
    387   if (!IsEnabled()) {
    388     return Handle<SharedFunctionInfo>::null();
    389   }
    390 
    391   return script_.Lookup(source, name, line_offset, column_offset);
    392 }
    393 
    394 
    395 Handle<SharedFunctionInfo> CompilationCache::LookupEval(
    396     Handle<String> source,
    397     Handle<Context> context,
    398     bool is_global,
    399     LanguageMode language_mode,
    400     int scope_position) {
    401   if (!IsEnabled()) {
    402     return Handle<SharedFunctionInfo>::null();
    403   }
    404 
    405   Handle<SharedFunctionInfo> result;
    406   if (is_global) {
    407     result = eval_global_.Lookup(
    408         source, context, language_mode, scope_position);
    409   } else {
    410     ASSERT(scope_position != RelocInfo::kNoPosition);
    411     result = eval_contextual_.Lookup(
    412         source, context, language_mode, scope_position);
    413   }
    414   return result;
    415 }
    416 
    417 
    418 Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
    419                                                   JSRegExp::Flags flags) {
    420   if (!IsEnabled()) {
    421     return Handle<FixedArray>::null();
    422   }
    423 
    424   return reg_exp_.Lookup(source, flags);
    425 }
    426 
    427 
    428 void CompilationCache::PutScript(Handle<String> source,
    429                                  Handle<SharedFunctionInfo> function_info) {
    430   if (!IsEnabled()) {
    431     return;
    432   }
    433 
    434   script_.Put(source, function_info);
    435 }
    436 
    437 
    438 void CompilationCache::PutEval(Handle<String> source,
    439                                Handle<Context> context,
    440                                bool is_global,
    441                                Handle<SharedFunctionInfo> function_info,
    442                                int scope_position) {
    443   if (!IsEnabled()) {
    444     return;
    445   }
    446 
    447   HandleScope scope(isolate());
    448   if (is_global) {
    449     eval_global_.Put(source, context, function_info, scope_position);
    450   } else {
    451     ASSERT(scope_position != RelocInfo::kNoPosition);
    452     eval_contextual_.Put(source, context, function_info, scope_position);
    453   }
    454 }
    455 
    456 
    457 
    458 void CompilationCache::PutRegExp(Handle<String> source,
    459                                  JSRegExp::Flags flags,
    460                                  Handle<FixedArray> data) {
    461   if (!IsEnabled()) {
    462     return;
    463   }
    464 
    465   reg_exp_.Put(source, flags, data);
    466 }
    467 
    468 
    469 void CompilationCache::Clear() {
    470   for (int i = 0; i < kSubCacheCount; i++) {
    471     subcaches_[i]->Clear();
    472   }
    473 }
    474 
    475 
    476 void CompilationCache::Iterate(ObjectVisitor* v) {
    477   for (int i = 0; i < kSubCacheCount; i++) {
    478     subcaches_[i]->Iterate(v);
    479   }
    480 }
    481 
    482 
    483 void CompilationCache::IterateFunctions(ObjectVisitor* v) {
    484   for (int i = 0; i < kSubCacheCount; i++) {
    485     subcaches_[i]->IterateFunctions(v);
    486   }
    487 }
    488 
    489 
    490 void CompilationCache::MarkCompactPrologue() {
    491   for (int i = 0; i < kSubCacheCount; i++) {
    492     subcaches_[i]->Age();
    493   }
    494 }
    495 
    496 
    497 void CompilationCache::Enable() {
    498   enabled_ = true;
    499 }
    500 
    501 
    502 void CompilationCache::Disable() {
    503   enabled_ = false;
    504   Clear();
    505 }
    506 
    507 
    508 } }  // namespace v8::internal
    509