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 60744 : CompilationCache::CompilationCache(Isolate* isolate)
25 : : isolate_(isolate),
26 : script_(isolate),
27 : eval_global_(isolate),
28 : eval_contextual_(isolate),
29 : reg_exp_(isolate, kRegExpGenerations),
30 121488 : enabled_(true) {
31 : CompilationSubCache* subcaches[kSubCacheCount] =
32 60744 : {&script_, &eval_global_, &eval_contextual_, ®_exp_};
33 303720 : for (int i = 0; i < kSubCacheCount; ++i) {
34 242976 : subcaches_[i] = subcaches[i];
35 : }
36 60744 : }
37 :
38 :
39 118518 : CompilationCache::~CompilationCache() {}
40 :
41 :
42 18547328 : Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
43 : DCHECK(generation < generations_);
44 : Handle<CompilationCacheTable> result;
45 18547328 : if (tables_[generation]->IsUndefined(isolate())) {
46 65502 : result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
47 131004 : tables_[generation] = *result;
48 : } else {
49 : CompilationCacheTable* table =
50 9208162 : CompilationCacheTable::cast(tables_[generation]);
51 : result = Handle<CompilationCacheTable>(table, isolate());
52 : }
53 9273664 : return result;
54 : }
55 :
56 :
57 435944 : void CompilationSubCache::Age() {
58 : // Don't directly age single-generation caches.
59 217972 : if (generations_ == 1) {
60 326958 : if (!tables_[0]->IsUndefined(isolate())) {
61 19083 : CompilationCacheTable::cast(tables_[0])->Age();
62 : }
63 217972 : return;
64 : }
65 :
66 : // Age the generations implicitly killing off the oldest.
67 108986 : for (int i = generations_ - 1; i > 0; i--) {
68 54493 : tables_[i] = tables_[i - 1];
69 : }
70 :
71 : // Set the first generation as unborn.
72 108986 : tables_[0] = isolate()->heap()->undefined_value();
73 : }
74 :
75 :
76 213176 : void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
77 213176 : Object* undefined = isolate()->heap()->undefined_value();
78 479646 : for (int i = 0; i < generations_; i++) {
79 266470 : if (tables_[i] != undefined) {
80 18090 : reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
81 : }
82 : }
83 213176 : }
84 :
85 0 : void CompilationSubCache::Iterate(RootVisitor* v) {
86 : v->VisitRootPointers(Root::kCompilationCache, &tables_[0],
87 963932 : &tables_[generations_]);
88 0 : }
89 :
90 :
91 2698368 : void CompilationSubCache::Clear() {
92 5396736 : 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 141713 : 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 200876 : 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 141713 : if (name.is_null()) {
122 59163 : return script->name()->IsUndefined(isolate());
123 : }
124 : // Do the fast bailout checks first.
125 82550 : if (line_offset != script->line_offset()) return false;
126 82542 : if (column_offset != script->column_offset()) return false;
127 : // Check that both names are strings. If not, no match.
128 165084 : if (!name->IsString() || !script->name()->IsString()) return false;
129 : // Are the origin_options same?
130 82542 : 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 82536 : 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 310823 : 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 814400 : { HandleScope scope(isolate());
151 : const int generation = 0;
152 : DCHECK(generations() == 1);
153 310823 : Handle<CompilationCacheTable> table = GetTable(generation);
154 310823 : InfoVectorPair probe = table->LookupScript(source, context, language_mode);
155 310823 : if (probe.has_shared()) {
156 : Handle<SharedFunctionInfo> function_info(probe.shared(), isolate());
157 : Handle<Cell> vector_handle;
158 141713 : 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 141713 : if (HasOrigin(function_info, name, line_offset, column_offset,
164 : resource_options)) {
165 : result = InfoVectorPair(*function_info,
166 141089 : 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 310823 : 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 141089 : isolate()->counters()->compilation_cache_hits()->Increment();
189 : } else {
190 169734 : isolate()->counters()->compilation_cache_misses()->Increment();
191 : }
192 310823 : return result;
193 : }
194 :
195 168303 : void CompilationCacheScript::Put(Handle<String> source, Handle<Context> context,
196 : LanguageMode language_mode,
197 : Handle<SharedFunctionInfo> function_info,
198 : Handle<Cell> literals) {
199 336606 : HandleScope scope(isolate());
200 336606 : Handle<CompilationCacheTable> table = GetFirstTable();
201 : SetFirstTable(CompilationCacheTable::PutScript(
202 168303 : table, source, context, language_mode, function_info, literals));
203 168303 : }
204 :
205 5554379 : InfoVectorPair CompilationCacheEval::Lookup(
206 : Handle<String> source, Handle<SharedFunctionInfo> outer_info,
207 : Handle<Context> native_context, LanguageMode language_mode, int position) {
208 11108758 : 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 5554379 : Handle<CompilationCacheTable> table = GetTable(generation);
216 : result = table->LookupEval(source, outer_info, native_context, language_mode,
217 5554379 : position);
218 5554379 : if (result.has_shared()) {
219 4302212 : isolate()->counters()->compilation_cache_hits()->Increment();
220 : } else {
221 1252167 : isolate()->counters()->compilation_cache_misses()->Increment();
222 : }
223 11108758 : return result;
224 : }
225 :
226 1817488 : 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 3634976 : HandleScope scope(isolate());
232 1817488 : Handle<CompilationCacheTable> table = GetFirstTable();
233 : table =
234 : CompilationCacheTable::PutEval(table, source, outer_info, function_info,
235 1817488 : native_context, literals, position);
236 : SetFirstTable(table);
237 1817488 : }
238 :
239 :
240 712892 : MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
241 : Handle<String> source,
242 : JSRegExp::Flags flags) {
243 2851826 : 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 2852084 : for (generation = 0; generation < generations(); generation++) {
250 1069562 : Handle<CompilationCacheTable> table = GetTable(generation);
251 1069562 : result = table->LookupRegExp(source, flags);
252 1069562 : if (result->IsFixedArray()) break;
253 : }
254 712892 : if (result->IsFixedArray()) {
255 356412 : Handle<FixedArray> data = Handle<FixedArray>::cast(result);
256 356412 : if (generation != 0) {
257 190 : Put(source, flags, data);
258 : }
259 356412 : isolate()->counters()->compilation_cache_hits()->Increment();
260 356412 : return scope.CloseAndEscape(data);
261 : } else {
262 356480 : isolate()->counters()->compilation_cache_misses()->Increment();
263 : return MaybeHandle<FixedArray>();
264 : }
265 : }
266 :
267 :
268 353109 : void CompilationCacheRegExp::Put(Handle<String> source,
269 : JSRegExp::Flags flags,
270 : Handle<FixedArray> data) {
271 706218 : HandleScope scope(isolate());
272 706218 : Handle<CompilationCacheTable> table = GetFirstTable();
273 353109 : SetFirstTable(CompilationCacheTable::PutRegExp(table, source, flags, data));
274 353109 : }
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 343887 : 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 343887 : if (!IsEnabled()) return empty_result;
291 :
292 : return script_.Lookup(source, name, line_offset, column_offset,
293 310823 : resource_options, context, language_mode);
294 : }
295 :
296 5596561 : 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 5596561 : if (!IsEnabled()) return result;
301 :
302 5554379 : if (context->IsNativeContext()) {
303 : result = eval_global_.Lookup(source, outer_info, context, language_mode,
304 2093636 : 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 5554379 : return result;
313 : }
314 :
315 :
316 719976 : MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
317 : JSRegExp::Flags flags) {
318 719976 : if (!IsEnabled()) return MaybeHandle<FixedArray>();
319 :
320 712892 : return reg_exp_.Lookup(source, flags);
321 : }
322 :
323 200660 : void CompilationCache::PutScript(Handle<String> source, Handle<Context> context,
324 : LanguageMode language_mode,
325 : Handle<SharedFunctionInfo> function_info,
326 : Handle<Cell> literals) {
327 401320 : if (!IsEnabled()) return;
328 :
329 168303 : script_.Put(source, context, language_mode, function_info, literals);
330 : }
331 :
332 1859496 : void CompilationCache::PutEval(Handle<String> source,
333 : Handle<SharedFunctionInfo> outer_info,
334 : Handle<Context> context,
335 : Handle<SharedFunctionInfo> function_info,
336 2811181 : Handle<Cell> literals, int position) {
337 3718992 : if (!IsEnabled()) return;
338 :
339 : HandleScope scope(isolate());
340 1817488 : if (context->IsNativeContext()) {
341 : eval_global_.Put(source, outer_info, function_info, context, literals,
342 823795 : 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 993693 : literals, position);
348 : }
349 : }
350 :
351 :
352 :
353 360002 : void CompilationCache::PutRegExp(Handle<String> source,
354 : JSRegExp::Flags flags,
355 : Handle<FixedArray> data) {
356 360002 : if (!IsEnabled()) {
357 360002 : return;
358 : }
359 :
360 352919 : reg_exp_.Put(source, flags, data);
361 : }
362 :
363 :
364 7438 : void CompilationCache::Clear() {
365 2705806 : for (int i = 0; i < kSubCacheCount; i++) {
366 2698368 : subcaches_[i]->Clear();
367 : }
368 7438 : }
369 :
370 240983 : void CompilationCache::Iterate(RootVisitor* v) {
371 1204915 : for (int i = 0; i < kSubCacheCount; i++) {
372 963932 : subcaches_[i]->Iterate(v);
373 : }
374 240983 : }
375 :
376 :
377 53294 : void CompilationCache::IterateFunctions(ObjectVisitor* v) {
378 266470 : for (int i = 0; i < kSubCacheCount; i++) {
379 213176 : subcaches_[i]->IterateFunctions(v);
380 : }
381 53294 : }
382 :
383 :
384 54493 : void CompilationCache::MarkCompactPrologue() {
385 272465 : for (int i = 0; i < kSubCacheCount; i++) {
386 217972 : subcaches_[i]->Age();
387 : }
388 54493 : }
389 :
390 :
391 5237 : void CompilationCache::Enable() {
392 5237 : enabled_ = true;
393 5237 : }
394 :
395 :
396 667154 : void CompilationCache::Disable() {
397 667154 : enabled_ = false;
398 : Clear();
399 667154 : }
400 :
401 :
402 : } // namespace internal
403 : } // namespace v8
|