- Added caching of regexp data in the compilation cache.

- Changed the structure of regexp objects from having two internal
  fields to having a single field containing a fixed array, since it's
  easier to store the whole fixed array in the cache.
- Move printing of the command to after printing std{err,out} in the
  compact progress indicators in the test framework.


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@579 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 5d36cc80
......@@ -32,7 +32,7 @@
namespace v8 { namespace internal {
enum {
NUMBER_OF_ENTRY_KINDS = CompilationCache::EVAL_CONTEXTUAL + 1
NUMBER_OF_ENTRY_KINDS = CompilationCache::LAST_ENTRY + 1
};
......@@ -132,9 +132,9 @@ Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source,
}
void CompilationCache::Associate(Handle<String> source,
Entry entry,
Handle<JSFunction> boilerplate) {
void CompilationCache::PutFunction(Handle<String> source,
Entry entry,
Handle<JSFunction> boilerplate) {
HandleScope scope;
ASSERT(boilerplate->IsBoilerplate());
Handle<CompilationCacheTable> table = GetTable(entry);
......@@ -142,6 +142,29 @@ void CompilationCache::Associate(Handle<String> source,
}
Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
JSRegExp::Flags flags) {
Handle<CompilationCacheTable> table = GetTable(REGEXP);
Object* result = table->LookupRegExp(*source, flags);
if (result->IsFixedArray()) {
Counters::regexp_cache_hits.Increment();
return Handle<FixedArray>(FixedArray::cast(result));
} else {
Counters::regexp_cache_misses.Increment();
return Handle<FixedArray>();
}
}
void CompilationCache::PutRegExp(Handle<String> source,
JSRegExp::Flags flags,
Handle<FixedArray> data) {
HandleScope scope;
Handle<CompilationCacheTable> table = GetTable(REGEXP);
CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data));
}
void CompilationCache::Clear() {
for (int i = 0; i < NUMBER_OF_ENTRY_KINDS; i++) {
tables[i] = Heap::undefined_value();
......
......@@ -42,7 +42,9 @@ class CompilationCache {
enum Entry {
SCRIPT,
EVAL_GLOBAL,
EVAL_CONTEXTUAL
EVAL_CONTEXTUAL,
REGEXP,
LAST_ENTRY = REGEXP
};
// Finds the script function boilerplate for a source
......@@ -59,11 +61,22 @@ class CompilationCache {
static Handle<JSFunction> LookupEval(Handle<String> source,
Entry entry);
// Returns the regexp data associated with the given regexp if it
// is in cache, otherwise an empty handle.
static Handle<FixedArray> LookupRegExp(Handle<String> source,
JSRegExp::Flags flags);
// Associate the (source, flags) pair to the given regexp data.
// This may overwrite an existing mapping.
static void PutRegExp(Handle<String> source,
JSRegExp::Flags flags,
Handle<FixedArray> data);
// 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);
static void PutFunction(Handle<String> source,
Entry entry,
Handle<JSFunction> boilerplate);
// Clear the cache - also used to initialize the cache at startup.
static void Clear();
......
......@@ -189,7 +189,7 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source,
// Compile the function and add it to the cache.
result = MakeFunction(true, false, script, extension, pre_data);
if (extension == NULL && !result.is_null()) {
CompilationCache::Associate(source, CompilationCache::SCRIPT, result);
CompilationCache::PutFunction(source, CompilationCache::SCRIPT, result);
}
// Get rid of the pre-parsing data (if necessary).
......@@ -223,7 +223,7 @@ Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
script->set_line_offset(Smi::FromInt(line_offset));
result = MakeFunction(is_global, true, script, NULL, NULL);
if (!result.is_null()) {
CompilationCache::Associate(source, entry, result);
CompilationCache::PutFunction(source, entry, result);
}
}
return result;
......
......@@ -808,6 +808,20 @@ Handle<Map> Factory::ObjectLiteralMapFromCache(Handle<Context> context,
}
void Factory::SetRegExpData(Handle<JSRegExp> regexp,
JSRegExp::Type type,
Handle<String> source,
JSRegExp::Flags flags,
Handle<Object> data) {
Handle<FixedArray> store = NewFixedArray(JSRegExp::kDataSize);
store->set(JSRegExp::kTagIndex, Smi::FromInt(type));
store->set(JSRegExp::kSourceIndex, *source);
store->set(JSRegExp::kFlagsIndex, Smi::FromInt(flags.value()));
store->set(JSRegExp::kAtomPatternIndex, *data);
regexp->set_data(*store);
}
void Factory::ConfigureInstance(Handle<FunctionTemplateInfo> desc,
Handle<JSObject> instance,
bool* pending_exception) {
......
......@@ -308,6 +308,14 @@ class Factory : public AllStatic {
static Handle<Map> ObjectLiteralMapFromCache(Handle<Context> context,
Handle<FixedArray> keys);
// Creates a new FixedArray that holds the data associated with the
// regexp and stores it in the regexp.
static void SetRegExpData(Handle<JSRegExp> regexp,
JSRegExp::Type type,
Handle<String> source,
JSRegExp::Flags flags,
Handle<Object> data);
private:
static Handle<JSFunction> NewFunctionHelper(Handle<String> name,
Handle<Object> prototype);
......
......@@ -34,6 +34,7 @@
#include "platform.h"
#include "runtime.h"
#include "top.h"
#include "compilation-cache.h"
namespace v8 { namespace internal {
......@@ -143,29 +144,59 @@ Handle<String> RegExpImpl::StringToTwoByte(Handle<String> pattern) {
}
static JSRegExp::Flags RegExpFlagsFromString(Handle<String> str) {
int flags = JSRegExp::NONE;
for (int i = 0; i < str->length(); i++) {
switch (str->Get(i)) {
case 'i':
flags |= JSRegExp::IGNORE_CASE;
break;
case 'g':
flags |= JSRegExp::GLOBAL;
break;
case 'm':
flags |= JSRegExp::MULTILINE;
break;
}
}
return JSRegExp::Flags(flags);
}
unibrow::Predicate<unibrow::RegExpSpecialChar, 128> is_reg_exp_special_char;
Handle<Object> RegExpImpl::Compile(Handle<JSRegExp> re,
Handle<String> pattern,
Handle<String> flags) {
bool is_atom = true;
for (int i = 0; is_atom && i < flags->length(); i++) {
if (flags->Get(i) == 'i')
is_atom = false;
}
for (int i = 0; is_atom && i < pattern->length(); i++) {
if (is_reg_exp_special_char.get(pattern->Get(i)))
is_atom = false;
}
Handle<String> flag_str) {
JSRegExp::Flags flags = RegExpFlagsFromString(flag_str);
Handle<FixedArray> cached = CompilationCache::LookupRegExp(pattern, flags);
bool in_cache = !cached.is_null();
Handle<Object> result;
if (is_atom) {
result = AtomCompile(re, pattern);
if (in_cache) {
re->set_data(*cached);
result = re;
} else {
result = JsreCompile(re, pattern, flags);
bool is_atom = !flags.is_ignore_case();
for (int i = 0; is_atom && i < pattern->length(); i++) {
if (is_reg_exp_special_char.get(pattern->Get(i)))
is_atom = false;
}
if (is_atom) {
result = AtomCompile(re, pattern, flags);
} else {
result = JsreCompile(re, pattern, flags);
}
Object* data = re->data();
if (data->IsFixedArray()) {
// If compilation succeeded then the data is set on the regexp
// and we can store it in the cache.
Handle<FixedArray> data(FixedArray::cast(re->data()));
CompilationCache::PutRegExp(pattern, flags, data);
}
}
LOG(RegExpCompileEvent(re));
LOG(RegExpCompileEvent(re, in_cache));
return result;
}
......@@ -173,7 +204,7 @@ Handle<Object> RegExpImpl::Compile(Handle<JSRegExp> re,
Handle<Object> RegExpImpl::Exec(Handle<JSRegExp> regexp,
Handle<String> subject,
Handle<Object> index) {
switch (regexp->type_tag()) {
switch (regexp->TypeTag()) {
case JSRegExp::JSCRE:
return JsreExec(regexp, subject, index);
case JSRegExp::ATOM:
......@@ -187,7 +218,7 @@ Handle<Object> RegExpImpl::Exec(Handle<JSRegExp> regexp,
Handle<Object> RegExpImpl::ExecGlobal(Handle<JSRegExp> regexp,
Handle<String> subject) {
switch (regexp->type_tag()) {
switch (regexp->TypeTag()) {
case JSRegExp::JSCRE:
return JsreExecGlobal(regexp, subject);
case JSRegExp::ATOM:
......@@ -200,9 +231,9 @@ Handle<Object> RegExpImpl::ExecGlobal(Handle<JSRegExp> regexp,
Handle<Object> RegExpImpl::AtomCompile(Handle<JSRegExp> re,
Handle<String> pattern) {
re->set_type_tag(JSRegExp::ATOM);
re->set_data(*pattern);
Handle<String> pattern,
JSRegExp::Flags flags) {
Factory::SetRegExpData(re, JSRegExp::ATOM, pattern, flags, pattern);
return re;
}
......@@ -210,7 +241,7 @@ Handle<Object> RegExpImpl::AtomCompile(Handle<JSRegExp> re,
Handle<Object> RegExpImpl::AtomExec(Handle<JSRegExp> re,
Handle<String> subject,
Handle<Object> index) {
Handle<String> needle(String::cast(re->data()));
Handle<String> needle(String::cast(re->DataAt(JSRegExp::kAtomPatternIndex)));
uint32_t start_index;
if (!Array::IndexFromObject(*index, &start_index)) {
......@@ -234,7 +265,7 @@ Handle<Object> RegExpImpl::AtomExec(Handle<JSRegExp> re,
Handle<Object> RegExpImpl::AtomExecGlobal(Handle<JSRegExp> re,
Handle<String> subject) {
Handle<String> needle(String::cast(re->data()));
Handle<String> needle(String::cast(re->DataAt(JSRegExp::kAtomPatternIndex)));
Handle<JSArray> result = Factory::NewJSArray(1);
int index = 0;
int match_count = 0;
......@@ -269,14 +300,13 @@ Handle<Object> RegExpImpl::AtomExecGlobal(Handle<JSRegExp> re,
Handle<Object> RegExpImpl::JsreCompile(Handle<JSRegExp> re,
Handle<String> pattern,
Handle<String> flags) {
JSRegExpIgnoreCaseOption case_option = JSRegExpDoNotIgnoreCase;
JSRegExpMultilineOption multiline_option = JSRegExpSingleLine;
FlattenString(flags);
for (int i = 0; i < flags->length(); i++) {
if (flags->Get(i) == 'i') case_option = JSRegExpIgnoreCase;
if (flags->Get(i) == 'm') multiline_option = JSRegExpMultiline;
}
JSRegExp::Flags flags) {
JSRegExpIgnoreCaseOption case_option = flags.is_ignore_case()
? JSRegExpIgnoreCase
: JSRegExpDoNotIgnoreCase;
JSRegExpMultilineOption multiline_option = flags.is_multiline()
? JSRegExpMultiline
: JSRegExpSingleLine;
Handle<String> two_byte_pattern = StringToTwoByte(pattern);
......@@ -328,8 +358,7 @@ Handle<Object> RegExpImpl::JsreCompile(Handle<JSRegExp> re,
Handle<FixedArray> value = Factory::NewFixedArray(2);
value->set(CAPTURE_INDEX, Smi::FromInt(number_of_captures));
value->set(INTERNAL_INDEX, *internal);
re->set_type_tag(JSRegExp::JSCRE);
re->set_data(*value);
Factory::SetRegExpData(re, JSRegExp::JSCRE, pattern, flags, value);
return re;
}
......@@ -499,16 +528,14 @@ Handle<Object> RegExpImpl::JsreExecGlobal(Handle<JSRegExp> regexp,
int RegExpImpl::JsreCapture(Handle<JSRegExp> re) {
Object* value = re->data();
ASSERT(value->IsFixedArray());
return Smi::cast(FixedArray::cast(value)->get(CAPTURE_INDEX))->value();
FixedArray* value = FixedArray::cast(re->DataAt(JSRegExp::kJscreDataIndex));
return Smi::cast(value->get(CAPTURE_INDEX))->value();
}
ByteArray* RegExpImpl::JsreInternal(Handle<JSRegExp> re) {
Object* value = re->data();
ASSERT(value->IsFixedArray());
return ByteArray::cast(FixedArray::cast(value)->get(INTERNAL_INDEX));
FixedArray* value = FixedArray::cast(re->DataAt(JSRegExp::kJscreDataIndex));
return ByteArray::cast(value->get(INTERNAL_INDEX));
}
}} // namespace v8::internal
......@@ -62,7 +62,8 @@ class RegExpImpl {
Handle<String> subject);
static Handle<Object> AtomCompile(Handle<JSRegExp> re,
Handle<String> pattern);
Handle<String> pattern,
JSRegExp::Flags flags);
static Handle<Object> AtomExec(Handle<JSRegExp> regexp,
Handle<String> subject,
......@@ -73,7 +74,7 @@ class RegExpImpl {
static Handle<Object> JsreCompile(Handle<JSRegExp> re,
Handle<String> pattern,
Handle<String> flags);
JSRegExp::Flags flags);
static Handle<Object> JsreExec(Handle<JSRegExp> regexp,
Handle<String> subject,
......
......@@ -377,14 +377,12 @@ void Logger::LogRegExpSource(Handle<JSRegExp> regexp) {
return;
}
if (regexp->type()->IsSmi()) {
switch (regexp->type_tag()) {
switch (regexp->TypeTag()) {
case JSRegExp::ATOM:
fprintf(logfile_, "a");
break;
default:
break;
}
}
fprintf(logfile_, "/");
LogString(Handle<String>::cast(source));
......@@ -409,14 +407,14 @@ void Logger::LogRegExpSource(Handle<JSRegExp> regexp) {
#endif // ENABLE_LOGGING_AND_PROFILING
void Logger::RegExpCompileEvent(Handle<JSRegExp> regexp) {
void Logger::RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (logfile_ == NULL || !FLAG_log_regexp) return;
ScopedLock sl(mutex_);
fprintf(logfile_, "regexp-compile,");
LogRegExpSource(regexp);
fprintf(logfile_, "\n");
fprintf(logfile_, in_cache ? ",hit\n" : ",miss\n");
#endif
}
......
......@@ -181,7 +181,7 @@ class Logger {
// ==== Events logged by --log-regexp ====
// Regexp compilation and execution events.
static void RegExpCompileEvent(Handle<JSRegExp> regexp);
static void RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache);
static void RegExpExecEvent(Handle<JSRegExp> regexp,
int start_index,
......
......@@ -660,19 +660,22 @@ void JSArray::JSArrayVerify() {
void JSRegExp::JSRegExpVerify() {
JSObjectVerify();
ASSERT(type()->IsSmi() || type()->IsUndefined());
if (type()->IsSmi()) {
switch (type_tag()) {
case JSRegExp::JSCRE:
ASSERT(data()->IsFixedArray());
break;
default:
ASSERT_EQ(JSRegExp::ATOM, type_tag());
ASSERT(data()->IsString());
break;
ASSERT(data()->IsUndefined() || data()->IsFixedArray());
switch (TypeTag()) {
case JSRegExp::ATOM: {
FixedArray* arr = FixedArray::cast(data());
ASSERT(arr->get(JSRegExp::kAtomPatternIndex)->IsString());
break;
}
} else {
ASSERT(data()->IsUndefined());
case JSRegExp::JSCRE: {
FixedArray* arr = FixedArray::cast(data());
ASSERT(arr->get(JSRegExp::kJscreDataIndex)->IsFixedArray());
break;
}
default:
ASSERT_EQ(JSRegExp::NOT_COMPILED, TypeTag());
ASSERT(data()->IsUndefined());
break;
}
}
......
......@@ -2166,16 +2166,19 @@ ACCESSORS(JSArray, length, Object, kLengthOffset)
ACCESSORS(JSRegExp, data, Object, kDataOffset)
ACCESSORS(JSRegExp, type, Object, kTypeOffset)
JSRegExp::Type JSRegExp::type_tag() {
return static_cast<JSRegExp::Type>(Smi::cast(type())->value());
JSRegExp::Type JSRegExp::TypeTag() {
Object* data = this->data();
if (data->IsUndefined()) return JSRegExp::NOT_COMPILED;
Smi* smi = Smi::cast(FixedArray::cast(data)->get(kTagIndex));
return static_cast<JSRegExp::Type>(smi->value());
}
void JSRegExp::set_type_tag(JSRegExp::Type value) {
set_type(Smi::FromInt(value));
Object* JSRegExp::DataAt(int index) {
ASSERT(TypeTag() != NOT_COMPILED);
return FixedArray::cast(data())->get(index);
}
......
......@@ -5551,6 +5551,46 @@ class StringKey : public HashTableKey {
String* string_;
};
// RegExpKey carries the source and flags of a regular expression as key.
class RegExpKey : public HashTableKey {
public:
RegExpKey(String* string, JSRegExp::Flags flags)
: string_(string),
flags_(Smi::FromInt(flags.value())) { }
bool IsMatch(Object* obj) {
FixedArray* val = FixedArray::cast(obj);
return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
&& (flags_ == val->get(JSRegExp::kFlagsIndex));
}
uint32_t Hash() { return RegExpHash(string_, flags_); }
HashFunction GetHashFunction() { return RegExpObjectHash; }
Object* GetObject() {
// Plain hash maps, which is where regexp keys are used, don't
// use this function.
UNREACHABLE();
return NULL;
}
static uint32_t RegExpObjectHash(Object* obj) {
FixedArray* val = FixedArray::cast(obj);
return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
Smi::cast(val->get(JSRegExp::kFlagsIndex)));
}
static uint32_t RegExpHash(String* string, Smi* flags) {
return string->Hash() + flags->value();
}
bool IsStringKey() { return false; }
String* string_;
Smi* flags_;
};
// Utf8SymbolKey carries a vector of chars as key.
class Utf8SymbolKey : public HashTableKey {
public:
......@@ -5825,6 +5865,15 @@ Object* CompilationCacheTable::Lookup(String* src) {
}
Object* CompilationCacheTable::LookupRegExp(String* src,
JSRegExp::Flags flags) {
RegExpKey key(src, flags);
int entry = FindEntry(&key);
if (entry == -1) return Heap::undefined_value();
return get(EntryToIndex(entry) + 1);
}
Object* CompilationCacheTable::Put(String* src, Object* value) {
StringKey key(src);
Object* obj = EnsureCapacity(1, &key);
......@@ -5840,6 +5889,23 @@ Object* CompilationCacheTable::Put(String* src, Object* value) {
}
Object* CompilationCacheTable::PutRegExp(String* src,
JSRegExp::Flags flags,
FixedArray* value) {
RegExpKey key(src, flags);
Object* obj = EnsureCapacity(1, &key);
if (obj->IsFailure()) return obj;
CompilationCacheTable* cache =
reinterpret_cast<CompilationCacheTable*>(obj);
int entry = cache->FindInsertionEntry(value, key.Hash());
cache->set(EntryToIndex(entry), value);
cache->set(EntryToIndex(entry) + 1, value);
cache->ElementAdded();
return cache;
}
// SymbolsKey used for HashTable where key is array of symbols.
class SymbolsKey : public HashTableKey {
public:
......
......@@ -1856,19 +1856,6 @@ class SymbolTable: public HashTable<0, 1> {
};
class CompilationCacheTable: public HashTable<0, 2> {
public:
// Find cached value for a string key, otherwise return null.
Object* Lookup(String* src);
Object* Put(String* src, Object* value);
static inline CompilationCacheTable* cast(Object* obj);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheTable);
};
// MapCache.
//
// Maps keys that are a fixed array of symbols to a map.
......@@ -2907,18 +2894,28 @@ class JSValue: public JSObject {
DISALLOW_IMPLICIT_CONSTRUCTORS(JSValue);
};
// Regular expressions
class JSRegExp: public JSObject {
public:
enum Type { JSCRE, ATOM };
enum Type { NOT_COMPILED, JSCRE, ATOM };
enum Flag { NONE = 0, GLOBAL = 1, IGNORE_CASE = 2, MULTILINE = 4 };
inline Type type_tag();
inline void set_type_tag(Type value);
class Flags {
public:
explicit Flags(uint32_t value) : value_(value) { }
bool is_global() { return (value_ & GLOBAL) != 0; }
bool is_ignore_case() { return (value_ & IGNORE_CASE) != 0; }
bool is_multiline() { return (value_ & MULTILINE) != 0; }
uint32_t value() { return value_; }
private:
uint32_t value_;
};
DECL_ACCESSORS(type, Object)
DECL_ACCESSORS(data, Object)
inline Type TypeTag();
inline Object* DataAt(int index);
static inline JSRegExp* cast(Object* obj);
// Dispatched behavior.
......@@ -2927,9 +2924,32 @@ class JSRegExp: public JSObject {
void JSRegExpVerify();
#endif
static const int kTypeOffset = JSObject::kHeaderSize;
static const int kDataOffset = kTypeOffset + kIntSize;
static const int kDataOffset = JSObject::kHeaderSize;
static const int kSize = kDataOffset + kIntSize;
static const int kTagIndex = 0;
static const int kSourceIndex = kTagIndex + 1;
static const int kFlagsIndex = kSourceIndex + 1;
// These two are the same since the same entry is shared for
// different purposes in different types of regexps.
static const int kAtomPatternIndex = kFlagsIndex + 1;
static const int kJscreDataIndex = kFlagsIndex + 1;
static const int kDataSize = kAtomPatternIndex + 1;
};
class CompilationCacheTable: public HashTable<0, 2> {
public:
// Find cached value for a string key, otherwise return null.
Object* Lookup(String* src);
Object* LookupRegExp(String* source, JSRegExp::Flags flags);
Object* Put(String* src, Object* value);
Object* PutRegExp(String* src, JSRegExp::Flags flags, FixedArray* value);
static inline CompilationCacheTable* cast(Object* obj);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheTable);
};
......
......@@ -73,6 +73,8 @@ namespace v8 { namespace internal {
SC(arguments_adaptors, V8.ArgumentsAdaptors) \
SC(compilation_cache_hits, V8.CompilationCacheHits) \
SC(compilation_cache_misses, V8.CompilationCacheMisses) \
SC(regexp_cache_hits, V8.RegExpCacheHits) \
SC(regexp_cache_misses, V8.RegExpCacheMisses) \
/* Amount of evaled source code. */ \
SC(total_eval_size, V8.TotalEvalSize) \
/* Amount of loaded source code. */ \
......
......@@ -222,13 +222,13 @@ class CompactProgressIndicator(ProgressIndicator):
if output.UnexpectedOutput():
self.ClearLine(self.last_status_length)
self.PrintFailureHeader(output.test)
print "Command: %s" % EscapeCommand(output.command)
stdout = output.output.stdout.strip()
if len(stdout):
print self.templates['stdout'] % stdout
stderr = output.output.stderr.strip()
if len(stderr):
print self.templates['stderr'] % stderr
print "Command: %s" % EscapeCommand(output.command)
def Truncate(self, str, length):
if length and (len(str) > (length - 3)):
......
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