Commit fbee722c authored by ulan's avatar ulan Committed by Commit bot

[heap] Notify GC on potentially unsafe object layout changes.

During concurrent marking we need special handling of object layout
changes that remove tagged in-object fields or replaces them with
untagged in-object fields.

This patch adds a function for notifying object layout changes and
verification code that is triggered on each map change in runtime.

BUG=chromium:694255

Review-Url: https://codereview.chromium.org/2702303002
Cr-Commit-Position: refs/heads/master@{#43342}
parent a182f8d5
......@@ -159,7 +159,8 @@ Heap::Heap()
local_embedder_heap_tracer_(nullptr),
fast_promotion_mode_(false),
force_oom_(false),
delay_sweeper_tasks_for_testing_(false) {
delay_sweeper_tasks_for_testing_(false),
pending_layout_change_object_(nullptr) {
// Allow build-time customization of the max semispace size. Building
// V8 with snapshots and a non-default max semispace size is much
// easier if you can define it as part of the build environment.
......@@ -4298,6 +4299,27 @@ void Heap::RegisterReservationsForBlackAllocation(Reservation* reservations) {
}
}
void Heap::NotifyObjectLayoutChange(HeapObject* object,
const DisallowHeapAllocation&) {
// TODO(ulan): Add synchronization with the concurrent marker.
#ifdef VERIFY_HEAP
DCHECK(pending_layout_change_object_ == nullptr);
pending_layout_change_object_ = object;
#endif
}
#ifdef VERIFY_HEAP
void Heap::VerifyObjectLayoutChange(HeapObject* object, Map* new_map) {
if (pending_layout_change_object_ == nullptr) {
DCHECK(!object->IsJSObject() ||
!object->map()->TransitionRequiresSynchronizationWithGC(new_map));
} else {
DCHECK_EQ(pending_layout_change_object_, object);
pending_layout_change_object_ = nullptr;
}
}
#endif
GCIdleTimeHeapState Heap::ComputeHeapState() {
GCIdleTimeHeapState heap_state;
heap_state.contexts_disposed = contexts_disposed_;
......
......@@ -1238,6 +1238,20 @@ class Heap {
IncrementalMarking* incremental_marking() { return incremental_marking_; }
// The runtime uses this function to notify potentially unsafe object layout
// changes that require special synchronization with the concurrent marker.
// A layout change is unsafe if
// - it removes a tagged in-object field.
// - it replaces a tagged in-objects field with an untagged in-object field.
void NotifyObjectLayoutChange(HeapObject* object,
const DisallowHeapAllocation&);
#ifdef VERIFY_HEAP
// This function checks that either
// - the map transition is safe,
// - or it was communicated to GC using NotifyObjectLayoutChange.
void VerifyObjectLayoutChange(HeapObject* object, Map* new_map);
#endif
// ===========================================================================
// Embedder heap tracer support. =============================================
// ===========================================================================
......@@ -2355,6 +2369,8 @@ class Heap {
bool force_oom_;
bool delay_sweeper_tasks_for_testing_;
HeapObject* pending_layout_change_object_;
// Classes in "heap" can be friends.
friend class AlwaysAllocateScope;
friend class GCCallbacksScope;
......
......@@ -1468,10 +1468,13 @@ Map* HeapObject::map() const {
void HeapObject::set_map(Map* value) {
set_map_word(MapWord::FromMap(value));
if (value != NULL) {
if (value != nullptr) {
// 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);
value->GetHeap()->incremental_marking()->RecordWrite(this, nullptr, value);
#ifdef VERIFY_HEAP
value->GetHeap()->VerifyObjectLayoutChange(this, value);
#endif
}
}
......@@ -1483,10 +1486,13 @@ Map* HeapObject::synchronized_map() {
void HeapObject::synchronized_set_map(Map* value) {
synchronized_set_map_word(MapWord::FromMap(value));
if (value != NULL) {
if (value != nullptr) {
// 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);
value->GetHeap()->incremental_marking()->RecordWrite(this, nullptr, value);
#ifdef VERIFY_HEAP
value->GetHeap()->VerifyObjectLayoutChange(this, value);
#endif
}
}
......
......@@ -3275,6 +3275,34 @@ const char* Representation::Mnemonic() const {
}
}
bool Map::TransitionRemovesTaggedField(Map* target) {
int inobject = GetInObjectProperties();
int target_inobject = target->GetInObjectProperties();
for (int i = target_inobject; i < inobject; i++) {
FieldIndex index = FieldIndex::ForPropertyIndex(this, i);
if (!IsUnboxedDoubleField(index)) return true;
}
return false;
}
bool Map::TransitionChangesTaggedFieldToUntaggedField(Map* target) {
int inobject = GetInObjectProperties();
int target_inobject = target->GetInObjectProperties();
int limit = Min(inobject, target_inobject);
for (int i = 0; i < limit; i++) {
FieldIndex index = FieldIndex::ForPropertyIndex(target, i);
if (!IsUnboxedDoubleField(index) && target->IsUnboxedDoubleField(index)) {
return true;
}
}
return false;
}
bool Map::TransitionRequiresSynchronizationWithGC(Map* target) {
return TransitionRemovesTaggedField(target) ||
TransitionChangesTaggedFieldToUntaggedField(target);
}
bool Map::InstancesNeedRewriting(Map* target) {
int target_number_of_fields = target->NumberOfFields();
int target_inobject = target->GetInObjectProperties();
......@@ -3525,6 +3553,8 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
Heap* heap = isolate->heap();
heap->NotifyObjectLayoutChange(*object, no_allocation);
// Copy (real) inobject properties. If necessary, stop at number_of_fields to
// avoid overwriting |one_pointer_filler_map|.
int limit = Min(inobject, number_of_fields);
......@@ -3639,13 +3669,15 @@ void MigrateFastToSlow(Handle<JSObject> object, Handle<Map> new_map,
// From here on we cannot fail and we shouldn't GC anymore.
DisallowHeapAllocation no_allocation;
Heap* heap = isolate->heap();
heap->NotifyObjectLayoutChange(*object, no_allocation);
// Resize the object in the heap if necessary.
int new_instance_size = new_map->instance_size();
int instance_size_delta = map->instance_size() - new_instance_size;
DCHECK(instance_size_delta >= 0);
if (instance_size_delta > 0) {
Heap* heap = isolate->heap();
heap->CreateFillerObjectAt(object->address() + new_instance_size,
instance_size_delta, ClearRecordedSlots::kYes);
heap->AdjustLiveBytes(*object, -instance_size_delta);
......
......@@ -5838,6 +5838,16 @@ class Map: public HeapObject {
int NumberOfFields();
// Returns true if transition to the given map requires special
// synchronization with the concurrent marker.
bool TransitionRequiresSynchronizationWithGC(Map* target);
// Returns true if transition to the given map removes a tagged in-object
// field.
bool TransitionRemovesTaggedField(Map* target);
// Returns true if transition to the given map replaces a tagged in-object
// field with an untagged in-object field.
bool TransitionChangesTaggedFieldToUntaggedField(Map* target);
// TODO(ishell): candidate with JSObject::MigrateToMap().
bool InstancesNeedRewriting(Map* target);
bool InstancesNeedRewriting(Map* target, int target_number_of_fields,
......
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