Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: src/compilation-cache.cc

Issue 131100: Add generations to the compilation cache for eval and regexp (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: '' Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/compilation-cache.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2008 the V8 project authors. All rights reserved. 1 // Copyright 2008 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 14 matching lines...) Expand all
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 27
28 #include "v8.h" 28 #include "v8.h"
29 29
30 #include "compilation-cache.h" 30 #include "compilation-cache.h"
31 31
32 namespace v8 { 32 namespace v8 {
33 namespace internal { 33 namespace internal {
34 34
35 enum {
36 // The number of script generations tell how many GCs a script can
37 // survive in the compilation cache, before it will be flushed if it
38 // hasn't been used.
39 NUMBER_OF_SCRIPT_GENERATIONS = 5,
40 35
41 // The compilation cache consists of tables - one for each entry 36 // The number of sub caches covering the different types to cache.
42 // kind plus extras for the script generations. 37 #define NUMBER_OF_SUB_CACHES 4
43 NUMBER_OF_TABLE_ENTRIES = 38
44 CompilationCache::LAST_ENTRY + NUMBER_OF_SCRIPT_GENERATIONS 39 // The number of generations for each sub cache.
45 }; 40 #define NUMBER_OF_SCRIPT_GENERATIONS 5
41 #define NUMBER_OF_EVAL_GLOBAL_GENERATIONS 2
42 #define NUMBER_OF_EVAL_CONTEXTUAL_GENERATIONS 2
43 #define NUMBER_OF_REGEXP_GENERATIONS 2
44
45
46 // Statically allocate all the sub-caches.
47 static CompilationCacheScript script(NUMBER_OF_SCRIPT_GENERATIONS);
48 static CompilationCacheEval eval_global(NUMBER_OF_EVAL_GLOBAL_GENERATIONS);
49 static CompilationCacheEval
50 eval_contextual(NUMBER_OF_EVAL_CONTEXTUAL_GENERATIONS);
51 static CompilationCacheRegExp reg_exp(NUMBER_OF_REGEXP_GENERATIONS);
52 static CompilationSubCache* entry[NUMBER_OF_SUB_CACHES] =
53 {&script, &eval_global, &eval_contextual, &reg_exp};
46 54
47 55
48 // Current enable state of the compilation cache. 56 // Current enable state of the compilation cache.
49 static bool enabled = true; 57 static bool enabled = true;
50 static inline bool IsEnabled() { 58 static inline bool IsEnabled() {
51 return FLAG_compilation_cache && enabled; 59 return FLAG_compilation_cache && enabled;
52 } 60 }
53 61
54 // Keep separate tables for the different entry kinds.
55 static Object* tables[NUMBER_OF_TABLE_ENTRIES] = { 0, };
56
57 62
58 static Handle<CompilationCacheTable> AllocateTable(int size) { 63 static Handle<CompilationCacheTable> AllocateTable(int size) {
59 CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size), 64 CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size),
60 CompilationCacheTable); 65 CompilationCacheTable);
61 } 66 }
62 67
63 68
64 static Handle<CompilationCacheTable> GetTable(int index) { 69 Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
65 ASSERT(index >= 0 && index < NUMBER_OF_TABLE_ENTRIES); 70 ASSERT(generation < generations_);
66 Handle<CompilationCacheTable> result; 71 Handle<CompilationCacheTable> result;
67 if (tables[index]->IsUndefined()) { 72 if (tables_[generation]->IsUndefined()) {
68 static const int kInitialCacheSize = 64; 73 static const int kInitialCacheSize = 64;
69 result = AllocateTable(kInitialCacheSize); 74 result = AllocateTable(kInitialCacheSize);
70 tables[index] = *result; 75 tables_[generation] = *result;
71 } else { 76 } else {
72 CompilationCacheTable* table = CompilationCacheTable::cast(tables[index]); 77 CompilationCacheTable* table =
78 CompilationCacheTable::cast(tables_[generation]);
73 result = Handle<CompilationCacheTable>(table); 79 result = Handle<CompilationCacheTable>(table);
74 } 80 }
75 return result; 81 return result;
76 } 82 }
77 83
78 84
79 static Handle<JSFunction> Lookup(Handle<String> source, 85 void CompilationSubCache::Age() {
80 Handle<Context> context, 86 // Age the generations implicitly killing off the oldest.
81 CompilationCache::Entry entry) { 87 for (int generation = generations_ - 1;
82 // Make sure not to leak the table into the surrounding handle 88 generation > 0;
83 // scope. Otherwise, we risk keeping old tables around even after 89 generation--) {
84 // having cleared the cache. 90 tables_[generation] = tables_[generation - 1];
85 Object* result;
86 { HandleScope scope;
87 Handle<CompilationCacheTable> table = GetTable(entry);
88 result = table->LookupEval(*source, *context);
89 } 91 }
90 if (result->IsJSFunction()) { 92
91 return Handle<JSFunction>(JSFunction::cast(result)); 93 // Set the first generation as unborn.
92 } else { 94 tables_[0] = Heap::undefined_value();
93 return Handle<JSFunction>::null();
94 }
95 } 95 }
96 96
97 97
98 static Handle<FixedArray> Lookup(Handle<String> source, 98 void CompilationSubCache::Iterate(ObjectVisitor* v) {
99 JSRegExp::Flags flags) { 99 v->VisitPointers(&tables_[0], &tables_[generations_]);
100 // Make sure not to leak the table into the surrounding handle 100 }
101 // scope. Otherwise, we risk keeping old tables around even after 101
102 // having cleared the cache. 102
103 Object* result; 103 void CompilationSubCache::Clear() {
104 { HandleScope scope; 104 for (int i = 0; i < generations_; i++) {
105 Handle<CompilationCacheTable> table = GetTable(CompilationCache::REGEXP); 105 tables_[i] = Heap::undefined_value();
106 result = table->LookupRegExp(*source, flags);
107 }
108 if (result->IsFixedArray()) {
109 return Handle<FixedArray>(FixedArray::cast(result));
110 } else {
111 return Handle<FixedArray>::null();
112 } 106 }
113 } 107 }
114 108
115 109
116 // We only re-use a cached function for some script source code if the 110 // We only re-use a cached function for some script source code if the
117 // script originates from the same place. This is to avoid issues 111 // script originates from the same place. This is to avoid issues
118 // when reporting errors, etc. 112 // when reporting errors, etc.
119 static bool HasOrigin(Handle<JSFunction> boilerplate, 113 bool CompilationCacheScript::HasOrigin(Handle<JSFunction> boilerplate,
120 Handle<Object> name, 114 Handle<Object> name,
121 int line_offset, 115 int line_offset,
122 int column_offset) { 116 int column_offset) {
123 Handle<Script> script = 117 Handle<Script> script =
124 Handle<Script>(Script::cast(boilerplate->shared()->script())); 118 Handle<Script>(Script::cast(boilerplate->shared()->script()));
125 // If the script name isn't set, the boilerplate script should have 119 // If the script name isn't set, the boilerplate script should have
126 // an undefined name to have the same origin. 120 // an undefined name to have the same origin.
127 if (name.is_null()) { 121 if (name.is_null()) {
128 return script->name()->IsUndefined(); 122 return script->name()->IsUndefined();
129 } 123 }
130 // Do the fast bailout checks first. 124 // Do the fast bailout checks first.
131 if (line_offset != script->line_offset()->value()) return false; 125 if (line_offset != script->line_offset()->value()) return false;
132 if (column_offset != script->column_offset()->value()) return false; 126 if (column_offset != script->column_offset()->value()) return false;
133 // Check that both names are strings. If not, no match. 127 // Check that both names are strings. If not, no match.
134 if (!name->IsString() || !script->name()->IsString()) return false; 128 if (!name->IsString() || !script->name()->IsString()) return false;
135 // Compare the two name strings for equality. 129 // Compare the two name strings for equality.
136 return String::cast(*name)->Equals(String::cast(script->name())); 130 return String::cast(*name)->Equals(String::cast(script->name()));
137 } 131 }
138 132
139 133
140 // TODO(245): Need to allow identical code from different contexts to 134 // TODO(245): Need to allow identical code from different contexts to
141 // be cached in the same script generation. Currently the first use 135 // be cached in the same script generation. Currently the first use
142 // will be cached, but subsequent code from different source / line 136 // will be cached, but subsequent code from different source / line
143 // won't. 137 // won't.
144 Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source, 138 Handle<JSFunction> CompilationCacheScript::Lookup(Handle<String> source,
145 Handle<Object> name, 139 Handle<Object> name,
146 int line_offset, 140 int line_offset,
147 int column_offset) { 141 int column_offset) {
148 if (!IsEnabled()) {
149 return Handle<JSFunction>::null();
150 }
151
152 // Use an int for the generation index, so value range propagation
153 // in gcc 4.3+ won't assume it can only go up to LAST_ENTRY when in
154 // fact it can go up to SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS.
155 int generation = SCRIPT;
156 Object* result = NULL; 142 Object* result = NULL;
143 int generation;
157 144
158 // Probe the script generation tables. Make sure not to leak handles 145 // Probe the script generation tables. Make sure not to leak handles
159 // into the caller's handle scope. 146 // into the caller's handle scope.
160 { HandleScope scope; 147 { HandleScope scope;
161 while (generation < SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS) { 148 for (generation = 0; generation < generations(); generation++) {
162 Handle<CompilationCacheTable> table = GetTable(generation); 149 Handle<CompilationCacheTable> table = GetTable(generation);
163 Handle<Object> probe(table->Lookup(*source)); 150 Handle<Object> probe(table->Lookup(*source));
164 if (probe->IsJSFunction()) { 151 if (probe->IsJSFunction()) {
165 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(probe); 152 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(probe);
166 // Break when we've found a suitable boilerplate function that 153 // Break when we've found a suitable boilerplate function that
167 // matches the origin. 154 // matches the origin.
168 if (HasOrigin(boilerplate, name, line_offset, column_offset)) { 155 if (HasOrigin(boilerplate, name, line_offset, column_offset)) {
169 result = *boilerplate; 156 result = *boilerplate;
170 break; 157 break;
171 } 158 }
172 } 159 }
173 // Go to the next generation.
174 generation++;
175 } 160 }
176 } 161 }
177 162
178 static void* script_histogram = StatsTable::CreateHistogram( 163 static void* script_histogram = StatsTable::CreateHistogram(
179 "V8.ScriptCache", 164 "V8.ScriptCache",
180 0, 165 0,
181 NUMBER_OF_SCRIPT_GENERATIONS, 166 NUMBER_OF_SCRIPT_GENERATIONS,
182 NUMBER_OF_SCRIPT_GENERATIONS + 1); 167 NUMBER_OF_SCRIPT_GENERATIONS + 1);
183 168
184 if (script_histogram != NULL) { 169 if (script_histogram != NULL) {
185 // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss. 170 // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss.
186 StatsTable::AddHistogramSample(script_histogram, generation - SCRIPT); 171 StatsTable::AddHistogramSample(script_histogram, generation);
187 } 172 }
188 173
189 // Once outside the manacles of the handle scope, we need to recheck 174 // Once outside the manacles of the handle scope, we need to recheck
190 // to see if we actually found a cached script. If so, we return a 175 // to see if we actually found a cached script. If so, we return a
191 // handle created in the caller's handle scope. 176 // handle created in the caller's handle scope.
192 if (result != NULL) { 177 if (result != NULL) {
193 Handle<JSFunction> boilerplate(JSFunction::cast(result)); 178 Handle<JSFunction> boilerplate(JSFunction::cast(result));
194 ASSERT(HasOrigin(boilerplate, name, line_offset, column_offset)); 179 ASSERT(HasOrigin(boilerplate, name, line_offset, column_offset));
195 // If the script was found in a later generation, we promote it to 180 // If the script was found in a later generation, we promote it to
196 // the first generation to let it survive longer in the cache. 181 // the first generation to let it survive longer in the cache.
197 if (generation != SCRIPT) PutScript(source, boilerplate); 182 if (generation != 0) Put(source, boilerplate);
198 Counters::compilation_cache_hits.Increment(); 183 Counters::compilation_cache_hits.Increment();
199 return boilerplate; 184 return boilerplate;
200 } else { 185 } else {
201 Counters::compilation_cache_misses.Increment(); 186 Counters::compilation_cache_misses.Increment();
202 return Handle<JSFunction>::null(); 187 return Handle<JSFunction>::null();
203 } 188 }
204 } 189 }
205 190
206 191
192 void CompilationCacheScript::Put(Handle<String> source,
193 Handle<JSFunction> boilerplate) {
194 HandleScope scope;
195 ASSERT(boilerplate->IsBoilerplate());
196 Handle<CompilationCacheTable> table = GetTable(0);
197 CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate));
198 }
199
200
201 Handle<JSFunction> CompilationCacheEval::Lookup(Handle<String> source,
202 Handle<Context> context) {
203 // Make sure not to leak the table into the surrounding handle
204 // scope. Otherwise, we risk keeping old tables around even after
205 // having cleared the cache.
206 Object* result = NULL;
207 int generation;
208 { HandleScope scope;
209 for (generation = 0; generation < generations(); generation++) {
210 Handle<CompilationCacheTable> table = GetTable(generation);
211 result = table->LookupEval(*source, *context);
212 if (result->IsJSFunction()) {
213 break;
214 }
215 }
216 }
217 if (result->IsJSFunction()) {
218 Handle<JSFunction> boilerplate(JSFunction::cast(result));
219 if (generation != 0) {
220 Put(source, context, boilerplate);
221 }
222 Counters::compilation_cache_hits.Increment();
223 return boilerplate;
224 } else {
225 Counters::compilation_cache_misses.Increment();
226 return Handle<JSFunction>::null();
227 }
228 }
229
230
231 void CompilationCacheEval::Put(Handle<String> source,
232 Handle<Context> context,
233 Handle<JSFunction> boilerplate) {
234 HandleScope scope;
235 ASSERT(boilerplate->IsBoilerplate());
236 Handle<CompilationCacheTable> table = GetTable(0);
237 CALL_HEAP_FUNCTION_VOID(table->PutEval(*source, *context, *boilerplate));
238 }
239
240
241 Handle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source,
242 JSRegExp::Flags flags) {
243 // Make sure not to leak the table into the surrounding handle
244 // scope. Otherwise, we risk keeping old tables around even after
245 // having cleared the cache.
246 Object* result = NULL;
247 int generation;
248 { HandleScope scope;
249 for (generation = 0; generation < generations(); generation++) {
250 Handle<CompilationCacheTable> table = GetTable(generation);
251 result = table->LookupRegExp(*source, flags);
252 if (result->IsFixedArray()) {
253 break;
254 }
255 }
256 }
257 if (result->IsFixedArray()) {
258 Handle<FixedArray> data(FixedArray::cast(result));
259 if (generation != 0) {
260 Put(source, flags, data);
261 }
262 Counters::compilation_cache_hits.Increment();
263 return data;
264 } else {
265 Counters::compilation_cache_misses.Increment();
266 return Handle<FixedArray>::null();
267 }
268 }
269
270
271 void CompilationCacheRegExp::Put(Handle<String> source,
272 JSRegExp::Flags flags,
273 Handle<FixedArray> data) {
274 HandleScope scope;
275 Handle<CompilationCacheTable> table = GetTable(0);
276 CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data));
277 }
278
279
280 Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source,
281 Handle<Object> name,
282 int line_offset,
283 int column_offset) {
284 if (!IsEnabled()) {
285 return Handle<JSFunction>::null();
286 }
287
288 return script.Lookup(source, name, line_offset, column_offset);
289 }
290
291
207 Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source, 292 Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source,
208 Handle<Context> context, 293 Handle<Context> context,
209 Entry entry) { 294 bool is_global) {
210 if (!IsEnabled()) { 295 if (!IsEnabled()) {
211 return Handle<JSFunction>::null(); 296 return Handle<JSFunction>::null();
212 } 297 }
213 298
214 ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL); 299 Handle<JSFunction> result;
215 Handle<JSFunction> result = Lookup(source, context, entry); 300 if (is_global) {
216 if (result.is_null()) { 301 result = eval_global.Lookup(source, context);
217 Counters::compilation_cache_misses.Increment();
218 } else { 302 } else {
219 Counters::compilation_cache_hits.Increment(); 303 result = eval_contextual.Lookup(source, context);
220 } 304 }
221 return result; 305 return result;
222 } 306 }
223 307
224 308
225 Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source, 309 Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
226 JSRegExp::Flags flags) { 310 JSRegExp::Flags flags) {
227 if (!IsEnabled()) { 311 if (!IsEnabled()) {
228 return Handle<FixedArray>::null(); 312 return Handle<FixedArray>::null();
229 } 313 }
230 314
231 Handle<FixedArray> result = Lookup(source, flags); 315 return reg_exp.Lookup(source, flags);
232 if (result.is_null()) {
233 Counters::compilation_cache_misses.Increment();
234 } else {
235 Counters::compilation_cache_hits.Increment();
236 }
237 return result;
238 } 316 }
239 317
240 318
241 void CompilationCache::PutScript(Handle<String> source, 319 void CompilationCache::PutScript(Handle<String> source,
242 Handle<JSFunction> boilerplate) { 320 Handle<JSFunction> boilerplate) {
243 if (!IsEnabled()) { 321 if (!IsEnabled()) {
244 return; 322 return;
245 } 323 }
246 324
247 HandleScope scope;
248 ASSERT(boilerplate->IsBoilerplate()); 325 ASSERT(boilerplate->IsBoilerplate());
249 Handle<CompilationCacheTable> table = GetTable(SCRIPT); 326 script.Put(source, boilerplate);
250 CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate));
251 } 327 }
252 328
253 329
254 void CompilationCache::PutEval(Handle<String> source, 330 void CompilationCache::PutEval(Handle<String> source,
255 Handle<Context> context, 331 Handle<Context> context,
256 Entry entry, 332 bool is_global,
257 Handle<JSFunction> boilerplate) { 333 Handle<JSFunction> boilerplate) {
258 if (!IsEnabled()) { 334 if (!IsEnabled()) {
259 return; 335 return;
260 } 336 }
261 337
262 HandleScope scope; 338 HandleScope scope;
263 ASSERT(boilerplate->IsBoilerplate()); 339 ASSERT(boilerplate->IsBoilerplate());
264 Handle<CompilationCacheTable> table = GetTable(entry); 340 if (is_global) {
265 CALL_HEAP_FUNCTION_VOID(table->PutEval(*source, *context, *boilerplate)); 341 eval_global.Put(source, context, boilerplate);
342 } else {
343 eval_contextual.Put(source, context, boilerplate);
344 }
266 } 345 }
267 346
268 347
269 348
270 void CompilationCache::PutRegExp(Handle<String> source, 349 void CompilationCache::PutRegExp(Handle<String> source,
271 JSRegExp::Flags flags, 350 JSRegExp::Flags flags,
272 Handle<FixedArray> data) { 351 Handle<FixedArray> data) {
273 if (!IsEnabled()) { 352 if (!IsEnabled()) {
274 return; 353 return;
275 } 354 }
276 355
277 HandleScope scope; 356 reg_exp.Put(source, flags, data);
278 Handle<CompilationCacheTable> table = GetTable(REGEXP);
279 CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data));
280 } 357 }
281 358
282 359
283 void CompilationCache::Clear() { 360 void CompilationCache::Clear() {
284 for (int i = 0; i < NUMBER_OF_TABLE_ENTRIES; i++) { 361 for (int i = 0; i < NUMBER_OF_SUB_CACHES; i++) {
285 tables[i] = Heap::undefined_value(); 362 entry[i]->Clear();
286 } 363 }
287 } 364 }
288 365
289 366
290 void CompilationCache::Iterate(ObjectVisitor* v) { 367 void CompilationCache::Iterate(ObjectVisitor* v) {
291 v->VisitPointers(&tables[0], &tables[NUMBER_OF_TABLE_ENTRIES]); 368 for (int i = 0; i < NUMBER_OF_SUB_CACHES; i++) {
369 entry[i]->Iterate(v);
370 }
292 } 371 }
293 372
294 373
295 void CompilationCache::MarkCompactPrologue() { 374 void CompilationCache::MarkCompactPrologue() {
296 ASSERT(LAST_ENTRY == SCRIPT); 375 for (int i = 0; i < NUMBER_OF_SUB_CACHES; i++) {
297 for (int i = NUMBER_OF_TABLE_ENTRIES - 1; i > SCRIPT; i--) { 376 entry[i]->Age();
298 tables[i] = tables[i - 1];
299 }
300 for (int j = 0; j <= LAST_ENTRY; j++) {
301 tables[j] = Heap::undefined_value();
302 } 377 }
303 } 378 }
304 379
305 380
306 void CompilationCache::Enable() { 381 void CompilationCache::Enable() {
307 enabled = true; 382 enabled = true;
308 } 383 }
309 384
310 385
311 void CompilationCache::Disable() { 386 void CompilationCache::Disable() {
312 enabled = false; 387 enabled = false;
313 Clear(); 388 Clear();
314 } 389 }
315 390
316 391
317 } } // namespace v8::internal 392 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/compilation-cache.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698