[V8] Introduce a QML compilation mode
[profile/ivi/qtjsbackend.git] / src / 3rdparty / v8 / src / compilation-cache.cc
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