Version 2.3.9
Fix compilation for ARMv4 on OpenBSD/FreeBSD.
Removed specialized handling of GCC 4.4 (issue 830).
Fixed DST cache to take into account the suspension of DST in Egypt during the 2010 Ramadan (issue http://crbug.com/51855).
Performance improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@5295 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index 136c82e..6df6411 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -1820,6 +1820,7 @@
ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(offset % 4 == 0);
ASSERT((offset / 4) < 256);
+ ASSERT(offset >= 0);
emit(cond | 0xD9*B20 | base.code()*B16 | dst.code()*B12 |
0xB*B8 | ((offset / 4) & 255));
}
@@ -1836,6 +1837,7 @@
ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(offset % 4 == 0);
ASSERT((offset / 4) < 256);
+ ASSERT(offset >= 0);
emit(cond | 0xD9*B20 | base.code()*B16 | dst.code()*B12 |
0xA*B8 | ((offset / 4) & 255));
}
@@ -1852,11 +1854,29 @@
ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(offset % 4 == 0);
ASSERT((offset / 4) < 256);
+ ASSERT(offset >= 0);
emit(cond | 0xD8*B20 | base.code()*B16 | src.code()*B12 |
0xB*B8 | ((offset / 4) & 255));
}
+void Assembler::vstr(const SwVfpRegister src,
+ const Register base,
+ int offset,
+ const Condition cond) {
+ // MEM(Rbase + offset) = SSrc.
+ // Instruction details available in ARM DDI 0406A, A8-786.
+ // cond(31-28) | 1101(27-24)| 1000(23-20) | Rbase(19-16) |
+ // Vdst(15-12) | 1010(11-8) | (offset/4)
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
+ ASSERT(offset % 4 == 0);
+ ASSERT((offset / 4) < 256);
+ ASSERT(offset >= 0);
+ emit(cond | 0xD8*B20 | base.code()*B16 | src.code()*B12 |
+ 0xA*B8 | ((offset / 4) & 255));
+}
+
+
static void DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi) {
uint64_t i;
memcpy(&i, &d, 8);
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index 218eb97..cc6ec05 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -966,6 +966,11 @@
int offset, // Offset must be a multiple of 4.
const Condition cond = al);
+ void vstr(const SwVfpRegister src,
+ const Register base,
+ int offset, // Offset must be a multiple of 4.
+ const Condition cond = al);
+
void vmov(const DwVfpRegister dst,
double imm,
const Condition cond = al);
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index df17b6f..15f34b5 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -3445,12 +3445,18 @@
frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
frame_->EmitPush(Operand(node->constant_elements()));
int length = node->values()->length();
- if (node->depth() > 1) {
+ if (node->constant_elements()->map() == Heap::fixed_cow_array_map()) {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
+ frame_->CallStub(&stub, 3);
+ __ IncrementCounter(&Counters::cow_arrays_created_stub, 1, r1, r2);
+ } else if (node->depth() > 1) {
frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
+ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
} else {
- FastCloneShallowArrayStub stub(length);
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
frame_->CallStub(&stub, 3);
}
frame_->EmitPush(r0); // save the result
@@ -5402,7 +5408,7 @@
__ tst(tmp2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
deferred->Branch(nz);
- // Check the object's elements are in fast case.
+ // Check the object's elements are in fast case and writable.
__ ldr(tmp1, FieldMemOperand(object, JSObject::kElementsOffset));
__ ldr(tmp2, FieldMemOperand(tmp1, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
@@ -6694,15 +6700,9 @@
__ cmp(scratch1, scratch2);
deferred->Branch(ne);
- // Get the elements array from the receiver and check that it
- // is not a dictionary.
+ // Get the elements array from the receiver.
__ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
- if (FLAG_debug_code) {
- __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
- __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
- __ cmp(scratch2, ip);
- __ Assert(eq, "JSObject with fast elements map has slow elements");
- }
+ __ AssertFastElements(scratch1);
// Check that key is within bounds. Use unsigned comparison to handle
// negative keys.
@@ -7112,6 +7112,26 @@
__ cmp(r3, ip);
__ b(eq, &slow_case);
+ if (FLAG_debug_code) {
+ const char* message;
+ Heap::RootListIndex expected_map_index;
+ if (mode_ == CLONE_ELEMENTS) {
+ message = "Expected (writable) fixed array";
+ expected_map_index = Heap::kFixedArrayMapRootIndex;
+ } else {
+ ASSERT(mode_ == COPY_ON_WRITE_ELEMENTS);
+ message = "Expected copy-on-write fixed array";
+ expected_map_index = Heap::kFixedCOWArrayMapRootIndex;
+ }
+ __ push(r3);
+ __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset));
+ __ ldr(r3, FieldMemOperand(r3, HeapObject::kMapOffset));
+ __ LoadRoot(ip, expected_map_index);
+ __ cmp(r3, ip);
+ __ Assert(eq, message);
+ __ pop(r3);
+ }
+
// Allocate both the JS array and the elements array in one big
// allocation. This avoids multiple limit checks.
__ AllocateInNewSpace(size,
diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h
index 029d599..8a632a9 100644
--- a/src/arm/codegen-arm.h
+++ b/src/arm/codegen-arm.h
@@ -278,7 +278,7 @@
// Constants related to patching of inlined load/store.
static int GetInlinedKeyedLoadInstructionsAfterPatch() {
- return FLAG_debug_code ? 27 : 13;
+ return FLAG_debug_code ? 32 : 13;
}
static const int kInlinedKeyedStoreInstructionsAfterPatch = 5;
static int GetInlinedNamedStoreInstructionsAfterPatch() {
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index b58a4a5..f3adab2 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -1223,12 +1223,18 @@
__ mov(r2, Operand(Smi::FromInt(expr->literal_index())));
__ mov(r1, Operand(expr->constant_elements()));
__ Push(r3, r2, r1);
- if (expr->depth() > 1) {
+ if (expr->constant_elements()->map() == Heap::fixed_cow_array_map()) {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
+ __ CallStub(&stub);
+ __ IncrementCounter(&Counters::cow_arrays_created_stub, 1, r1, r2);
+ } else if (expr->depth() > 1) {
__ CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
+ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
__ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
} else {
- FastCloneShallowArrayStub stub(length);
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
__ CallStub(&stub);
}
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index 1fd7098..abc0922 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -414,17 +414,17 @@
// Falls through for regular JS object.
static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
Register receiver,
- Register scratch1,
- Register scratch2,
+ Register map,
+ Register scratch,
int interceptor_bit,
Label* slow) {
// Check that the object isn't a smi.
__ BranchOnSmi(receiver, slow);
// Get the map of the receiver.
- __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
// Check bit field.
- __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitFieldOffset));
- __ tst(scratch2,
+ __ ldrb(scratch, FieldMemOperand(map, Map::kBitFieldOffset));
+ __ tst(scratch,
Operand((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)));
__ b(nz, slow);
// Check that the object is some kind of JS object EXCEPT JS Value type.
@@ -432,13 +432,14 @@
// we enter the runtime system to make sure that indexing into string
// objects work as intended.
ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
- __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
- __ cmp(scratch1, Operand(JS_OBJECT_TYPE));
+ __ ldrb(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ __ cmp(scratch, Operand(JS_OBJECT_TYPE));
__ b(lt, slow);
}
// Loads an indexed element from a fast case array.
+// If not_fast_array is NULL, doesn't perform the elements map check.
static void GenerateFastArrayLoad(MacroAssembler* masm,
Register receiver,
Register key,
@@ -471,11 +472,15 @@
// scratch2 - used to hold the loaded value.
__ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
- __ ldr(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
- __ cmp(scratch1, ip);
- __ b(ne, not_fast_array);
+ if (not_fast_array != NULL) {
+ // Check that the object is in fast mode and writable.
+ __ ldr(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
+ __ cmp(scratch1, ip);
+ __ b(ne, not_fast_array);
+ } else {
+ __ AssertFastElements(elements);
+ }
// Check that the key (index) is within bounds.
__ ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
__ cmp(key, Operand(scratch1));
@@ -1120,16 +1125,23 @@
GenerateKeyedLoadReceiverCheck(
masm, receiver, r2, r3, Map::kHasIndexedInterceptor, &slow);
+ // Check the "has fast elements" bit in the receiver's map which is
+ // now in r2.
+ __ ldrb(r3, FieldMemOperand(r2, Map::kBitField2Offset));
+ __ tst(r3, Operand(1 << Map::kHasFastElements));
+ __ b(eq, &check_pixel_array);
+
GenerateFastArrayLoad(
- masm, receiver, key, r4, r3, r2, r0, &check_pixel_array, &slow);
+ masm, receiver, key, r4, r3, r2, r0, NULL, &slow);
__ IncrementCounter(&Counters::keyed_load_generic_smi, 1, r2, r3);
__ Ret();
// Check whether the elements is a pixel array.
// r0: key
- // r3: elements map
- // r4: elements
+ // r1: receiver
__ bind(&check_pixel_array);
+ __ ldr(r4, FieldMemOperand(r1, JSObject::kElementsOffset));
+ __ ldr(r3, FieldMemOperand(r4, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kPixelArrayMapRootIndex);
__ cmp(r3, ip);
__ b(ne, &check_number_dictionary);
@@ -1690,7 +1702,7 @@
// Object case: Check key against length in the elements array.
__ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
+ // Check that the object is in fast mode and writable.
__ ldr(r4, FieldMemOperand(elements, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
__ cmp(r4, ip);
@@ -1748,8 +1760,8 @@
__ b(&fast);
// Array case: Get the length and the elements array from the JS
- // array. Check that the array is in fast mode; if it is the
- // length is always a smi.
+ // array. Check that the array is in fast mode (and writable); if it
+ // is the length is always a smi.
__ bind(&array);
__ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
__ ldr(r4, FieldMemOperand(elements, HeapObject::kMapOffset));
@@ -1779,19 +1791,22 @@
}
-// Convert int passed in register ival to IEE 754 single precision
-// floating point value and store it into register fval.
+// Convert and store int passed in register ival to IEEE 754 single precision
+// floating point value at memory location (dst + 4 * wordoffset)
// If VFP3 is available use it for conversion.
-static void ConvertIntToFloat(MacroAssembler* masm,
- Register ival,
- Register fval,
- Register scratch1,
- Register scratch2) {
+static void StoreIntAsFloat(MacroAssembler* masm,
+ Register dst,
+ Register wordoffset,
+ Register ival,
+ Register fval,
+ Register scratch1,
+ Register scratch2) {
if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ vmov(s0, ival);
+ __ add(scratch1, dst, Operand(wordoffset, LSL, 2));
__ vcvt_f32_s32(s0, s0);
- __ vmov(fval, s0);
+ __ vstr(s0, scratch1, 0);
} else {
Label not_special, done;
// Move sign bit from source to destination. This works because the sign
@@ -1841,6 +1856,7 @@
Operand(ival, LSR, kBitsPerInt - kBinary32MantissaBits));
__ bind(&done);
+ __ str(fval, MemOperand(dst, wordoffset, LSL, 2));
}
}
@@ -1935,9 +1951,8 @@
__ str(r5, MemOperand(r3, r4, LSL, 2));
break;
case kExternalFloatArray:
- // Need to perform int-to-float conversion.
- ConvertIntToFloat(masm, r5, r6, r7, r9);
- __ str(r6, MemOperand(r3, r4, LSL, 2));
+ // Perform int-to-float conversion and store to memory.
+ StoreIntAsFloat(masm, r3, r4, r5, r6, r7, r9);
break;
default:
UNREACHABLE();
@@ -1971,9 +1986,9 @@
// include -kHeapObjectTag into it.
__ sub(r5, r0, Operand(kHeapObjectTag));
__ vldr(d0, r5, HeapNumber::kValueOffset);
+ __ add(r5, r3, Operand(r4, LSL, 2));
__ vcvt_f32_f64(s0, d0);
- __ vmov(r5, s0);
- __ str(r5, MemOperand(r3, r4, LSL, 2));
+ __ vstr(s0, r5, 0);
} else {
// Need to perform float-to-int conversion.
// Test for NaN or infinity (both give zero).
@@ -2217,6 +2232,8 @@
__ b(ne, &miss);
// Check that elements are FixedArray.
+ // We rely on StoreIC_ArrayLength below to deal with all types of
+ // fast elements (including COW).
__ ldr(scratch, FieldMemOperand(receiver, JSArray::kElementsOffset));
__ CompareObjectType(scratch, scratch, scratch, FIXED_ARRAY_TYPE);
__ b(ne, &miss);
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index 38c7c28..2058ee2 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -1567,6 +1567,25 @@
}
+void MacroAssembler::AssertFastElements(Register elements) {
+ if (FLAG_debug_code) {
+ ASSERT(!elements.is(ip));
+ Label ok;
+ push(elements);
+ ldr(elements, FieldMemOperand(elements, HeapObject::kMapOffset));
+ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
+ cmp(elements, ip);
+ b(eq, &ok);
+ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
+ cmp(elements, ip);
+ b(eq, &ok);
+ Abort("JSObject with fast elements map has slow elements");
+ bind(&ok);
+ pop(elements);
+ }
+}
+
+
void MacroAssembler::Check(Condition cc, const char* msg) {
Label L;
b(cc, &L);
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index 836ed74..7843f00 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -597,6 +597,7 @@
// Use --debug_code to enable.
void Assert(Condition cc, const char* msg);
void AssertRegisterIsRoot(Register reg, Heap::RootListIndex index);
+ void AssertFastElements(Register elements);
// Like Assert(), but always enabled.
void Check(Condition cc, const char* msg);
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index fa90ca7..2a7c22d 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -1297,11 +1297,6 @@
// Check that the maps haven't changed.
CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, r4, name, &miss);
- if (object->IsGlobalObject()) {
- __ ldr(r3, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
- __ str(r3, MemOperand(sp, argc * kPointerSize));
- }
-
__ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
argc + 1,
1);
@@ -1349,11 +1344,6 @@
// Check that the maps haven't changed.
CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, r4, name, &miss);
- if (object->IsGlobalObject()) {
- __ ldr(r3, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
- __ str(r3, MemOperand(sp, argc * kPointerSize));
- }
-
__ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop),
argc + 1,
1);
diff --git a/src/builtins.cc b/src/builtins.cc
index 3a0393e..eedee9f 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -269,6 +269,7 @@
int src_index,
int len) {
ASSERT(dst != src); // Use MoveElements instead.
+ ASSERT(dst->map() != Heap::fixed_cow_array_map());
ASSERT(len > 0);
CopyWords(dst->data_start() + dst_index,
src->data_start() + src_index,
@@ -286,6 +287,7 @@
FixedArray* src,
int src_index,
int len) {
+ ASSERT(dst->map() != Heap::fixed_cow_array_map());
memmove(dst->data_start() + dst_index,
src->data_start() + src_index,
len * kPointerSize);
@@ -297,11 +299,13 @@
static void FillWithHoles(FixedArray* dst, int from, int to) {
+ ASSERT(dst->map() != Heap::fixed_cow_array_map());
MemsetPointer(dst->data_start() + from, Heap::the_hole_value(), to - from);
}
static FixedArray* LeftTrimFixedArray(FixedArray* elms, int to_trim) {
+ ASSERT(elms->map() != Heap::fixed_cow_array_map());
// For now this trick is only applied to fixed arrays in new space.
// In large object space the object's start must coincide with chunk
// and thus the trick is just not applicable.
@@ -348,33 +352,24 @@
}
-static bool IsJSArrayWithFastElements(Object* receiver,
- FixedArray** elements) {
- if (!receiver->IsJSArray()) {
- return false;
- }
-
+static inline Object* EnsureJSArrayWithWritableFastElements(Object* receiver) {
+ if (!receiver->IsJSArray()) return NULL;
JSArray* array = JSArray::cast(receiver);
-
HeapObject* elms = HeapObject::cast(array->elements());
- if (elms->map() != Heap::fixed_array_map()) {
- return false;
+ if (elms->map() == Heap::fixed_array_map()) return elms;
+ if (elms->map() == Heap::fixed_cow_array_map()) {
+ return array->EnsureWritableFastElements();
}
-
- *elements = FixedArray::cast(elms);
- return true;
+ return NULL;
}
-static bool IsFastElementMovingAllowed(Object* receiver,
- FixedArray** elements) {
- if (!IsJSArrayWithFastElements(receiver, elements)) return false;
-
+static inline bool IsJSArrayFastElementMovingAllowed(JSArray* receiver) {
Context* global_context = Top::context()->global_context();
JSObject* array_proto =
JSObject::cast(global_context->array_function()->prototype());
- if (JSArray::cast(receiver)->GetPrototype() != array_proto) return false;
- return ArrayPrototypeHasNoElements(global_context, array_proto);
+ return receiver->GetPrototype() == array_proto &&
+ ArrayPrototypeHasNoElements(global_context, array_proto);
}
@@ -405,10 +400,10 @@
BUILTIN(ArrayPush) {
Object* receiver = *args.receiver();
- FixedArray* elms = NULL;
- if (!IsJSArrayWithFastElements(receiver, &elms)) {
- return CallJsBuiltin("ArrayPush", args);
- }
+ Object* elms_obj = EnsureJSArrayWithWritableFastElements(receiver);
+ if (elms_obj == NULL) return CallJsBuiltin("ArrayPush", args);
+ if (elms_obj->IsFailure()) return elms_obj;
+ FixedArray* elms = FixedArray::cast(elms_obj);
JSArray* array = JSArray::cast(receiver);
int len = Smi::cast(array->length())->value();
@@ -454,10 +449,10 @@
BUILTIN(ArrayPop) {
Object* receiver = *args.receiver();
- FixedArray* elms = NULL;
- if (!IsJSArrayWithFastElements(receiver, &elms)) {
- return CallJsBuiltin("ArrayPop", args);
- }
+ Object* elms_obj = EnsureJSArrayWithWritableFastElements(receiver);
+ if (elms_obj == NULL) return CallJsBuiltin("ArrayPop", args);
+ if (elms_obj->IsFailure()) return elms_obj;
+ FixedArray* elms = FixedArray::cast(elms_obj);
JSArray* array = JSArray::cast(receiver);
int len = Smi::cast(array->length())->value();
@@ -483,10 +478,13 @@
BUILTIN(ArrayShift) {
Object* receiver = *args.receiver();
- FixedArray* elms = NULL;
- if (!IsFastElementMovingAllowed(receiver, &elms)) {
+ Object* elms_obj = EnsureJSArrayWithWritableFastElements(receiver);
+ if (elms_obj->IsFailure()) return elms_obj;
+ if (elms_obj == NULL ||
+ !IsJSArrayFastElementMovingAllowed(JSArray::cast(receiver))) {
return CallJsBuiltin("ArrayShift", args);
}
+ FixedArray* elms = FixedArray::cast(elms_obj);
JSArray* array = JSArray::cast(receiver);
ASSERT(array->HasFastElements());
@@ -519,10 +517,13 @@
BUILTIN(ArrayUnshift) {
Object* receiver = *args.receiver();
- FixedArray* elms = NULL;
- if (!IsFastElementMovingAllowed(receiver, &elms)) {
+ Object* elms_obj = EnsureJSArrayWithWritableFastElements(receiver);
+ if (elms_obj->IsFailure()) return elms_obj;
+ if (elms_obj == NULL ||
+ !IsJSArrayFastElementMovingAllowed(JSArray::cast(receiver))) {
return CallJsBuiltin("ArrayUnshift", args);
}
+ FixedArray* elms = FixedArray::cast(elms_obj);
JSArray* array = JSArray::cast(receiver);
ASSERT(array->HasFastElements());
@@ -568,10 +569,13 @@
BUILTIN(ArraySlice) {
Object* receiver = *args.receiver();
- FixedArray* elms = NULL;
- if (!IsFastElementMovingAllowed(receiver, &elms)) {
+ Object* elms_obj = EnsureJSArrayWithWritableFastElements(receiver);
+ if (elms_obj->IsFailure()) return elms_obj;
+ if (elms_obj == NULL ||
+ !IsJSArrayFastElementMovingAllowed(JSArray::cast(receiver))) {
return CallJsBuiltin("ArraySlice", args);
}
+ FixedArray* elms = FixedArray::cast(elms_obj);
JSArray* array = JSArray::cast(receiver);
ASSERT(array->HasFastElements());
@@ -637,10 +641,13 @@
BUILTIN(ArraySplice) {
Object* receiver = *args.receiver();
- FixedArray* elms = NULL;
- if (!IsFastElementMovingAllowed(receiver, &elms)) {
+ Object* elms_obj = EnsureJSArrayWithWritableFastElements(receiver);
+ if (elms_obj->IsFailure()) return elms_obj;
+ if (elms_obj == NULL ||
+ !IsJSArrayFastElementMovingAllowed(JSArray::cast(receiver))) {
return CallJsBuiltin("ArraySplice", args);
}
+ FixedArray* elms = FixedArray::cast(elms_obj);
JSArray* array = JSArray::cast(receiver);
ASSERT(array->HasFastElements());
diff --git a/src/codegen.h b/src/codegen.h
index 3b31c04..353e186 100644
--- a/src/codegen.h
+++ b/src/codegen.h
@@ -181,6 +181,7 @@
CodeGenerator* previous_;
};
+
#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
// State of used registers in a virtual frame.
@@ -395,20 +396,33 @@
class FastCloneShallowArrayStub : public CodeStub {
public:
- static const int kMaximumLength = 8;
+ // Maximum length of copied elements array.
+ static const int kMaximumClonedLength = 8;
- explicit FastCloneShallowArrayStub(int length) : length_(length) {
- ASSERT(length >= 0 && length <= kMaximumLength);
+ enum Mode {
+ CLONE_ELEMENTS,
+ COPY_ON_WRITE_ELEMENTS
+ };
+
+ FastCloneShallowArrayStub(Mode mode, int length)
+ : mode_(mode),
+ length_((mode == COPY_ON_WRITE_ELEMENTS) ? 0 : length) {
+ ASSERT(length_ >= 0);
+ ASSERT(length_ <= kMaximumClonedLength);
}
void Generate(MacroAssembler* masm);
private:
+ Mode mode_;
int length_;
const char* GetName() { return "FastCloneShallowArrayStub"; }
Major MajorKey() { return FastCloneShallowArray; }
- int MinorKey() { return length_; }
+ int MinorKey() {
+ ASSERT(mode_ == 0 || mode_ == 1);
+ return (length_ << 1) | mode_;
+ }
};
diff --git a/src/compilation-cache.cc b/src/compilation-cache.cc
index 14252a5..7402e68 100644
--- a/src/compilation-cache.cc
+++ b/src/compilation-cache.cc
@@ -79,10 +79,9 @@
// young generation.
void Age();
- bool HasFunction(SharedFunctionInfo* function_info);
-
// GC support.
void Iterate(ObjectVisitor* v);
+ void IterateFunctions(ObjectVisitor* v);
// Clear this sub-cache evicting all its content.
void Clear();
@@ -206,27 +205,6 @@
}
-bool CompilationSubCache::HasFunction(SharedFunctionInfo* function_info) {
- if (function_info->script()->IsUndefined() ||
- Script::cast(function_info->script())->source()->IsUndefined()) {
- return false;
- }
-
- String* source =
- String::cast(Script::cast(function_info->script())->source());
- // Check all generations.
- for (int generation = 0; generation < generations(); generation++) {
- if (tables_[generation]->IsUndefined()) continue;
-
- CompilationCacheTable* table =
- CompilationCacheTable::cast(tables_[generation]);
- Object* object = table->Lookup(source);
- if (object->IsSharedFunctionInfo()) return true;
- }
- return false;
-}
-
-
void CompilationSubCache::Age() {
// Age the generations implicitly killing off the oldest.
for (int i = generations_ - 1; i > 0; i--) {
@@ -238,6 +216,16 @@
}
+void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
+ Object* undefined = Heap::raw_unchecked_undefined_value();
+ for (int i = 0; i < generations_; i++) {
+ if (tables_[i] != undefined) {
+ reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
+ }
+ }
+}
+
+
void CompilationSubCache::Iterate(ObjectVisitor* v) {
v->VisitPointers(&tables_[0], &tables_[generations_]);
}
@@ -528,12 +516,6 @@
}
}
-
-bool CompilationCache::HasFunction(SharedFunctionInfo* function_info) {
- return script.HasFunction(function_info);
-}
-
-
void CompilationCache::Iterate(ObjectVisitor* v) {
for (int i = 0; i < kSubCacheCount; i++) {
subcaches[i]->Iterate(v);
@@ -541,6 +523,13 @@
}
+void CompilationCache::IterateFunctions(ObjectVisitor* v) {
+ for (int i = 0; i < kSubCacheCount; i++) {
+ subcaches[i]->IterateFunctions(v);
+ }
+}
+
+
void CompilationCache::MarkCompactPrologue() {
for (int i = 0; i < kSubCacheCount; i++) {
subcaches[i]->Age();
diff --git a/src/compilation-cache.h b/src/compilation-cache.h
index 583f04c..22ecff8 100644
--- a/src/compilation-cache.h
+++ b/src/compilation-cache.h
@@ -79,11 +79,9 @@
// Clear the cache - also used to initialize the cache at startup.
static void Clear();
-
- static bool HasFunction(SharedFunctionInfo* function_info);
-
// GC support.
static void Iterate(ObjectVisitor* v);
+ static void IterateFunctions(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
diff --git a/src/compiler.cc b/src/compiler.cc
index 9f0162e..ff3cb7a 100755
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -454,6 +454,7 @@
// Check the function has compiled code.
ASSERT(shared->is_compiled());
+ shared->set_code_age(0);
return true;
}
diff --git a/src/contexts.h b/src/contexts.h
index d1c98bd..181efc9 100644
--- a/src/contexts.h
+++ b/src/contexts.h
@@ -243,7 +243,8 @@
GlobalObject* global() {
Object* result = get(GLOBAL_INDEX);
- ASSERT(IsBootstrappingOrGlobalObject(result));
+ ASSERT(Heap::gc_state() != Heap::NOT_IN_GC ||
+ IsBootstrappingOrGlobalObject(result));
return reinterpret_cast<GlobalObject*>(result);
}
void set_global(GlobalObject* global) { set(GLOBAL_INDEX, global); }
diff --git a/src/date.js b/src/date.js
index 9c42a04..b101ea6 100644
--- a/src/date.js
+++ b/src/date.js
@@ -137,12 +137,18 @@
// Time interval where the cached offset is valid.
start: 0, end: -1,
// Size of next interval expansion.
- increment: 0
+ increment: 0,
+ initial_increment: 19 * msPerDay
};
// NOTE: The implementation relies on the fact that no time zones have
-// more than one daylight savings offset change per month.
+// more than one daylight savings offset change per 19 days.
+//
+// In Egypt in 2010 they decided to suspend DST during Ramadan. This
+// led to a short interval where DST is in effect from September 10 to
+// September 30.
+//
// If this function is called with NaN it returns NaN.
function DaylightSavingsOffset(t) {
// Load the cache object from the builtins object.
@@ -171,7 +177,7 @@
// the offset in the cache, we grow the cached time interval
// and return the offset.
cache.end = new_end;
- cache.increment = msPerMonth;
+ cache.increment = cache.initial_increment;
return end_offset;
} else {
var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
@@ -182,7 +188,7 @@
// the interval to reflect this and reset the increment.
cache.start = t;
cache.end = new_end;
- cache.increment = msPerMonth;
+ cache.increment = cache.initial_increment;
} else {
// The interval contains a DST offset change and the given time is
// before it. Adjust the increment to avoid a linear search for
@@ -207,7 +213,7 @@
var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
cache.offset = offset;
cache.start = cache.end = t;
- cache.increment = msPerMonth;
+ cache.increment = cache.initial_increment;
return offset;
}
diff --git a/src/debug.cc b/src/debug.cc
index c13c8c9..1234196 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -461,6 +461,8 @@
KeyedStoreIC::ClearInlinedVersion(pc());
} else if (code->is_load_stub()) {
LoadIC::ClearInlinedVersion(pc());
+ } else if (code->is_store_stub()) {
+ StoreIC::ClearInlinedVersion(pc());
}
}
}
@@ -549,6 +551,7 @@
thread_local_.after_break_target_ = 0;
thread_local_.debugger_entry_ = NULL;
thread_local_.pending_interrupts_ = 0;
+ thread_local_.restarter_frame_function_pointer_ = NULL;
}
diff --git a/src/frames.cc b/src/frames.cc
index bdd5100..9f815c3 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -372,8 +372,8 @@
}
-Code* EntryFrame::code() const {
- return Heap::js_entry_code();
+Code* EntryFrame::unchecked_code() const {
+ return Heap::raw_unchecked_js_entry_code();
}
@@ -395,8 +395,8 @@
}
-Code* EntryConstructFrame::code() const {
- return Heap::js_construct_entry_code();
+Code* EntryConstructFrame::unchecked_code() const {
+ return Heap::raw_unchecked_js_construct_entry_code();
}
@@ -406,8 +406,8 @@
}
-Code* ExitFrame::code() const {
- return Code::cast(code_slot());
+Code* ExitFrame::unchecked_code() const {
+ return reinterpret_cast<Code*>(code_slot());
}
@@ -493,22 +493,22 @@
}
-Code* JavaScriptFrame::code() const {
+Code* JavaScriptFrame::unchecked_code() const {
JSFunction* function = JSFunction::cast(this->function());
- return function->shared()->code();
+ return function->unchecked_code();
}
-Code* ArgumentsAdaptorFrame::code() const {
+Code* ArgumentsAdaptorFrame::unchecked_code() const {
return Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline);
}
-Code* InternalFrame::code() const {
+Code* InternalFrame::unchecked_code() const {
const int offset = InternalFrameConstants::kCodeOffset;
Object* code = Memory::Object_at(fp() + offset);
ASSERT(code != NULL);
- return Code::cast(code);
+ return reinterpret_cast<Code*>(code);
}
diff --git a/src/frames.h b/src/frames.h
index 102244c..cb791d2 100644
--- a/src/frames.h
+++ b/src/frames.h
@@ -112,7 +112,13 @@
// Opaque data type for identifying stack frames. Used extensively
// by the debugger.
- enum Id { NO_ID = 0 };
+ // ID_MIN_VALUE and ID_MAX_VALUE are specified to ensure that enumeration type
+ // has correct value range (see Issue 830 for more details).
+ enum Id {
+ ID_MIN_VALUE = kMinInt,
+ ID_MAX_VALUE = kMaxInt,
+ NO_ID = 0
+ };
// Copy constructor; it breaks the connection to host iterator.
StackFrame(const StackFrame& original) {
@@ -152,7 +158,13 @@
virtual Type type() const = 0;
// Get the code associated with this frame.
- virtual Code* code() const = 0;
+ // This method could be called during marking phase of GC.
+ virtual Code* unchecked_code() const = 0;
+
+ // Get the code associated with this frame.
+ inline Code* code() const {
+ return Code::cast(unchecked_code());
+ }
// Garbage collection support.
static void CookFramesForThread(ThreadLocalTop* thread);
@@ -218,7 +230,7 @@
public:
virtual Type type() const { return ENTRY; }
- virtual Code* code() const;
+ virtual Code* unchecked_code() const;
// Garbage collection support.
virtual void Iterate(ObjectVisitor* v) const;
@@ -249,7 +261,7 @@
public:
virtual Type type() const { return ENTRY_CONSTRUCT; }
- virtual Code* code() const;
+ virtual Code* unchecked_code() const;
static EntryConstructFrame* cast(StackFrame* frame) {
ASSERT(frame->is_entry_construct());
@@ -271,7 +283,7 @@
enum Mode { MODE_NORMAL, MODE_DEBUG };
virtual Type type() const { return EXIT; }
- virtual Code* code() const;
+ virtual Code* unchecked_code() const;
Object*& code_slot() const;
@@ -397,7 +409,7 @@
int index) const;
// Determine the code for the frame.
- virtual Code* code() const;
+ virtual Code* unchecked_code() const;
static JavaScriptFrame* cast(StackFrame* frame) {
ASSERT(frame->is_java_script());
@@ -433,7 +445,7 @@
virtual Type type() const { return ARGUMENTS_ADAPTOR; }
// Determine the code for the frame.
- virtual Code* code() const;
+ virtual Code* unchecked_code() const;
static ArgumentsAdaptorFrame* cast(StackFrame* frame) {
ASSERT(frame->is_arguments_adaptor());
@@ -463,7 +475,7 @@
virtual void Iterate(ObjectVisitor* v) const;
// Determine the code for the frame.
- virtual Code* code() const;
+ virtual Code* unchecked_code() const;
static InternalFrame* cast(StackFrame* frame) {
ASSERT(frame->is_internal());
diff --git a/src/handles.cc b/src/handles.cc
index 7b76e92..0146401 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -773,6 +773,7 @@
ClearExceptionFlag flag) {
if (function->shared()->is_compiled()) {
function->set_code(function->shared()->code());
+ function->shared()->set_code_age(0);
return true;
} else {
CompilationInfo info(function, 0, receiver);
@@ -788,6 +789,7 @@
ClearExceptionFlag flag) {
if (function->shared()->is_compiled()) {
function->set_code(function->shared()->code());
+ function->shared()->set_code_age(0);
return true;
} else {
CompilationInfo info(function, 1, receiver);
diff --git a/src/heap.cc b/src/heap.cc
index 1d696c7..5f46fa7 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -637,12 +637,6 @@
int start_new_space_size = Heap::new_space()->Size();
if (collector == MARK_COMPACTOR) {
- if (FLAG_flush_code) {
- // Flush all potentially unused code.
- GCTracer::Scope gc_scope(tracer, GCTracer::Scope::MC_FLUSH_CODE);
- FlushCode();
- }
-
// Perform mark-sweep with optional compaction.
MarkCompact(tracer);
@@ -1100,6 +1094,10 @@
&ObjectEvacuationStrategy<POINTER_OBJECT>::
VisitSpecialized<SharedFunctionInfo::kSize>);
+ table_.Register(kVisitJSFunction,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ VisitSpecialized<JSFunction::kSize>);
+
table_.RegisterSpecializations<ObjectEvacuationStrategy<DATA_OBJECT>,
kVisitDataObject,
kVisitDataObjectGeneric>();
@@ -1457,6 +1455,11 @@
oddball_map()->set_prototype(null_value());
oddball_map()->set_constructor(null_value());
+ obj = AllocateMap(FIXED_ARRAY_TYPE, FixedArray::kHeaderSize);
+ if (obj->IsFailure()) return false;
+ set_fixed_cow_array_map(Map::cast(obj));
+ ASSERT(fixed_array_map() != fixed_cow_array_map());
+
obj = AllocateMap(HEAP_NUMBER_TYPE, HeapNumber::kSize);
if (obj->IsFailure()) return false;
set_heap_number_map(Map::cast(obj));
@@ -2354,109 +2357,6 @@
}
-// The StackVisitor is used to traverse all the archived threads to see if
-// there are activations on any of the stacks corresponding to the code.
-class FlushingStackVisitor : public ThreadVisitor {
- public:
- explicit FlushingStackVisitor(Code* code) : found_(false), code_(code) {}
-
- void VisitThread(ThreadLocalTop* top) {
- // If we already found the code in a previous traversed thread we return.
- if (found_) return;
-
- for (StackFrameIterator it(top); !it.done(); it.Advance()) {
- if (code_->contains(it.frame()->pc())) {
- found_ = true;
- return;
- }
- }
- }
- bool FoundCode() {return found_;}
-
- private:
- bool found_;
- Code* code_;
-};
-
-
-static bool CodeIsActive(Code* code) {
- // Make sure we are not referencing the code from the stack.
- for (StackFrameIterator it; !it.done(); it.Advance()) {
- if (code->contains(it.frame()->pc())) return true;
- }
- // Iterate the archived stacks in all threads to check if
- // the code is referenced.
- FlushingStackVisitor threadvisitor(code);
- ThreadManager::IterateArchivedThreads(&threadvisitor);
- if (threadvisitor.FoundCode()) return true;
- return false;
-}
-
-
-static void FlushCodeForFunction(JSFunction* function) {
- SharedFunctionInfo* shared_info = function->shared();
-
- // Special handling if the function and shared info objects
- // have different code objects.
- if (function->code() != shared_info->code()) {
- // If the shared function has been flushed but the function has not,
- // we flush the function if possible.
- if (!shared_info->is_compiled() && function->is_compiled() &&
- !CodeIsActive(function->code())) {
- function->set_code(shared_info->code());
- }
- return;
- }
-
- // The function must be compiled and have the source code available,
- // to be able to recompile it in case we need the function again.
- if (!(shared_info->is_compiled() && shared_info->HasSourceCode())) return;
-
- // We never flush code for Api functions.
- if (shared_info->IsApiFunction()) return;
-
- // Only flush code for functions.
- if (!shared_info->code()->kind() == Code::FUNCTION) return;
-
- // Function must be lazy compilable.
- if (!shared_info->allows_lazy_compilation()) return;
-
- // If this is a full script wrapped in a function we do no flush the code.
- if (shared_info->is_toplevel()) return;
-
- // If this function is in the compilation cache we do not flush the code.
- if (CompilationCache::HasFunction(shared_info)) return;
-
- // Check stack and archived threads for the code.
- if (CodeIsActive(shared_info->code())) return;
-
- // Compute the lazy compilable version of the code.
- Code* code = Builtins::builtin(Builtins::LazyCompile);
- shared_info->set_code(code);
- function->set_code(code);
-}
-
-
-void Heap::FlushCode() {
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Do not flush code if the debugger is loaded or there are breakpoints.
- if (Debug::IsLoaded() || Debug::has_break_points()) return;
-#endif
- HeapObjectIterator it(old_pointer_space());
- for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) {
- if (obj->IsJSFunction()) {
- JSFunction* function = JSFunction::cast(obj);
-
- // The function must have a valid context and not be a builtin.
- if (function->unchecked_context()->IsContext() &&
- !function->IsBuiltin()) {
- FlushCodeForFunction(function);
- }
- }
- }
-}
-
-
Object* Heap::CreateCode(const CodeDesc& desc,
Code::Flags flags,
Handle<Object> self_reference) {
@@ -2910,7 +2810,9 @@
FixedArray* properties = FixedArray::cast(source->properties());
// Update elements if necessary.
if (elements->length() > 0) {
- Object* elem = CopyFixedArray(elements);
+ Object* elem =
+ (elements->map() == fixed_cow_array_map()) ?
+ elements : CopyFixedArray(elements);
if (elem->IsFailure()) return elem;
JSObject::cast(clone)->set_elements(FixedArray::cast(elem));
}
@@ -4057,8 +3959,8 @@
void Heap::RecordStats(HeapStats* stats, bool take_snapshot) {
- *stats->start_marker = 0xDECADE00;
- *stats->end_marker = 0xDECADE01;
+ *stats->start_marker = HeapStats::kStartMarker;
+ *stats->end_marker = HeapStats::kEndMarker;
*stats->new_space_size = new_space_.Size();
*stats->new_space_capacity = new_space_.Capacity();
*stats->old_pointer_space_size = old_pointer_space_->Size();
@@ -4815,7 +4717,6 @@
PrintF("sweep=%d ", static_cast<int>(scopes_[Scope::MC_SWEEP]));
PrintF("sweepns=%d ", static_cast<int>(scopes_[Scope::MC_SWEEP_NEWSPACE]));
PrintF("compact=%d ", static_cast<int>(scopes_[Scope::MC_COMPACT]));
- PrintF("flushcode=%d ", static_cast<int>(scopes_[Scope::MC_FLUSH_CODE]));
PrintF("total_size_before=%d ", start_size_);
PrintF("total_size_after=%d ", Heap::SizeOfObjects());
diff --git a/src/heap.h b/src/heap.h
index 93b90b1..45fee17 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -55,6 +55,7 @@
V(Map, heap_number_map, HeapNumberMap) \
V(Map, global_context_map, GlobalContextMap) \
V(Map, fixed_array_map, FixedArrayMap) \
+ V(Map, fixed_cow_array_map, FixedCOWArrayMap) \
V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \
V(Map, meta_map, MetaMap) \
V(Object, termination_exception, TerminationException) \
@@ -1257,10 +1258,6 @@
// Flush the number to string cache.
static void FlushNumberStringCache();
- // Flush code from functions we do not expect to use again. The code will
- // be replaced with a lazy compilable version.
- static void FlushCode();
-
static void UpdateSurvivalRateTrend(int start_new_space_size);
enum SurvivalRateTrend { INCREASING, STABLE, DECREASING, FLUCTUATING };
@@ -1322,6 +1319,9 @@
class HeapStats {
public:
+ static const int kStartMarker = 0xDECADE00;
+ static const int kEndMarker = 0xDECADE01;
+
int* start_marker; // 0
int* new_space_size; // 1
int* new_space_capacity; // 2
diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
index cc89cc7..02a03fc 100644
--- a/src/ia32/codegen-ia32.cc
+++ b/src/ia32/codegen-ia32.cc
@@ -5712,12 +5712,18 @@
frame_->Push(node->constant_elements());
int length = node->values()->length();
Result clone;
- if (node->depth() > 1) {
+ if (node->constant_elements()->map() == Heap::fixed_cow_array_map()) {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
+ clone = frame_->CallStub(&stub, 3);
+ __ IncrementCounter(&Counters::cow_arrays_created_stub, 1);
+ } else if (node->depth() > 1) {
clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
+ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
} else {
- FastCloneShallowArrayStub stub(length);
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
clone = frame_->CallStub(&stub, 3);
}
frame_->Push(&clone);
@@ -5727,12 +5733,9 @@
for (int i = 0; i < length; i++) {
Expression* value = node->values()->at(i);
- // If value is a literal the property value is already set in the
- // boilerplate object.
- if (value->AsLiteral() != NULL) continue;
- // If value is a materialized literal the property value is already set
- // in the boilerplate object if it is simple.
- if (CompileTimeValue::IsCompileTimeValue(value)) continue;
+ if (!CompileTimeValue::ArrayLiteralElementNeedsInitialization(value)) {
+ continue;
+ }
// The property must be set by generated code.
Load(value);
@@ -7601,7 +7604,7 @@
KeyedLoadIC::kSlowCaseBitFieldMask);
deferred->Branch(not_zero);
- // Check the object's elements are in fast case.
+ // Check the object's elements are in fast case and writable.
__ mov(tmp1.reg(), FieldOperand(object.reg(), JSObject::kElementsOffset));
__ cmp(FieldOperand(tmp1.reg(), HeapObject::kMapOffset),
Immediate(Factory::fixed_array_map()));
@@ -9496,15 +9499,10 @@
if (FLAG_debug_code) __ AbortIfNotSmi(key.reg());
}
- // Get the elements array from the receiver and check that it
- // is not a dictionary.
+ // Get the elements array from the receiver.
__ mov(elements.reg(),
FieldOperand(receiver.reg(), JSObject::kElementsOffset));
- if (FLAG_debug_code) {
- __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- __ Assert(equal, "JSObject with fast elements map has slow elements");
- }
+ __ AssertFastElements(elements.reg());
// Check that the key is within bounds.
__ cmp(key.reg(),
@@ -9901,6 +9899,24 @@
__ cmp(ecx, Factory::undefined_value());
__ j(equal, &slow_case);
+ if (FLAG_debug_code) {
+ const char* message;
+ Handle<Map> expected_map;
+ if (mode_ == CLONE_ELEMENTS) {
+ message = "Expected (writable) fixed array";
+ expected_map = Factory::fixed_array_map();
+ } else {
+ ASSERT(mode_ == COPY_ON_WRITE_ELEMENTS);
+ message = "Expected copy-on-write fixed array";
+ expected_map = Factory::fixed_cow_array_map();
+ }
+ __ push(ecx);
+ __ mov(ecx, FieldOperand(ecx, JSArray::kElementsOffset));
+ __ cmp(FieldOperand(ecx, HeapObject::kMapOffset), expected_map);
+ __ Assert(equal, message);
+ __ pop(ecx);
+ }
+
// Allocate both the JS array and the elements array in one big
// allocation. This avoids multiple limit checks.
__ AllocateInNewSpace(size, eax, ebx, edx, &slow_case, TAG_OBJECT);
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index cb36904..68a0a96 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -1326,12 +1326,18 @@
__ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
__ push(Immediate(Smi::FromInt(expr->literal_index())));
__ push(Immediate(expr->constant_elements()));
- if (expr->depth() > 1) {
+ if (expr->constant_elements()->map() == Heap::fixed_cow_array_map()) {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
+ __ CallStub(&stub);
+ __ IncrementCounter(&Counters::cow_arrays_created_stub, 1);
+ } else if (expr->depth() > 1) {
__ CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
+ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
__ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
} else {
- FastCloneShallowArrayStub stub(length);
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
__ CallStub(&stub);
}
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index 2cd41a1..283ae4d 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -452,6 +452,7 @@
// Loads an indexed element from a fast case array.
+// If not_fast_array is NULL, doesn't perform the elements map check.
static void GenerateFastArrayLoad(MacroAssembler* masm,
Register receiver,
Register key,
@@ -468,8 +469,12 @@
// we fall through.
__ mov(scratch, FieldOperand(receiver, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
- __ CheckMap(scratch, Factory::fixed_array_map(), not_fast_array, true);
+ if (not_fast_array != NULL) {
+ // Check that the object is in fast mode and writable.
+ __ CheckMap(scratch, Factory::fixed_array_map(), not_fast_array, true);
+ } else {
+ __ AssertFastElements(scratch);
+ }
// Check that the key (index) is within bounds.
__ cmp(key, FieldOperand(scratch, FixedArray::kLengthOffset));
__ j(above_equal, out_of_range);
@@ -558,12 +563,18 @@
GenerateKeyedLoadReceiverCheck(
masm, edx, ecx, Map::kHasIndexedInterceptor, &slow);
+ // Check the "has fast elements" bit in the receiver's map which is
+ // now in ecx.
+ __ test_b(FieldOperand(ecx, Map::kBitField2Offset),
+ 1 << Map::kHasFastElements);
+ __ j(zero, &check_pixel_array, not_taken);
+
GenerateFastArrayLoad(masm,
edx,
eax,
ecx,
eax,
- &check_pixel_array,
+ NULL,
&slow);
__ IncrementCounter(&Counters::keyed_load_generic_smi, 1);
__ ret(0);
@@ -572,7 +583,7 @@
// Check whether the elements is a pixel array.
// edx: receiver
// eax: key
- // ecx: elements
+ __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
__ mov(ebx, eax);
__ SmiUntag(ebx);
__ CheckMap(ecx, Factory::pixel_array_map(), &check_number_dictionary, true);
@@ -967,7 +978,7 @@
// edx: JSObject
// ecx: key (a smi)
__ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
+ // Check that the object is in fast mode and writable.
__ CheckMap(edi, Factory::fixed_array_map(), &check_pixel_array, true);
__ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
__ j(below, &fast, taken);
@@ -1023,8 +1034,8 @@
__ jmp(&fast);
// Array case: Get the length and the elements array from the JS
- // array. Check that the array is in fast mode; if it is the
- // length is always a smi.
+ // array. Check that the array is in fast mode (and writable); if it
+ // is the length is always a smi.
__ bind(&array);
// eax: value
// edx: receiver, a JSArray
@@ -1872,6 +1883,8 @@
__ j(not_equal, &miss, not_taken);
// Check that elements are FixedArray.
+ // We rely on StoreIC_ArrayLength below to deal with all types of
+ // fast elements (including COW).
__ mov(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
__ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
__ j(not_equal, &miss, not_taken);
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index 2453846..658caf1 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -1464,6 +1464,21 @@
}
+void MacroAssembler::AssertFastElements(Register elements) {
+ if (FLAG_debug_code) {
+ Label ok;
+ cmp(FieldOperand(elements, HeapObject::kMapOffset),
+ Immediate(Factory::fixed_array_map()));
+ j(equal, &ok);
+ cmp(FieldOperand(elements, HeapObject::kMapOffset),
+ Immediate(Factory::fixed_cow_array_map()));
+ j(equal, &ok);
+ Abort("JSObject with fast elements map has slow elements");
+ bind(&ok);
+ }
+}
+
+
void MacroAssembler::Check(Condition cc, const char* msg) {
Label L;
j(cc, &L, taken);
diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h
index 0b16f0d..c23f687 100644
--- a/src/ia32/macro-assembler-ia32.h
+++ b/src/ia32/macro-assembler-ia32.h
@@ -508,6 +508,8 @@
// Use --debug_code to enable.
void Assert(Condition cc, const char* msg);
+ void AssertFastElements(Register elements);
+
// Like Assert(), but always enabled.
void Check(Condition cc, const char* msg);
diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc
index b2c9dab..c6c65f0 100644
--- a/src/ia32/stub-cache-ia32.cc
+++ b/src/ia32/stub-cache-ia32.cc
@@ -1366,16 +1366,18 @@
__ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
__ ret((argc + 1) * kPointerSize);
} else {
+ Label call_builtin;
+
// Get the elements array of the object.
__ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset));
- // Check that the elements are in fast mode (not dictionary).
+ // Check that the elements are in fast mode and writable.
__ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &miss);
+ __ j(not_equal, &call_builtin);
if (argc == 1) { // Otherwise fall through to call builtin.
- Label call_builtin, exit, with_write_barrier, attempt_to_grow_elements;
+ Label exit, with_write_barrier, attempt_to_grow_elements;
// Get the array's length into eax and calculate new length.
__ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
@@ -1456,10 +1458,9 @@
// Elements are in new space, so write barrier is not required.
__ ret((argc + 1) * kPointerSize);
-
- __ bind(&call_builtin);
}
+ __ bind(&call_builtin);
__ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
argc + 1,
1);
@@ -1511,10 +1512,10 @@
// Get the elements array of the object.
__ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset));
- // Check that the elements are in fast mode (not dictionary).
+ // Check that the elements are in fast mode and writable.
__ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &miss);
+ __ j(not_equal, &call_builtin);
// Get the array's length into ecx and calculate new length.
__ mov(ecx, FieldOperand(edx, JSArray::kLengthOffset));
diff --git a/src/jsregexp.cc b/src/jsregexp.cc
index c9afc62..5dfde94 100644
--- a/src/jsregexp.cc
+++ b/src/jsregexp.cc
@@ -478,11 +478,9 @@
OffsetsVector registers(required_registers);
- IrregexpResult res = IrregexpExecOnce(jsregexp,
- subject,
- previous_index,
- Vector<int>(registers.vector(),
- registers.length()));
+ IrregexpResult res = RegExpImpl::IrregexpExecOnce(
+ jsregexp, subject, previous_index, Vector<int>(registers.vector(),
+ registers.length()));
if (res == RE_SUCCESS) {
int capture_register_count =
(IrregexpNumberOfCaptures(FixedArray::cast(jsregexp->data())) + 1) * 2;
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index 1a020e5..c5eabeb 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -27,6 +27,7 @@
#include "v8.h"
+#include "compilation-cache.h"
#include "execution.h"
#include "heap-profiler.h"
#include "global-handles.h"
@@ -252,6 +253,15 @@
table_.GetVisitor(map)(map, obj);
}
+ static void EnableCodeFlushing(bool enabled) {
+ if (enabled) {
+ table_.Register(kVisitJSFunction, &VisitJSFunction);
+ } else {
+ table_.Register(kVisitJSFunction,
+ &JSObjectVisitor::VisitSpecialized<JSFunction::kSize>);
+ }
+ }
+
static void Initialize() {
table_.Register(kVisitShortcutCandidate,
&FixedBodyVisitor<StaticMarkingVisitor,
@@ -289,6 +299,8 @@
table_.Register(kVisitCode, &VisitCode);
+ table_.Register(kVisitJSFunction, &VisitJSFunction);
+
table_.Register(kVisitPropertyCell,
&FixedBodyVisitor<StaticMarkingVisitor,
JSGlobalPropertyCell::BodyDescriptor,
@@ -405,6 +417,134 @@
reinterpret_cast<Code*>(object)->CodeIterateBody<StaticMarkingVisitor>();
}
+ // Code flushing support.
+
+ // How many collections newly compiled code object will survive before being
+ // flushed.
+ static const int kCodeAgeThreshold = 5;
+
+ inline static bool HasSourceCode(SharedFunctionInfo* info) {
+ Object* undefined = Heap::raw_unchecked_undefined_value();
+ return (info->script() != undefined) &&
+ (reinterpret_cast<Script*>(info->script())->source() != undefined);
+ }
+
+
+ inline static bool IsCompiled(JSFunction* function) {
+ return
+ function->unchecked_code() != Builtins::builtin(Builtins::LazyCompile);
+ }
+
+
+ inline static bool IsCompiled(SharedFunctionInfo* function) {
+ return
+ function->unchecked_code() != Builtins::builtin(Builtins::LazyCompile);
+ }
+
+
+ static void FlushCodeForFunction(JSFunction* function) {
+ SharedFunctionInfo* shared_info = function->unchecked_shared();
+
+ if (shared_info->IsMarked()) return;
+
+ // Special handling if the function and shared info objects
+ // have different code objects.
+ if (function->unchecked_code() != shared_info->unchecked_code()) {
+ // If the shared function has been flushed but the function has not,
+ // we flush the function if possible.
+ if (!IsCompiled(shared_info) &&
+ IsCompiled(function) &&
+ !function->unchecked_code()->IsMarked()) {
+ function->set_code(shared_info->unchecked_code());
+ }
+ return;
+ }
+
+ // Code is either on stack or in compilation cache.
+ if (shared_info->unchecked_code()->IsMarked()) {
+ shared_info->set_code_age(0);
+ return;
+ }
+
+ // The function must be compiled and have the source code available,
+ // to be able to recompile it in case we need the function again.
+ if (!(shared_info->is_compiled() && HasSourceCode(shared_info))) return;
+
+ // We never flush code for Api functions.
+ Object* function_data = shared_info->function_data();
+ if (function_data->IsHeapObject() &&
+ (SafeMap(function_data)->instance_type() ==
+ FUNCTION_TEMPLATE_INFO_TYPE)) {
+ return;
+ }
+
+ // Only flush code for functions.
+ if (shared_info->code()->kind() != Code::FUNCTION) return;
+
+ // Function must be lazy compilable.
+ if (!shared_info->allows_lazy_compilation()) return;
+
+ // If this is a full script wrapped in a function we do no flush the code.
+ if (shared_info->is_toplevel()) return;
+
+ // Age this shared function info.
+ if (shared_info->code_age() < kCodeAgeThreshold) {
+ shared_info->set_code_age(shared_info->code_age() + 1);
+ return;
+ }
+
+ // Compute the lazy compilable version of the code.
+ Code* code = Builtins::builtin(Builtins::LazyCompile);
+ shared_info->set_code(code);
+ function->set_code(code);
+ }
+
+
+ static inline Map* SafeMap(Object* obj) {
+ MapWord map_word = HeapObject::cast(obj)->map_word();
+ map_word.ClearMark();
+ map_word.ClearOverflow();
+ return map_word.ToMap();
+ }
+
+
+ static inline bool IsJSBuiltinsObject(Object* obj) {
+ return obj->IsHeapObject() &&
+ (SafeMap(obj)->instance_type() == JS_BUILTINS_OBJECT_TYPE);
+ }
+
+
+ static inline bool IsValidNotBuiltinContext(Object* ctx) {
+ if (!ctx->IsHeapObject()) return false;
+
+ Map* map = SafeMap(ctx);
+ if (!(map == Heap::raw_unchecked_context_map() ||
+ map == Heap::raw_unchecked_catch_context_map() ||
+ map == Heap::raw_unchecked_global_context_map())) {
+ return false;
+ }
+
+ Context* context = reinterpret_cast<Context*>(ctx);
+
+ if (IsJSBuiltinsObject(context->global())) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ static void VisitJSFunction(Map* map, HeapObject* object) {
+ JSFunction* jsfunction = reinterpret_cast<JSFunction*>(object);
+
+ // The function must have a valid context and not be a builtin.
+ if (IsValidNotBuiltinContext(jsfunction->unchecked_context())) {
+ FlushCodeForFunction(jsfunction);
+ }
+
+ JSObjectVisitor::VisitSpecialized<JSFunction::kSize>(map, object);
+ }
+
typedef void (*Callback)(Map* map, HeapObject* object);
static VisitorDispatchTable<Callback> table_;
@@ -435,6 +575,62 @@
};
+class CodeMarkingVisitor : public ThreadVisitor {
+ public:
+ void VisitThread(ThreadLocalTop* top) {
+ for (StackFrameIterator it(top); !it.done(); it.Advance()) {
+ MarkCompactCollector::MarkObject(it.frame()->unchecked_code());
+ }
+ }
+};
+
+
+class SharedFunctionInfoMarkingVisitor : public ObjectVisitor {
+ public:
+ void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) VisitPointer(p);
+ }
+
+ void VisitPointer(Object** slot) {
+ Object* obj = *slot;
+ if (obj->IsHeapObject()) {
+ MarkCompactCollector::MarkObject(HeapObject::cast(obj));
+ }
+ }
+};
+
+
+void MarkCompactCollector::PrepareForCodeFlushing() {
+ if (!FLAG_flush_code) {
+ StaticMarkingVisitor::EnableCodeFlushing(false);
+ return;
+ }
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (Debug::IsLoaded() || Debug::has_break_points()) {
+ StaticMarkingVisitor::EnableCodeFlushing(false);
+ return;
+ }
+#endif
+ StaticMarkingVisitor::EnableCodeFlushing(true);
+
+ // Make sure we are not referencing the code from the stack.
+ for (StackFrameIterator it; !it.done(); it.Advance()) {
+ MarkCompactCollector::MarkObject(it.frame()->unchecked_code());
+ }
+
+ // Iterate the archived stacks in all threads to check if
+ // the code is referenced.
+ CodeMarkingVisitor code_marking_visitor;
+ ThreadManager::IterateArchivedThreads(&code_marking_visitor);
+
+ SharedFunctionInfoMarkingVisitor visitor;
+ CompilationCache::IterateFunctions(&visitor);
+
+ MarkCompactCollector::ProcessMarkingStack();
+}
+
+
// Visitor class for marking heap roots.
class RootMarkingVisitor : public ObjectVisitor {
public:
@@ -793,6 +989,8 @@
ASSERT(!marking_stack.overflowed());
+ PrepareForCodeFlushing();
+
RootMarkingVisitor root_visitor;
MarkRoots(&root_visitor);
diff --git a/src/mark-compact.h b/src/mark-compact.h
index ad63586..a5fd8d3 100644
--- a/src/mark-compact.h
+++ b/src/mark-compact.h
@@ -175,6 +175,10 @@
friend class RootMarkingVisitor;
friend class MarkingVisitor;
friend class StaticMarkingVisitor;
+ friend class CodeMarkingVisitor;
+ friend class SharedFunctionInfoMarkingVisitor;
+
+ static void PrepareForCodeFlushing();
// Marking operations for objects reachable from roots.
static void MarkLiveObjects();
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index d340e4b..0a97128 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -540,7 +540,8 @@
map()->NextFreePropertyIndex()));
}
ASSERT(map()->has_fast_elements() ==
- (elements()->map() == Heap::fixed_array_map()));
+ (elements()->map() == Heap::fixed_array_map() ||
+ elements()->map() == Heap::fixed_cow_array_map()));
ASSERT(map()->has_fast_elements() == HasFastElements());
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 5e8022e..c70ab02 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -1167,7 +1167,8 @@
void JSObject::set_elements(HeapObject* value, WriteBarrierMode mode) {
ASSERT(map()->has_fast_elements() ==
- (value->map() == Heap::fixed_array_map()));
+ (value->map() == Heap::fixed_array_map() ||
+ value->map() == Heap::fixed_cow_array_map()));
// In the assert below Dictionary is covered under FixedArray.
ASSERT(value->IsFixedArray() || value->IsPixelArray() ||
value->IsExternalArray());
@@ -1397,6 +1398,7 @@
void FixedArray::set(int index, Smi* value) {
+ ASSERT(map() != Heap::fixed_cow_array_map());
ASSERT(reinterpret_cast<Object*>(value)->IsSmi());
int offset = kHeaderSize + index * kPointerSize;
WRITE_FIELD(this, offset, value);
@@ -1404,6 +1406,7 @@
void FixedArray::set(int index, Object* value) {
+ ASSERT(map() != Heap::fixed_cow_array_map());
ASSERT(index >= 0 && index < this->length());
int offset = kHeaderSize + index * kPointerSize;
WRITE_FIELD(this, offset, value);
@@ -1420,6 +1423,7 @@
void FixedArray::set(int index,
Object* value,
WriteBarrierMode mode) {
+ ASSERT(map() != Heap::fixed_cow_array_map());
ASSERT(index >= 0 && index < this->length());
int offset = kHeaderSize + index * kPointerSize;
WRITE_FIELD(this, offset, value);
@@ -1428,6 +1432,7 @@
void FixedArray::fast_set(FixedArray* array, int index, Object* value) {
+ ASSERT(array->map() != Heap::raw_unchecked_fixed_cow_array_map());
ASSERT(index >= 0 && index < array->length());
ASSERT(!Heap::InNewSpace(value));
WRITE_FIELD(array, kHeaderSize + index * kPointerSize, value);
@@ -1435,6 +1440,7 @@
void FixedArray::set_undefined(int index) {
+ ASSERT(map() != Heap::fixed_cow_array_map());
ASSERT(index >= 0 && index < this->length());
ASSERT(!Heap::InNewSpace(Heap::undefined_value()));
WRITE_FIELD(this, kHeaderSize + index * kPointerSize,
@@ -1443,6 +1449,7 @@
void FixedArray::set_null(int index) {
+ ASSERT(map() != Heap::fixed_cow_array_map());
ASSERT(index >= 0 && index < this->length());
ASSERT(!Heap::InNewSpace(Heap::null_value()));
WRITE_FIELD(this, kHeaderSize + index * kPointerSize, Heap::null_value());
@@ -1450,12 +1457,27 @@
void FixedArray::set_the_hole(int index) {
+ ASSERT(map() != Heap::fixed_cow_array_map());
ASSERT(index >= 0 && index < this->length());
ASSERT(!Heap::InNewSpace(Heap::the_hole_value()));
WRITE_FIELD(this, kHeaderSize + index * kPointerSize, Heap::the_hole_value());
}
+void FixedArray::set_unchecked(int index, Smi* value) {
+ ASSERT(reinterpret_cast<Object*>(value)->IsSmi());
+ int offset = kHeaderSize + index * kPointerSize;
+ WRITE_FIELD(this, offset, value);
+}
+
+
+void FixedArray::set_null_unchecked(int index) {
+ ASSERT(index >= 0 && index < this->length());
+ ASSERT(!Heap::InNewSpace(Heap::null_value()));
+ WRITE_FIELD(this, kHeaderSize + index * kPointerSize, Heap::null_value());
+}
+
+
Object** FixedArray::data_start() {
return HeapObject::RawField(this, kHeaderSize);
}
@@ -2398,6 +2420,7 @@
if (obj->IsFailure()) return obj;
Map* new_map = Map::cast(obj);
new_map->set_has_fast_elements(true);
+ Counters::map_slow_to_fast_elements.Increment();
return new_map;
}
@@ -2408,6 +2431,7 @@
if (obj->IsFailure()) return obj;
Map* new_map = Map::cast(obj);
new_map->set_has_fast_elements(false);
+ Counters::map_fast_to_slow_elements.Increment();
return new_map;
}
@@ -2539,6 +2563,7 @@
allows_lazy_compilation,
kAllowLazyCompilation)
+
#if V8_HOST_ARCH_32_BIT
SMI_ACCESSORS(SharedFunctionInfo, length, kLengthOffset)
SMI_ACCESSORS(SharedFunctionInfo, formal_parameter_count,
@@ -2638,6 +2663,11 @@
}
+Code* SharedFunctionInfo::unchecked_code() {
+ return reinterpret_cast<Code*>(READ_FIELD(this, kCodeOffset));
+}
+
+
void SharedFunctionInfo::set_code(Code* value, WriteBarrierMode mode) {
WRITE_FIELD(this, kCodeOffset, value);
CONDITIONAL_WRITE_BARRIER(this, kCodeOffset, mode);
@@ -2684,6 +2714,17 @@
}
+int SharedFunctionInfo::code_age() {
+ return (compiler_hints() >> kCodeAgeShift) & kCodeAgeMask;
+}
+
+
+void SharedFunctionInfo::set_code_age(int code_age) {
+ set_compiler_hints(compiler_hints() |
+ ((code_age & kCodeAgeMask) << kCodeAgeShift));
+}
+
+
bool JSFunction::IsBuiltin() {
return context()->global()->IsJSBuiltinsObject();
}
@@ -2694,6 +2735,11 @@
}
+Code* JSFunction::unchecked_code() {
+ return reinterpret_cast<Code*>(READ_FIELD(this, kCodeOffset));
+}
+
+
void JSFunction::set_code(Code* value) {
// Skip the write barrier because code is never in new space.
ASSERT(!Heap::InNewSpace(value));
@@ -2711,6 +2757,12 @@
}
+SharedFunctionInfo* JSFunction::unchecked_shared() {
+ return reinterpret_cast<SharedFunctionInfo*>(
+ READ_FIELD(this, kSharedFunctionInfoOffset));
+}
+
+
void JSFunction::set_context(Object* value) {
ASSERT(value == Heap::undefined_value() || value->IsContext());
WRITE_FIELD(this, kContextOffset, value);
@@ -2928,18 +2980,18 @@
JSObject::ElementsKind JSObject::GetElementsKind() {
+ if (map()->has_fast_elements()) {
+ ASSERT(elements()->map() == Heap::fixed_array_map() ||
+ elements()->map() == Heap::fixed_cow_array_map());
+ return FAST_ELEMENTS;
+ }
HeapObject* array = elements();
if (array->IsFixedArray()) {
- // FAST_ELEMENTS or DICTIONARY_ELEMENTS are both stored in a FixedArray.
- if (array->map() == Heap::fixed_array_map()) {
- ASSERT(map()->has_fast_elements());
- return FAST_ELEMENTS;
- }
+ // FAST_ELEMENTS or DICTIONARY_ELEMENTS are both stored in a
+ // FixedArray, but FAST_ELEMENTS is already handled above.
ASSERT(array->IsDictionary());
- ASSERT(!map()->has_fast_elements());
return DICTIONARY_ELEMENTS;
}
- ASSERT(!map()->has_fast_elements());
if (array->IsExternalArray()) {
switch (array->map()->instance_type()) {
case EXTERNAL_BYTE_ARRAY_TYPE:
@@ -3042,6 +3094,19 @@
}
+Object* JSObject::EnsureWritableFastElements() {
+ ASSERT(HasFastElements());
+ FixedArray* elems = FixedArray::cast(elements());
+ if (elems->map() != Heap::fixed_cow_array_map()) return elems;
+ Object* writable_elems = Heap::CopyFixedArray(elems);
+ if (writable_elems->IsFailure()) return writable_elems;
+ FixedArray::cast(writable_elems)->set_map(Heap::fixed_array_map());
+ set_elements(FixedArray::cast(writable_elems));
+ Counters::cow_arrays_converted.Increment();
+ return writable_elems;
+}
+
+
StringDictionary* JSObject::property_dictionary() {
ASSERT(!HasFastProperties());
return StringDictionary::cast(properties());
diff --git a/src/objects-visiting.cc b/src/objects-visiting.cc
index 293c9bf..c35e02c 100644
--- a/src/objects-visiting.cc
+++ b/src/objects-visiting.cc
@@ -101,7 +101,6 @@
case JS_VALUE_TYPE:
case JS_ARRAY_TYPE:
case JS_REGEXP_TYPE:
- case JS_FUNCTION_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
case JS_BUILTINS_OBJECT_TYPE:
@@ -109,6 +108,9 @@
kVisitJSObjectGeneric,
instance_size);
+ case JS_FUNCTION_TYPE:
+ return kVisitJSFunction;
+
case HEAP_NUMBER_TYPE:
case PIXEL_ARRAY_TYPE:
case EXTERNAL_BYTE_ARRAY_TYPE:
diff --git a/src/objects-visiting.h b/src/objects-visiting.h
index 6280bac..335a4be 100644
--- a/src/objects-visiting.h
+++ b/src/objects-visiting.h
@@ -100,6 +100,7 @@
kVisitMap,
kVisitPropertyCell,
kVisitSharedFunctionInfo,
+ kVisitJSFunction,
kVisitorIdCount,
kMinObjectSizeInWords = 2
@@ -204,6 +205,7 @@
template<int object_size>
static inline ReturnType VisitSpecialized(Map* map, HeapObject* object) {
+ ASSERT(BodyDescriptor::SizeOf(map, object) == object_size);
IteratePointers(object, BodyDescriptor::kStartOffset, object_size);
return static_cast<ReturnType>(object_size);
}
@@ -268,6 +270,10 @@
table_.Register(kVisitSeqTwoByteString, &VisitSeqTwoByteString);
+ table_.Register(kVisitJSFunction,
+ &JSObjectVisitor::
+ template VisitSpecialized<JSFunction::kSize>);
+
table_.RegisterSpecializations<DataObjectVisitor,
kVisitDataObject,
kVisitDataObjectGeneric>();
@@ -275,8 +281,8 @@
kVisitJSObject,
kVisitJSObjectGeneric>();
table_.RegisterSpecializations<StructVisitor,
- kVisitStruct,
- kVisitStructGeneric>();
+ kVisitStruct,
+ kVisitStructGeneric>();
}
static inline int IterateBody(Map* map, HeapObject* obj) {
diff --git a/src/objects.cc b/src/objects.cc
index 5687a3a..8359aa3 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -2338,6 +2338,8 @@
ASSERT(!HasPixelElements() && !HasExternalArrayElements());
switch (GetElementsKind()) {
case FAST_ELEMENTS: {
+ Object* obj = EnsureWritableFastElements();
+ if (obj->IsFailure()) return obj;
uint32_t length = IsJSArray() ?
static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
static_cast<uint32_t>(FixedArray::cast(elements())->length());
@@ -2418,6 +2420,8 @@
switch (GetElementsKind()) {
case FAST_ELEMENTS: {
+ Object* obj = EnsureWritableFastElements();
+ if (obj->IsFailure()) return obj;
uint32_t length = IsJSArray() ?
static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
static_cast<uint32_t>(FixedArray::cast(elements())->length());
@@ -5009,8 +5013,8 @@
ASSERT(target->IsHeapObject());
if (!target->IsMarked()) {
ASSERT(target->IsMap());
- contents->set(i + 1, NullDescriptorDetails);
- contents->set_null(i);
+ contents->set_unchecked(i + 1, NullDescriptorDetails);
+ contents->set_null_unchecked(i);
ASSERT(target->prototype() == this ||
target->prototype() == real_prototype);
// Getter prototype() is read-only, set_prototype() has side effects.
@@ -5095,7 +5099,7 @@
bool SharedFunctionInfo::HasSourceCode() {
return !script()->IsUndefined() &&
- !Script::cast(script())->source()->IsUndefined();
+ !reinterpret_cast<Script*>(script())->source()->IsUndefined();
}
@@ -5591,6 +5595,8 @@
int old_capacity = FixedArray::cast(elements())->length();
if (value <= old_capacity) {
if (IsJSArray()) {
+ Object* obj = EnsureWritableFastElements();
+ if (obj->IsFailure()) return obj;
int old_length = FastD2I(JSArray::cast(this)->length()->Number());
// NOTE: We may be able to optimize this by removing the
// last part of the elements backing storage array and
@@ -6051,7 +6057,9 @@
Object* JSObject::SetFastElement(uint32_t index, Object* value) {
ASSERT(HasFastElements());
- FixedArray* elms = FixedArray::cast(elements());
+ Object* elms_obj = EnsureWritableFastElements();
+ if (elms_obj->IsFailure()) return elms_obj;
+ FixedArray* elms = FixedArray::cast(elms_obj);
uint32_t elms_length = static_cast<uint32_t>(elms->length());
if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
@@ -6095,6 +6103,7 @@
return SetElement(index, value);
}
+
Object* JSObject::SetElement(uint32_t index, Object* value) {
// Check access rights if needed.
if (IsAccessCheckNeeded() &&
@@ -7573,6 +7582,9 @@
set_map(new_map);
set_elements(fast_elements);
+ } else {
+ Object* obj = EnsureWritableFastElements();
+ if (obj->IsFailure()) return obj;
}
ASSERT(HasFastElements());
diff --git a/src/objects.h b/src/objects.h
index d2f6d35..65e0f36 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -230,19 +230,21 @@
V(CONS_SYMBOL_TYPE) \
V(CONS_ASCII_SYMBOL_TYPE) \
V(EXTERNAL_SYMBOL_TYPE) \
+ V(EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE) \
V(EXTERNAL_ASCII_SYMBOL_TYPE) \
V(STRING_TYPE) \
V(ASCII_STRING_TYPE) \
V(CONS_STRING_TYPE) \
V(CONS_ASCII_STRING_TYPE) \
V(EXTERNAL_STRING_TYPE) \
+ V(EXTERNAL_STRING_WITH_ASCII_DATA_TYPE) \
V(EXTERNAL_ASCII_STRING_TYPE) \
V(PRIVATE_EXTERNAL_ASCII_STRING_TYPE) \
\
V(MAP_TYPE) \
V(CODE_TYPE) \
- V(JS_GLOBAL_PROPERTY_CELL_TYPE) \
V(ODDBALL_TYPE) \
+ V(JS_GLOBAL_PROPERTY_CELL_TYPE) \
\
V(HEAP_NUMBER_TYPE) \
V(PROXY_TYPE) \
@@ -260,11 +262,9 @@
V(EXTERNAL_FLOAT_ARRAY_TYPE) \
V(FILLER_TYPE) \
\
- V(FIXED_ARRAY_TYPE) \
V(ACCESSOR_INFO_TYPE) \
V(ACCESS_CHECK_INFO_TYPE) \
V(INTERCEPTOR_INFO_TYPE) \
- V(SHARED_FUNCTION_INFO_TYPE) \
V(CALL_HANDLER_INFO_TYPE) \
V(FUNCTION_TEMPLATE_INFO_TYPE) \
V(OBJECT_TEMPLATE_INFO_TYPE) \
@@ -273,6 +273,9 @@
V(SCRIPT_TYPE) \
V(CODE_CACHE_TYPE) \
\
+ V(FIXED_ARRAY_TYPE) \
+ V(SHARED_FUNCTION_INFO_TYPE) \
+ \
V(JS_VALUE_TYPE) \
V(JS_OBJECT_TYPE) \
V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \
@@ -1211,7 +1214,9 @@
public:
enum DeleteMode { NORMAL_DELETION, FORCE_DELETION };
enum ElementsKind {
+ // The only "fast" kind.
FAST_ELEMENTS,
+ // All the kinds below are "slow".
DICTIONARY_ELEMENTS,
PIXEL_ELEMENTS,
EXTERNAL_BYTE_ELEMENTS,
@@ -1232,8 +1237,21 @@
inline StringDictionary* property_dictionary(); // Gets slow properties.
// [elements]: The elements (properties with names that are integers).
- // elements is a FixedArray in the fast case, a Dictionary in the slow
- // case, and a PixelArray or ExternalArray in special cases.
+ //
+ // Elements can be in two general modes: fast and slow. Each mode
+ // corrensponds to a set of object representations of elements that
+ // have something in common.
+ //
+ // In the fast mode elements is a FixedArray and so each element can
+ // be quickly accessed. This fact is used in the generated code. The
+ // elements array can have one of the two maps in this mode:
+ // fixed_array_map or fixed_cow_array_map (for copy-on-write
+ // arrays). In the latter case the elements array may be shared by a
+ // few objects and so before writing to any element the array must
+ // be copied. Use EnsureWritableFastElements in this case.
+ //
+ // In the slow mode elements is either a NumberDictionary or a
+ // PixelArray or an ExternalArray.
DECL_ACCESSORS(elements, HeapObject)
inline void initialize_elements();
inline Object* ResetElements();
@@ -1251,6 +1269,8 @@
inline bool HasExternalFloatElements();
inline bool AllowsSetElementsLength();
inline NumberDictionary* element_dictionary(); // Gets slow elements.
+ // Requires: this->HasFastElements().
+ inline Object* EnsureWritableFastElements();
// Collects elements starting at index 0.
// Undefined values are placed after non-undefined values.
@@ -1703,6 +1723,10 @@
inline void set_null(int index);
inline void set_the_hole(int index);
+ // Setters with less debug checks for the GC to use.
+ inline void set_unchecked(int index, Smi* value);
+ inline void set_null_unchecked(int index);
+
// Gives access to raw memory which stores the array's data.
inline Object** data_start();
@@ -2762,7 +2786,12 @@
public:
// Opaque data type for encapsulating code flags like kind, inline
// cache state, and arguments count.
- enum Flags { };
+ // FLAGS_MIN_VALUE and FLAGS_MAX_VALUE are specified to ensure that
+ // enumeration type has correct value range (see Issue 830 for more details).
+ enum Flags {
+ FLAGS_MIN_VALUE = kMinInt,
+ FLAGS_MAX_VALUE = kMaxInt
+ };
enum Kind {
FUNCTION,
@@ -3061,7 +3090,8 @@
inline bool is_extensible();
// Tells whether the instance has fast elements.
- void set_has_fast_elements(bool value) {
+ // Equivalent to instance->GetElementsKind() == FAST_ELEMENTS.
+ inline void set_has_fast_elements(bool value) {
if (value) {
set_bit_field2(bit_field2() | (1 << kHasFastElements));
} else {
@@ -3069,7 +3099,7 @@
}
}
- bool has_fast_elements() {
+ inline bool has_fast_elements() {
return ((1 << kHasFastElements) & bit_field2()) != 0;
}
@@ -3344,6 +3374,8 @@
// [construct stub]: Code stub for constructing instances of this function.
DECL_ACCESSORS(construct_stub, Code)
+ inline Code* unchecked_code();
+
// Returns if this function has been compiled to native code yet.
inline bool is_compiled();
@@ -3451,6 +3483,15 @@
inline bool allows_lazy_compilation();
inline void set_allows_lazy_compilation(bool flag);
+ // Indicates how many full GCs this function has survived with assigned
+ // code object. Used to determine when it is relatively safe to flush
+ // this code object and replace it with lazy compilation stub.
+ // Age is reset when GC notices that the code object is referenced
+ // from the stack or compilation cache.
+ inline int code_age();
+ inline void set_code_age(int age);
+
+
// Check whether a inlined constructor can be generated with the given
// prototype.
bool CanGenerateInlineConstructor(Object* prototype);
@@ -3581,6 +3622,8 @@
static const int kHasOnlySimpleThisPropertyAssignments = 0;
static const int kTryFullCodegen = 1;
static const int kAllowLazyCompilation = 2;
+ static const int kCodeAgeShift = 3;
+ static const int kCodeAgeMask = 7;
DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo);
};
@@ -3596,6 +3639,8 @@
// can be shared by instances.
DECL_ACCESSORS(shared, SharedFunctionInfo)
+ inline SharedFunctionInfo* unchecked_shared();
+
// [context]: The context for this function.
inline Context* context();
inline Object* unchecked_context();
@@ -3608,6 +3653,8 @@
inline Code* code();
inline void set_code(Code* value);
+ inline Code* unchecked_code();
+
// Tells whether this function is builtin.
inline bool IsBuiltin();
diff --git a/src/parser.cc b/src/parser.cc
index 1df7c21..0fef2e2 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -3461,6 +3461,12 @@
}
}
+ // Simple and shallow arrays can be lazily copied, we transform the
+ // elements array to a copy-on-write array.
+ if (is_simple && depth == 1 && values.length() > 0) {
+ literals->set_map(Heap::fixed_cow_array_map());
+ }
+
return NEW(ArrayLiteral(literals, values.elements(),
literal_index, is_simple, depth));
}
@@ -3477,6 +3483,19 @@
return lit != NULL && lit->is_simple();
}
+
+bool CompileTimeValue::ArrayLiteralElementNeedsInitialization(
+ Expression* value) {
+ // If value is a literal the property value is already set in the
+ // boilerplate object.
+ if (value->AsLiteral() != NULL) return false;
+ // If value is a materialized literal the property value is already set
+ // in the boilerplate object if it is simple.
+ if (CompileTimeValue::IsCompileTimeValue(value)) return false;
+ return true;
+}
+
+
Handle<FixedArray> CompileTimeValue::GetValue(Expression* expression) {
ASSERT(IsCompileTimeValue(expression));
Handle<FixedArray> result = Factory::NewFixedArray(2, TENURED);
diff --git a/src/parser.h b/src/parser.h
index 89966a6..f918a3a 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -175,6 +175,8 @@
static bool IsCompileTimeValue(Expression* expression);
+ static bool ArrayLiteralElementNeedsInitialization(Expression* value);
+
// Get the value as a compile time value.
static Handle<FixedArray> GetValue(Expression* expression);
diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc
index 9b8b206..ae44944 100644
--- a/src/platform-freebsd.cc
+++ b/src/platform-freebsd.cc
@@ -198,9 +198,10 @@
void OS::DebugBreak() {
-#if (defined(__arm__) || defined(__thumb__)) && \
- defined(CAN_USE_ARMV5_INSTRUCTIONS)
+#if (defined(__arm__) || defined(__thumb__))
+# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
asm("bkpt 0");
+# endif
#else
asm("int $3");
#endif
diff --git a/src/platform-openbsd.cc b/src/platform-openbsd.cc
index 58ff154..05ed9ee 100644
--- a/src/platform-openbsd.cc
+++ b/src/platform-openbsd.cc
@@ -196,9 +196,10 @@
void OS::DebugBreak() {
-#if (defined(__arm__) || defined(__thumb__)) && \
- defined(CAN_USE_ARMV5_INSTRUCTIONS)
+#if (defined(__arm__) || defined(__thumb__))
+# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
asm("bkpt 0");
+# endif
#else
asm("int $3");
#endif
diff --git a/src/platform.h b/src/platform.h
index 7539fd2..e0dd66e 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -363,7 +363,13 @@
class Thread: public ThreadHandle {
public:
// Opaque data type for thread-local storage keys.
- enum LocalStorageKey {};
+ // LOCAL_STORAGE_KEY_MIN_VALUE and LOCAL_STORAGE_KEY_MAX_VALUE are specified
+ // to ensure that enumeration type has correct value range (see Issue 830 for
+ // more details).
+ enum LocalStorageKey {
+ LOCAL_STORAGE_KEY_MIN_VALUE = kMinInt,
+ LOCAL_STORAGE_KEY_MAX_VALUE = kMaxInt
+ };
// Create new thread.
Thread();
diff --git a/src/runtime.cc b/src/runtime.cc
index fc6ca76..2eddaab 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -160,13 +160,22 @@
switch (copy->GetElementsKind()) {
case JSObject::FAST_ELEMENTS: {
FixedArray* elements = FixedArray::cast(copy->elements());
- for (int i = 0; i < elements->length(); i++) {
- Object* value = elements->get(i);
- if (value->IsJSObject()) {
- JSObject* js_object = JSObject::cast(value);
- result = DeepCopyBoilerplate(js_object);
- if (result->IsFailure()) return result;
- elements->set(i, result);
+ if (elements->map() == Heap::fixed_cow_array_map()) {
+ Counters::cow_arrays_created_runtime.Increment();
+#ifdef DEBUG
+ for (int i = 0; i < elements->length(); i++) {
+ ASSERT(!elements->get(i)->IsJSObject());
+ }
+#endif
+ } else {
+ for (int i = 0; i < elements->length(); i++) {
+ Object* value = elements->get(i);
+ if (value->IsJSObject()) {
+ JSObject* js_object = JSObject::cast(value);
+ result = DeepCopyBoilerplate(js_object);
+ if (result->IsFailure()) return result;
+ elements->set(i, result);
+ }
}
}
break;
@@ -343,18 +352,29 @@
JSFunction::GlobalContextFromLiterals(*literals)->array_function());
Handle<Object> object = Factory::NewJSObject(constructor);
- Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
+ const bool is_cow = (elements->map() == Heap::fixed_cow_array_map());
+ Handle<FixedArray> copied_elements =
+ is_cow ? elements : Factory::CopyFixedArray(elements);
Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
- for (int i = 0; i < content->length(); i++) {
- if (content->get(i)->IsFixedArray()) {
- // The value contains the constant_properties of a
- // simple object literal.
- Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
- Handle<Object> result =
- CreateLiteralBoilerplate(literals, fa);
- if (result.is_null()) return result;
- content->set(i, *result);
+ if (is_cow) {
+#ifdef DEBUG
+ // Copy-on-write arrays must be shallow (and simple).
+ for (int i = 0; i < content->length(); i++) {
+ ASSERT(!content->get(i)->IsFixedArray());
+ }
+#endif
+ } else {
+ for (int i = 0; i < content->length(); i++) {
+ if (content->get(i)->IsFixedArray()) {
+ // The value contains the constant_properties of a
+ // simple object literal.
+ Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
+ Handle<Object> result =
+ CreateLiteralBoilerplate(literals, fa);
+ if (result.is_null()) return result;
+ content->set(i, *result);
+ }
}
}
@@ -483,6 +503,10 @@
// Update the functions literal and return the boilerplate.
literals->set(literals_index, *boilerplate);
}
+ if (JSObject::cast(*boilerplate)->elements()->map() ==
+ Heap::fixed_cow_array_map()) {
+ Counters::cow_arrays_created_runtime.Increment();
+ }
return Heap::CopyJSObject(JSObject::cast(*boilerplate));
}
@@ -6699,9 +6723,13 @@
int year, month, day;
DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
- res_array->SetElement(0, Smi::FromInt(year));
- res_array->SetElement(1, Smi::FromInt(month));
- res_array->SetElement(2, Smi::FromInt(day));
+ RUNTIME_ASSERT(res_array->elements()->map() == Heap::fixed_array_map());
+ FixedArray* elms = FixedArray::cast(res_array->elements());
+ RUNTIME_ASSERT(elms->length() == 3);
+
+ elms->set(0, Smi::FromInt(year));
+ elms->set(1, Smi::FromInt(month));
+ elms->set(2, Smi::FromInt(day));
return Heap::undefined_value();
}
@@ -8057,7 +8085,8 @@
CONVERT_CHECKED(JSArray, to, args[1]);
HeapObject* new_elements = from->elements();
Object* new_map;
- if (new_elements->map() == Heap::fixed_array_map()) {
+ if (new_elements->map() == Heap::fixed_array_map() ||
+ new_elements->map() == Heap::fixed_cow_array_map()) {
new_map = to->map()->GetFastElementsMap();
} else {
new_map = to->map()->GetSlowElementsMap();
diff --git a/src/serialize.h b/src/serialize.h
index d1b668d..80be50d 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -536,7 +536,8 @@
// would cause dupes.
ASSERT(!o->IsScript());
return o->IsString() || o->IsSharedFunctionInfo() ||
- o->IsHeapNumber() || o->IsCode();
+ o->IsHeapNumber() || o->IsCode() ||
+ o->map() == Heap::fixed_cow_array_map();
}
private:
diff --git a/src/stub-cache.h b/src/stub-cache.h
index 663201b..19d54f8 100644
--- a/src/stub-cache.h
+++ b/src/stub-cache.h
@@ -210,7 +210,6 @@
static Object* ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind);
#endif
-
// Update cache for entry hash(name, map).
static Code* Set(String* name, Map* map, Code* code);
diff --git a/src/v8-counters.h b/src/v8-counters.h
index 64ea9fc..2f2edfe 100644
--- a/src/v8-counters.h
+++ b/src/v8-counters.h
@@ -120,6 +120,8 @@
V8.GCCompactorCausedByWeakHandles) \
SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \
+ SC(map_slow_to_fast_elements, V8.MapSlowToFastElements) \
+ SC(map_fast_to_slow_elements, V8.MapFastToSlowElements) \
/* How is the generic keyed-load stub used? */ \
SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \
@@ -155,6 +157,9 @@
SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
SC(store_normal_miss, V8.StoreNormalMiss) \
SC(store_normal_hit, V8.StoreNormalHit) \
+ SC(cow_arrays_created_stub, V8.COWArraysCreatedStub) \
+ SC(cow_arrays_created_runtime, V8.COWArraysCreatedRuntime) \
+ SC(cow_arrays_converted, V8.COWArraysConverted) \
SC(call_miss, V8.CallMiss) \
SC(keyed_call_miss, V8.KeyedCallMiss) \
SC(load_miss, V8.LoadMiss) \
diff --git a/src/version.cc b/src/version.cc
index 61c0a0e..c448a0f 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 2
#define MINOR_VERSION 3
-#define BUILD_NUMBER 8
+#define BUILD_NUMBER 9
#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index e545ffa..1885182 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -4989,12 +4989,18 @@
frame_->Push(node->constant_elements());
int length = node->values()->length();
Result clone;
- if (node->depth() > 1) {
+ if (node->constant_elements()->map() == Heap::fixed_cow_array_map()) {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
+ clone = frame_->CallStub(&stub, 3);
+ __ IncrementCounter(&Counters::cow_arrays_created_stub, 1);
+ } else if (node->depth() > 1) {
clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
+ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
} else {
- FastCloneShallowArrayStub stub(length);
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
clone = frame_->CallStub(&stub, 3);
}
frame_->Push(&clone);
@@ -5004,12 +5010,9 @@
for (int i = 0; i < length; i++) {
Expression* value = node->values()->at(i);
- // If value is a literal the property value is already set in the
- // boilerplate object.
- if (value->AsLiteral() != NULL) continue;
- // If value is a materialized literal the property value is already set
- // in the boilerplate object if it is simple.
- if (CompileTimeValue::IsCompileTimeValue(value)) continue;
+ if (!CompileTimeValue::ArrayLiteralElementNeedsInitialization(value)) {
+ continue;
+ }
// The property must be set by generated code.
Load(value);
@@ -6875,7 +6878,7 @@
Immediate(KeyedLoadIC::kSlowCaseBitFieldMask));
deferred->Branch(not_zero);
- // Check the object's elements are in fast case.
+ // Check the object's elements are in fast case and writable.
__ movq(tmp1.reg(), FieldOperand(object.reg(), JSObject::kElementsOffset));
__ CompareRoot(FieldOperand(tmp1.reg(), HeapObject::kMapOffset),
Heap::kFixedArrayMapRootIndex);
@@ -8419,15 +8422,10 @@
// Check that the key is a non-negative smi.
__ JumpIfNotPositiveSmi(key.reg(), deferred->entry_label());
- // Get the elements array from the receiver and check that it
- // is not a dictionary.
+ // Get the elements array from the receiver.
__ movq(elements.reg(),
FieldOperand(receiver.reg(), JSObject::kElementsOffset));
- if (FLAG_debug_code) {
- __ Cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset),
- Factory::fixed_array_map());
- __ Assert(equal, "JSObject with fast elements map has slow elements");
- }
+ __ AssertFastElements(elements.reg());
// Check that key is within bounds.
__ SmiCompare(key.reg(),
@@ -8841,6 +8839,25 @@
__ CompareRoot(rcx, Heap::kUndefinedValueRootIndex);
__ j(equal, &slow_case);
+ if (FLAG_debug_code) {
+ const char* message;
+ Heap::RootListIndex expected_map_index;
+ if (mode_ == CLONE_ELEMENTS) {
+ message = "Expected (writable) fixed array";
+ expected_map_index = Heap::kFixedArrayMapRootIndex;
+ } else {
+ ASSERT(mode_ == COPY_ON_WRITE_ELEMENTS);
+ message = "Expected copy-on-write fixed array";
+ expected_map_index = Heap::kFixedCOWArrayMapRootIndex;
+ }
+ __ push(rcx);
+ __ movq(rcx, FieldOperand(rcx, JSArray::kElementsOffset));
+ __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset),
+ expected_map_index);
+ __ Assert(equal, message);
+ __ pop(rcx);
+ }
+
// Allocate both the JS array and the elements array in one big
// allocation. This avoids multiple limit checks.
__ AllocateInNewSpace(size, rax, rbx, rdx, &slow_case, TAG_OBJECT);
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 725cbb0..a5ccaf5 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -1330,12 +1330,18 @@
__ push(FieldOperand(rbx, JSFunction::kLiteralsOffset));
__ Push(Smi::FromInt(expr->literal_index()));
__ Push(expr->constant_elements());
- if (expr->depth() > 1) {
+ if (expr->constant_elements()->map() == Heap::fixed_cow_array_map()) {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
+ __ CallStub(&stub);
+ __ IncrementCounter(&Counters::cow_arrays_created_stub, 1);
+ } else if (expr->depth() > 1) {
__ CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
+ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
__ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
} else {
- FastCloneShallowArrayStub stub(length);
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
__ CallStub(&stub);
}
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index a8971f5..114ae84 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -487,6 +487,7 @@
// Loads an indexed element from a fast case array.
+// If not_fast_array is NULL, doesn't perform the elements map check.
static void GenerateFastArrayLoad(MacroAssembler* masm,
Register receiver,
Register key,
@@ -515,10 +516,14 @@
// scratch - used to hold elements of the receiver and the loaded value.
__ movq(elements, FieldOperand(receiver, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
- __ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
- Heap::kFixedArrayMapRootIndex);
- __ j(not_equal, not_fast_array);
+ if (not_fast_array != NULL) {
+ // Check that the object is in fast mode and writable.
+ __ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
+ __ j(not_equal, not_fast_array);
+ } else {
+ __ AssertFastElements(elements);
+ }
// Check that the key (index) is within bounds.
__ SmiCompare(key, FieldOperand(elements, FixedArray::kLengthOffset));
// Unsigned comparison rejects negative indices.
@@ -611,13 +616,19 @@
GenerateKeyedLoadReceiverCheck(
masm, rdx, rcx, Map::kHasIndexedInterceptor, &slow);
+ // Check the "has fast elements" bit in the receiver's map which is
+ // now in rcx.
+ __ testb(FieldOperand(rcx, Map::kBitField2Offset),
+ Immediate(1 << Map::kHasFastElements));
+ __ j(zero, &check_pixel_array);
+
GenerateFastArrayLoad(masm,
rdx,
rax,
rcx,
rbx,
rax,
- &check_pixel_array,
+ NULL,
&slow);
__ IncrementCounter(&Counters::keyed_load_generic_smi, 1);
__ ret(0);
@@ -626,7 +637,7 @@
// Check whether the elements object is a pixel array.
// rdx: receiver
// rax: key
- // rcx: elements array
+ __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
__ SmiToInteger32(rbx, rax); // Used on both directions of next branch.
__ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset),
Heap::kPixelArrayMapRootIndex);
@@ -1012,7 +1023,7 @@
// rdx: JSObject
// rcx: index
__ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
+ // Check that the object is in fast mode and writable.
__ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
Heap::kFixedArrayMapRootIndex);
__ j(not_equal, &check_pixel_array);
@@ -1075,8 +1086,8 @@
__ jmp(&fast);
// Array case: Get the length and the elements array from the JS
- // array. Check that the array is in fast mode; if it is the
- // length is always a smi.
+ // array. Check that the array is in fast mode (and writable); if it
+ // is the length is always a smi.
__ bind(&array);
// rax: value
// rdx: receiver (a JSArray)
@@ -1862,6 +1873,8 @@
__ j(not_equal, &miss);
// Check that elements are FixedArray.
+ // We rely on StoreIC_ArrayLength below to deal with all types of
+ // fast elements (including COW).
__ movq(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
__ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
__ j(not_equal, &miss);
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index c1954a8..2f4b5f6 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -262,6 +262,21 @@
}
+void MacroAssembler::AssertFastElements(Register elements) {
+ if (FLAG_debug_code) {
+ Label ok;
+ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
+ j(equal, &ok);
+ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
+ Heap::kFixedCOWArrayMapRootIndex);
+ j(equal, &ok);
+ Abort("JSObject with fast elements map has slow elements");
+ bind(&ok);
+ }
+}
+
+
void MacroAssembler::Check(Condition cc, const char* msg) {
Label L;
j(cc, &L);
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index 7083224..aedc3b9 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -832,6 +832,8 @@
// Use --debug_code to enable.
void Assert(Condition cc, const char* msg);
+ void AssertFastElements(Register elements);
+
// Like Assert(), but always enabled.
void Check(Condition cc, const char* msg);
diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
index 7aaeab7..e0644cd 100644
--- a/src/x64/stub-cache-x64.cc
+++ b/src/x64/stub-cache-x64.cc
@@ -1084,16 +1084,18 @@
__ movq(rax, FieldOperand(rdx, JSArray::kLengthOffset));
__ ret((argc + 1) * kPointerSize);
} else {
+ Label call_builtin;
+
// Get the elements array of the object.
__ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset));
- // Check that the elements are in fast mode (not dictionary).
+ // Check that the elements are in fast mode and writable.
__ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
Factory::fixed_array_map());
- __ j(not_equal, &miss);
+ __ j(not_equal, &call_builtin);
if (argc == 1) { // Otherwise fall through to call builtin.
- Label call_builtin, exit, with_write_barrier, attempt_to_grow_elements;
+ Label exit, with_write_barrier, attempt_to_grow_elements;
// Get the array's length into rax and calculate new length.
__ SmiToInteger32(rax, FieldOperand(rdx, JSArray::kLengthOffset));
@@ -1164,7 +1166,7 @@
// Push the argument...
__ movq(Operand(rdx, 0), rcx);
// ... and fill the rest with holes.
- __ Move(kScratchRegister, Factory::the_hole_value());
+ __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
for (int i = 1; i < kAllocationDelta; i++) {
__ movq(Operand(rdx, i * kPointerSize), kScratchRegister);
}
@@ -1175,15 +1177,16 @@
// Increment element's and array's sizes.
__ SmiAddConstant(FieldOperand(rbx, FixedArray::kLengthOffset),
Smi::FromInt(kAllocationDelta));
+
// Make new length a smi before returning it.
__ Integer32ToSmi(rax, rax);
__ movq(FieldOperand(rdx, JSArray::kLengthOffset), rax);
+
// Elements are in new space, so write barrier is not required.
__ ret((argc + 1) * kPointerSize);
-
- __ bind(&call_builtin);
}
+ __ bind(&call_builtin);
__ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
argc + 1,
1);
@@ -1204,11 +1207,11 @@
String* name,
CheckType check) {
// ----------- S t a t e -------------
- // -- ecx : name
- // -- esp[0] : return address
- // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
// -- ...
- // -- esp[(argc + 1) * 4] : receiver
+ // -- rsp[(argc + 1) * 8] : receiver
// -----------------------------------
ASSERT(check == RECEIVER_MAP_CHECK);
@@ -1235,9 +1238,10 @@
// Get the elements array of the object.
__ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset));
- // Check that the elements are in fast mode (not dictionary).
- __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), Factory::fixed_array_map());
- __ j(not_equal, &miss);
+ // Check that the elements are in fast mode and writable.
+ __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
+ __ j(not_equal, &call_builtin);
// Get the array's length into rcx and calculate new length.
__ SmiToInteger32(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
@@ -1245,7 +1249,7 @@
__ j(negative, &return_undefined);
// Get the last element.
- __ Move(r9, Factory::the_hole_value());
+ __ LoadRoot(r9, Heap::kTheHoleValueRootIndex);
__ movq(rax, FieldOperand(rbx,
rcx, times_pointer_size,
FixedArray::kHeaderSize));
@@ -1265,14 +1269,14 @@
__ ret((argc + 1) * kPointerSize);
__ bind(&return_undefined);
-
- __ Move(rax, Factory::undefined_value());
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
__ ret((argc + 1) * kPointerSize);
__ bind(&call_builtin);
__ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop),
argc + 1,
1);
+
__ bind(&miss);
Object* obj = GenerateMissBranch();
if (obj->IsFailure()) return obj;