• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/compilation-cache.h"
6 
7 #include "src/assembler.h"
8 #include "src/counters.h"
9 #include "src/factory.h"
10 #include "src/objects-inl.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 
16 // The number of generations for each sub cache.
17 static const int kRegExpGenerations = 2;
18 
19 // Initial size of each compilation cache table allocated.
20 static const int kInitialCacheSize = 64;
21 
22 
CompilationCache(Isolate * isolate)23 CompilationCache::CompilationCache(Isolate* isolate)
24     : isolate_(isolate),
25       script_(isolate, 1),
26       eval_global_(isolate, 1),
27       eval_contextual_(isolate, 1),
28       reg_exp_(isolate, kRegExpGenerations),
29       enabled_(true) {
30   CompilationSubCache* subcaches[kSubCacheCount] =
31     {&script_, &eval_global_, &eval_contextual_, &reg_exp_};
32   for (int i = 0; i < kSubCacheCount; ++i) {
33     subcaches_[i] = subcaches[i];
34   }
35 }
36 
37 
~CompilationCache()38 CompilationCache::~CompilationCache() {}
39 
40 
GetTable(int generation)41 Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
42   DCHECK(generation < generations_);
43   Handle<CompilationCacheTable> result;
44   if (tables_[generation]->IsUndefined()) {
45     result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
46     tables_[generation] = *result;
47   } else {
48     CompilationCacheTable* table =
49         CompilationCacheTable::cast(tables_[generation]);
50     result = Handle<CompilationCacheTable>(table, isolate());
51   }
52   return result;
53 }
54 
55 
Age()56 void CompilationSubCache::Age() {
57   // Don't directly age single-generation caches.
58   if (generations_ == 1) {
59     if (tables_[0] != isolate()->heap()->undefined_value()) {
60       CompilationCacheTable::cast(tables_[0])->Age();
61     }
62     return;
63   }
64 
65   // Age the generations implicitly killing off the oldest.
66   for (int i = generations_ - 1; i > 0; i--) {
67     tables_[i] = tables_[i - 1];
68   }
69 
70   // Set the first generation as unborn.
71   tables_[0] = isolate()->heap()->undefined_value();
72 }
73 
74 
IterateFunctions(ObjectVisitor * v)75 void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
76   Object* undefined = isolate()->heap()->undefined_value();
77   for (int i = 0; i < generations_; i++) {
78     if (tables_[i] != undefined) {
79       reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
80     }
81   }
82 }
83 
84 
Iterate(ObjectVisitor * v)85 void CompilationSubCache::Iterate(ObjectVisitor* v) {
86   v->VisitPointers(&tables_[0], &tables_[generations_]);
87 }
88 
89 
Clear()90 void CompilationSubCache::Clear() {
91   MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
92 }
93 
94 
Remove(Handle<SharedFunctionInfo> function_info)95 void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
96   // Probe the script generation tables. Make sure not to leak handles
97   // into the caller's handle scope.
98   { HandleScope scope(isolate());
99     for (int generation = 0; generation < generations(); generation++) {
100       Handle<CompilationCacheTable> table = GetTable(generation);
101       table->Remove(*function_info);
102     }
103   }
104 }
105 
106 
CompilationCacheScript(Isolate * isolate,int generations)107 CompilationCacheScript::CompilationCacheScript(Isolate* isolate,
108                                                int generations)
109     : CompilationSubCache(isolate, generations) {}
110 
111 
112 // We only re-use a cached function for some script source code if the
113 // script originates from the same place. This is to avoid issues
114 // when reporting errors, etc.
HasOrigin(Handle<SharedFunctionInfo> function_info,Handle<Object> name,int line_offset,int column_offset,ScriptOriginOptions resource_options)115 bool CompilationCacheScript::HasOrigin(Handle<SharedFunctionInfo> function_info,
116                                        Handle<Object> name, int line_offset,
117                                        int column_offset,
118                                        ScriptOriginOptions resource_options) {
119   Handle<Script> script =
120       Handle<Script>(Script::cast(function_info->script()), isolate());
121   // If the script name isn't set, the boilerplate script should have
122   // an undefined name to have the same origin.
123   if (name.is_null()) {
124     return script->name()->IsUndefined();
125   }
126   // Do the fast bailout checks first.
127   if (line_offset != script->line_offset()) return false;
128   if (column_offset != script->column_offset()) return false;
129   // Check that both names are strings. If not, no match.
130   if (!name->IsString() || !script->name()->IsString()) return false;
131   // Are the origin_options same?
132   if (resource_options.Flags() != script->origin_options().Flags())
133     return false;
134   // Compare the two name strings for equality.
135   return String::Equals(Handle<String>::cast(name),
136                         Handle<String>(String::cast(script->name())));
137 }
138 
139 
140 // TODO(245): Need to allow identical code from different contexts to
141 // be cached in the same script generation. Currently the first use
142 // will be cached, but subsequent code from different source / line
143 // won't.
Lookup(Handle<String> source,Handle<Object> name,int line_offset,int column_offset,ScriptOriginOptions resource_options,Handle<Context> context,LanguageMode language_mode)144 Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(
145     Handle<String> source, Handle<Object> name, int line_offset,
146     int column_offset, ScriptOriginOptions resource_options,
147     Handle<Context> context, LanguageMode language_mode) {
148   Object* result = NULL;
149   int generation;
150 
151   // Probe the script generation tables. Make sure not to leak handles
152   // into the caller's handle scope.
153   { HandleScope scope(isolate());
154     for (generation = 0; generation < generations(); generation++) {
155       Handle<CompilationCacheTable> table = GetTable(generation);
156       Handle<Object> probe = table->Lookup(source, context, language_mode);
157       if (probe->IsSharedFunctionInfo()) {
158         Handle<SharedFunctionInfo> function_info =
159             Handle<SharedFunctionInfo>::cast(probe);
160         // Break when we've found a suitable shared function info that
161         // matches the origin.
162         if (HasOrigin(function_info, name, line_offset, column_offset,
163                       resource_options)) {
164           result = *function_info;
165           break;
166         }
167       }
168     }
169   }
170 
171   // Once outside the manacles of the handle scope, we need to recheck
172   // to see if we actually found a cached script. If so, we return a
173   // handle created in the caller's handle scope.
174   if (result != NULL) {
175     Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result),
176                                       isolate());
177     DCHECK(
178         HasOrigin(shared, name, line_offset, column_offset, resource_options));
179     // If the script was found in a later generation, we promote it to
180     // the first generation to let it survive longer in the cache.
181     if (generation != 0) Put(source, context, language_mode, shared);
182     isolate()->counters()->compilation_cache_hits()->Increment();
183     return shared;
184   } else {
185     isolate()->counters()->compilation_cache_misses()->Increment();
186     return Handle<SharedFunctionInfo>::null();
187   }
188 }
189 
190 
Put(Handle<String> source,Handle<Context> context,LanguageMode language_mode,Handle<SharedFunctionInfo> function_info)191 void CompilationCacheScript::Put(Handle<String> source,
192                                  Handle<Context> context,
193                                  LanguageMode language_mode,
194                                  Handle<SharedFunctionInfo> function_info) {
195   HandleScope scope(isolate());
196   Handle<CompilationCacheTable> table = GetFirstTable();
197   SetFirstTable(CompilationCacheTable::Put(table, source, context,
198                                            language_mode, function_info));
199 }
200 
201 
Lookup(Handle<String> source,Handle<SharedFunctionInfo> outer_info,LanguageMode language_mode,int scope_position)202 MaybeHandle<SharedFunctionInfo> CompilationCacheEval::Lookup(
203     Handle<String> source, Handle<SharedFunctionInfo> outer_info,
204     LanguageMode language_mode, int scope_position) {
205   HandleScope scope(isolate());
206   // Make sure not to leak the table into the surrounding handle
207   // scope. Otherwise, we risk keeping old tables around even after
208   // having cleared the cache.
209   Handle<Object> result = isolate()->factory()->undefined_value();
210   int generation;
211   for (generation = 0; generation < generations(); generation++) {
212     Handle<CompilationCacheTable> table = GetTable(generation);
213     result =
214         table->LookupEval(source, outer_info, language_mode, scope_position);
215     if (result->IsSharedFunctionInfo()) break;
216   }
217   if (result->IsSharedFunctionInfo()) {
218     Handle<SharedFunctionInfo> function_info =
219         Handle<SharedFunctionInfo>::cast(result);
220     if (generation != 0) {
221       Put(source, outer_info, function_info, scope_position);
222     }
223     isolate()->counters()->compilation_cache_hits()->Increment();
224     return scope.CloseAndEscape(function_info);
225   } else {
226     isolate()->counters()->compilation_cache_misses()->Increment();
227     return MaybeHandle<SharedFunctionInfo>();
228   }
229 }
230 
231 
Put(Handle<String> source,Handle<SharedFunctionInfo> outer_info,Handle<SharedFunctionInfo> function_info,int scope_position)232 void CompilationCacheEval::Put(Handle<String> source,
233                                Handle<SharedFunctionInfo> outer_info,
234                                Handle<SharedFunctionInfo> function_info,
235                                int scope_position) {
236   HandleScope scope(isolate());
237   Handle<CompilationCacheTable> table = GetFirstTable();
238   table = CompilationCacheTable::PutEval(table, source, outer_info,
239                                          function_info, scope_position);
240   SetFirstTable(table);
241 }
242 
243 
Lookup(Handle<String> source,JSRegExp::Flags flags)244 MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
245     Handle<String> source,
246     JSRegExp::Flags flags) {
247   HandleScope scope(isolate());
248   // Make sure not to leak the table into the surrounding handle
249   // scope. Otherwise, we risk keeping old tables around even after
250   // having cleared the cache.
251   Handle<Object> result = isolate()->factory()->undefined_value();
252   int generation;
253   for (generation = 0; generation < generations(); generation++) {
254     Handle<CompilationCacheTable> table = GetTable(generation);
255     result = table->LookupRegExp(source, flags);
256     if (result->IsFixedArray()) break;
257   }
258   if (result->IsFixedArray()) {
259     Handle<FixedArray> data = Handle<FixedArray>::cast(result);
260     if (generation != 0) {
261       Put(source, flags, data);
262     }
263     isolate()->counters()->compilation_cache_hits()->Increment();
264     return scope.CloseAndEscape(data);
265   } else {
266     isolate()->counters()->compilation_cache_misses()->Increment();
267     return MaybeHandle<FixedArray>();
268   }
269 }
270 
271 
Put(Handle<String> source,JSRegExp::Flags flags,Handle<FixedArray> data)272 void CompilationCacheRegExp::Put(Handle<String> source,
273                                  JSRegExp::Flags flags,
274                                  Handle<FixedArray> data) {
275   HandleScope scope(isolate());
276   Handle<CompilationCacheTable> table = GetFirstTable();
277   SetFirstTable(CompilationCacheTable::PutRegExp(table, source, flags, data));
278 }
279 
280 
Remove(Handle<SharedFunctionInfo> function_info)281 void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
282   if (!IsEnabled()) return;
283 
284   eval_global_.Remove(function_info);
285   eval_contextual_.Remove(function_info);
286   script_.Remove(function_info);
287 }
288 
289 
LookupScript(Handle<String> source,Handle<Object> name,int line_offset,int column_offset,ScriptOriginOptions resource_options,Handle<Context> context,LanguageMode language_mode)290 MaybeHandle<SharedFunctionInfo> CompilationCache::LookupScript(
291     Handle<String> source, Handle<Object> name, int line_offset,
292     int column_offset, ScriptOriginOptions resource_options,
293     Handle<Context> context, LanguageMode language_mode) {
294   if (!IsEnabled()) return MaybeHandle<SharedFunctionInfo>();
295 
296   return script_.Lookup(source, name, line_offset, column_offset,
297                         resource_options, context, language_mode);
298 }
299 
300 
LookupEval(Handle<String> source,Handle<SharedFunctionInfo> outer_info,Handle<Context> context,LanguageMode language_mode,int scope_position)301 MaybeHandle<SharedFunctionInfo> CompilationCache::LookupEval(
302     Handle<String> source, Handle<SharedFunctionInfo> outer_info,
303     Handle<Context> context, LanguageMode language_mode, int scope_position) {
304   if (!IsEnabled()) return MaybeHandle<SharedFunctionInfo>();
305 
306   MaybeHandle<SharedFunctionInfo> result;
307   if (context->IsNativeContext()) {
308     result =
309         eval_global_.Lookup(source, outer_info, language_mode, scope_position);
310   } else {
311     DCHECK(scope_position != RelocInfo::kNoPosition);
312     result = eval_contextual_.Lookup(source, outer_info, language_mode,
313                                      scope_position);
314   }
315   return result;
316 }
317 
318 
LookupRegExp(Handle<String> source,JSRegExp::Flags flags)319 MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
320                                                        JSRegExp::Flags flags) {
321   if (!IsEnabled()) return MaybeHandle<FixedArray>();
322 
323   return reg_exp_.Lookup(source, flags);
324 }
325 
326 
PutScript(Handle<String> source,Handle<Context> context,LanguageMode language_mode,Handle<SharedFunctionInfo> function_info)327 void CompilationCache::PutScript(Handle<String> source,
328                                  Handle<Context> context,
329                                  LanguageMode language_mode,
330                                  Handle<SharedFunctionInfo> function_info) {
331   if (!IsEnabled()) return;
332 
333   script_.Put(source, context, language_mode, function_info);
334 }
335 
336 
PutEval(Handle<String> source,Handle<SharedFunctionInfo> outer_info,Handle<Context> context,Handle<SharedFunctionInfo> function_info,int scope_position)337 void CompilationCache::PutEval(Handle<String> source,
338                                Handle<SharedFunctionInfo> outer_info,
339                                Handle<Context> context,
340                                Handle<SharedFunctionInfo> function_info,
341                                int scope_position) {
342   if (!IsEnabled()) return;
343 
344   HandleScope scope(isolate());
345   if (context->IsNativeContext()) {
346     eval_global_.Put(source, outer_info, function_info, scope_position);
347   } else {
348     DCHECK(scope_position != RelocInfo::kNoPosition);
349     eval_contextual_.Put(source, outer_info, function_info, scope_position);
350   }
351 }
352 
353 
354 
PutRegExp(Handle<String> source,JSRegExp::Flags flags,Handle<FixedArray> data)355 void CompilationCache::PutRegExp(Handle<String> source,
356                                  JSRegExp::Flags flags,
357                                  Handle<FixedArray> data) {
358   if (!IsEnabled()) {
359     return;
360   }
361 
362   reg_exp_.Put(source, flags, data);
363 }
364 
365 
Clear()366 void CompilationCache::Clear() {
367   for (int i = 0; i < kSubCacheCount; i++) {
368     subcaches_[i]->Clear();
369   }
370 }
371 
372 
Iterate(ObjectVisitor * v)373 void CompilationCache::Iterate(ObjectVisitor* v) {
374   for (int i = 0; i < kSubCacheCount; i++) {
375     subcaches_[i]->Iterate(v);
376   }
377 }
378 
379 
IterateFunctions(ObjectVisitor * v)380 void CompilationCache::IterateFunctions(ObjectVisitor* v) {
381   for (int i = 0; i < kSubCacheCount; i++) {
382     subcaches_[i]->IterateFunctions(v);
383   }
384 }
385 
386 
MarkCompactPrologue()387 void CompilationCache::MarkCompactPrologue() {
388   for (int i = 0; i < kSubCacheCount; i++) {
389     subcaches_[i]->Age();
390   }
391 }
392 
393 
Enable()394 void CompilationCache::Enable() {
395   enabled_ = true;
396 }
397 
398 
Disable()399 void CompilationCache::Disable() {
400   enabled_ = false;
401   Clear();
402 }
403 
404 
405 }  // namespace internal
406 }  // namespace v8
407