Revision: 5310
Author: [email protected]
Date: Fri Aug 20 02:37:22 2010
Log: Use Copy-on-write arrays for cached regexp results.
Review URL: http://codereview.chromium.org/3158020
http://code.google.com/p/v8/source/detail?r=5310
Modified:
/branches/bleeding_edge/src/arm/codegen-arm.cc
/branches/bleeding_edge/src/arm/codegen-arm.h
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/arm/simulator-arm.cc
/branches/bleeding_edge/src/codegen.h
/branches/bleeding_edge/src/full-codegen.cc
/branches/bleeding_edge/src/full-codegen.h
/branches/bleeding_edge/src/ia32/codegen-ia32.cc
/branches/bleeding_edge/src/ia32/codegen-ia32.h
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/regexp.js
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/runtime.h
/branches/bleeding_edge/src/x64/codegen-x64.cc
/branches/bleeding_edge/src/x64/codegen-x64.h
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
/branches/bleeding_edge/test/mjsunit/regexp.js
=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.cc Fri Aug 20 00:10:18 2010
+++ /branches/bleeding_edge/src/arm/codegen-arm.cc Fri Aug 20 02:37:22 2010
@@ -5263,6 +5263,67 @@
frame_->Forget(3);
frame_->EmitPush(r0);
}
+
+
+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);
+
+ // 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(!Heap::InNewSpace(Heap::fixed_cow_array_map()));
+ 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 {
=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.h Mon Aug 16 09:06:46 2010
+++ /branches/bleeding_edge/src/arm/codegen-arm.h Fri Aug 20 02:37:22 2010
@@ -528,6 +528,8 @@
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
+ void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
+
// Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args);
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Mon Aug 16 09:06:46
2010
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Fri Aug 20 02:37:22
2010
@@ -2534,6 +2534,14 @@
__ CallRuntime(Runtime::kRegExpConstructResult, 3);
Apply(context_, r0);
}
+
+
+void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args)
{
+ ASSERT(args->length() == 1);
+ VisitForValue(args->at(0), kStack);
+ __ CallRuntime(Runtime::kRegExpConstructResult, 1);
+ Apply(context_, r0);
+}
void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
=======================================
--- /branches/bleeding_edge/src/arm/simulator-arm.cc Mon Aug 16 00:52:49
2010
+++ /branches/bleeding_edge/src/arm/simulator-arm.cc Fri Aug 20 02:37:22
2010
@@ -727,6 +727,10 @@
// the special case of accessing the PC register.
int32_t Simulator::get_register(int reg) const {
ASSERT((reg >= 0) && (reg < num_registers));
+ // Stupid code added to avoid bug in GCC.
+ // See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949
+ if (reg >= num_registers) return 0;
+ // End stupid code.
return registers_[reg] + ((reg == pc) ? Instr::kPCReadOffset : 0);
}
@@ -1378,7 +1382,9 @@
}
case 3: {
// Print("ib");
- UNIMPLEMENTED();
+ start_address = rn_val + 4;
+ end_address = rn_val + (num_regs * 4);
+ rn_val = end_address;
break;
}
default: {
=======================================
--- /branches/bleeding_edge/src/codegen.h Mon Aug 16 09:06:46 2010
+++ /branches/bleeding_edge/src/codegen.h Fri Aug 20 02:37:22 2010
@@ -108,6 +108,7 @@
F(StringCompare, 2,
1) \
F(RegExpExec, 4,
1) \
F(RegExpConstructResult, 3,
1) \
+ F(RegExpCloneResult, 1,
1) \
F(GetFromCache, 2,
1) \
F(NumberToString, 1,
1) \
F(SwapElements, 3,
1) \
=======================================
--- /branches/bleeding_edge/src/full-codegen.cc Fri Aug 13 02:07:09 2010
+++ /branches/bleeding_edge/src/full-codegen.cc Fri Aug 20 02:37:22 2010
@@ -851,80 +851,17 @@
void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
Handle<String> name = expr->name();
- if (strcmp("_IsSmi", *name->ToCString()) == 0) {
- EmitIsSmi(expr->arguments());
- } else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) {
- EmitIsNonNegativeSmi(expr->arguments());
- } else if (strcmp("_IsObject", *name->ToCString()) == 0) {
- EmitIsObject(expr->arguments());
- } else if (strcmp("_IsSpecObject", *name->ToCString()) == 0) {
- EmitIsSpecObject(expr->arguments());
- } else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
- EmitIsUndetectableObject(expr->arguments());
- } else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
- EmitIsFunction(expr->arguments());
- } else if (strcmp("_IsArray", *name->ToCString()) == 0) {
- EmitIsArray(expr->arguments());
- } else if (strcmp("_IsRegExp", *name->ToCString()) == 0) {
- EmitIsRegExp(expr->arguments());
- } else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) {
- EmitIsConstructCall(expr->arguments());
- } else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) {
- EmitObjectEquals(expr->arguments());
- } else if (strcmp("_Arguments", *name->ToCString()) == 0) {
- EmitArguments(expr->arguments());
- } else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) {
- EmitArgumentsLength(expr->arguments());
- } else if (strcmp("_ClassOf", *name->ToCString()) == 0) {
- EmitClassOf(expr->arguments());
- } else if (strcmp("_Log", *name->ToCString()) == 0) {
- EmitLog(expr->arguments());
- } else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) {
- EmitRandomHeapNumber(expr->arguments());
- } else if (strcmp("_SubString", *name->ToCString()) == 0) {
- EmitSubString(expr->arguments());
- } else if (strcmp("_RegExpExec", *name->ToCString()) == 0) {
- EmitRegExpExec(expr->arguments());
- } else if (strcmp("_ValueOf", *name->ToCString()) == 0) {
- EmitValueOf(expr->arguments());
- } else if (strcmp("_SetValueOf", *name->ToCString()) == 0) {
- EmitSetValueOf(expr->arguments());
- } else if (strcmp("_NumberToString", *name->ToCString()) == 0) {
- EmitNumberToString(expr->arguments());
- } else if (strcmp("_StringCharFromCode", *name->ToCString()) == 0) {
- EmitStringCharFromCode(expr->arguments());
- } else if (strcmp("_StringCharCodeAt", *name->ToCString()) == 0) {
- EmitStringCharCodeAt(expr->arguments());
- } else if (strcmp("_StringCharAt", *name->ToCString()) == 0) {
- EmitStringCharAt(expr->arguments());
- } else if (strcmp("_StringAdd", *name->ToCString()) == 0) {
- EmitStringAdd(expr->arguments());
- } else if (strcmp("_StringCompare", *name->ToCString()) == 0) {
- EmitStringCompare(expr->arguments());
- } else if (strcmp("_MathPow", *name->ToCString()) == 0) {
- EmitMathPow(expr->arguments());
- } else if (strcmp("_MathSin", *name->ToCString()) == 0) {
- EmitMathSin(expr->arguments());
- } else if (strcmp("_MathCos", *name->ToCString()) == 0) {
- EmitMathCos(expr->arguments());
- } else if (strcmp("_MathSqrt", *name->ToCString()) == 0) {
- EmitMathSqrt(expr->arguments());
- } else if (strcmp("_CallFunction", *name->ToCString()) == 0) {
- EmitCallFunction(expr->arguments());
- } else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) {
- EmitRegExpConstructResult(expr->arguments());
- } else if (strcmp("_SwapElements", *name->ToCString()) == 0) {
- EmitSwapElements(expr->arguments());
- } else if (strcmp("_GetFromCache", *name->ToCString()) == 0) {
- EmitGetFromCache(expr->arguments());
- } else if (strcmp("_IsRegExpEquivalent", *name->ToCString()) == 0) {
- EmitIsRegExpEquivalent(expr->arguments());
- } else if (strcmp("_IsStringWrapperSafeForDefaultValueOf",
- *name->ToCString()) == 0) {
- EmitIsStringWrapperSafeForDefaultValueOf(expr->arguments());
- } else {
- UNREACHABLE();
- }
+ SmartPointer<char> cstring = name->ToCString();
+
+#define CHECK_EMIT_INLINE_CALL(name, x, y) \
+ if (strcmp("_"#name, *cstring) == 0) { \
+ Emit##name(expr->arguments()); \
+ return; \
+ }
+
+ INLINE_RUNTIME_FUNCTION_LIST(CHECK_EMIT_INLINE_CALL)
+ UNREACHABLE();
+#undef CHECK_EMIT_INLINE_CALL
}
=======================================
--- /branches/bleeding_edge/src/full-codegen.h Fri Aug 13 02:07:09 2010
+++ /branches/bleeding_edge/src/full-codegen.h Fri Aug 20 02:37:22 2010
@@ -394,42 +394,11 @@
// Platform-specific code for inline runtime calls.
void EmitInlineRuntimeCall(CallRuntime* expr);
- void EmitIsSmi(ZoneList<Expression*>* arguments);
- void EmitIsNonNegativeSmi(ZoneList<Expression*>* arguments);
- void EmitIsObject(ZoneList<Expression*>* arguments);
- void EmitIsSpecObject(ZoneList<Expression*>* arguments);
- void EmitIsUndetectableObject(ZoneList<Expression*>* arguments);
- void EmitIsFunction(ZoneList<Expression*>* arguments);
- void EmitIsArray(ZoneList<Expression*>* arguments);
- void EmitIsRegExp(ZoneList<Expression*>* arguments);
- void EmitIsConstructCall(ZoneList<Expression*>* arguments);
- void EmitIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* arguments);
- void EmitObjectEquals(ZoneList<Expression*>* arguments);
- void EmitArguments(ZoneList<Expression*>* arguments);
- void EmitArgumentsLength(ZoneList<Expression*>* arguments);
- void EmitClassOf(ZoneList<Expression*>* arguments);
- void EmitValueOf(ZoneList<Expression*>* arguments);
- void EmitSetValueOf(ZoneList<Expression*>* arguments);
- void EmitNumberToString(ZoneList<Expression*>* arguments);
- void EmitStringCharFromCode(ZoneList<Expression*>* arguments);
- void EmitStringCharCodeAt(ZoneList<Expression*>* arguments);
- void EmitStringCharAt(ZoneList<Expression*>* arguments);
- void EmitStringCompare(ZoneList<Expression*>* arguments);
- void EmitStringAdd(ZoneList<Expression*>* arguments);
- void EmitLog(ZoneList<Expression*>* arguments);
- void EmitRandomHeapNumber(ZoneList<Expression*>* arguments);
- void EmitSubString(ZoneList<Expression*>* arguments);
- void EmitRegExpExec(ZoneList<Expression*>* arguments);
- void EmitMathPow(ZoneList<Expression*>* arguments);
- void EmitMathSin(ZoneList<Expression*>* arguments);
- void EmitMathCos(ZoneList<Expression*>* arguments);
- void EmitMathSqrt(ZoneList<Expression*>* arguments);
- void EmitCallFunction(ZoneList<Expression*>* arguments);
- void EmitRegExpConstructResult(ZoneList<Expression*>* arguments);
- void EmitSwapElements(ZoneList<Expression*>* arguments);
- void EmitGetFromCache(ZoneList<Expression*>* arguments);
- void EmitIsRegExpEquivalent(ZoneList<Expression*>* arguments);
+
+#define EMIT_INLINE_RUNTIME_CALL(name, x, y) \
+ void Emit##name(ZoneList<Expression*>* arguments);
+ INLINE_RUNTIME_FUNCTION_LIST(EMIT_INLINE_RUNTIME_CALL)
+#undef EMIT_INLINE_RUNTIME_CALL
// Platform-specific code for loading variables.
void EmitVariableLoad(Variable* expr, Expression::Context context);
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Fri Aug 20 00:10:18
2010
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Fri Aug 20 02:37:22
2010
@@ -5522,9 +5522,12 @@
class DeferredAllocateInNewSpace: public DeferredCode {
public:
- DeferredAllocateInNewSpace(int size, Register target)
- : size_(size), target_(target) {
+ DeferredAllocateInNewSpace(int size,
+ Register target,
+ int registers_to_save = 0)
+ : size_(size), target_(target), registers_to_save_(registers_to_save) {
ASSERT(size >= kPointerSize && size <=
Heap::MaxObjectSizeInNewSpace());
+ ASSERT_EQ(0, registers_to_save & target.bit());
set_comment("[ DeferredAllocateInNewSpace");
}
void Generate();
@@ -5532,15 +5535,28 @@
private:
int size_;
Register target_;
+ int registers_to_save_;
};
void DeferredAllocateInNewSpace::Generate() {
+ for (int i = 0; i < kNumRegs; i++) {
+ if (registers_to_save_ & (1 << i)) {
+ Register save_register = { i };
+ __ push(save_register);
+ }
+ }
__ push(Immediate(Smi::FromInt(size_)));
__ CallRuntime(Runtime::kAllocateInNewSpace, 1);
if (!target_.is(eax)) {
__ mov(target_, eax);
}
+ for (int i = kNumRegs - 1; i >= 0; i--) {
+ if (registers_to_save_ & (1 << i)) {
+ Register save_register = { i };
+ __ pop(save_register);
+ }
+ }
}
@@ -7362,6 +7378,89 @@
frame_->Forget(3);
frame_->Push(eax);
}
+
+
+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);
+ ASSERT(!Heap::InNewSpace(Heap::fixed_cow_array_map()));
+ __ 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 {
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.h Mon Aug 16 00:52:49 2010
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.h Fri Aug 20 02:37:22 2010
@@ -699,8 +699,14 @@
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
+ // Construct a RegExp exec result with two in-object properties.
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.
void GenerateGetFromCache(ZoneList<Expression*>* args);
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Mon Aug 16
09:06:46 2010
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Fri Aug 20
02:37:22 2010
@@ -2638,6 +2638,14 @@
__ CallRuntime(Runtime::kRegExpConstructResult, 3);
Apply(context_, eax);
}
+
+
+void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args)
{
+ ASSERT(args->length() == 1);
+ VisitForValue(args->at(0), kStack);
+ __ CallRuntime(Runtime::kRegExpCloneResult, 1);
+ Apply(context_, eax);
+}
void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
=======================================
--- /branches/bleeding_edge/src/regexp.js Fri Aug 6 06:04:27 2010
+++ /branches/bleeding_edge/src/regexp.js Fri Aug 20 02:37:22 2010
@@ -137,17 +137,6 @@
var regExpCache = new RegExpCache();
-function CloneRegExpResult(array) {
- if (array == null) return null;
- var length = array.length;
- var answer = %_RegExpConstructResult(length, array.index, array.input);
- for (var i = 0; i < length; i++) {
- answer[i] = array[i];
- }
- return answer;
-}
-
-
function BuildResultFromMatchInfo(lastMatchInfo, s) {
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
var result = %_RegExpConstructResult(numResults,
lastMatchInfo[CAPTURE0], s);
@@ -197,7 +186,7 @@
%_IsRegExpEquivalent(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string)) {
if (cache.answerSaved) {
- return CloneRegExpResult(cache.answer);
+ return %_RegExpCloneResult(cache.answer);
} else {
saveAnswer = true;
}
@@ -251,7 +240,7 @@
cache.regExp = this;
cache.subject = s;
cache.lastIndex = lastIndex;
- if (saveAnswer) cache.answer = CloneRegExpResult(result);
+ if (saveAnswer) cache.answer = %_RegExpCloneResult(result);
cache.answerSaved = saveAnswer;
cache.type = 'exec';
}
=======================================
--- /branches/bleeding_edge/src/runtime.cc Wed Aug 18 01:54:25 2010
+++ /branches/bleeding_edge/src/runtime.cc Fri Aug 20 02:37:22 2010
@@ -1362,6 +1362,65 @@
array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
return array;
}
+
+
+static Object* 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 = Heap::AllocateRaw(JSRegExpResult::kSize,
+ NEW_SPACE,
+ OLD_POINTER_SPACE);
+ if (new_array_alloc->IsFailure()) return 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()) {
+ ASSERT(!Heap::InNewSpace(Heap::fixed_cow_array_map()));
+ // No write barrier is necessary when writing old-space pointer.
+ 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 Object* Runtime_RegExpInitializeObject(Arguments args) {
=======================================
--- /branches/bleeding_edge/src/runtime.h Fri Aug 6 06:04:27 2010
+++ /branches/bleeding_edge/src/runtime.h Fri Aug 20 02:37:22 2010
@@ -162,6 +162,7 @@
F(RegExpExecMultiple, 4, 1) \
F(RegExpInitializeObject, 5, 1) \
F(RegExpConstructResult, 3, 1) \
+ F(RegExpCloneResult, 1, 1) \
\
/* Strings */ \
F(StringCharCodeAt, 2, 1) \
=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.cc Fri Aug 20 00:10:18 2010
+++ /branches/bleeding_edge/src/x64/codegen-x64.cc Fri Aug 20 02:37:22 2010
@@ -4802,8 +4802,10 @@
class DeferredAllocateInNewSpace: public DeferredCode {
public:
- DeferredAllocateInNewSpace(int size, Register target)
- : size_(size), target_(target) {
+ DeferredAllocateInNewSpace(int size,
+ Register target,
+ int registers_to_save = 0)
+ : size_(size), target_(target), registers_to_save_(registers_to_save) {
ASSERT(size >= kPointerSize && size <=
Heap::MaxObjectSizeInNewSpace());
set_comment("[ DeferredAllocateInNewSpace");
}
@@ -4812,15 +4814,28 @@
private:
int size_;
Register target_;
+ int registers_to_save_;
};
void DeferredAllocateInNewSpace::Generate() {
+ for (int i = 0; i < kNumRegs; i++) {
+ if (registers_to_save_ & (1 << i)) {
+ Register save_register = { i };
+ __ push(save_register);
+ }
+ }
__ Push(Smi::FromInt(size_));
__ CallRuntime(Runtime::kAllocateInNewSpace, 1);
if (!target_.is(rax)) {
__ movq(target_, rax);
}
+ for (int i = kNumRegs - 1; i >= 0; i--) {
+ if (registers_to_save_ & (1 << i)) {
+ Register save_register = { i };
+ __ push(save_register);
+ }
+ }
}
@@ -6606,6 +6621,79 @@
frame_->Forget(3);
frame_->Push(rax);
}
+
+
+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);
+
+ 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);
+ ASSERT(!Heap::InNewSpace(Heap::fixed_cow_array_map()));
+ __ 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 {
=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.h Mon Aug 16 00:52:49 2010
+++ /branches/bleeding_edge/src/x64/codegen-x64.h Fri Aug 20 02:37:22 2010
@@ -659,6 +659,8 @@
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
+ void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
+
// Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args);
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Mon Aug 16 09:06:46
2010
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Fri Aug 20 02:37:22
2010
@@ -2626,6 +2626,14 @@
__ CallRuntime(Runtime::kRegExpConstructResult, 3);
Apply(context_, rax);
}
+
+
+void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args)
{
+ ASSERT(args->length() == 1);
+ VisitForValue(args->at(0), kStack);
+ __ CallRuntime(Runtime::kRegExpCloneResult, 1);
+ Apply(context_, rax);
+}
void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
=======================================
--- /branches/bleeding_edge/test/mjsunit/regexp.js Mon Apr 26 08:10:42 2010
+++ /branches/bleeding_edge/test/mjsunit/regexp.js Fri Aug 20 02:37:22 2010
@@ -484,3 +484,21 @@
assertRegExpTest(/[,b]\B[,b]/, ",b", false);
assertRegExpTest(/[,b]\b[,b]/, "b,", true);
assertRegExpTest(/[,b]\B[,b]/, "b,", false);
+
+// Test that caching of result doesn't share result objects.
+// More iterations increases the chance of hitting a GC.
+for (var i = 0; i < 100; i++) {
+ var re = /x(y)z/;
+ var res = re.exec("axyzb");
+ assertTrue(!!res);
+ assertEquals(2, res.length);
+ assertEquals("xyz", res[0]);
+ assertEquals("y", res[1]);
+ assertEquals(1, res.index);
+ assertEquals("axyzb", res.input);
+ assertEquals(undefined, res.foobar);
+
+ res.foobar = "Arglebargle";
+ res[3] = "Glopglyf";
+ assertEquals("Arglebargle", res.foobar);
+}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev