Commit 702ba5a0 authored by hpayer@chromium.org's avatar hpayer@chromium.org

Make sure when we shrink an object that we store a filler first into the free...

Make sure when we shrink an object that we store a filler first into the free memory before updating the map or size.

BUG=
R=jarin@chromium.org

Review URL: https://codereview.chromium.org/219103002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20361 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 019e27d8
......@@ -210,6 +210,8 @@ static void MoveDoubleElements(FixedDoubleArray* dst,
static FixedArrayBase* LeftTrimFixedArray(Heap* heap,
FixedArrayBase* elms,
int to_trim) {
ASSERT(heap->CanMoveObjectStart(elms));
Map* map = elms->map();
int entry_size;
if (elms->IsFixedArray()) {
......@@ -246,6 +248,8 @@ static FixedArrayBase* LeftTrimFixedArray(Heap* heap,
// Technically in new space this write might be omitted (except for
// debug mode which iterates through the heap), but to play safer
// we still do it.
// Since left trimming is only performed on pages which are not concurrently
// swept creating a filler object does not require synchronization.
heap->CreateFillerObjectAt(elms->address(), to_trim * entry_size);
int new_start_index = to_trim * (entry_size / kPointerSize);
......
......@@ -962,11 +962,15 @@ class FastElementsAccessor
if (length == 0) {
array->initialize_elements();
} else {
backing_store->set_length(length);
int filler_size = (old_capacity - length) * ElementSize;
Address filler_start = backing_store->address() +
BackingStore::OffsetOfElementAt(length);
int filler_size = (old_capacity - length) * ElementSize;
array->GetHeap()->CreateFillerObjectAt(filler_start, filler_size);
// We are storing the new length using release store after creating a
// filler for the left-over space to avoid races with the sweeper
// thread.
backing_store->synchronized_set_length(length);
}
} else {
// Otherwise, fill the unused tail with holes.
......
......@@ -125,6 +125,15 @@ PropertyDetails PropertyDetails::AsDeleted() const {
WRITE_FIELD(this, offset, Smi::FromInt(value)); \
}
#define SYNCHRONIZED_SMI_ACCESSORS(holder, name, offset) \
int holder::synchronized_##name() { \
Object* value = ACQUIRE_READ_FIELD(this, offset); \
return Smi::cast(value)->value(); \
} \
void holder::synchronized_set_##name(int value) { \
RELEASE_WRITE_FIELD(this, offset, Smi::FromInt(value)); \
}
#define BOOL_GETTER(holder, field, name, offset) \
bool holder::name() { \
......@@ -1095,9 +1104,25 @@ MaybeObject* Object::GetProperty(Name* key, PropertyAttributes* attributes) {
#define READ_FIELD(p, offset) \
(*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)))
#define ACQUIRE_READ_FIELD(p, offset) \
reinterpret_cast<Object*>( \
Acquire_Load(reinterpret_cast<AtomicWord*>(FIELD_ADDR(p, offset))))
#define NO_BARRIER_READ_FIELD(p, offset) \
reinterpret_cast<Object*>( \
NoBarrier_Load(reinterpret_cast<AtomicWord*>(FIELD_ADDR(p, offset))))
#define WRITE_FIELD(p, offset, value) \
(*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
#define RELEASE_WRITE_FIELD(p, offset, value) \
Release_Store(reinterpret_cast<AtomicWord*>(FIELD_ADDR(p, offset)), \
reinterpret_cast<AtomicWord>(value));
#define NO_BARRIER_WRITE_FIELD(p, offset, value) \
NoBarrier_Store(reinterpret_cast<AtomicWord*>(FIELD_ADDR(p, offset)), \
reinterpret_cast<AtomicWord>(value));
#define WRITE_BARRIER(heap, object, offset, value) \
heap->incremental_marking()->RecordWrite( \
object, HeapObject::RawField(object, offset), value); \
......@@ -1348,6 +1373,21 @@ void HeapObject::set_map(Map* value) {
}
Map* HeapObject::synchronized_map() {
return synchronized_map_word().ToMap();
}
void HeapObject::synchronized_set_map(Map* value) {
synchronized_set_map_word(MapWord::FromMap(value));
if (value != NULL) {
// TODO(1600) We are passing NULL as a slot because maps can never be on
// evacuation candidate.
value->GetHeap()->incremental_marking()->RecordWrite(this, NULL, value);
}
}
// Unsafe accessor omitting write barrier.
void HeapObject::set_map_no_write_barrier(Map* value) {
set_map_word(MapWord::FromMap(value));
......@@ -1355,14 +1395,26 @@ void HeapObject::set_map_no_write_barrier(Map* value) {
MapWord HeapObject::map_word() {
return MapWord(reinterpret_cast<uintptr_t>(READ_FIELD(this, kMapOffset)));
return MapWord(
reinterpret_cast<uintptr_t>(NO_BARRIER_READ_FIELD(this, kMapOffset)));
}
void HeapObject::set_map_word(MapWord map_word) {
// WRITE_FIELD does not invoke write barrier, but there is no need
// here.
WRITE_FIELD(this, kMapOffset, reinterpret_cast<Object*>(map_word.value_));
NO_BARRIER_WRITE_FIELD(
this, kMapOffset, reinterpret_cast<Object*>(map_word.value_));
}
MapWord HeapObject::synchronized_map_word() {
return MapWord(
reinterpret_cast<uintptr_t>(ACQUIRE_READ_FIELD(this, kMapOffset)));
}
void HeapObject::synchronized_set_map_word(MapWord map_word) {
RELEASE_WRITE_FIELD(
this, kMapOffset, reinterpret_cast<Object*>(map_word.value_));
}
......@@ -2947,9 +2999,12 @@ HashTable<Shape, Key>* HashTable<Shape, Key>::cast(Object* obj) {
SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset)
SYNCHRONIZED_SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset)
SMI_ACCESSORS(FreeSpace, size, kSizeOffset)
SMI_ACCESSORS(String, length, kLengthOffset)
SYNCHRONIZED_SMI_ACCESSORS(String, length, kLengthOffset)
uint32_t Name::hash_field() {
......
......@@ -1284,34 +1284,39 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
// In either case we resort to a short external string instead, omitting
// the field caching the address of the backing store. When we encounter
// short external strings in generated code, we need to bailout to runtime.
Map* new_map;
if (size < ExternalString::kSize ||
heap->old_pointer_space()->Contains(this)) {
this->set_map_no_write_barrier(
is_internalized
? (is_ascii
? heap->
short_external_internalized_string_with_one_byte_data_map()
: heap->short_external_internalized_string_map())
: (is_ascii
? heap->short_external_string_with_one_byte_data_map()
: heap->short_external_string_map()));
new_map = is_internalized
? (is_ascii
? heap->
short_external_internalized_string_with_one_byte_data_map()
: heap->short_external_internalized_string_map())
: (is_ascii
? heap->short_external_string_with_one_byte_data_map()
: heap->short_external_string_map());
} else {
this->set_map_no_write_barrier(
is_internalized
? (is_ascii
? heap->external_internalized_string_with_one_byte_data_map()
: heap->external_internalized_string_map())
: (is_ascii
? heap->external_string_with_one_byte_data_map()
: heap->external_string_map()));
new_map = is_internalized
? (is_ascii
? heap->external_internalized_string_with_one_byte_data_map()
: heap->external_internalized_string_map())
: (is_ascii
? heap->external_string_with_one_byte_data_map()
: heap->external_string_map());
}
// Byte size of the external String object.
int new_size = this->SizeFromMap(new_map);
heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
// We are storing the new map using release store after creating a filler for
// the left-over space to avoid races with the sweeper thread.
this->synchronized_set_map(new_map);
ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
self->set_resource(resource);
if (is_internalized) self->Hash(); // Force regeneration of the hash value.
// Fill the remainder of the string with dead wood.
int new_size = this->Size(); // Byte size of the external String object.
heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
heap->AdjustLiveBytes(this->address(), new_size - size, Heap::FROM_MUTATOR);
return true;
}
......@@ -1351,23 +1356,30 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
// In either case we resort to a short external string instead, omitting
// the field caching the address of the backing store. When we encounter
// short external strings in generated code, we need to bailout to runtime.
Map* new_map;
if (size < ExternalString::kSize ||
heap->old_pointer_space()->Contains(this)) {
this->set_map_no_write_barrier(
is_internalized ? heap->short_external_ascii_internalized_string_map()
: heap->short_external_ascii_string_map());
new_map = is_internalized
? heap->short_external_ascii_internalized_string_map()
: heap->short_external_ascii_string_map();
} else {
this->set_map_no_write_barrier(
is_internalized ? heap->external_ascii_internalized_string_map()
: heap->external_ascii_string_map());
new_map = is_internalized
? heap->external_ascii_internalized_string_map()
: heap->external_ascii_string_map();
}
// Byte size of the external String object.
int new_size = this->SizeFromMap(new_map);
heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
// We are storing the new map using release store after creating a filler for
// the left-over space to avoid races with the sweeper thread.
this->synchronized_set_map(new_map);
ExternalAsciiString* self = ExternalAsciiString::cast(this);
self->set_resource(resource);
if (is_internalized) self->Hash(); // Force regeneration of the hash value.
// Fill the remainder of the string with dead wood.
int new_size = this->Size(); // Byte size of the external String object.
heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
heap->AdjustLiveBytes(this->address(), new_size - size, Heap::FROM_MUTATOR);
return true;
}
......@@ -2301,7 +2313,9 @@ static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) {
// we still do it.
heap->CreateFillerObjectAt(new_end, size_delta);
elms->set_length(len - to_trim);
// We are storing the new length using release store after creating a filler
// for the left-over space to avoid races with the sweeper thread.
elms->synchronized_set_length(len - to_trim);
heap->AdjustLiveBytes(elms->address(), -size_delta, mode);
......@@ -2376,7 +2390,9 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
// converted to doubles.
if (!old_map->InstancesNeedRewriting(
*new_map, number_of_fields, inobject, unused)) {
object->set_map(*new_map);
// Writing the new map here does not require synchronization since it does
// not change the actual object size.
object->synchronized_set_map(*new_map);
return;
}
......@@ -2446,6 +2462,11 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
int instance_size_delta = old_map->instance_size() - new_instance_size;
ASSERT(instance_size_delta >= 0);
Address address = object->address() + new_instance_size;
// The trimming is performed on a newly allocated object, which is on a
// fresly allocated page or on an already swept page. Hence, the sweeper
// thread can not get confused with the filler creation. No synchronization
// needed.
isolate->heap()->CreateFillerObjectAt(address, instance_size_delta);
// If there are properties in the new backing store, trim it to the correct
......@@ -2455,6 +2476,10 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
object->set_properties(*array);
}
// The trimming is performed on a newly allocated object, which is on a
// fresly allocated page or on an already swept page. Hence, the sweeper
// thread can not get confused with the filler creation. No synchronization
// needed.
object->set_map(*new_map);
}
......@@ -4645,7 +4670,10 @@ void JSObject::NormalizeProperties(Handle<JSObject> object,
-instance_size_delta,
Heap::FROM_MUTATOR);
object->set_map(*new_map);
// We are storing the new map using release store after creating a filler for
// the left-over space to avoid races with the sweeper thread.
object->synchronized_set_map(*new_map);
map->NotifyLeafMapLayoutChange();
object->set_properties(*dictionary);
......@@ -9158,7 +9186,6 @@ Handle<String> SeqString::Truncate(Handle<SeqString> string, int new_length) {
}
int delta = old_size - new_size;
string->set_length(new_length);
Address start_of_string = string->address();
ASSERT_OBJECT_ALIGNED(start_of_string);
......@@ -9177,6 +9204,10 @@ Handle<String> SeqString::Truncate(Handle<SeqString> string, int new_length) {
}
heap->AdjustLiveBytes(start_of_string, -delta, Heap::FROM_MUTATOR);
// We are storing the new length using release store after creating a filler
// for the left-over space to avoid races with the sweeper thread.
string->synchronized_set_length(new_length);
if (new_length == 0) return heap->isolate()->factory()->empty_string();
return string;
}
......
......@@ -1816,6 +1816,14 @@ class HeapObject: public Object {
// of primitive (non-JS) objects like strings, heap numbers etc.
inline void set_map_no_write_barrier(Map* value);
// Get the map using acquire load.
inline Map* synchronized_map();
inline MapWord synchronized_map_word();
// Set the map using release store
inline void synchronized_set_map(Map* value);
inline void synchronized_set_map_word(MapWord map_word);
// During garbage collection, the map word of a heap object does not
// necessarily contain a map pointer.
inline MapWord map_word();
......@@ -3016,6 +3024,10 @@ class FixedArrayBase: public HeapObject {
inline int length();
inline void set_length(int value);
// Get and set the length using acquire loads and release stores.
inline int synchronized_length();
inline void synchronized_set_length(int value);
inline static FixedArrayBase* cast(Object* object);
// Layout description.
......@@ -8788,6 +8800,11 @@ class String: public Name {
inline int length();
inline void set_length(int value);
// Get and set the length of the string using acquire loads and release
// stores.
inline int synchronized_length();
inline void synchronized_set_length(int value);
// Returns whether this string has only ASCII chars, i.e. all of them can
// be ASCII encoded. This might be the case even if the string is
// two-byte. Such strings may appear when the embedder prefers
......
......@@ -4201,6 +4201,11 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithEmptyString(
Address end_of_string = answer->address() + string_size;
Heap* heap = isolate->heap();
// The trimming is performed on a newly allocated object, which is on a
// fresly allocated page or on an already swept page. Hence, the sweeper
// thread can not get confused with the filler creation. No synchronization
// needed.
heap->CreateFillerObjectAt(end_of_string, delta);
heap->AdjustLiveBytes(answer->address(), -delta, Heap::FROM_MUTATOR);
return *answer;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment