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 : // The number of generations for each sub cache.
18 : static const int kRegExpGenerations = 2;
19 :
20 : // Initial size of each compilation cache table allocated.
21 : static const int kInitialCacheSize = 64;
22 :
23 54999 : CompilationCache::CompilationCache(Isolate* isolate)
24 : : isolate_(isolate),
25 : script_(isolate),
26 : eval_global_(isolate),
27 : eval_contextual_(isolate),
28 : reg_exp_(isolate, kRegExpGenerations),
29 109998 : enabled_(true) {
30 : CompilationSubCache* subcaches[kSubCacheCount] =
31 54999 : {&script_, &eval_global_, &eval_contextual_, ®_exp_};
32 274995 : for (int i = 0; i < kSubCacheCount; ++i) {
33 219996 : subcaches_[i] = subcaches[i];
34 : }
35 54999 : }
36 :
37 106730 : CompilationCache::~CompilationCache() {}
38 :
39 12504953 : Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
40 : DCHECK(generation < generations_);
41 : Handle<CompilationCacheTable> result;
42 12504953 : if (tables_[generation]->IsUndefined(isolate())) {
43 58980 : result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
44 117960 : tables_[generation] = *result;
45 : } else {
46 : CompilationCacheTable* table =
47 6193497 : CompilationCacheTable::cast(tables_[generation]);
48 : result = Handle<CompilationCacheTable>(table, isolate());
49 : }
50 6252477 : return result;
51 : }
52 :
53 589168 : void CompilationSubCache::Age() {
54 : // Don't directly age single-generation caches.
55 294584 : if (generations_ == 1) {
56 441876 : if (!tables_[0]->IsUndefined(isolate())) {
57 37857 : CompilationCacheTable::cast(tables_[0])->Age();
58 : }
59 294584 : return;
60 : }
61 :
62 : // Age the generations implicitly killing off the oldest.
63 147292 : for (int i = generations_ - 1; i > 0; i--) {
64 73646 : tables_[i] = tables_[i - 1];
65 : }
66 :
67 : // Set the first generation as unborn.
68 147292 : tables_[0] = isolate()->heap()->undefined_value();
69 : }
70 :
71 0 : void CompilationSubCache::Iterate(RootVisitor* v) {
72 : v->VisitRootPointers(Root::kCompilationCache, &tables_[0],
73 929692 : &tables_[generations_]);
74 0 : }
75 :
76 1336408 : void CompilationSubCache::Clear() {
77 2672816 : MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
78 0 : }
79 :
80 0 : void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
81 : // Probe the script generation tables. Make sure not to leak handles
82 : // into the caller's handle scope.
83 : { HandleScope scope(isolate());
84 0 : for (int generation = 0; generation < generations(); generation++) {
85 0 : Handle<CompilationCacheTable> table = GetTable(generation);
86 0 : table->Remove(*function_info);
87 : }
88 : }
89 0 : }
90 :
91 0 : CompilationCacheScript::CompilationCacheScript(Isolate* isolate)
92 0 : : CompilationSubCache(isolate, 1) {}
93 :
94 : // We only re-use a cached function for some script source code if the
95 : // script originates from the same place. This is to avoid issues
96 : // when reporting errors, etc.
97 92034 : bool CompilationCacheScript::HasOrigin(Handle<SharedFunctionInfo> function_info,
98 : MaybeHandle<Object> maybe_name,
99 : int line_offset, int column_offset,
100 : ScriptOriginOptions resource_options) {
101 : Handle<Script> script =
102 140527 : Handle<Script>(Script::cast(function_info->script()), isolate());
103 : // If the script name isn't set, the boilerplate script should have
104 : // an undefined name to have the same origin.
105 : Handle<Object> name;
106 92034 : if (!maybe_name.ToHandle(&name)) {
107 48493 : return script->name()->IsUndefined(isolate());
108 : }
109 : // Do the fast bailout checks first.
110 43541 : if (line_offset != script->line_offset()) return false;
111 43535 : if (column_offset != script->column_offset()) return false;
112 : // Check that both names are strings. If not, no match.
113 87070 : if (!name->IsString() || !script->name()->IsString()) return false;
114 : // Are the origin_options same?
115 43535 : if (resource_options.Flags() != script->origin_options().Flags())
116 : return false;
117 : // Compare the two name strings for equality.
118 : return String::Equals(Handle<String>::cast(name),
119 43530 : Handle<String>(String::cast(script->name())));
120 : }
121 :
122 : // TODO(245): Need to allow identical code from different contexts to
123 : // be cached in the same script generation. Currently the first use
124 : // will be cached, but subsequent code from different source / line
125 : // won't.
126 211326 : InfoVectorPair CompilationCacheScript::Lookup(
127 : Handle<String> source, MaybeHandle<Object> name, int line_offset,
128 : int column_offset, ScriptOriginOptions resource_options,
129 : Handle<Context> context, LanguageMode language_mode) {
130 : InfoVectorPair result;
131 :
132 : // Probe the script generation tables. Make sure not to leak handles
133 : // into the caller's handle scope.
134 554213 : { HandleScope scope(isolate());
135 : const int generation = 0;
136 : DCHECK_EQ(generations(), 1);
137 211326 : Handle<CompilationCacheTable> table = GetTable(generation);
138 211326 : InfoVectorPair probe = table->LookupScript(source, context, language_mode);
139 211326 : if (probe.has_shared()) {
140 : Handle<SharedFunctionInfo> function_info(probe.shared(), isolate());
141 : Handle<Cell> vector_handle;
142 92034 : if (probe.has_vector()) {
143 : vector_handle = Handle<Cell>(probe.vector(), isolate());
144 : }
145 : // Break when we've found a suitable shared function info that
146 : // matches the origin.
147 92034 : if (HasOrigin(function_info, name, line_offset, column_offset,
148 : resource_options)) {
149 : result = InfoVectorPair(*function_info,
150 91473 : probe.has_vector() ? *vector_handle : nullptr);
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 211326 : if (result.has_shared()) {
159 : #ifdef DEBUG
160 : // Since HasOrigin can allocate, we need to protect the SharedFunctionInfo
161 : // and the FeedbackVector with handles during the call.
162 : Handle<SharedFunctionInfo> shared(result.shared(), isolate());
163 : Handle<Cell> vector_handle;
164 : if (result.has_vector()) {
165 : vector_handle = Handle<Cell>(result.vector(), isolate());
166 : }
167 : DCHECK(
168 : HasOrigin(shared, name, line_offset, column_offset, resource_options));
169 : result =
170 : InfoVectorPair(*shared, result.has_vector() ? *vector_handle : nullptr);
171 : #endif
172 91473 : isolate()->counters()->compilation_cache_hits()->Increment();
173 : } else {
174 119853 : isolate()->counters()->compilation_cache_misses()->Increment();
175 : }
176 211326 : return result;
177 : }
178 :
179 118424 : void CompilationCacheScript::Put(Handle<String> source, Handle<Context> context,
180 : LanguageMode language_mode,
181 : Handle<SharedFunctionInfo> function_info,
182 : Handle<Cell> literals) {
183 236848 : HandleScope scope(isolate());
184 236848 : Handle<CompilationCacheTable> table = GetFirstTable();
185 : SetFirstTable(CompilationCacheTable::PutScript(
186 118424 : table, source, context, language_mode, function_info, literals));
187 118424 : }
188 :
189 3620221 : InfoVectorPair CompilationCacheEval::Lookup(
190 : Handle<String> source, Handle<SharedFunctionInfo> outer_info,
191 : Handle<Context> native_context, LanguageMode language_mode, int position) {
192 7240442 : HandleScope scope(isolate());
193 : // Make sure not to leak the table into the surrounding handle
194 : // scope. Otherwise, we risk keeping old tables around even after
195 : // having cleared the cache.
196 : InfoVectorPair result;
197 : const int generation = 0;
198 : DCHECK_EQ(generations(), 1);
199 3620221 : Handle<CompilationCacheTable> table = GetTable(generation);
200 : result = table->LookupEval(source, outer_info, native_context, language_mode,
201 3620221 : position);
202 3620221 : if (result.has_shared()) {
203 2676093 : isolate()->counters()->compilation_cache_hits()->Increment();
204 : } else {
205 944128 : isolate()->counters()->compilation_cache_misses()->Increment();
206 : }
207 7240442 : return result;
208 : }
209 :
210 1201484 : void CompilationCacheEval::Put(Handle<String> source,
211 : Handle<SharedFunctionInfo> outer_info,
212 : Handle<SharedFunctionInfo> function_info,
213 : Handle<Context> native_context,
214 : Handle<Cell> literals, int position) {
215 2402968 : HandleScope scope(isolate());
216 1201484 : Handle<CompilationCacheTable> table = GetFirstTable();
217 : table =
218 : CompilationCacheTable::PutEval(table, source, outer_info, function_info,
219 1201484 : native_context, literals, position);
220 : SetFirstTable(table);
221 1201484 : }
222 :
223 512735 : MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
224 : Handle<String> source,
225 : JSRegExp::Flags flags) {
226 2641542 : HandleScope scope(isolate());
227 : // Make sure not to leak the table into the surrounding handle
228 : // scope. Otherwise, we risk keeping old tables around even after
229 : // having cleared the cache.
230 : Handle<Object> result = isolate()->factory()->undefined_value();
231 : int generation;
232 2206674 : for (generation = 0; generation < generations(); generation++) {
233 808106 : Handle<CompilationCacheTable> table = GetTable(generation);
234 808106 : result = table->LookupRegExp(source, flags);
235 808106 : if (result->IsFixedArray()) break;
236 : }
237 512735 : if (result->IsFixedArray()) {
238 217504 : Handle<FixedArray> data = Handle<FixedArray>::cast(result);
239 217504 : if (generation != 0) {
240 140 : Put(source, flags, data);
241 : }
242 217504 : isolate()->counters()->compilation_cache_hits()->Increment();
243 217504 : return scope.CloseAndEscape(data);
244 : } else {
245 295231 : isolate()->counters()->compilation_cache_misses()->Increment();
246 295231 : return MaybeHandle<FixedArray>();
247 : }
248 : }
249 :
250 292916 : void CompilationCacheRegExp::Put(Handle<String> source,
251 : JSRegExp::Flags flags,
252 : Handle<FixedArray> data) {
253 585832 : HandleScope scope(isolate());
254 585832 : Handle<CompilationCacheTable> table = GetFirstTable();
255 292916 : SetFirstTable(CompilationCacheTable::PutRegExp(table, source, flags, data));
256 292916 : }
257 :
258 3576 : void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
259 7152 : if (!IsEnabled()) return;
260 :
261 0 : eval_global_.Remove(function_info);
262 0 : eval_contextual_.Remove(function_info);
263 0 : script_.Remove(function_info);
264 : }
265 :
266 233598 : InfoVectorPair CompilationCache::LookupScript(
267 : Handle<String> source, MaybeHandle<Object> name, int line_offset,
268 : int column_offset, ScriptOriginOptions resource_options,
269 : Handle<Context> context, LanguageMode language_mode) {
270 : InfoVectorPair empty_result;
271 233598 : if (!IsEnabled()) return empty_result;
272 :
273 : return script_.Lookup(source, name, line_offset, column_offset,
274 211326 : resource_options, context, language_mode);
275 : }
276 :
277 3647841 : InfoVectorPair CompilationCache::LookupEval(
278 : Handle<String> source, Handle<SharedFunctionInfo> outer_info,
279 2228059 : Handle<Context> context, LanguageMode language_mode, int position) {
280 : InfoVectorPair result;
281 3647841 : if (!IsEnabled()) return result;
282 :
283 3620221 : if (context->IsNativeContext()) {
284 : result = eval_global_.Lookup(source, outer_info, context, language_mode,
285 1392162 : position);
286 : } else {
287 : DCHECK_NE(position, kNoSourcePosition);
288 : Handle<Context> native_context(context->native_context(), isolate());
289 : result = eval_contextual_.Lookup(source, outer_info, native_context,
290 2228059 : language_mode, position);
291 : }
292 :
293 3620221 : return result;
294 : }
295 :
296 518367 : MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
297 : JSRegExp::Flags flags) {
298 518367 : if (!IsEnabled()) return MaybeHandle<FixedArray>();
299 :
300 512735 : return reg_exp_.Lookup(source, flags);
301 : }
302 :
303 140626 : void CompilationCache::PutScript(Handle<String> source, Handle<Context> context,
304 : LanguageMode language_mode,
305 : Handle<SharedFunctionInfo> function_info,
306 : Handle<Cell> literals) {
307 281252 : if (!IsEnabled()) return;
308 :
309 118424 : script_.Put(source, context, language_mode, function_info, literals);
310 : }
311 :
312 1228997 : void CompilationCache::PutEval(Handle<String> source,
313 : Handle<SharedFunctionInfo> outer_info,
314 : Handle<Context> context,
315 : Handle<SharedFunctionInfo> function_info,
316 1852323 : Handle<Cell> literals, int position) {
317 2457994 : if (!IsEnabled()) return;
318 :
319 : HandleScope scope(isolate());
320 1201484 : if (context->IsNativeContext()) {
321 : eval_global_.Put(source, outer_info, function_info, context, literals,
322 550645 : position);
323 : } else {
324 : DCHECK_NE(position, kNoSourcePosition);
325 : Handle<Context> native_context(context->native_context(), isolate());
326 : eval_contextual_.Put(source, outer_info, function_info, native_context,
327 650839 : literals, position);
328 : }
329 : }
330 :
331 298407 : void CompilationCache::PutRegExp(Handle<String> source,
332 : JSRegExp::Flags flags,
333 : Handle<FixedArray> data) {
334 298407 : if (!IsEnabled()) {
335 298407 : return;
336 : }
337 :
338 292776 : reg_exp_.Put(source, flags, data);
339 : }
340 :
341 6769 : void CompilationCache::Clear() {
342 1343177 : for (int i = 0; i < kSubCacheCount; i++) {
343 1336408 : subcaches_[i]->Clear();
344 : }
345 6769 : }
346 :
347 232423 : void CompilationCache::Iterate(RootVisitor* v) {
348 1162115 : for (int i = 0; i < kSubCacheCount; i++) {
349 929692 : subcaches_[i]->Iterate(v);
350 : }
351 232423 : }
352 :
353 73646 : void CompilationCache::MarkCompactPrologue() {
354 368230 : for (int i = 0; i < kSubCacheCount; i++) {
355 294584 : subcaches_[i]->Age();
356 : }
357 73646 : }
358 :
359 3820 : void CompilationCache::Enable() {
360 3820 : enabled_ = true;
361 3820 : }
362 :
363 327333 : void CompilationCache::Disable() {
364 327333 : enabled_ = false;
365 : Clear();
366 327333 : }
367 :
368 : } // namespace internal
369 : } // namespace v8
|