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