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/globals.h"
9 : #include "src/heap/factory.h"
10 : #include "src/log.h"
11 : #include "src/objects-inl.h"
12 : #include "src/objects/compilation-cache-inl.h"
13 : #include "src/objects/slots.h"
14 : #include "src/visitors.h"
15 :
16 : namespace v8 {
17 : namespace internal {
18 :
19 : // The number of generations for each sub cache.
20 : static const int kRegExpGenerations = 2;
21 :
22 : // Initial size of each compilation cache table allocated.
23 : static const int kInitialCacheSize = 64;
24 :
25 62442 : CompilationCache::CompilationCache(Isolate* isolate)
26 : : isolate_(isolate),
27 : script_(isolate),
28 : eval_global_(isolate),
29 : eval_contextual_(isolate),
30 : reg_exp_(isolate, kRegExpGenerations),
31 124884 : enabled_(true) {
32 : CompilationSubCache* subcaches[kSubCacheCount] =
33 62442 : {&script_, &eval_global_, &eval_contextual_, ®_exp_};
34 561978 : for (int i = 0; i < kSubCacheCount; ++i) {
35 249768 : subcaches_[i] = subcaches[i];
36 : }
37 62442 : }
38 :
39 6134987 : Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
40 : DCHECK(generation < generations_);
41 : Handle<CompilationCacheTable> result;
42 12269974 : if (tables_[generation]->IsUndefined(isolate())) {
43 62643 : result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
44 125286 : tables_[generation] = *result;
45 : } else {
46 : CompilationCacheTable table =
47 : CompilationCacheTable::cast(tables_[generation]);
48 : result = Handle<CompilationCacheTable>(table, isolate());
49 : }
50 6134987 : return result;
51 : }
52 :
53 384676 : void CompilationSubCache::Age() {
54 : // Don't directly age single-generation caches.
55 384676 : if (generations_ == 1) {
56 577014 : if (!tables_[0]->IsUndefined(isolate())) {
57 62272 : CompilationCacheTable::cast(tables_[0])->Age();
58 : }
59 : return;
60 : }
61 :
62 : // Age the generations implicitly killing off the oldest.
63 192338 : for (int i = generations_ - 1; i > 0; i--) {
64 96169 : tables_[i] = tables_[i - 1];
65 : }
66 :
67 : // Set the first generation as unborn.
68 192338 : tables_[0] = ReadOnlyRoots(isolate()).undefined_value();
69 : }
70 :
71 0 : void CompilationSubCache::Iterate(RootVisitor* v) {
72 2204312 : v->VisitRootPointers(Root::kCompilationCache, nullptr,
73 : FullObjectSlot(&tables_[0]),
74 2204312 : FullObjectSlot(&tables_[generations_]));
75 0 : }
76 :
77 0 : void CompilationSubCache::Clear() {
78 44224 : MemsetPointer(reinterpret_cast<Address*>(tables_),
79 : ReadOnlyRoots(isolate()).undefined_value()->ptr(),
80 22112 : generations_);
81 0 : }
82 :
83 804 : void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
84 : // Probe the script generation tables. Make sure not to leak handles
85 : // into the caller's handle scope.
86 : { HandleScope scope(isolate());
87 2412 : for (int generation = 0; generation < generations(); generation++) {
88 804 : Handle<CompilationCacheTable> table = GetTable(generation);
89 1608 : table->Remove(*function_info);
90 : }
91 : }
92 804 : }
93 :
94 0 : CompilationCacheScript::CompilationCacheScript(Isolate* isolate)
95 0 : : CompilationSubCache(isolate, 1) {}
96 :
97 : // We only re-use a cached function for some script source code if the
98 : // script originates from the same place. This is to avoid issues
99 : // when reporting errors, etc.
100 131505 : bool CompilationCacheScript::HasOrigin(Handle<SharedFunctionInfo> function_info,
101 : MaybeHandle<Object> maybe_name,
102 : int line_offset, int column_offset,
103 : ScriptOriginOptions resource_options) {
104 : Handle<Script> script =
105 263010 : Handle<Script>(Script::cast(function_info->script()), isolate());
106 : // If the script name isn't set, the boilerplate script should have
107 : // an undefined name to have the same origin.
108 : Handle<Object> name;
109 131505 : if (!maybe_name.ToHandle(&name)) {
110 : return script->name()->IsUndefined(isolate());
111 : }
112 : // Do the fast bailout checks first.
113 63983 : if (line_offset != script->line_offset()) return false;
114 63977 : if (column_offset != script->column_offset()) return false;
115 : // Check that both names are strings. If not, no match.
116 127954 : if (!name->IsString() || !script->name()->IsString()) return false;
117 : // Are the origin_options same?
118 63977 : if (resource_options.Flags() != script->origin_options().Flags())
119 : return false;
120 : // Compare the two name strings for equality.
121 63967 : return String::Equals(
122 : isolate(), Handle<String>::cast(name),
123 63967 : Handle<String>(String::cast(script->name()), isolate()));
124 : }
125 :
126 : // TODO(245): Need to allow identical code from different contexts to
127 : // be cached in the same script generation. Currently the first use
128 : // will be cached, but subsequent code from different source / line
129 : // won't.
130 272078 : MaybeHandle<SharedFunctionInfo> CompilationCacheScript::Lookup(
131 : Handle<String> source, MaybeHandle<Object> name, int line_offset,
132 : int column_offset, ScriptOriginOptions resource_options,
133 : Handle<Context> native_context, LanguageMode language_mode) {
134 : MaybeHandle<SharedFunctionInfo> result;
135 :
136 : // Probe the script generation tables. Make sure not to leak handles
137 : // into the caller's handle scope.
138 : { HandleScope scope(isolate());
139 : const int generation = 0;
140 : DCHECK_EQ(generations(), 1);
141 272078 : Handle<CompilationCacheTable> table = GetTable(generation);
142 : MaybeHandle<SharedFunctionInfo> probe = CompilationCacheTable::LookupScript(
143 272078 : table, source, native_context, language_mode);
144 : Handle<SharedFunctionInfo> function_info;
145 272078 : if (probe.ToHandle(&function_info)) {
146 : // Break when we've found a suitable shared function info that
147 : // matches the origin.
148 131505 : if (HasOrigin(function_info, name, line_offset, column_offset,
149 : resource_options)) {
150 131025 : result = scope.CloseAndEscape(function_info);
151 : }
152 : }
153 : }
154 :
155 : // Once outside the manacles of the handle scope, we need to recheck
156 : // to see if we actually found a cached script. If so, we return a
157 : // handle created in the caller's handle scope.
158 : Handle<SharedFunctionInfo> function_info;
159 272078 : if (result.ToHandle(&function_info)) {
160 : #ifdef DEBUG
161 : // Since HasOrigin can allocate, we need to protect the SharedFunctionInfo
162 : // with handles during the call.
163 : DCHECK(HasOrigin(function_info, name, line_offset, column_offset,
164 : resource_options));
165 : #endif
166 131025 : isolate()->counters()->compilation_cache_hits()->Increment();
167 131025 : LOG(isolate(), CompilationCacheEvent("hit", "script", *function_info));
168 : } else {
169 141053 : isolate()->counters()->compilation_cache_misses()->Increment();
170 : }
171 272078 : return result;
172 : }
173 :
174 138860 : void CompilationCacheScript::Put(Handle<String> source,
175 : Handle<Context> native_context,
176 : LanguageMode language_mode,
177 : Handle<SharedFunctionInfo> function_info) {
178 : HandleScope scope(isolate());
179 277720 : Handle<CompilationCacheTable> table = GetFirstTable();
180 138860 : SetFirstTable(CompilationCacheTable::PutScript(table, source, native_context,
181 : language_mode, function_info));
182 138860 : }
183 :
184 3526571 : InfoCellPair CompilationCacheEval::Lookup(Handle<String> source,
185 : Handle<SharedFunctionInfo> outer_info,
186 : Handle<Context> native_context,
187 : LanguageMode language_mode,
188 : int position) {
189 : HandleScope scope(isolate());
190 : // Make sure not to leak the table into the surrounding handle
191 : // scope. Otherwise, we risk keeping old tables around even after
192 : // having cleared the cache.
193 : InfoCellPair result;
194 : const int generation = 0;
195 : DCHECK_EQ(generations(), 1);
196 3526571 : Handle<CompilationCacheTable> table = GetTable(generation);
197 : result = CompilationCacheTable::LookupEval(
198 3526571 : table, source, outer_info, native_context, language_mode, position);
199 3526571 : if (result.has_shared()) {
200 2593390 : isolate()->counters()->compilation_cache_hits()->Increment();
201 : } else {
202 933181 : isolate()->counters()->compilation_cache_misses()->Increment();
203 : }
204 3526571 : return result;
205 : }
206 :
207 1188860 : void CompilationCacheEval::Put(Handle<String> source,
208 : Handle<SharedFunctionInfo> outer_info,
209 : Handle<SharedFunctionInfo> function_info,
210 : Handle<Context> native_context,
211 : Handle<FeedbackCell> feedback_cell,
212 : int position) {
213 : HandleScope scope(isolate());
214 1188860 : Handle<CompilationCacheTable> table = GetFirstTable();
215 : table =
216 : CompilationCacheTable::PutEval(table, source, outer_info, function_info,
217 1188860 : native_context, feedback_cell, position);
218 : SetFirstTable(table);
219 1188860 : }
220 :
221 459168 : MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
222 : Handle<String> source,
223 : JSRegExp::Flags flags) {
224 : HandleScope scope(isolate());
225 : // Make sure not to leak the table into the surrounding handle
226 : // scope. Otherwise, we risk keeping old tables around even after
227 : // having cleared the cache.
228 : Handle<Object> result = isolate()->factory()->undefined_value();
229 : int generation;
230 1517092 : for (generation = 0; generation < generations(); generation++) {
231 734904 : Handle<CompilationCacheTable> table = GetTable(generation);
232 734904 : result = table->LookupRegExp(source, flags);
233 734904 : if (result->IsFixedArray()) break;
234 : }
235 459168 : if (result->IsFixedArray()) {
236 205942 : Handle<FixedArray> data = Handle<FixedArray>::cast(result);
237 205942 : if (generation != 0) {
238 22510 : Put(source, flags, data);
239 : }
240 205942 : isolate()->counters()->compilation_cache_hits()->Increment();
241 205942 : return scope.CloseAndEscape(data);
242 : } else {
243 253226 : isolate()->counters()->compilation_cache_misses()->Increment();
244 253226 : return MaybeHandle<FixedArray>();
245 : }
246 : }
247 :
248 272910 : void CompilationCacheRegExp::Put(Handle<String> source,
249 : JSRegExp::Flags flags,
250 : Handle<FixedArray> data) {
251 : HandleScope scope(isolate());
252 545820 : Handle<CompilationCacheTable> table = GetFirstTable();
253 272910 : SetFirstTable(
254 : CompilationCacheTable::PutRegExp(isolate(), table, source, flags, data));
255 272910 : }
256 :
257 1819 : void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
258 1819 : if (!IsEnabled()) return;
259 :
260 268 : eval_global_.Remove(function_info);
261 268 : eval_contextual_.Remove(function_info);
262 268 : script_.Remove(function_info);
263 : }
264 :
265 285296 : MaybeHandle<SharedFunctionInfo> CompilationCache::LookupScript(
266 : Handle<String> source, MaybeHandle<Object> name, int line_offset,
267 : int column_offset, ScriptOriginOptions resource_options,
268 : Handle<Context> native_context, LanguageMode language_mode) {
269 285296 : if (!IsEnabled()) return MaybeHandle<SharedFunctionInfo>();
270 :
271 : return script_.Lookup(source, name, line_offset, column_offset,
272 272078 : resource_options, native_context, language_mode);
273 : }
274 :
275 3556334 : InfoCellPair CompilationCache::LookupEval(Handle<String> source,
276 : Handle<SharedFunctionInfo> outer_info,
277 : Handle<Context> context,
278 : LanguageMode language_mode,
279 : int position) {
280 : InfoCellPair result;
281 3556334 : if (!IsEnabled()) return result;
282 :
283 : const char* cache_type;
284 :
285 3526571 : if (context->IsNativeContext()) {
286 : result = eval_global_.Lookup(source, outer_info, context, language_mode,
287 1271307 : position);
288 : cache_type = "eval-global";
289 :
290 : } else {
291 : DCHECK_NE(position, kNoSourcePosition);
292 : Handle<Context> native_context(context->native_context(), isolate());
293 : result = eval_contextual_.Lookup(source, outer_info, native_context,
294 2255264 : language_mode, position);
295 : cache_type = "eval-contextual";
296 : }
297 :
298 3526571 : if (result.has_shared()) {
299 2593390 : LOG(isolate(), CompilationCacheEvent("hit", cache_type, result.shared()));
300 : }
301 :
302 : return result;
303 : }
304 :
305 460435 : MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
306 : JSRegExp::Flags flags) {
307 460435 : if (!IsEnabled()) return MaybeHandle<FixedArray>();
308 :
309 459168 : return reg_exp_.Lookup(source, flags);
310 : }
311 :
312 151988 : void CompilationCache::PutScript(Handle<String> source,
313 : Handle<Context> native_context,
314 : LanguageMode language_mode,
315 : Handle<SharedFunctionInfo> function_info) {
316 151988 : if (!IsEnabled()) return;
317 138911 : LOG(isolate(), CompilationCacheEvent("put", "script", *function_info));
318 :
319 138860 : script_.Put(source, native_context, language_mode, function_info);
320 : }
321 :
322 1218479 : void CompilationCache::PutEval(Handle<String> source,
323 : Handle<SharedFunctionInfo> outer_info,
324 : Handle<Context> context,
325 : Handle<SharedFunctionInfo> function_info,
326 : Handle<FeedbackCell> feedback_cell,
327 : int position) {
328 1218479 : if (!IsEnabled()) return;
329 :
330 : const char* cache_type;
331 : HandleScope scope(isolate());
332 1188860 : if (context->IsNativeContext()) {
333 535230 : eval_global_.Put(source, outer_info, function_info, context, feedback_cell,
334 535230 : position);
335 : cache_type = "eval-global";
336 : } else {
337 : DCHECK_NE(position, kNoSourcePosition);
338 : Handle<Context> native_context(context->native_context(), isolate());
339 653630 : eval_contextual_.Put(source, outer_info, function_info, native_context,
340 653630 : feedback_cell, position);
341 : cache_type = "eval-contextual";
342 : }
343 1188860 : LOG(isolate(), CompilationCacheEvent("put", cache_type, *function_info));
344 : }
345 :
346 251662 : void CompilationCache::PutRegExp(Handle<String> source,
347 : JSRegExp::Flags flags,
348 : Handle<FixedArray> data) {
349 251662 : if (!IsEnabled()) return;
350 :
351 250400 : reg_exp_.Put(source, flags, data);
352 : }
353 :
354 1476 : void CompilationCache::Clear() {
355 49752 : for (int i = 0; i < kSubCacheCount; i++) {
356 22112 : subcaches_[i]->Clear();
357 : }
358 1476 : }
359 :
360 275539 : void CompilationCache::Iterate(RootVisitor* v) {
361 2479851 : for (int i = 0; i < kSubCacheCount; i++) {
362 1102156 : subcaches_[i]->Iterate(v);
363 : }
364 275539 : }
365 :
366 96169 : void CompilationCache::MarkCompactPrologue() {
367 865521 : for (int i = 0; i < kSubCacheCount; i++) {
368 384676 : subcaches_[i]->Age();
369 : }
370 96169 : }
371 :
372 3932 : void CompilationCache::Enable() {
373 3932 : enabled_ = true;
374 3932 : }
375 :
376 4052 : void CompilationCache::Disable() {
377 4052 : enabled_ = false;
378 : Clear();
379 4052 : }
380 :
381 : } // namespace internal
382 122036 : } // namespace v8
|