blob: 0fddef218f80624f6e39e8f8d80266c4805453ef [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/become.h"
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/dart_api_state.h"
#include "vm/isolate_reload.h"
#include "vm/object.h"
#include "vm/raw_object.h"
#include "vm/safepoint.h"
#include "vm/timeline.h"
#include "vm/visitor.h"
namespace dart {
ForwardingCorpse* ForwardingCorpse::AsForwarder(uword addr, intptr_t size) {
ASSERT(size >= kObjectAlignment);
ASSERT(Utils::IsAligned(size, kObjectAlignment));
ForwardingCorpse* result = reinterpret_cast<ForwardingCorpse*>(addr);
uword tags = 0;
tags = RawObject::SizeTag::update(size, tags);
tags = RawObject::ClassIdTag::update(kForwardingCorpse, tags);
result->tags_ = tags;
if (size > RawObject::SizeTag::kMaxSizeTag) {
*result->SizeAddress() = size;
}
result->set_target(Object::null());
return result;
}
void ForwardingCorpse::InitOnce() {
ASSERT(sizeof(ForwardingCorpse) == kObjectAlignment);
ASSERT(OFFSET_OF(ForwardingCorpse, tags_) == Object::tags_offset());
}
// Free list elements are used as a marker for forwarding objects. This is
// safe because we cannot reach free list elements from live objects. Ideally
// forwarding objects would have their own class id. See TODO below.
static bool IsForwardingObject(RawObject* object) {
return object->IsHeapObject() && object->IsForwardingCorpse();
}
static RawObject* GetForwardedObject(RawObject* object) {
ASSERT(IsForwardingObject(object));
uword addr = reinterpret_cast<uword>(object) - kHeapObjectTag;
ForwardingCorpse* forwarder = reinterpret_cast<ForwardingCorpse*>(addr);
return forwarder->target();
}
static void ForwardObjectTo(RawObject* before_obj, RawObject* after_obj) {
const intptr_t size_before = before_obj->Size();
uword corpse_addr = reinterpret_cast<uword>(before_obj) - kHeapObjectTag;
ForwardingCorpse* forwarder =
ForwardingCorpse::AsForwarder(corpse_addr, size_before);
forwarder->set_target(after_obj);
if (!IsForwardingObject(before_obj)) {
FATAL("become: ForwardObjectTo failure.");
}
// Still need to be able to iterate over the forwarding corpse.
const intptr_t size_after = before_obj->Size();
if (size_before != size_after) {
FATAL("become: Before and after sizes do not match.");
}
}
class ForwardPointersVisitor : public ObjectPointerVisitor {
public:
explicit ForwardPointersVisitor(Isolate* isolate)
: ObjectPointerVisitor(isolate), visiting_object_(NULL), count_(0) {}
virtual void VisitPointers(RawObject** first, RawObject** last) {
for (RawObject** p = first; p <= last; p++) {
RawObject* old_target = *p;
if (IsForwardingObject(old_target)) {
RawObject* new_target = GetForwardedObject(old_target);
if (visiting_object_ == NULL) {
*p = new_target;
} else {
visiting_object_->StorePointer(p, new_target);
}
count_++;
}
}
}
void VisitingObject(RawObject* obj) { visiting_object_ = obj; }
intptr_t count() const { return count_; }
private:
RawObject* visiting_object_;
intptr_t count_;
DISALLOW_COPY_AND_ASSIGN(ForwardPointersVisitor);
};
class ForwardHeapPointersVisitor : public ObjectVisitor {
public:
explicit ForwardHeapPointersVisitor(ForwardPointersVisitor* pointer_visitor)
: pointer_visitor_(pointer_visitor) {}
virtual void VisitObject(RawObject* obj) {
pointer_visitor_->VisitingObject(obj);
obj->VisitPointers(pointer_visitor_);
}
private:
ForwardPointersVisitor* pointer_visitor_;
DISALLOW_COPY_AND_ASSIGN(ForwardHeapPointersVisitor);
};
class ForwardHeapPointersHandleVisitor : public HandleVisitor {
public:
ForwardHeapPointersHandleVisitor()
: HandleVisitor(Thread::Current()), count_(0) {}
virtual void VisitHandle(uword addr) {
FinalizablePersistentHandle* handle =
reinterpret_cast<FinalizablePersistentHandle*>(addr);
if (IsForwardingObject(handle->raw())) {
*handle->raw_addr() = GetForwardedObject(handle->raw());
count_++;
}
}
intptr_t count() const { return count_; }
private:
int count_;
DISALLOW_COPY_AND_ASSIGN(ForwardHeapPointersHandleVisitor);
};
// On IA32, object pointers are embedded directly in the instruction stream,
// which is normally write-protected, so we need to make it temporarily writable
// to forward the pointers. On all other architectures, object pointers are
// accessed through ObjectPools.
#if defined(TARGET_ARCH_IA32)
class WritableCodeLiteralsScope : public ValueObject {
public:
explicit WritableCodeLiteralsScope(Heap* heap) : heap_(heap) {
if (FLAG_write_protect_code) {
heap_->WriteProtectCode(false);
}
}
~WritableCodeLiteralsScope() {
if (FLAG_write_protect_code) {
heap_->WriteProtectCode(true);
}
}
private:
Heap* heap_;
};
#else
class WritableCodeLiteralsScope : public ValueObject {
public:
explicit WritableCodeLiteralsScope(Heap* heap) {}
~WritableCodeLiteralsScope() {}
};
#endif
void Become::MakeDummyObject(const Instance& instance) {
// Make the forward pointer point to itself.
// This is needed to distinguish it from a real forward object.
ForwardObjectTo(instance.raw(), instance.raw());
}
static bool IsDummyObject(RawObject* object) {
if (!object->IsForwardingCorpse()) return false;
return GetForwardedObject(object) == object;
}
void Become::CrashDump(RawObject* before_obj, RawObject* after_obj) {
OS::PrintErr("DETECTED FATAL ISSUE IN BECOME MAPPINGS\n");
OS::PrintErr("BEFORE ADDRESS: %p\n", before_obj);
OS::PrintErr("BEFORE IS HEAP OBJECT: %s",
before_obj->IsHeapObject() ? "YES" : "NO");
OS::PrintErr("BEFORE IS VM HEAP OBJECT: %s",
before_obj->IsVMHeapObject() ? "YES" : "NO");
OS::PrintErr("AFTER ADDRESS: %p\n", after_obj);
OS::PrintErr("AFTER IS HEAP OBJECT: %s",
after_obj->IsHeapObject() ? "YES" : "NO");
OS::PrintErr("AFTER IS VM HEAP OBJECT: %s",
after_obj->IsVMHeapObject() ? "YES" : "NO");
if (before_obj->IsHeapObject()) {
OS::PrintErr("BEFORE OBJECT CLASS ID=%" Pd "\n", before_obj->GetClassId());
const Object& obj = Object::Handle(before_obj);
OS::PrintErr("BEFORE OBJECT AS STRING=%s\n", obj.ToCString());
}
if (after_obj->IsHeapObject()) {
OS::PrintErr("AFTER OBJECT CLASS ID=%" Pd "\n", after_obj->GetClassId());
const Object& obj = Object::Handle(after_obj);
OS::PrintErr("AFTER OBJECT AS STRING=%s\n", obj.ToCString());
}
}
void Become::ElementsForwardIdentity(const Array& before, const Array& after) {
Thread* thread = Thread::Current();
Isolate* isolate = thread->isolate();
Heap* heap = isolate->heap();
TIMELINE_FUNCTION_GC_DURATION(thread, "Become::ElementsForwardIdentity");
HeapIterationScope his;
// Setup forwarding pointers.
ASSERT(before.Length() == after.Length());
for (intptr_t i = 0; i < before.Length(); i++) {
RawObject* before_obj = before.At(i);
RawObject* after_obj = after.At(i);
if (before_obj == after_obj) {
FATAL("become: Cannot self-forward");
}
if (!before_obj->IsHeapObject()) {
CrashDump(before_obj, after_obj);
FATAL("become: Cannot forward immediates");
}
if (!after_obj->IsHeapObject()) {
CrashDump(before_obj, after_obj);
FATAL("become: Cannot become immediates");
}
if (before_obj->IsVMHeapObject()) {
CrashDump(before_obj, after_obj);
FATAL("become: Cannot forward VM heap objects");
}
if (before_obj->IsForwardingCorpse() && !IsDummyObject(before_obj)) {
FATAL("become: Cannot forward to multiple targets");
}
if (after_obj->IsForwardingCorpse()) {
// The Smalltalk become does allow this, and for very special cases
// it is important (shape changes to Class or Mixin), but as these
// cases do not arise in Dart, better to prohibit it.
FATAL("become: No indirect chains of forwarding");
}
ForwardObjectTo(before_obj, after_obj);
// Forward the identity hash too if it has one.
intptr_t hash = heap->GetHash(before_obj);
if (hash != 0) {
ASSERT(heap->GetHash(after_obj) == 0);
heap->SetHash(after_obj, hash);
}
}
{
// Follow forwarding pointers.
// C++ pointers
ForwardPointersVisitor pointer_visitor(isolate);
isolate->VisitObjectPointers(&pointer_visitor, true);
// Weak persistent handles.
ForwardHeapPointersHandleVisitor handle_visitor;
isolate->VisitWeakPersistentHandles(&handle_visitor);
// Heap pointers (may require updating the remembered set)
{
WritableCodeLiteralsScope writable_code(heap);
ForwardHeapPointersVisitor object_visitor(&pointer_visitor);
heap->VisitObjects(&object_visitor);
pointer_visitor.VisitingObject(NULL);
}
#if !defined(PRODUCT)
tds.SetNumArguments(2);
tds.FormatArgument(0, "Remapped objects", "%" Pd, before.Length());
tds.FormatArgument(1, "Remapped references", "%" Pd,
pointer_visitor.count() + handle_visitor.count());
#endif
}
#if defined(DEBUG)
for (intptr_t i = 0; i < before.Length(); i++) {
ASSERT(before.At(i) == after.At(i));
}
#endif
}
} // namespace dart