summaryrefslogtreecommitdiff
path: root/deps/v8/src/compilation-cache.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compilation-cache.cc')
-rw-r--r--deps/v8/src/compilation-cache.cc159
1 files changed, 123 insertions, 36 deletions
diff --git a/deps/v8/src/compilation-cache.cc b/deps/v8/src/compilation-cache.cc
index 4c02d86ce2..421b6766fe 100644
--- a/deps/v8/src/compilation-cache.cc
+++ b/deps/v8/src/compilation-cache.cc
@@ -29,15 +29,30 @@
#include "compilation-cache.h"
-namespace v8 { namespace internal {
+namespace v8 {
+namespace internal {
enum {
- NUMBER_OF_ENTRY_KINDS = CompilationCache::LAST_ENTRY + 1
+ // The number of script generations tell how many GCs a script can
+ // survive in the compilation cache, before it will be flushed if it
+ // hasn't been used.
+ NUMBER_OF_SCRIPT_GENERATIONS = 5,
+
+ // The compilation cache consists of tables - one for each entry
+ // kind plus extras for the script generations.
+ NUMBER_OF_TABLE_ENTRIES =
+ CompilationCache::LAST_ENTRY + NUMBER_OF_SCRIPT_GENERATIONS
};
+// Current enable state of the compilation cache.
+static bool enabled = true;
+static inline bool IsEnabled() {
+ return FLAG_compilation_cache && enabled;
+}
+
// Keep separate tables for the different entry kinds.
-static Object* tables[NUMBER_OF_ENTRY_KINDS] = { 0, };
+static Object* tables[NUMBER_OF_TABLE_ENTRIES] = { 0, };
static Handle<CompilationCacheTable> AllocateTable(int size) {
@@ -46,14 +61,15 @@ static Handle<CompilationCacheTable> AllocateTable(int size) {
}
-static Handle<CompilationCacheTable> GetTable(CompilationCache::Entry entry) {
+static Handle<CompilationCacheTable> GetTable(int index) {
+ ASSERT(index >= 0 && index < NUMBER_OF_TABLE_ENTRIES);
Handle<CompilationCacheTable> result;
- if (tables[entry]->IsUndefined()) {
+ if (tables[index]->IsUndefined()) {
static const int kInitialCacheSize = 64;
result = AllocateTable(kInitialCacheSize);
- tables[entry] = *result;
+ tables[index] = *result;
} else {
- CompilationCacheTable* table = CompilationCacheTable::cast(tables[entry]);
+ CompilationCacheTable* table = CompilationCacheTable::cast(tables[index]);
result = Handle<CompilationCacheTable>(table);
}
return result;
@@ -121,47 +137,80 @@ static bool HasOrigin(Handle<JSFunction> boilerplate,
}
-static Handle<JSFunction> Lookup(Handle<String> source,
- CompilationCache::Entry entry) {
- // Make sure not to leak the table into the surrounding handle
- // scope. Otherwise, we risk keeping old tables around even after
- // having cleared the cache.
- Object* result;
- { HandleScope scope;
- Handle<CompilationCacheTable> table = GetTable(entry);
- result = table->Lookup(*source);
- }
- if (result->IsJSFunction()) {
- return Handle<JSFunction>(JSFunction::cast(result));
- } else {
- return Handle<JSFunction>::null();
- }
-}
-
-
-// TODO(245): Need to allow identical code from different contexts to be
-// cached. Currently the first use will be cached, but subsequent code
-// from different source / line won't.
+// TODO(245): Need to allow identical code from different contexts to
+// be cached in the same script generation. Currently the first use
+// will be cached, but subsequent code from different source / line
+// won't.
Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source,
Handle<Object> name,
int line_offset,
int column_offset) {
- Handle<JSFunction> result = Lookup(source, SCRIPT);
- if (result.is_null()) {
- Counters::compilation_cache_misses.Increment();
- } else if (HasOrigin(result, name, line_offset, column_offset)) {
+ if (!IsEnabled()) {
+ return Handle<JSFunction>::null();
+ }
+
+ // Use an int for the generation index, so value range propagation
+ // in gcc 4.3+ won't assume it can only go up to LAST_ENTRY when in
+ // fact it can go up to SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS.
+ int generation = SCRIPT;
+ Object* result = NULL;
+
+ // Probe the script generation tables. Make sure not to leak handles
+ // into the caller's handle scope.
+ { HandleScope scope;
+ while (generation < SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS) {
+ Handle<CompilationCacheTable> table = GetTable(generation);
+ Handle<Object> probe(table->Lookup(*source));
+ if (probe->IsJSFunction()) {
+ Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(probe);
+ // Break when we've found a suitable boilerplate function that
+ // matches the origin.
+ if (HasOrigin(boilerplate, name, line_offset, column_offset)) {
+ result = *boilerplate;
+ break;
+ }
+ }
+ // Go to the next generation.
+ generation++;
+ }
+ }
+
+ static void* script_histogram = StatsTable::CreateHistogram(
+ "V8.ScriptCache",
+ 0,
+ NUMBER_OF_SCRIPT_GENERATIONS,
+ NUMBER_OF_SCRIPT_GENERATIONS + 1);
+
+ if (script_histogram != NULL) {
+ // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss.
+ StatsTable::AddHistogramSample(script_histogram, generation - SCRIPT);
+ }
+
+ // Once outside the manacles of the handle scope, we need to recheck
+ // to see if we actually found a cached script. If so, we return a
+ // handle created in the caller's handle scope.
+ if (result != NULL) {
+ Handle<JSFunction> boilerplate(JSFunction::cast(result));
+ ASSERT(HasOrigin(boilerplate, name, line_offset, column_offset));
+ // If the script was found in a later generation, we promote it to
+ // the first generation to let it survive longer in the cache.
+ if (generation != SCRIPT) PutScript(source, boilerplate);
Counters::compilation_cache_hits.Increment();
+ return boilerplate;
} else {
- result = Handle<JSFunction>::null();
Counters::compilation_cache_misses.Increment();
+ return Handle<JSFunction>::null();
}
- return result;
}
Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source,
Handle<Context> context,
Entry entry) {
+ if (!IsEnabled()) {
+ return Handle<JSFunction>::null();
+ }
+
ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL);
Handle<JSFunction> result = Lookup(source, context, entry);
if (result.is_null()) {
@@ -175,6 +224,10 @@ Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source,
Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
JSRegExp::Flags flags) {
+ if (!IsEnabled()) {
+ return Handle<FixedArray>::null();
+ }
+
Handle<FixedArray> result = Lookup(source, flags);
if (result.is_null()) {
Counters::compilation_cache_misses.Increment();
@@ -187,6 +240,10 @@ Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
void CompilationCache::PutScript(Handle<String> source,
Handle<JSFunction> boilerplate) {
+ if (!IsEnabled()) {
+ return;
+ }
+
HandleScope scope;
ASSERT(boilerplate->IsBoilerplate());
Handle<CompilationCacheTable> table = GetTable(SCRIPT);
@@ -198,6 +255,10 @@ void CompilationCache::PutEval(Handle<String> source,
Handle<Context> context,
Entry entry,
Handle<JSFunction> boilerplate) {
+ if (!IsEnabled()) {
+ return;
+ }
+
HandleScope scope;
ASSERT(boilerplate->IsBoilerplate());
Handle<CompilationCacheTable> table = GetTable(entry);
@@ -209,6 +270,10 @@ void CompilationCache::PutEval(Handle<String> source,
void CompilationCache::PutRegExp(Handle<String> source,
JSRegExp::Flags flags,
Handle<FixedArray> data) {
+ if (!IsEnabled()) {
+ return;
+ }
+
HandleScope scope;
Handle<CompilationCacheTable> table = GetTable(REGEXP);
CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data));
@@ -216,14 +281,36 @@ void CompilationCache::PutRegExp(Handle<String> source,
void CompilationCache::Clear() {
- for (int i = 0; i < NUMBER_OF_ENTRY_KINDS; i++) {
+ for (int i = 0; i < NUMBER_OF_TABLE_ENTRIES; i++) {
tables[i] = Heap::undefined_value();
}
}
void CompilationCache::Iterate(ObjectVisitor* v) {
- v->VisitPointers(&tables[0], &tables[NUMBER_OF_ENTRY_KINDS]);
+ v->VisitPointers(&tables[0], &tables[NUMBER_OF_TABLE_ENTRIES]);
+}
+
+
+void CompilationCache::MarkCompactPrologue() {
+ ASSERT(LAST_ENTRY == SCRIPT);
+ for (int i = NUMBER_OF_TABLE_ENTRIES - 1; i > SCRIPT; i--) {
+ tables[i] = tables[i - 1];
+ }
+ for (int j = 0; j <= LAST_ENTRY; j++) {
+ tables[j] = Heap::undefined_value();
+ }
+}
+
+
+void CompilationCache::Enable() {
+ enabled = true;
+}
+
+
+void CompilationCache::Disable() {
+ enabled = false;
+ Clear();
}