• 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(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 
Age()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 
IterateFunctions(ObjectVisitor * v)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 
Iterate(ObjectVisitor * v)110 void CompilationSubCache::Iterate(ObjectVisitor* v) {
111   v->VisitPointers(&tables_[0], &tables_[generations_]);
112 }
113 
114 
Clear()115 void CompilationSubCache::Clear() {
116   MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
117 }
118 
119 
Remove(Handle<SharedFunctionInfo> function_info)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 
CompilationCacheScript(Isolate * isolate,int generations)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.
HasOrigin(Handle<SharedFunctionInfo> function_info,Handle<Object> name,int line_offset,int column_offset)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.
Lookup(Handle<String> source,Handle<Object> name,int line_offset,int column_offset)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 
TryTablePut(Handle<String> source,Handle<SharedFunctionInfo> function_info)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 
TablePut(Handle<String> source,Handle<SharedFunctionInfo> function_info)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 
Put(Handle<String> source,Handle<SharedFunctionInfo> function_info)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 
Lookup(Handle<String> source,Handle<Context> context,LanguageMode language_mode,int scope_position)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 
TryTablePut(Handle<String> source,Handle<Context> context,Handle<SharedFunctionInfo> function_info,int scope_position)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 
TablePut(Handle<String> source,Handle<Context> context,Handle<SharedFunctionInfo> function_info,int scope_position)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 
Put(Handle<String> source,Handle<Context> context,Handle<SharedFunctionInfo> function_info,int scope_position)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 
Lookup(Handle<String> source,JSRegExp::Flags flags)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 
TryTablePut(Handle<String> source,JSRegExp::Flags flags,Handle<FixedArray> data)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 
TablePut(Handle<String> source,JSRegExp::Flags flags,Handle<FixedArray> data)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 
Put(Handle<String> source,JSRegExp::Flags flags,Handle<FixedArray> data)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 
Remove(Handle<SharedFunctionInfo> function_info)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 
LookupScript(Handle<String> source,Handle<Object> name,int line_offset,int column_offset)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 
LookupEval(Handle<String> source,Handle<Context> context,bool is_global,LanguageMode language_mode,int scope_position)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 
LookupRegExp(Handle<String> source,JSRegExp::Flags flags)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 
PutScript(Handle<String> source,Handle<SharedFunctionInfo> function_info)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 
PutEval(Handle<String> source,Handle<Context> context,bool is_global,Handle<SharedFunctionInfo> function_info,int scope_position)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 
PutRegExp(Handle<String> source,JSRegExp::Flags flags,Handle<FixedArray> data)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 
Clear()469 void CompilationCache::Clear() {
470   for (int i = 0; i < kSubCacheCount; i++) {
471     subcaches_[i]->Clear();
472   }
473 }
474 
475 
Iterate(ObjectVisitor * v)476 void CompilationCache::Iterate(ObjectVisitor* v) {
477   for (int i = 0; i < kSubCacheCount; i++) {
478     subcaches_[i]->Iterate(v);
479   }
480 }
481 
482 
IterateFunctions(ObjectVisitor * v)483 void CompilationCache::IterateFunctions(ObjectVisitor* v) {
484   for (int i = 0; i < kSubCacheCount; i++) {
485     subcaches_[i]->IterateFunctions(v);
486   }
487 }
488 
489 
MarkCompactPrologue()490 void CompilationCache::MarkCompactPrologue() {
491   for (int i = 0; i < kSubCacheCount; i++) {
492     subcaches_[i]->Age();
493   }
494 }
495 
496 
Enable()497 void CompilationCache::Enable() {
498   enabled_ = true;
499 }
500 
501 
Disable()502 void CompilationCache::Disable() {
503   enabled_ = false;
504   Clear();
505 }
506 
507 
508 } }  // namespace v8::internal
509