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