| OLD | NEW |
| 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 Loading... |
| 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 { namespace internal { | 32 namespace v8 { namespace internal { |
| 33 | 33 |
| 34 enum { | 34 enum { |
| 35 NUMBER_OF_ENTRY_KINDS = CompilationCache::LAST_ENTRY + 1 | 35 // The number of script generations tell how many GCs a script can |
| 36 // survive in the compilation cache, before it will be flushed if it |
| 37 // hasn't been used. |
| 38 NUMBER_OF_SCRIPT_GENERATIONS = 5, |
| 39 |
| 40 // The compilation cache consists of tables - one for each entry |
| 41 // kind plus extras for the script generations. |
| 42 NUMBER_OF_TABLE_ENTRIES = |
| 43 CompilationCache::LAST_ENTRY + NUMBER_OF_SCRIPT_GENERATIONS |
| 36 }; | 44 }; |
| 37 | 45 |
| 38 | 46 |
| 39 // Keep separate tables for the different entry kinds. | 47 // Keep separate tables for the different entry kinds. |
| 40 static Object* tables[NUMBER_OF_ENTRY_KINDS] = { 0, }; | 48 static Object* tables[NUMBER_OF_TABLE_ENTRIES] = { 0, }; |
| 41 | 49 |
| 42 | 50 |
| 43 static Handle<CompilationCacheTable> AllocateTable(int size) { | 51 static Handle<CompilationCacheTable> AllocateTable(int size) { |
| 44 CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size), | 52 CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size), |
| 45 CompilationCacheTable); | 53 CompilationCacheTable); |
| 46 } | 54 } |
| 47 | 55 |
| 48 | 56 |
| 49 static Handle<CompilationCacheTable> GetTable(CompilationCache::Entry entry) { | 57 static Handle<CompilationCacheTable> GetTable(CompilationCache::Entry entry) { |
| 50 Handle<CompilationCacheTable> result; | 58 Handle<CompilationCacheTable> result; |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 // Do the fast bailout checks first. | 122 // Do the fast bailout checks first. |
| 115 if (line_offset != script->line_offset()->value()) return false; | 123 if (line_offset != script->line_offset()->value()) return false; |
| 116 if (column_offset != script->column_offset()->value()) return false; | 124 if (column_offset != script->column_offset()->value()) return false; |
| 117 // Check that both names are strings. If not, no match. | 125 // Check that both names are strings. If not, no match. |
| 118 if (!name->IsString() || !script->name()->IsString()) return false; | 126 if (!name->IsString() || !script->name()->IsString()) return false; |
| 119 // Compare the two name strings for equality. | 127 // Compare the two name strings for equality. |
| 120 return String::cast(*name)->Equals(String::cast(script->name())); | 128 return String::cast(*name)->Equals(String::cast(script->name())); |
| 121 } | 129 } |
| 122 | 130 |
| 123 | 131 |
| 124 static Handle<JSFunction> Lookup(Handle<String> source, | 132 // TODO(245): Need to allow identical code from different contexts to |
| 125 CompilationCache::Entry entry) { | 133 // be cached in the same script generation. Currently the first use |
| 126 // Make sure not to leak the table into the surrounding handle | 134 // will be cached, but subsequent code from different source / line |
| 127 // scope. Otherwise, we risk keeping old tables around even after | 135 // won't. |
| 128 // having cleared the cache. | 136 Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source, |
| 129 Object* result; | 137 Handle<Object> name, |
| 138 int line_offset, |
| 139 int column_offset) { |
| 140 Object* result = NULL; |
| 141 Entry generation = SCRIPT; // First generation. |
| 142 |
| 143 // Probe the script generation tables. Make sure not to leak handles |
| 144 // into the caller's handle scope. |
| 130 { HandleScope scope; | 145 { HandleScope scope; |
| 131 Handle<CompilationCacheTable> table = GetTable(entry); | 146 while (generation < SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS) { |
| 132 result = table->Lookup(*source); | 147 Handle<CompilationCacheTable> table = GetTable(generation); |
| 148 Handle<Object> probe(table->Lookup(*source)); |
| 149 if (probe->IsJSFunction()) { |
| 150 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(probe); |
| 151 // Break when we've found a suitable boilerplate function that |
| 152 // matches the origin. |
| 153 if (HasOrigin(boilerplate, name, line_offset, column_offset)) { |
| 154 result = *boilerplate; |
| 155 break; |
| 156 } |
| 157 } |
| 158 // Go to the next generation. |
| 159 generation = static_cast<Entry>(generation + 1); |
| 160 } |
| 133 } | 161 } |
| 134 if (result->IsJSFunction()) { | 162 |
| 135 return Handle<JSFunction>(JSFunction::cast(result)); | 163 // Once outside the menacles of the handle scope, we need to recheck |
| 164 // to see if we actually found a cached script. If so, we return a |
| 165 // handle created in the caller's handle scope. |
| 166 if (result != NULL) { |
| 167 Handle<JSFunction> boilerplate(JSFunction::cast(result)); |
| 168 ASSERT(HasOrigin(boilerplate, name, line_offset, column_offset)); |
| 169 // If the script was found in a later generation, we promote it to |
| 170 // the first generation to let it survive longer in the cache. |
| 171 if (generation != SCRIPT) PutScript(source, boilerplate); |
| 172 Counters::compilation_cache_hits.Increment(); |
| 173 return boilerplate; |
| 136 } else { | 174 } else { |
| 175 Counters::compilation_cache_misses.Increment(); |
| 137 return Handle<JSFunction>::null(); | 176 return Handle<JSFunction>::null(); |
| 138 } | 177 } |
| 139 } | 178 } |
| 140 | 179 |
| 141 | 180 |
| 142 // TODO(245): Need to allow identical code from different contexts to be | |
| 143 // cached. Currently the first use will be cached, but subsequent code | |
| 144 // from different source / line won't. | |
| 145 Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source, | |
| 146 Handle<Object> name, | |
| 147 int line_offset, | |
| 148 int column_offset) { | |
| 149 Handle<JSFunction> result = Lookup(source, SCRIPT); | |
| 150 if (result.is_null()) { | |
| 151 Counters::compilation_cache_misses.Increment(); | |
| 152 } else if (HasOrigin(result, name, line_offset, column_offset)) { | |
| 153 Counters::compilation_cache_hits.Increment(); | |
| 154 } else { | |
| 155 result = Handle<JSFunction>::null(); | |
| 156 Counters::compilation_cache_misses.Increment(); | |
| 157 } | |
| 158 return result; | |
| 159 } | |
| 160 | |
| 161 | |
| 162 Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source, | 181 Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source, |
| 163 Handle<Context> context, | 182 Handle<Context> context, |
| 164 Entry entry) { | 183 Entry entry) { |
| 165 ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL); | 184 ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL); |
| 166 Handle<JSFunction> result = Lookup(source, context, entry); | 185 Handle<JSFunction> result = Lookup(source, context, entry); |
| 167 if (result.is_null()) { | 186 if (result.is_null()) { |
| 168 Counters::compilation_cache_misses.Increment(); | 187 Counters::compilation_cache_misses.Increment(); |
| 169 } else { | 188 } else { |
| 170 Counters::compilation_cache_hits.Increment(); | 189 Counters::compilation_cache_hits.Increment(); |
| 171 } | 190 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 void CompilationCache::PutRegExp(Handle<String> source, | 228 void CompilationCache::PutRegExp(Handle<String> source, |
| 210 JSRegExp::Flags flags, | 229 JSRegExp::Flags flags, |
| 211 Handle<FixedArray> data) { | 230 Handle<FixedArray> data) { |
| 212 HandleScope scope; | 231 HandleScope scope; |
| 213 Handle<CompilationCacheTable> table = GetTable(REGEXP); | 232 Handle<CompilationCacheTable> table = GetTable(REGEXP); |
| 214 CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data)); | 233 CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data)); |
| 215 } | 234 } |
| 216 | 235 |
| 217 | 236 |
| 218 void CompilationCache::Clear() { | 237 void CompilationCache::Clear() { |
| 219 for (int i = 0; i < NUMBER_OF_ENTRY_KINDS; i++) { | 238 for (int i = 0; i < NUMBER_OF_TABLE_ENTRIES; i++) { |
| 220 tables[i] = Heap::undefined_value(); | 239 tables[i] = Heap::undefined_value(); |
| 221 } | 240 } |
| 222 } | 241 } |
| 223 | 242 |
| 224 | 243 |
| 225 void CompilationCache::Iterate(ObjectVisitor* v) { | 244 void CompilationCache::Iterate(ObjectVisitor* v) { |
| 226 v->VisitPointers(&tables[0], &tables[NUMBER_OF_ENTRY_KINDS]); | 245 v->VisitPointers(&tables[0], &tables[NUMBER_OF_TABLE_ENTRIES]); |
| 246 } |
| 247 |
| 248 |
| 249 void CompilationCache::MarkCompactPrologue() { |
| 250 ASSERT(LAST_ENTRY == SCRIPT); |
| 251 for (int i = NUMBER_OF_TABLE_ENTRIES - 1; i > SCRIPT; i--) { |
| 252 tables[i] = tables[i - 1]; |
| 253 } |
| 254 for (int j = 0; j <= LAST_ENTRY; j++) { |
| 255 tables[j] = Heap::undefined_value(); |
| 256 } |
| 227 } | 257 } |
| 228 | 258 |
| 229 | 259 |
| 230 } } // namespace v8::internal | 260 } } // namespace v8::internal |
| OLD | NEW |