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