Remove regexp caching.

Review URL: http://codereview.chromium.org/4308001

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5755 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
lrn@chromium.org 2010-11-02 13:37:59 +00:00
parent b9860d3af8
commit e1458503d1
11 changed files with 28 additions and 475 deletions

View File

@ -5496,73 +5496,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
} }
void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
ASSERT_EQ(1, args->length());
Load(args->at(0));
frame_->PopToR0();
{
VirtualFrame::SpilledScope spilled_scope(frame_);
Label done;
Label call_runtime;
__ BranchOnSmi(r0, &done);
// Load JSRegExp map into r1. Check that argument object has this map.
// Arguments to this function should be results of calling RegExp exec,
// which is either an unmodified JSRegExpResult or null. Anything not having
// the unmodified JSRegExpResult map is returned unmodified.
// This also ensures that elements are fast.
__ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
__ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
__ ldr(r1, ContextOperand(r1, Context::REGEXP_RESULT_MAP_INDEX));
__ ldr(ip, FieldMemOperand(r0, HeapObject::kMapOffset));
__ cmp(r1, Operand(ip));
__ b(ne, &done);
if (FLAG_debug_code) {
__ LoadRoot(r2, Heap::kEmptyFixedArrayRootIndex);
__ ldr(ip, FieldMemOperand(r0, JSObject::kPropertiesOffset));
__ cmp(ip, r2);
__ Check(eq, "JSRegExpResult: default map but non-empty properties.");
}
// All set, copy the contents to a new object.
__ AllocateInNewSpace(JSRegExpResult::kSize,
r2,
r3,
r4,
&call_runtime,
NO_ALLOCATION_FLAGS);
// Store RegExpResult map as map of allocated object.
ASSERT(JSRegExpResult::kSize == 6 * kPointerSize);
// Copy all fields (map is already in r1) from (untagged) r0 to r2.
// Change map of elements array (ends up in r4) to be a FixedCOWArray.
__ bic(r0, r0, Operand(kHeapObjectTagMask));
__ ldm(ib, r0, r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit());
__ stm(ia, r2,
r1.bit() | r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit());
ASSERT(JSRegExp::kElementsOffset == 2 * kPointerSize);
// Check whether elements array is empty fixed array, and otherwise make
// it copy-on-write (it never should be empty unless someone is messing
// with the arguments to the runtime function).
__ LoadRoot(ip, Heap::kEmptyFixedArrayRootIndex);
__ add(r0, r2, Operand(kHeapObjectTag)); // Tag result and move it to r0.
__ cmp(r4, ip);
__ b(eq, &done);
__ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
__ str(ip, FieldMemOperand(r4, HeapObject::kMapOffset));
__ b(&done);
__ bind(&call_runtime);
__ push(r0);
__ CallRuntime(Runtime::kRegExpCloneResult, 1);
__ bind(&done);
}
frame_->EmitPush(r0);
}
class DeferredSearchCache: public DeferredCode { class DeferredSearchCache: public DeferredCode {
public: public:
DeferredSearchCache(Register dst, Register cache, Register key) DeferredSearchCache(Register dst, Register cache, Register key)

View File

@ -518,8 +518,6 @@ class CodeGenerator: public AstVisitor {
void GenerateRegExpConstructResult(ZoneList<Expression*>* args); void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
// Support for fast native caches. // Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args); void GenerateGetFromCache(ZoneList<Expression*>* args);

View File

@ -1225,13 +1225,6 @@ int FullCodeGenerator::TryCatch::Exit(int stack_depth) {
} }
void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
VisitForStackValue(args->at(0));
__ CallRuntime(Runtime::kRegExpCloneResult, 1);
context()->Plug(result_register());
}
#undef __ #undef __

View File

@ -7292,88 +7292,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
} }
void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
ASSERT_EQ(1, args->length());
Load(args->at(0));
Result object_result = frame_->Pop();
object_result.ToRegister(eax);
object_result.Unuse();
{
VirtualFrame::SpilledScope spilled_scope;
Label done;
__ test(eax, Immediate(kSmiTagMask));
__ j(zero, &done);
// Load JSRegExpResult map into edx.
// Arguments to this function should be results of calling RegExp exec,
// which is either an unmodified JSRegExpResult or null. Anything not having
// the unmodified JSRegExpResult map is returned unmodified.
// This also ensures that elements are fast.
__ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX));
__ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset));
__ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
__ cmp(edx, FieldOperand(eax, HeapObject::kMapOffset));
__ j(not_equal, &done);
if (FLAG_debug_code) {
// Check that object really has empty properties array, as the map
// should guarantee.
__ cmp(FieldOperand(eax, JSObject::kPropertiesOffset),
Immediate(Factory::empty_fixed_array()));
__ Check(equal, "JSRegExpResult: default map but non-empty properties.");
}
DeferredAllocateInNewSpace* allocate_fallback =
new DeferredAllocateInNewSpace(JSRegExpResult::kSize,
ebx,
edx.bit() | eax.bit());
// All set, copy the contents to a new object.
__ AllocateInNewSpace(JSRegExpResult::kSize,
ebx,
ecx,
no_reg,
allocate_fallback->entry_label(),
TAG_OBJECT);
__ bind(allocate_fallback->exit_label());
// Copy all fields from eax to ebx.
STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0);
// There is an even number of fields, so unroll the loop once
// for efficiency.
for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) {
STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0);
if (i != JSObject::kMapOffset) {
// The map was already loaded into edx.
__ mov(edx, FieldOperand(eax, i));
}
__ mov(ecx, FieldOperand(eax, i + kPointerSize));
STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0);
if (i == JSObject::kElementsOffset) {
// If the elements array isn't empty, make it copy-on-write
// before copying it.
Label empty;
__ cmp(Operand(edx), Immediate(Factory::empty_fixed_array()));
__ j(equal, &empty);
__ mov(FieldOperand(edx, HeapObject::kMapOffset),
Immediate(Factory::fixed_cow_array_map()));
__ bind(&empty);
}
__ mov(FieldOperand(ebx, i), edx);
__ mov(FieldOperand(ebx, i + kPointerSize), ecx);
}
__ mov(eax, ebx);
__ bind(&done);
}
frame_->Push(eax);
}
class DeferredSearchCache: public DeferredCode { class DeferredSearchCache: public DeferredCode {
public: public:
DeferredSearchCache(Register dst, Register cache, Register key) DeferredSearchCache(Register dst, Register cache, Register key)

View File

@ -697,11 +697,6 @@ class CodeGenerator: public AstVisitor {
// Construct a RegExp exec result with two in-object properties. // Construct a RegExp exec result with two in-object properties.
void GenerateRegExpConstructResult(ZoneList<Expression*>* args); void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
// Clone the result of a regexp function.
// Must be an object created by GenerateRegExpConstructResult with
// no extra properties.
void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
// Support for fast native caches. // Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args); void GenerateGetFromCache(ZoneList<Expression*>* args);

View File

@ -71,9 +71,6 @@ function DoConstructRegExp(object, pattern, flags, isConstructorCall) {
} }
} }
if (!isConstructorCall) {
regExpCache.type = 'none';
}
%RegExpInitializeObject(object, pattern, global, ignoreCase, multiline); %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
// Call internal function to compile the pattern. // Call internal function to compile the pattern.
@ -121,22 +118,6 @@ function DoRegExpExec(regexp, string, index) {
} }
function RegExpCache() {
this.type = 'none';
this.regExp = 0;
this.subject = 0;
this.replaceString = 0;
this.answer = 0;
// answerSaved marks whether the contents of answer is valid for a cache
// hit in RegExpExec, StringMatch and StringSplit.
this.answerSaved = false;
this.splitLimit = 0; // Used only when type is "split".
}
var regExpCache = new RegExpCache();
function BuildResultFromMatchInfo(lastMatchInfo, s) { function BuildResultFromMatchInfo(lastMatchInfo, s) {
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1; var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s); var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
@ -178,32 +159,6 @@ function RegExpExec(string) {
['RegExp.prototype.exec', this]); ['RegExp.prototype.exec', this]);
} }
var cache = regExpCache;
var saveAnswer = false;
var lastIndex = this.lastIndex;
// Since cache.subject is always a string, a matching input can not
// cause visible side-effects when converted to a string, so we can omit
// the conversion required by the specification.
// Likewise, the regexp.lastIndex and regexp.global properties are value
// properties that are not configurable, so reading them can also not cause
// any side effects (converting lastIndex to a number can, though).
if (%_ObjectEquals(cache.type, 'exec') &&
%_ObjectEquals(0, lastIndex) &&
%_IsRegExpEquivalent(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string)) {
if (cache.answerSaved) {
// The regexp.lastIndex value must be 0 for non-global RegExps, and for
// global RegExps we only cache negative results, which gives a lastIndex
// of zero as well.
this.lastIndex = 0;
return %_RegExpCloneResult(cache.answer);
} else {
saveAnswer = true;
}
}
if (%_ArgumentsLength() === 0) { if (%_ArgumentsLength() === 0) {
var regExpInput = LAST_INPUT(lastMatchInfo); var regExpInput = LAST_INPUT(lastMatchInfo);
if (IS_UNDEFINED(regExpInput)) { if (IS_UNDEFINED(regExpInput)) {
@ -217,11 +172,13 @@ function RegExpExec(string) {
} else { } else {
s = ToString(string); s = ToString(string);
} }
var global = this.global; var lastIndex = this.lastIndex;
// Conversion is required by the ES5 specification (RegExp.prototype.exec // Conversion is required by the ES5 specification (RegExp.prototype.exec
// algorithm, step 5) even if the value is discarded for non-global RegExps. // algorithm, step 5) even if the value is discarded for non-global RegExps.
var i = TO_INTEGER(lastIndex); var i = TO_INTEGER(lastIndex);
var global = this.global;
if (global) { if (global) {
if (i < 0 || i > s.length) { if (i < 0 || i > s.length) {
this.lastIndex = 0; this.lastIndex = 0;
@ -237,16 +194,9 @@ function RegExpExec(string) {
if (matchIndices === null) { if (matchIndices === null) {
if (global) { if (global) {
// Cache negative result only if initial lastIndex was zero.
this.lastIndex = 0; this.lastIndex = 0;
if (lastIndex !== 0) return matchIndices;
} }
cache.regExp = this; return null;
cache.subject = s; // Always a string.
cache.answer = null;
cache.answerSaved = true; // Safe since no cloning is needed.
cache.type = 'exec';
return matchIndices; // No match.
} }
// Successful match. // Successful match.
@ -254,17 +204,9 @@ function RegExpExec(string) {
var result = BuildResultFromMatchInfo(matchIndices, s); var result = BuildResultFromMatchInfo(matchIndices, s);
if (global) { if (global) {
// Don't cache positive results for global regexps.
this.lastIndex = lastMatchInfo[CAPTURE1]; this.lastIndex = lastMatchInfo[CAPTURE1];
} else {
cache.regExp = this;
cache.subject = s;
if (saveAnswer) cache.answer = %_RegExpCloneResult(result);
cache.answerSaved = saveAnswer;
cache.type = 'exec';
} }
return result; return result;
} }
@ -289,20 +231,6 @@ function RegExpTest(string) {
string = regExpInput; string = regExpInput;
} }
var lastIndex = this.lastIndex;
var cache = regExpCache;
if (%_ObjectEquals(cache.type, 'test') &&
%_IsRegExpEquivalent(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string) &&
%_ObjectEquals(0, lastIndex)) {
// The regexp.lastIndex value must be 0 for non-global RegExps, and for
// global RegExps we only cache negative results, which gives a resulting
// lastIndex of zero as well.
if (global) this.lastIndex = 0;
return cache.answer;
}
var s; var s;
if (IS_STRING(string)) { if (IS_STRING(string)) {
s = string; s = string;
@ -311,9 +239,13 @@ function RegExpTest(string) {
} }
var length = s.length; var length = s.length;
var lastIndex = this.lastIndex;
// Conversion is required by the ES5 specification (RegExp.prototype.exec // Conversion is required by the ES5 specification (RegExp.prototype.exec
// algorithm, step 5) even if the value is discarded for non-global RegExps. // algorithm, step 5) even if the value is discarded for non-global RegExps.
var i = TO_INTEGER(lastIndex); var i = TO_INTEGER(lastIndex);
var global = this.global;
if (global) { if (global) {
if (i < 0 || i > length) { if (i < 0 || i > length) {
this.lastIndex = 0; this.lastIndex = 0;
@ -323,8 +255,6 @@ function RegExpTest(string) {
i = 0; i = 0;
} }
var global = this.global;
// Remove irrelevant preceeding '.*' in a test regexp. The expression // Remove irrelevant preceeding '.*' in a test regexp. The expression
// checks whether this.source starts with '.*' and that the third // checks whether this.source starts with '.*' and that the third
// char is not a '?' // char is not a '?'
@ -334,35 +264,31 @@ function RegExpTest(string) {
if (!%_ObjectEquals(regexp_key, this)) { if (!%_ObjectEquals(regexp_key, this)) {
regexp_key = this; regexp_key = this;
regexp_val = new $RegExp(this.source.substring(2, this.source.length), regexp_val = new $RegExp(this.source.substring(2, this.source.length),
(this.global ? 'g' : '') (global ? 'g' : '')
+ (this.ignoreCase ? 'i' : '') + (this.ignoreCase ? 'i' : '')
+ (this.multiline ? 'm' : '')); + (this.multiline ? 'm' : ''));
} }
if (!regexp_val.test(s)) return false; if (!regexp_val.test(s)) return false;
} }
var length = s.length;
if (i < 0 || i > length) {
this.lastIndex = 0;
return false;
}
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
// matchIndices is either null or the lastMatchInfo array. // matchIndices is either null or the lastMatchInfo array.
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
var result = (matchIndices !== null); if (matchIndices === null) {
if (result) { if (global) this.lastIndex = 0;
lastMatchInfoOverride = null; return false;
} }
if (global) { lastMatchInfoOverride = null;
if (result) { if (global) this.lastIndex = lastMatchInfo[CAPTURE1];
this.lastIndex = lastMatchInfo[CAPTURE1]; return true;
return true;
} else {
this.lastIndex = 0;
if (lastIndex !== 0) return false;
}
}
cache.type = 'test';
cache.regExp = this;
cache.subject = s;
cache.answer = result;
return result;
} }
@ -510,7 +436,6 @@ function SetupRegExp() {
return IS_UNDEFINED(regExpInput) ? "" : regExpInput; return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
} }
function RegExpSetInput(string) { function RegExpSetInput(string) {
regExpCache.type = 'none';
LAST_INPUT(lastMatchInfo) = ToString(string); LAST_INPUT(lastMatchInfo) = ToString(string);
}; };

View File

@ -1424,66 +1424,6 @@ static MaybeObject* Runtime_RegExpConstructResult(Arguments args) {
} }
static MaybeObject* Runtime_RegExpCloneResult(Arguments args) {
ASSERT(args.length() == 1);
Map* regexp_result_map;
{
AssertNoAllocation no_gc;
HandleScope handles;
regexp_result_map = Top::global_context()->regexp_result_map();
}
if (!args[0]->IsJSArray()) return args[0];
JSArray* result = JSArray::cast(args[0]);
// Arguments to RegExpCloneResult should always be fresh RegExp exec call
// results (either a fresh JSRegExpResult or null).
// If the argument is not a JSRegExpResult, or isn't unmodified, just return
// the argument uncloned.
if (result->map() != regexp_result_map) return result;
// Having the original JSRegExpResult map guarantees that we have
// fast elements and no properties except the two in-object properties.
ASSERT(result->HasFastElements());
ASSERT(result->properties() == Heap::empty_fixed_array());
ASSERT_EQ(2, regexp_result_map->inobject_properties());
Object* new_array_alloc;
{ MaybeObject* maybe_new_array_alloc =
Heap::AllocateRaw(JSRegExpResult::kSize, NEW_SPACE, OLD_POINTER_SPACE);
if (!maybe_new_array_alloc->ToObject(&new_array_alloc)) {
return maybe_new_array_alloc;
}
}
// Set HeapObject map to JSRegExpResult map.
reinterpret_cast<HeapObject*>(new_array_alloc)->set_map(regexp_result_map);
JSArray* new_array = JSArray::cast(new_array_alloc);
// Copy JSObject properties.
new_array->set_properties(result->properties()); // Empty FixedArray.
// Copy JSObject elements as copy-on-write.
FixedArray* elements = FixedArray::cast(result->elements());
if (elements != Heap::empty_fixed_array()) {
elements->set_map(Heap::fixed_cow_array_map());
}
new_array->set_elements(elements);
// Copy JSArray length.
new_array->set_length(result->length());
// Copy JSRegExpResult in-object property fields input and index.
new_array->FastPropertyAtPut(JSRegExpResult::kIndexIndex,
result->FastPropertyAt(
JSRegExpResult::kIndexIndex));
new_array->FastPropertyAtPut(JSRegExpResult::kInputIndex,
result->FastPropertyAt(
JSRegExpResult::kInputIndex));
return new_array;
}
static MaybeObject* Runtime_RegExpInitializeObject(Arguments args) { static MaybeObject* Runtime_RegExpInitializeObject(Arguments args) {
AssertNoAllocation no_alloc; AssertNoAllocation no_alloc;
ASSERT(args.length() == 5); ASSERT(args.length() == 5);

View File

@ -162,7 +162,6 @@ namespace internal {
F(RegExpExecMultiple, 4, 1) \ F(RegExpExecMultiple, 4, 1) \
F(RegExpInitializeObject, 5, 1) \ F(RegExpInitializeObject, 5, 1) \
F(RegExpConstructResult, 3, 1) \ F(RegExpConstructResult, 3, 1) \
F(RegExpCloneResult, 1, 1) \
\ \
/* JSON */ \ /* JSON */ \
F(ParseJson, 1, 1) \ F(ParseJson, 1, 1) \
@ -425,7 +424,7 @@ namespace internal {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// INLINE_AND_RUNTIME_FUNCTION_LIST defines all inlined functions accessed // INLINE_AND_RUNTIME_FUNCTION_LIST defines all inlined functions accessed
// with a native call of the form %_name from within JS code that also have // with a native call of the form %_name from within JS code that also have
// a corresponding runtime function, that is called for slow cases. // a corresponding runtime function, that is called for slow cases.
// Entries have the form F(name, number of arguments, number of return values). // Entries have the form F(name, number of arguments, number of return values).
#define INLINE_RUNTIME_FUNCTION_LIST(F) \ #define INLINE_RUNTIME_FUNCTION_LIST(F) \
F(IsConstructCall, 0, 1) \ F(IsConstructCall, 0, 1) \
@ -437,7 +436,6 @@ namespace internal {
F(StringCompare, 2, 1) \ F(StringCompare, 2, 1) \
F(RegExpExec, 4, 1) \ F(RegExpExec, 4, 1) \
F(RegExpConstructResult, 3, 1) \ F(RegExpConstructResult, 3, 1) \
F(RegExpCloneResult, 1, 1) \
F(GetFromCache, 2, 1) \ F(GetFromCache, 2, 1) \
F(NumberToString, 1, 1) \ F(NumberToString, 1, 1) \
F(SwapElements, 3, 1) F(SwapElements, 3, 1)

View File

@ -144,16 +144,6 @@ function StringLastIndexOf(searchString /* position */) { // length == 1
} }
function CloneDenseArray(array) {
if (array === null) return null;
var clone = new $Array(array.length);
for (var i = 0; i < array.length; i++) {
clone[i] = array[i];
}
return clone;
}
// ECMA-262 section 15.5.4.9 // ECMA-262 section 15.5.4.9
// //
// This function is implementation specific. For now, we do not // This function is implementation specific. For now, we do not
@ -173,32 +163,14 @@ function StringMatch(regexp) {
if (IS_REGEXP(regexp)) { if (IS_REGEXP(regexp)) {
if (!regexp.global) return regexp.exec(subject); if (!regexp.global) return regexp.exec(subject);
var cache = regExpCache;
var saveAnswer = false; var saveAnswer = false;
if (%_ObjectEquals(cache.type, 'match') &&
%_IsRegExpEquivalent(cache.regExp, regexp) &&
%_ObjectEquals(cache.subject, subject)) {
if (cache.answerSaved) {
return CloneDenseArray(cache.answer);
} else {
saveAnswer = true;
}
}
%_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]); %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
// lastMatchInfo is defined in regexp.js. // lastMatchInfo is defined in regexp.js.
var result = %StringMatch(subject, regexp, lastMatchInfo); return %StringMatch(subject, regexp, lastMatchInfo);
cache.type = 'match';
cache.regExp = regexp;
cache.subject = subject;
if (saveAnswer) cache.answer = CloneDenseArray(result);
cache.answerSaved = saveAnswer;
return result;
} }
// Non-regexp argument. // Non-regexp argument.
regexp = new $RegExp(regexp); regexp = new $RegExp(regexp);
// Don't check regexp exec cache, since the regexp is new.
// TODO(lrn): Change this if we start caching regexps here.
return RegExpExecNoTests(regexp, subject, 0); return RegExpExecNoTests(regexp, subject, 0);
} }
@ -231,7 +203,6 @@ function StringReplace(search, replace) {
if (IS_REGEXP(search)) { if (IS_REGEXP(search)) {
%_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]); %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
if (IS_FUNCTION(replace)) { if (IS_FUNCTION(replace)) {
regExpCache.type = 'none';
if (search.global) { if (search.global) {
return StringReplaceGlobalRegExpWithFunction(subject, search, replace); return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
} else { } else {
@ -273,24 +244,10 @@ function StringReplace(search, replace) {
// Helper function for regular expressions in String.prototype.replace. // Helper function for regular expressions in String.prototype.replace.
function StringReplaceRegExp(subject, regexp, replace) { function StringReplaceRegExp(subject, regexp, replace) {
var cache = regExpCache; return %StringReplaceRegExpWithString(subject,
if (%_ObjectEquals(cache.type, 'replace') && regexp,
%_IsRegExpEquivalent(cache.regExp, regexp) && TO_STRING_INLINE(replace),
%_ObjectEquals(cache.replaceString, replace) && lastMatchInfo);
%_ObjectEquals(cache.subject, subject)) {
return cache.answer;
}
replace = TO_STRING_INLINE(replace);
var answer = %StringReplaceRegExpWithString(subject,
regexp,
replace,
lastMatchInfo);
cache.subject = subject;
cache.regExp = regexp;
cache.replaceString = replace;
cache.answer = answer;
cache.type = 'replace';
return answer;
} }
@ -605,34 +562,14 @@ function StringSplit(separator, limit) {
return result; return result;
} }
var cache = regExpCache;
var saveAnswer = false; var saveAnswer = false;
if (%_ObjectEquals(cache.type, 'split') &&
%_IsRegExpEquivalent(cache.regExp, separator) &&
%_ObjectEquals(cache.subject, subject) &&
%_ObjectEquals(cache.splitLimit, limit)) {
if (cache.answerSaved) {
return CloneDenseArray(cache.answer);
} else {
saveAnswer = true;
}
}
cache.type = 'split';
cache.regExp = separator;
cache.subject = subject;
cache.splitLimit = limit;
%_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]); %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
if (length === 0) { if (length === 0) {
cache.answerSaved = true;
if (splitMatch(separator, subject, 0, 0) != null) { if (splitMatch(separator, subject, 0, 0) != null) {
cache.answer = [];
return []; return [];
} }
cache.answer = [subject];
return [subject]; return [subject];
} }
@ -680,8 +617,6 @@ function StringSplit(separator, limit) {
startIndex = currentIndex = endIndex; startIndex = currentIndex = endIndex;
} }
if (saveAnswer) cache.answer = CloneDenseArray(result);
cache.answerSaved = saveAnswer;
return result; return result;
} }

View File

@ -6564,86 +6564,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
} }
void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
ASSERT_EQ(1, args->length());
Load(args->at(0));
Result object_result = frame_->Pop();
object_result.ToRegister(rax);
object_result.Unuse();
{
VirtualFrame::SpilledScope spilled_scope;
Label done;
__ JumpIfSmi(rax, &done);
// Load JSRegExpResult map into rdx.
// Arguments to this function should be results of calling RegExp exec,
// which is either an unmodified JSRegExpResult or null. Anything not having
// the unmodified JSRegExpResult map is returned unmodified.
// This also ensures that elements are fast.
__ movq(rdx, ContextOperand(rsi, Context::GLOBAL_INDEX));
__ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset));
__ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX));
__ cmpq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
__ j(not_equal, &done);
if (FLAG_debug_code) {
// Check that object really has empty properties array, as the map
// should guarantee.
__ CompareRoot(FieldOperand(rax, JSObject::kPropertiesOffset),
Heap::kEmptyFixedArrayRootIndex);
__ Check(equal, "JSRegExpResult: default map but non-empty properties.");
}
DeferredAllocateInNewSpace* allocate_fallback =
new DeferredAllocateInNewSpace(JSRegExpResult::kSize,
rbx,
rdx.bit() | rax.bit());
// All set, copy the contents to a new object.
__ AllocateInNewSpace(JSRegExpResult::kSize,
rbx,
no_reg,
no_reg,
allocate_fallback->entry_label(),
TAG_OBJECT);
__ bind(allocate_fallback->exit_label());
STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0);
// There is an even number of fields, so unroll the loop once
// for efficiency.
for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) {
STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0);
if (i != JSObject::kMapOffset) {
// The map was already loaded into edx.
__ movq(rdx, FieldOperand(rax, i));
}
__ movq(rcx, FieldOperand(rax, i + kPointerSize));
STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0);
if (i == JSObject::kElementsOffset) {
// If the elements array isn't empty, make it copy-on-write
// before copying it.
Label empty;
__ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
__ j(equal, &empty);
__ LoadRoot(kScratchRegister, Heap::kFixedCOWArrayMapRootIndex);
__ movq(FieldOperand(rdx, HeapObject::kMapOffset), kScratchRegister);
__ bind(&empty);
}
__ movq(FieldOperand(rbx, i), rdx);
__ movq(FieldOperand(rbx, i + kPointerSize), rcx);
}
__ movq(rax, rbx);
__ bind(&done);
}
frame_->Push(rax);
}
class DeferredSearchCache: public DeferredCode { class DeferredSearchCache: public DeferredCode {
public: public:
DeferredSearchCache(Register dst, DeferredSearchCache(Register dst,

View File

@ -656,8 +656,6 @@ class CodeGenerator: public AstVisitor {
void GenerateRegExpConstructResult(ZoneList<Expression*>* args); void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
// Support for fast native caches. // Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args); void GenerateGetFromCache(ZoneList<Expression*>* args);