Line data Source code
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/counters.h"
8 : #include "src/factory.h"
9 : #include "src/globals.h"
10 : #include "src/objects-inl.h"
11 : #include "src/objects/compilation-cache-inl.h"
12 : #include "src/visitors.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 :
18 : // The number of generations for each sub cache.
19 : static const int kRegExpGenerations = 2;
20 :
21 : // Initial size of each compilation cache table allocated.
22 : static const int kInitialCacheSize = 64;
23 :
24 60782 : CompilationCache::CompilationCache(Isolate* isolate)
25 : : isolate_(isolate),
26 : script_(isolate),
27 : eval_global_(isolate),
28 : eval_contextual_(isolate),
29 : reg_exp_(isolate, kRegExpGenerations),
30 121564 : enabled_(true) {
31 : CompilationSubCache* subcaches[kSubCacheCount] =
32 60782 : {&script_, &eval_global_, &eval_contextual_, ®_exp_};
33 303910 : for (int i = 0; i < kSubCacheCount; ++i) {
34 243128 : subcaches_[i] = subcaches[i];
35 : }
36 60782 : }
37 :
38 :
39 118570 : CompilationCache::~CompilationCache() {}
40 :
41 :
42 18547116 : Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
43 : DCHECK(generation < generations_);
44 : Handle<CompilationCacheTable> result;
45 18547116 : if (tables_[generation]->IsUndefined(isolate())) {
46 65550 : result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
47 131100 : tables_[generation] = *result;
48 : } else {
49 : CompilationCacheTable* table =
50 9208008 : CompilationCacheTable::cast(tables_[generation]);
51 : result = Handle<CompilationCacheTable>(table, isolate());
52 : }
53 9273558 : return result;
54 : }
55 :
56 :
57 436400 : void CompilationSubCache::Age() {
58 : // Don't directly age single-generation caches.
59 218200 : if (generations_ == 1) {
60 327300 : if (!tables_[0]->IsUndefined(isolate())) {
61 19088 : CompilationCacheTable::cast(tables_[0])->Age();
62 : }
63 218200 : return;
64 : }
65 :
66 : // Age the generations implicitly killing off the oldest.
67 109100 : for (int i = generations_ - 1; i > 0; i--) {
68 54550 : tables_[i] = tables_[i - 1];
69 : }
70 :
71 : // Set the first generation as unborn.
72 109100 : tables_[0] = isolate()->heap()->undefined_value();
73 : }
74 :
75 :
76 213384 : void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
77 213384 : Object* undefined = isolate()->heap()->undefined_value();
78 480114 : for (int i = 0; i < generations_; i++) {
79 266730 : if (tables_[i] != undefined) {
80 18086 : reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
81 : }
82 : }
83 213384 : }
84 :
85 0 : void CompilationSubCache::Iterate(RootVisitor* v) {
86 : v->VisitRootPointers(Root::kCompilationCache, &tables_[0],
87 966340 : &tables_[generations_]);
88 0 : }
89 :
90 :
91 2699824 : void CompilationSubCache::Clear() {
92 5399648 : MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
93 0 : }
94 :
95 :
96 0 : void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
97 : // Probe the script generation tables. Make sure not to leak handles
98 : // into the caller's handle scope.
99 : { HandleScope scope(isolate());
100 0 : for (int generation = 0; generation < generations(); generation++) {
101 0 : Handle<CompilationCacheTable> table = GetTable(generation);
102 0 : table->Remove(*function_info);
103 : }
104 : }
105 0 : }
106 :
107 0 : CompilationCacheScript::CompilationCacheScript(Isolate* isolate)
108 0 : : CompilationSubCache(isolate, 1) {}
109 :
110 : // We only re-use a cached function for some script source code if the
111 : // script originates from the same place. This is to avoid issues
112 : // when reporting errors, etc.
113 141811 : bool CompilationCacheScript::HasOrigin(Handle<SharedFunctionInfo> function_info,
114 : Handle<Object> name, int line_offset,
115 : int column_offset,
116 : ScriptOriginOptions resource_options) {
117 : Handle<Script> script =
118 200974 : Handle<Script>(Script::cast(function_info->script()), isolate());
119 : // If the script name isn't set, the boilerplate script should have
120 : // an undefined name to have the same origin.
121 141811 : if (name.is_null()) {
122 59163 : return script->name()->IsUndefined(isolate());
123 : }
124 : // Do the fast bailout checks first.
125 82648 : if (line_offset != script->line_offset()) return false;
126 82640 : if (column_offset != script->column_offset()) return false;
127 : // Check that both names are strings. If not, no match.
128 165280 : if (!name->IsString() || !script->name()->IsString()) return false;
129 : // Are the origin_options same?
130 82640 : if (resource_options.Flags() != script->origin_options().Flags())
131 : return false;
132 : // Compare the two name strings for equality.
133 : return String::Equals(Handle<String>::cast(name),
134 82634 : Handle<String>(String::cast(script->name())));
135 : }
136 :
137 :
138 : // TODO(245): Need to allow identical code from different contexts to
139 : // be cached in the same script generation. Currently the first use
140 : // will be cached, but subsequent code from different source / line
141 : // won't.
142 311147 : InfoVectorPair CompilationCacheScript::Lookup(
143 : Handle<String> source, Handle<Object> name, int line_offset,
144 : int column_offset, ScriptOriginOptions resource_options,
145 : Handle<Context> context, LanguageMode language_mode) {
146 : InfoVectorPair result;
147 :
148 : // Probe the script generation tables. Make sure not to leak handles
149 : // into the caller's handle scope.
150 815178 : { HandleScope scope(isolate());
151 : const int generation = 0;
152 : DCHECK(generations() == 1);
153 311147 : Handle<CompilationCacheTable> table = GetTable(generation);
154 311147 : InfoVectorPair probe = table->LookupScript(source, context, language_mode);
155 311147 : if (probe.has_shared()) {
156 : Handle<SharedFunctionInfo> function_info(probe.shared(), isolate());
157 : Handle<Cell> vector_handle;
158 141811 : if (probe.has_vector()) {
159 : vector_handle = Handle<Cell>(probe.vector(), isolate());
160 : }
161 : // Break when we've found a suitable shared function info that
162 : // matches the origin.
163 141811 : if (HasOrigin(function_info, name, line_offset, column_offset,
164 : resource_options)) {
165 : result = InfoVectorPair(*function_info,
166 141187 : probe.has_vector() ? *vector_handle : nullptr);
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 311147 : if (result.has_shared()) {
175 : #ifdef DEBUG
176 : // Since HasOrigin can allocate, we need to protect the SharedFunctionInfo
177 : // and the FeedbackVector with handles during the call.
178 : Handle<SharedFunctionInfo> shared(result.shared(), isolate());
179 : Handle<Cell> vector_handle;
180 : if (result.has_vector()) {
181 : vector_handle = Handle<Cell>(result.vector(), isolate());
182 : }
183 : DCHECK(
184 : HasOrigin(shared, name, line_offset, column_offset, resource_options));
185 : result =
186 : InfoVectorPair(*shared, result.has_vector() ? *vector_handle : nullptr);
187 : #endif
188 141187 : isolate()->counters()->compilation_cache_hits()->Increment();
189 : } else {
190 169960 : isolate()->counters()->compilation_cache_misses()->Increment();
191 : }
192 311147 : return result;
193 : }
194 :
195 168529 : void CompilationCacheScript::Put(Handle<String> source, Handle<Context> context,
196 : LanguageMode language_mode,
197 : Handle<SharedFunctionInfo> function_info,
198 : Handle<Cell> literals) {
199 337058 : HandleScope scope(isolate());
200 337058 : Handle<CompilationCacheTable> table = GetFirstTable();
201 : SetFirstTable(CompilationCacheTable::PutScript(
202 168529 : table, source, context, language_mode, function_info, literals));
203 168529 : }
204 :
205 5554356 : InfoVectorPair CompilationCacheEval::Lookup(
206 : Handle<String> source, Handle<SharedFunctionInfo> outer_info,
207 : Handle<Context> native_context, LanguageMode language_mode, int position) {
208 11108712 : HandleScope scope(isolate());
209 : // Make sure not to leak the table into the surrounding handle
210 : // scope. Otherwise, we risk keeping old tables around even after
211 : // having cleared the cache.
212 : InfoVectorPair result;
213 : const int generation = 0;
214 : DCHECK(generations() == 1);
215 5554356 : Handle<CompilationCacheTable> table = GetTable(generation);
216 : result = table->LookupEval(source, outer_info, native_context, language_mode,
217 5554356 : position);
218 5554356 : if (result.has_shared()) {
219 4302215 : isolate()->counters()->compilation_cache_hits()->Increment();
220 : } else {
221 1252141 : isolate()->counters()->compilation_cache_misses()->Increment();
222 : }
223 11108712 : return result;
224 : }
225 :
226 1816841 : void CompilationCacheEval::Put(Handle<String> source,
227 : Handle<SharedFunctionInfo> outer_info,
228 : Handle<SharedFunctionInfo> function_info,
229 : Handle<Context> native_context,
230 : Handle<Cell> literals, int position) {
231 3633682 : HandleScope scope(isolate());
232 1816841 : Handle<CompilationCacheTable> table = GetFirstTable();
233 : table =
234 : CompilationCacheTable::PutEval(table, source, outer_info, function_info,
235 1816841 : native_context, literals, position);
236 : SetFirstTable(table);
237 1816841 : }
238 :
239 :
240 712898 : MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
241 : Handle<String> source,
242 : JSRegExp::Flags flags) {
243 2851851 : HandleScope scope(isolate());
244 : // Make sure not to leak the table into the surrounding handle
245 : // scope. Otherwise, we risk keeping old tables around even after
246 : // having cleared the cache.
247 : Handle<Object> result = isolate()->factory()->undefined_value();
248 : int generation;
249 2852110 : for (generation = 0; generation < generations(); generation++) {
250 1069572 : Handle<CompilationCacheTable> table = GetTable(generation);
251 1069572 : result = table->LookupRegExp(source, flags);
252 1069572 : if (result->IsFixedArray()) break;
253 : }
254 712898 : if (result->IsFixedArray()) {
255 356415 : Handle<FixedArray> data = Handle<FixedArray>::cast(result);
256 356415 : if (generation != 0) {
257 191 : Put(source, flags, data);
258 : }
259 356415 : isolate()->counters()->compilation_cache_hits()->Increment();
260 356415 : return scope.CloseAndEscape(data);
261 : } else {
262 356483 : isolate()->counters()->compilation_cache_misses()->Increment();
263 : return MaybeHandle<FixedArray>();
264 : }
265 : }
266 :
267 :
268 353113 : void CompilationCacheRegExp::Put(Handle<String> source,
269 : JSRegExp::Flags flags,
270 : Handle<FixedArray> data) {
271 706226 : HandleScope scope(isolate());
272 706226 : Handle<CompilationCacheTable> table = GetFirstTable();
273 353113 : SetFirstTable(CompilationCacheTable::PutRegExp(table, source, flags, data));
274 353113 : }
275 :
276 :
277 6966 : void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
278 13932 : if (!IsEnabled()) return;
279 :
280 0 : eval_global_.Remove(function_info);
281 0 : eval_contextual_.Remove(function_info);
282 0 : script_.Remove(function_info);
283 : }
284 :
285 344247 : InfoVectorPair CompilationCache::LookupScript(
286 : Handle<String> source, Handle<Object> name, int line_offset,
287 : int column_offset, ScriptOriginOptions resource_options,
288 : Handle<Context> context, LanguageMode language_mode) {
289 : InfoVectorPair empty_result;
290 344247 : if (!IsEnabled()) return empty_result;
291 :
292 : return script_.Lookup(source, name, line_offset, column_offset,
293 311147 : resource_options, context, language_mode);
294 : }
295 :
296 5596538 : InfoVectorPair CompilationCache::LookupEval(
297 : Handle<String> source, Handle<SharedFunctionInfo> outer_info,
298 3460743 : Handle<Context> context, LanguageMode language_mode, int position) {
299 : InfoVectorPair result;
300 5596538 : if (!IsEnabled()) return result;
301 :
302 5554356 : if (context->IsNativeContext()) {
303 : result = eval_global_.Lookup(source, outer_info, context, language_mode,
304 2093613 : position);
305 : } else {
306 : DCHECK(position != kNoSourcePosition);
307 : Handle<Context> native_context(context->native_context(), isolate());
308 : result = eval_contextual_.Lookup(source, outer_info, native_context,
309 3460743 : language_mode, position);
310 : }
311 :
312 5554356 : return result;
313 : }
314 :
315 :
316 719988 : MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
317 : JSRegExp::Flags flags) {
318 719988 : if (!IsEnabled()) return MaybeHandle<FixedArray>();
319 :
320 712898 : return reg_exp_.Lookup(source, flags);
321 : }
322 :
323 200924 : void CompilationCache::PutScript(Handle<String> source, Handle<Context> context,
324 : LanguageMode language_mode,
325 : Handle<SharedFunctionInfo> function_info,
326 : Handle<Cell> literals) {
327 401848 : if (!IsEnabled()) return;
328 :
329 168529 : script_.Put(source, context, language_mode, function_info, literals);
330 : }
331 :
332 1858849 : void CompilationCache::PutEval(Handle<String> source,
333 : Handle<SharedFunctionInfo> outer_info,
334 : Handle<Context> context,
335 : Handle<SharedFunctionInfo> function_info,
336 2809970 : Handle<Cell> literals, int position) {
337 3717698 : if (!IsEnabled()) return;
338 :
339 : HandleScope scope(isolate());
340 1816841 : if (context->IsNativeContext()) {
341 : eval_global_.Put(source, outer_info, function_info, context, literals,
342 823712 : position);
343 : } else {
344 : DCHECK(position != kNoSourcePosition);
345 : Handle<Context> native_context(context->native_context(), isolate());
346 : eval_contextual_.Put(source, outer_info, function_info, native_context,
347 993129 : literals, position);
348 : }
349 : }
350 :
351 :
352 :
353 360011 : void CompilationCache::PutRegExp(Handle<String> source,
354 : JSRegExp::Flags flags,
355 : Handle<FixedArray> data) {
356 360011 : if (!IsEnabled()) {
357 360011 : return;
358 : }
359 :
360 352922 : reg_exp_.Put(source, flags, data);
361 : }
362 :
363 :
364 7448 : void CompilationCache::Clear() {
365 2707272 : for (int i = 0; i < kSubCacheCount; i++) {
366 2699824 : subcaches_[i]->Clear();
367 : }
368 7448 : }
369 :
370 241585 : void CompilationCache::Iterate(RootVisitor* v) {
371 1207925 : for (int i = 0; i < kSubCacheCount; i++) {
372 966340 : subcaches_[i]->Iterate(v);
373 : }
374 241585 : }
375 :
376 :
377 53346 : void CompilationCache::IterateFunctions(ObjectVisitor* v) {
378 266730 : for (int i = 0; i < kSubCacheCount; i++) {
379 213384 : subcaches_[i]->IterateFunctions(v);
380 : }
381 53346 : }
382 :
383 :
384 54550 : void CompilationCache::MarkCompactPrologue() {
385 272750 : for (int i = 0; i < kSubCacheCount; i++) {
386 218200 : subcaches_[i]->Age();
387 : }
388 54550 : }
389 :
390 :
391 5243 : void CompilationCache::Enable() {
392 5243 : enabled_ = true;
393 5243 : }
394 :
395 :
396 667508 : void CompilationCache::Disable() {
397 667508 : enabled_ = false;
398 : Clear();
399 667508 : }
400 :
401 :
402 : } // namespace internal
403 : } // namespace v8
|