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_, ®_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