Commit b5a72a16 authored by kasperl@chromium.org's avatar kasperl@chromium.org

Generalized the EvalCache into a CompilationCache and enabled

it for scripts too. In the context of Chromium, this should 
have a very positive impact on memory consumption for web apps
that run multiple tabs from the same domain with a lot of the
same JavaScript code.

For now, the cache retirement policy is really simple:
Whenever a mark-sweep collection is started we clear the
cache. This guarantees that this change will not have a
huge negative impact on memory consumption, but it may
not be ideal. We should consider a more sophisticated LRU
scheme.
Review URL: http://codereview.chromium.org/1933

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@270 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 6063e76c
...@@ -37,9 +37,9 @@ SOURCES = { ...@@ -37,9 +37,9 @@ SOURCES = {
'all': [ 'all': [
'accessors.cc', 'allocation.cc', 'api.cc', 'assembler.cc', 'ast.cc', 'accessors.cc', 'allocation.cc', 'api.cc', 'assembler.cc', 'ast.cc',
'bootstrapper.cc', 'builtins.cc', 'checks.cc', 'code-stubs.cc', 'bootstrapper.cc', 'builtins.cc', 'checks.cc', 'code-stubs.cc',
'codegen.cc', 'compiler.cc', 'contexts.cc', 'conversions.cc', 'codegen.cc', 'compilation-cache.cc', 'compiler.cc', 'contexts.cc',
'counters.cc', 'dateparser.cc', 'debug.cc', 'disassembler.cc', 'conversions.cc', 'counters.cc', 'dateparser.cc', 'debug.cc',
'execution.cc', 'factory.cc', 'flags.cc', 'frames.cc', 'disassembler.cc', 'execution.cc', 'factory.cc', 'flags.cc', 'frames.cc',
'global-handles.cc', 'handles.cc', 'hashmap.cc', 'heap.cc', 'ic.cc', 'global-handles.cc', 'handles.cc', 'hashmap.cc', 'heap.cc', 'ic.cc',
'jsregexp.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc', 'jsregexp.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc',
'parser.cc', 'property.cc', 'rewriter.cc', 'runtime.cc', 'scanner.cc', 'parser.cc', 'property.cc', 'rewriter.cc', 'runtime.cc', 'scanner.cc',
......
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "compilation-cache.h"
namespace v8 { namespace internal {
enum {
NUMBER_OF_ENTRY_KINDS = CompilationCache::EVAL_CONTEXTUAL + 1
};
// Keep separate tables for the different entry kinds.
static Object* tables[NUMBER_OF_ENTRY_KINDS] = { 0, };
static Handle<CompilationCacheTable> AllocateTable(int size) {
CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size),
CompilationCacheTable);
}
static Handle<CompilationCacheTable> GetTable(CompilationCache::Entry entry) {
Handle<CompilationCacheTable> result;
if (tables[entry]->IsUndefined()) {
static const int kInitialCacheSize = 64;
result = AllocateTable(kInitialCacheSize);
tables[entry] = *result;
} else {
CompilationCacheTable* table = CompilationCacheTable::cast(tables[entry]);
result = Handle<CompilationCacheTable>(table);
}
return result;
}
// We only re-use a cached function for some script source code if the
// script originates from the same places. This is to avoid issues
// when reporting errors, etc.
static bool HasOrigin(Handle<JSFunction> boilerplate,
Handle<Object> name,
int line_offset,
int column_offset) {
Handle<Script> script =
Handle<Script>(Script::cast(boilerplate->shared()->script()));
// If the script name isn't set, the boilerplate script should have
// an undefined name to have the same origin.
if (name.is_null()) {
return script->name()->IsUndefined();
}
// Do the fast bailout checks first.
if (line_offset != script->line_offset()->value()) return false;
if (column_offset != script->column_offset()->value()) return false;
// Check that both names are strings. If not, no match.
if (!name->IsString() || !script->name()->IsString()) return false;
// Compare the two name strings for equality.
return String::cast(*name)->Equals(String::cast(script->name()));
}
static Handle<JSFunction> Lookup(Handle<String> source,
CompilationCache::Entry entry) {
Handle<CompilationCacheTable> table = GetTable(entry);
Object* result = table->Lookup(*source);
if (result->IsJSFunction()) {
return Handle<JSFunction>(JSFunction::cast(result));
} else {
return Handle<JSFunction>::null();
}
}
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)) {
Counters::compilation_cache_hits.Increment();
} else {
result = Handle<JSFunction>::null();
Counters::compilation_cache_misses.Increment();
}
return result;
}
Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source,
Entry entry) {
ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL);
Handle<JSFunction> result = Lookup(source, entry);
if (result.is_null()) {
Counters::compilation_cache_misses.Increment();
} else {
Counters::compilation_cache_hits.Increment();
}
return result;
}
void CompilationCache::Associate(Handle<String> source,
Entry entry,
Handle<JSFunction> boilerplate) {
ASSERT(boilerplate->IsBoilerplate());
Handle<CompilationCacheTable> table = GetTable(entry);
CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate));
}
void CompilationCache::Clear() {
for (int i = 0; i < NUMBER_OF_ENTRY_KINDS; i++) {
tables[i] = Heap::undefined_value();
}
}
void CompilationCache::Iterate(ObjectVisitor* v) {
v->VisitPointers(&tables[0], &tables[NUMBER_OF_ENTRY_KINDS]);
}
} } // namespace v8::internal
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_COMPILATION_CACHE_H_
#define V8_COMPILATION_CACHE_H_
namespace v8 { namespace internal {
// The compilation cache keeps function boilerplates for compiled
// scripts and evals. The boilerplates are looked up using the source
// string as the key.
class CompilationCache {
public:
// The same source code string has different compiled code for
// scripts and evals. Internally, we use separate caches to avoid
// getting the wrong kind of entry when looking up.
enum Entry {
SCRIPT,
EVAL_GLOBAL,
EVAL_CONTEXTUAL
};
// Finds the script function boilerplate for a source
// string. Returns an empty handle if the cache doesn't contain a
// script for the given source string with the right origin.
static Handle<JSFunction> LookupScript(Handle<String> source,
Handle<Object> name,
int line_offset,
int column_offset);
// Finds the function boilerplate for a source string for
// eval. Returns an empty handle if the cache doesn't contain a
// script for the given source string.
static Handle<JSFunction> LookupEval(Handle<String> source,
Entry entry);
// Associate the (source, kind) pair to the boilerplate. This may
// overwrite an existing mapping.
static void Associate(Handle<String> source,
Entry entry,
Handle<JSFunction> boilerplate);
// Clear the cache - also used to initialize the cache at startup.
static void Clear();
// GC support.
static void Iterate(ObjectVisitor* v);
// Notify the cache that a mark-sweep garbage collection is about to
// take place. This is used to retire entries from the cache to
// avoid keeping them alive too long without using them. For now, we
// just clear the cache but we should consider are more
// sophisticated LRU scheme.
static void MarkCompactPrologue() { Clear(); }
};
} } // namespace v8::internal
#endif // V8_COMPILATION_CACHE_H_
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "bootstrapper.h" #include "bootstrapper.h"
#include "codegen-inl.h" #include "codegen-inl.h"
#include "compilation-cache.h"
#include "compiler.h" #include "compiler.h"
#include "debug.h" #include "debug.h"
#include "scopes.h" #include "scopes.h"
...@@ -170,6 +171,17 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source, ...@@ -170,6 +171,17 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source,
// The VM is in the COMPILER state until exiting this function. // The VM is in the COMPILER state until exiting this function.
VMState state(COMPILER); VMState state(COMPILER);
// Do a lookup in the compilation cache but not for extensions.
Handle<JSFunction> result;
if (extension == NULL) {
result = CompilationCache::LookupScript(source,
script_name,
line_offset,
column_offset);
}
if (result.is_null()) {
// No cache entry found. Do pre-parsing and compile the script.
ScriptDataImpl* pre_data = input_pre_data; ScriptDataImpl* pre_data = input_pre_data;
if (pre_data == NULL && source->length() >= FLAG_min_preparse_length) { if (pre_data == NULL && source->length() >= FLAG_min_preparse_length) {
Access<SafeStringInputBuffer> buf(&safe_string_input_buffer); Access<SafeStringInputBuffer> buf(&safe_string_input_buffer);
...@@ -185,11 +197,17 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source, ...@@ -185,11 +197,17 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source,
script->set_column_offset(Smi::FromInt(column_offset)); script->set_column_offset(Smi::FromInt(column_offset));
} }
Handle<JSFunction> result = // Compile the function and add it to the cache.
MakeFunction(true, false, script, extension, pre_data); result = MakeFunction(true, false, script, extension, pre_data);
if (extension == NULL && !result.is_null()) {
CompilationCache::Associate(source, CompilationCache::SCRIPT, result);
}
if (input_pre_data == NULL && pre_data != NULL) // Get rid of the pre-parsing data (if necessary).
if (input_pre_data == NULL && pre_data != NULL) {
delete pre_data; delete pre_data;
}
}
return result; return result;
} }
...@@ -202,10 +220,22 @@ Handle<JSFunction> Compiler::CompileEval(bool is_global, ...@@ -202,10 +220,22 @@ Handle<JSFunction> Compiler::CompileEval(bool is_global,
// The VM is in the COMPILER state until exiting this function. // The VM is in the COMPILER state until exiting this function.
VMState state(COMPILER); VMState state(COMPILER);
CompilationCache::Entry entry = is_global
? CompilationCache::EVAL_GLOBAL
: CompilationCache::EVAL_CONTEXTUAL;
// Do a lookup in the compilation cache; if the entry is not there,
// invoke the compiler and add the result to the cache.
Handle<JSFunction> result = CompilationCache::LookupEval(source, entry);
if (result.is_null()) {
// Create a script object describing the script to be compiled. // Create a script object describing the script to be compiled.
Handle<Script> script = Factory::NewScript(source); Handle<Script> script = Factory::NewScript(source);
return MakeFunction(is_global, true, script, NULL, NULL); result = MakeFunction(is_global, true, script, NULL, NULL);
if (!result.is_null()) {
CompilationCache::Associate(source, entry, result);
}
}
return result;
} }
......
...@@ -56,34 +56,6 @@ DECLARE_bool(gc_greedy); ...@@ -56,34 +56,6 @@ DECLARE_bool(gc_greedy);
} }
// Don't use the following names: __object__, __failure__.
#define CALL_HEAP_FUNCTION_VOID(FUNCTION_CALL) \
GC_GREEDY_CHECK(); \
Object* __object__ = FUNCTION_CALL; \
if (__object__->IsFailure()) { \
if (__object__->IsRetryAfterGC()) { \
Failure* __failure__ = Failure::cast(__object__); \
if (!Heap::CollectGarbage(__failure__->requested(), \
__failure__->allocation_space())) { \
/* TODO(1181417): Fix this. */ \
V8::FatalProcessOutOfMemory("Handles"); \
} \
__object__ = FUNCTION_CALL; \
if (__object__->IsFailure()) { \
if (__object__->IsRetryAfterGC()) { \
/* TODO(1181417): Fix this. */ \
V8::FatalProcessOutOfMemory("Handles"); \
} \
return; \
} \
} else { \
return; \
} \
}
Handle<FixedArray> AddKeysFromJSArray(Handle<FixedArray> content, Handle<FixedArray> AddKeysFromJSArray(Handle<FixedArray> content,
Handle<JSArray> array) { Handle<JSArray> array) {
CALL_HEAP_FUNCTION(content->AddKeysFromJSArray(*array), FixedArray); CALL_HEAP_FUNCTION(content->AddKeysFromJSArray(*array), FixedArray);
......
...@@ -196,6 +196,32 @@ OldSpace* Heap::TargetSpace(HeapObject* object) { ...@@ -196,6 +196,32 @@ OldSpace* Heap::TargetSpace(HeapObject* object) {
} while (false) } while (false)
// Don't use the following names: __object__, __failure__.
#define CALL_HEAP_FUNCTION_VOID(FUNCTION_CALL) \
GC_GREEDY_CHECK(); \
Object* __object__ = FUNCTION_CALL; \
if (__object__->IsFailure()) { \
if (__object__->IsRetryAfterGC()) { \
Failure* __failure__ = Failure::cast(__object__); \
if (!Heap::CollectGarbage(__failure__->requested(), \
__failure__->allocation_space())) { \
/* TODO(1181417): Fix this. */ \
V8::FatalProcessOutOfMemory("Handles"); \
} \
__object__ = FUNCTION_CALL; \
if (__object__->IsFailure()) { \
if (__object__->IsRetryAfterGC()) { \
/* TODO(1181417): Fix this. */ \
V8::FatalProcessOutOfMemory("Handles"); \
} \
return; \
} \
} else { \
return; \
} \
}
#ifdef DEBUG #ifdef DEBUG
inline bool Heap::allow_allocation(bool new_state) { inline bool Heap::allow_allocation(bool new_state) {
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "api.h" #include "api.h"
#include "bootstrapper.h" #include "bootstrapper.h"
#include "codegen-inl.h" #include "codegen-inl.h"
#include "compilation-cache.h"
#include "debug.h" #include "debug.h"
#include "global-handles.h" #include "global-handles.h"
#include "jsregexp.h" #include "jsregexp.h"
...@@ -447,10 +448,7 @@ void Heap::MarkCompact(GCTracer* tracer) { ...@@ -447,10 +448,7 @@ void Heap::MarkCompact(GCTracer* tracer) {
void Heap::MarkCompactPrologue() { void Heap::MarkCompactPrologue() {
// Empty eval caches CompilationCache::MarkCompactPrologue();
Heap::eval_cache_global_ = Heap::null_value();
Heap::eval_cache_non_global_ = Heap::null_value();
RegExpImpl::OldSpaceCollectionPrologue(); RegExpImpl::OldSpaceCollectionPrologue();
Top::MarkCompactPrologue(); Top::MarkCompactPrologue();
ThreadManager::MarkCompactPrologue(); ThreadManager::MarkCompactPrologue();
...@@ -1208,9 +1206,8 @@ bool Heap::CreateInitialObjects() { ...@@ -1208,9 +1206,8 @@ bool Heap::CreateInitialObjects() {
if (obj->IsFailure()) return false; if (obj->IsFailure()) return false;
natives_source_cache_ = FixedArray::cast(obj); natives_source_cache_ = FixedArray::cast(obj);
// Initialized eval cache to null value. // Initialize compilation cache.
eval_cache_global_ = null_value(); CompilationCache::Clear();
eval_cache_non_global_ = null_value();
return true; return true;
} }
...@@ -2279,34 +2276,6 @@ Object* Heap::LookupSymbol(String* string) { ...@@ -2279,34 +2276,6 @@ Object* Heap::LookupSymbol(String* string) {
} }
Object* Heap::LookupEvalCache(bool is_global_context, String* src) {
Object* cache = is_global_context ?
eval_cache_global_ : eval_cache_non_global_;
return cache == null_value() ?
null_value() : EvalCache::cast(cache)->Lookup(src);
}
Object* Heap::PutInEvalCache(bool is_global_context, String* src,
JSFunction* value) {
Object** cache_ptr = is_global_context ?
&eval_cache_global_ : &eval_cache_non_global_;
if (*cache_ptr == null_value()) {
Object* obj = EvalCache::Allocate(kInitialEvalCacheSize);
if (obj->IsFailure()) return false;
*cache_ptr = obj;
}
Object* new_cache =
EvalCache::cast(*cache_ptr)->Put(src, value);
if (new_cache->IsFailure()) return new_cache;
*cache_ptr = new_cache;
return value;
}
#ifdef DEBUG #ifdef DEBUG
void Heap::ZapFromSpace() { void Heap::ZapFromSpace() {
ASSERT(HAS_HEAP_OBJECT_TAG(kFromSpaceZapValue)); ASSERT(HAS_HEAP_OBJECT_TAG(kFromSpaceZapValue));
...@@ -2417,6 +2386,8 @@ void Heap::IterateStrongRoots(ObjectVisitor* v) { ...@@ -2417,6 +2386,8 @@ void Heap::IterateStrongRoots(ObjectVisitor* v) {
SYNCHRONIZE_TAG("top"); SYNCHRONIZE_TAG("top");
Debug::Iterate(v); Debug::Iterate(v);
SYNCHRONIZE_TAG("debug"); SYNCHRONIZE_TAG("debug");
CompilationCache::Iterate(v);
SYNCHRONIZE_TAG("compilationcache");
// Iterate over local handles in handle scopes. // Iterate over local handles in handle scopes.
HandleScopeImplementer::Iterate(v); HandleScopeImplementer::Iterate(v);
......
...@@ -122,9 +122,8 @@ namespace v8 { namespace internal { ...@@ -122,9 +122,8 @@ namespace v8 { namespace internal {
V(Code, c_entry_debug_break_code) \ V(Code, c_entry_debug_break_code) \
V(FixedArray, number_string_cache) \ V(FixedArray, number_string_cache) \
V(FixedArray, single_character_string_cache) \ V(FixedArray, single_character_string_cache) \
V(FixedArray, natives_source_cache) \ V(FixedArray, natives_source_cache)
V(Object, eval_cache_global) \
V(Object, eval_cache_non_global)
#define ROOT_LIST(V) \ #define ROOT_LIST(V) \
STRONG_ROOT_LIST(V) \ STRONG_ROOT_LIST(V) \
...@@ -531,28 +530,6 @@ class Heap : public AllStatic { ...@@ -531,28 +530,6 @@ class Heap : public AllStatic {
} }
static Object* LookupSymbol(String* str); static Object* LookupSymbol(String* str);
// EvalCache caches function boilerplates for compiled scripts
// from 'eval' function.
// Source string is used as the key, and compiled function
// boilerplate as value. Because the same source has different
// compiled code in global or local context, we use separate
// caches for global and local contexts.
// Caches are cleared before mark-compact/mark-sweep GC's.
// Finds the function boilerplate of a source string.
// It returns a JSFunction object if found in the cache.
// The first parameter specifies whether the code is
// compiled in a global context.
static Object* LookupEvalCache(bool is_global_context, String* src);
// Put a source string and its compiled function boilerplate
// in the eval cache. The cache may expand, and returns failure
// if it cannot expand the cache, otherwise the value is returned.
// The first parameter specifies whether the boilerplate is
// compiled in a global context.
static Object* PutInEvalCache(bool is_global_context,
String* src, JSFunction* value);
// Compute the matching symbol map for a string if possible. // Compute the matching symbol map for a string if possible.
// NULL is returned if string is in new space or not flattened. // NULL is returned if string is in new space or not flattened.
static Map* SymbolMapForString(String* str); static Map* SymbolMapForString(String* str);
......
...@@ -314,10 +314,8 @@ bool Object::IsSymbolTable() { ...@@ -314,10 +314,8 @@ bool Object::IsSymbolTable() {
} }
bool Object::IsEvalCache() { bool Object::IsCompilationCacheTable() {
return IsHashTable() && return IsHashTable();
(this == Heap::eval_cache_global() ||
this == Heap::eval_cache_non_global());
} }
...@@ -1096,7 +1094,7 @@ CAST_ACCESSOR(FixedArray) ...@@ -1096,7 +1094,7 @@ CAST_ACCESSOR(FixedArray)
CAST_ACCESSOR(DescriptorArray) CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(Dictionary) CAST_ACCESSOR(Dictionary)
CAST_ACCESSOR(SymbolTable) CAST_ACCESSOR(SymbolTable)
CAST_ACCESSOR(EvalCache) CAST_ACCESSOR(CompilationCacheTable)
CAST_ACCESSOR(String) CAST_ACCESSOR(String)
CAST_ACCESSOR(SeqString) CAST_ACCESSOR(SeqString)
CAST_ACCESSOR(AsciiString) CAST_ACCESSOR(AsciiString)
......
...@@ -5576,7 +5576,7 @@ Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) { ...@@ -5576,7 +5576,7 @@ Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
} }
Object* EvalCache::Lookup(String* src) { Object* CompilationCacheTable::Lookup(String* src) {
StringKey key(src); StringKey key(src);
int entry = FindEntry(&key); int entry = FindEntry(&key);
if (entry != -1) { if (entry != -1) {
...@@ -5587,12 +5587,13 @@ Object* EvalCache::Lookup(String* src) { ...@@ -5587,12 +5587,13 @@ Object* EvalCache::Lookup(String* src) {
} }
Object* EvalCache::Put(String* src, Object* value) { Object* CompilationCacheTable::Put(String* src, Object* value) {
StringKey key(src); StringKey key(src);
Object* obj = EnsureCapacity(1, &key); Object* obj = EnsureCapacity(1, &key);
if (obj->IsFailure()) return obj; if (obj->IsFailure()) return obj;
EvalCache* cache = reinterpret_cast<EvalCache*>(obj); CompilationCacheTable* cache =
reinterpret_cast<CompilationCacheTable*>(obj);
int entry = cache->FindInsertionEntry(src, key.Hash()); int entry = cache->FindInsertionEntry(src, key.Hash());
cache->set(EntryToIndex(entry), src); cache->set(EntryToIndex(entry), src);
cache->set(EntryToIndex(entry) + 1, value); cache->set(EntryToIndex(entry) + 1, value);
......
...@@ -614,7 +614,7 @@ class Object BASE_EMBEDDED { ...@@ -614,7 +614,7 @@ class Object BASE_EMBEDDED {
inline bool IsHashTable(); inline bool IsHashTable();
inline bool IsDictionary(); inline bool IsDictionary();
inline bool IsSymbolTable(); inline bool IsSymbolTable();
inline bool IsEvalCache(); inline bool IsCompilationCacheTable();
inline bool IsPrimitive(); inline bool IsPrimitive();
inline bool IsGlobalObject(); inline bool IsGlobalObject();
inline bool IsJSGlobalObject(); inline bool IsJSGlobalObject();
...@@ -1818,19 +1818,16 @@ class SymbolTable: public HashTable<0, 1> { ...@@ -1818,19 +1818,16 @@ class SymbolTable: public HashTable<0, 1> {
}; };
// EvalCache for caching eval'ed string and function. class CompilationCacheTable: public HashTable<0, 2> {
//
// The cache is cleaned up during a mark-compact GC.
class EvalCache: public HashTable<0, 2> {
public: public:
// Find cached value for a string key, otherwise return null. // Find cached value for a string key, otherwise return null.
Object* Lookup(String* src); Object* Lookup(String* src);
Object* Put(String* src, Object* value); Object* Put(String* src, Object* value);
static inline EvalCache* cast(Object* obj); static inline CompilationCacheTable* cast(Object* obj);
private: private:
DISALLOW_IMPLICIT_CONSTRUCTORS(EvalCache); DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheTable);
}; };
......
...@@ -3364,6 +3364,7 @@ static Object* Runtime_EvalReceiver(Arguments args) { ...@@ -3364,6 +3364,7 @@ static Object* Runtime_EvalReceiver(Arguments args) {
static Object* Runtime_CompileString(Arguments args) { static Object* Runtime_CompileString(Arguments args) {
HandleScope scope; HandleScope scope;
ASSERT(args.length() == 2); ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(String, source, 0);
bool contextual = args[1]->IsTrue(); bool contextual = args[1]->IsTrue();
RUNTIME_ASSERT(contextual || args[1]->IsFalse()); RUNTIME_ASSERT(contextual || args[1]->IsFalse());
...@@ -3380,27 +3381,12 @@ static Object* Runtime_CompileString(Arguments args) { ...@@ -3380,27 +3381,12 @@ static Object* Runtime_CompileString(Arguments args) {
context = Handle<Context>(Top::context()->global_context()); context = Handle<Context>(Top::context()->global_context());
} }
// Compile eval() source.
bool is_global_context = context->IsGlobalContext();
Handle<String> source(String::cast(args[0]));
Object* obj = Heap::LookupEvalCache(is_global_context, *source);
if (obj->IsFailure()) return obj;
Handle<JSFunction> boilerplate; // Compile source string.
if (!obj->IsJSFunction()) { bool is_global = context->IsGlobalContext();
Counters::eval_cache_misses.Increment(); Handle<JSFunction> boilerplate =
boilerplate = Compiler::CompileEval(is_global_context, source); Compiler::CompileEval(is_global, source);
if (boilerplate.is_null()) return Failure::Exception(); if (boilerplate.is_null()) return Failure::Exception();
Object* obj =
Heap::PutInEvalCache(is_global_context, *source, *boilerplate);
if (obj->IsFailure()) return obj;
} else {
Counters::eval_cache_hits.Increment();
boilerplate = Handle<JSFunction>(JSFunction::cast(obj));
}
Handle<JSFunction> fun = Handle<JSFunction> fun =
Factory::NewFunctionFromBoilerplate(boilerplate, context); Factory::NewFunctionFromBoilerplate(boilerplate, context);
return *fun; return *fun;
......
...@@ -71,8 +71,8 @@ namespace v8 { namespace internal { ...@@ -71,8 +71,8 @@ namespace v8 { namespace internal {
SC(call_normal_stubs, V8.CallNormalStubs) \ SC(call_normal_stubs, V8.CallNormalStubs) \
SC(call_megamorphic_stubs, V8.CallMegamorphicStubs) \ SC(call_megamorphic_stubs, V8.CallMegamorphicStubs) \
SC(arguments_adaptors, V8.ArgumentsAdaptors) \ SC(arguments_adaptors, V8.ArgumentsAdaptors) \
SC(eval_cache_hits, V8.EvalCacheHits) \ SC(compilation_cache_hits, V8.CompilationCacheHits) \
SC(eval_cache_misses, V8.EvalCacheMisses) \ SC(compilation_cache_misses, V8.CompilationCacheMisses) \
/* Amount of evaled source code. */ \ /* Amount of evaled source code. */ \
SC(total_eval_size, V8.TotalEvalSize) \ SC(total_eval_size, V8.TotalEvalSize) \
/* Amount of loaded source code. */ \ /* Amount of loaded source code. */ \
......
...@@ -4897,3 +4897,20 @@ THREADED_TEST(TryCatchSourceInfo) { ...@@ -4897,3 +4897,20 @@ THREADED_TEST(TryCatchSourceInfo) {
v8::String::AsciiValue name(message->GetScriptResourceName()); v8::String::AsciiValue name(message->GetScriptResourceName());
CHECK_EQ("test.js", *name); CHECK_EQ("test.js", *name);
} }
THREADED_TEST(CompilationCache) {
v8::HandleScope scope;
LocalContext context;
v8::Handle<v8::String> source0 = v8::String::New("1234");
v8::Handle<v8::String> source1 = v8::String::New("1234");
v8::Handle<v8::Script> script0 =
v8::Script::Compile(source0, v8::String::New("test.js"));
v8::Handle<v8::Script> script1 =
v8::Script::Compile(source1, v8::String::New("test.js"));
v8::Handle<v8::Script> script2 =
v8::Script::Compile(source0); // different origin
CHECK_EQ(1234, script0->Run()->Int32Value());
CHECK_EQ(1234, script1->Run()->Int32Value());
CHECK_EQ(1234, script2->Run()->Int32Value());
}
...@@ -380,6 +380,14 @@ ...@@ -380,6 +380,14 @@
RelativePath="..\..\src\codegen.h" RelativePath="..\..\src\codegen.h"
> >
</File> </File>
<File
RelativePath="..\..\src\compilation-cache.cc"
>
</File>
<File
RelativePath="..\..\src\compilation-cache.h"
>
</File>
<File <File
RelativePath="..\..\src\compiler.cc" RelativePath="..\..\src\compiler.cc"
> >
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment