1// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/heap/become.h"
6
7#include "platform/assert.h"
8#include "platform/utils.h"
9
10#include "vm/dart_api_state.h"
11#include "vm/heap/safepoint.h"
12#include "vm/isolate_reload.h"
13#include "vm/object.h"
14#include "vm/raw_object.h"
15#include "vm/timeline.h"
16#include "vm/visitor.h"
17
18namespace dart {
19
20ForwardingCorpse* ForwardingCorpse::AsForwarder(uword addr, intptr_t size) {
21 ASSERT(size >= kObjectAlignment);
22 ASSERT(Utils::IsAligned(size, kObjectAlignment));
23
24 ForwardingCorpse* result = reinterpret_cast<ForwardingCorpse*>(addr);
25
26 uint32_t tags = 0;
27 tags = ObjectLayout::SizeTag::update(size, tags);
28 tags = ObjectLayout::ClassIdTag::update(kForwardingCorpse, tags);
29 bool is_old = (addr & kNewObjectAlignmentOffset) == kOldObjectAlignmentOffset;
30 tags = ObjectLayout::OldBit::update(is_old, tags);
31 tags = ObjectLayout::OldAndNotMarkedBit::update(is_old, tags);
32 tags = ObjectLayout::OldAndNotRememberedBit::update(is_old, tags);
33 tags = ObjectLayout::NewBit::update(!is_old, tags);
34
35 result->tags_ = tags;
36 if (size > ObjectLayout::SizeTag::kMaxSizeTag) {
37 *result->SizeAddress() = size;
38 }
39 result->set_target(Object::null());
40 return result;
41}
42
43void ForwardingCorpse::Init() {
44 ASSERT(sizeof(ForwardingCorpse) == kObjectAlignment);
45 ASSERT(OFFSET_OF(ForwardingCorpse, tags_) == Object::tags_offset());
46}
47
48// Free list elements are used as a marker for forwarding objects. This is
49// safe because we cannot reach free list elements from live objects. Ideally
50// forwarding objects would have their own class id. See TODO below.
51static bool IsForwardingObject(ObjectPtr object) {
52 return object->IsHeapObject() && object->IsForwardingCorpse();
53}
54
55static ObjectPtr GetForwardedObject(ObjectPtr object) {
56 ASSERT(IsForwardingObject(object));
57 uword addr = static_cast<uword>(object) - kHeapObjectTag;
58 ForwardingCorpse* forwarder = reinterpret_cast<ForwardingCorpse*>(addr);
59 return forwarder->target();
60}
61
62static void ForwardObjectTo(ObjectPtr before_obj, ObjectPtr after_obj) {
63 const intptr_t size_before = before_obj->ptr()->HeapSize();
64
65 uword corpse_addr = static_cast<uword>(before_obj) - kHeapObjectTag;
66 ForwardingCorpse* forwarder =
67 ForwardingCorpse::AsForwarder(corpse_addr, size_before);
68 forwarder->set_target(after_obj);
69 if (!IsForwardingObject(before_obj)) {
70 FATAL("become: ForwardObjectTo failure.");
71 }
72 // Still need to be able to iterate over the forwarding corpse.
73 const intptr_t size_after = before_obj->ptr()->HeapSize();
74 if (size_before != size_after) {
75 FATAL("become: Before and after sizes do not match.");
76 }
77}
78
79class ForwardPointersVisitor : public ObjectPointerVisitor {
80 public:
81 explicit ForwardPointersVisitor(Thread* thread)
82 : ObjectPointerVisitor(thread->isolate_group()),
83 thread_(thread),
84 visiting_object_(nullptr) {}
85
86 virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
87 for (ObjectPtr* p = first; p <= last; p++) {
88 ObjectPtr old_target = *p;
89 if (IsForwardingObject(old_target)) {
90 ObjectPtr new_target = GetForwardedObject(old_target);
91 if (visiting_object_ == nullptr) {
92 *p = new_target;
93 } else {
94 visiting_object_->ptr()->StorePointer(p, new_target);
95 }
96 }
97 }
98 }
99
100 void VisitingObject(ObjectPtr obj) {
101 visiting_object_ = obj;
102 if ((obj != nullptr) && obj->IsOldObject() && obj->ptr()->IsRemembered()) {
103 ASSERT(!obj->IsForwardingCorpse());
104 ASSERT(!obj->IsFreeListElement());
105 thread_->StoreBufferAddObjectGC(obj);
106 }
107 }
108
109 private:
110 Thread* thread_;
111 ObjectPtr visiting_object_;
112
113 DISALLOW_COPY_AND_ASSIGN(ForwardPointersVisitor);
114};
115
116class ForwardHeapPointersVisitor : public ObjectVisitor {
117 public:
118 explicit ForwardHeapPointersVisitor(ForwardPointersVisitor* pointer_visitor)
119 : pointer_visitor_(pointer_visitor) {}
120
121 virtual void VisitObject(ObjectPtr obj) {
122 pointer_visitor_->VisitingObject(obj);
123 obj->ptr()->VisitPointers(pointer_visitor_);
124 }
125
126 private:
127 ForwardPointersVisitor* pointer_visitor_;
128
129 DISALLOW_COPY_AND_ASSIGN(ForwardHeapPointersVisitor);
130};
131
132class ForwardHeapPointersHandleVisitor : public HandleVisitor {
133 public:
134 explicit ForwardHeapPointersHandleVisitor(Thread* thread)
135 : HandleVisitor(thread) {}
136
137 virtual void VisitHandle(uword addr) {
138 FinalizablePersistentHandle* handle =
139 reinterpret_cast<FinalizablePersistentHandle*>(addr);
140 if (IsForwardingObject(handle->raw())) {
141 *handle->raw_addr() = GetForwardedObject(handle->raw());
142 }
143 }
144
145 private:
146 DISALLOW_COPY_AND_ASSIGN(ForwardHeapPointersHandleVisitor);
147};
148
149// On IA32, object pointers are embedded directly in the instruction stream,
150// which is normally write-protected, so we need to make it temporarily writable
151// to forward the pointers. On all other architectures, object pointers are
152// accessed through ObjectPools.
153#if defined(TARGET_ARCH_IA32)
154class WritableCodeLiteralsScope : public ValueObject {
155 public:
156 explicit WritableCodeLiteralsScope(Heap* heap) : heap_(heap) {
157 if (FLAG_write_protect_code) {
158 heap_->WriteProtectCode(false);
159 }
160 }
161
162 ~WritableCodeLiteralsScope() {
163 if (FLAG_write_protect_code) {
164 heap_->WriteProtectCode(true);
165 }
166 }
167
168 private:
169 Heap* heap_;
170};
171#else
172class WritableCodeLiteralsScope : public ValueObject {
173 public:
174 explicit WritableCodeLiteralsScope(Heap* heap) {}
175 ~WritableCodeLiteralsScope() {}
176};
177#endif
178
179void Become::MakeDummyObject(const Instance& instance) {
180 // Make the forward pointer point to itself.
181 // This is needed to distinguish it from a real forward object.
182 ForwardObjectTo(instance.raw(), instance.raw());
183}
184
185static bool IsDummyObject(ObjectPtr object) {
186 if (!object->IsForwardingCorpse()) return false;
187 return GetForwardedObject(object) == object;
188}
189
190void Become::CrashDump(ObjectPtr before_obj, ObjectPtr after_obj) {
191 OS::PrintErr("DETECTED FATAL ISSUE IN BECOME MAPPINGS\n");
192
193 OS::PrintErr("BEFORE ADDRESS: %#" Px "\n", static_cast<uword>(before_obj));
194 OS::PrintErr("BEFORE IS HEAP OBJECT: %s\n",
195 before_obj->IsHeapObject() ? "YES" : "NO");
196 OS::PrintErr("BEFORE IN VMISOLATE HEAP OBJECT: %s\n",
197 before_obj->ptr()->InVMIsolateHeap() ? "YES" : "NO");
198
199 OS::PrintErr("AFTER ADDRESS: %#" Px "\n", static_cast<uword>(after_obj));
200 OS::PrintErr("AFTER IS HEAP OBJECT: %s\n",
201 after_obj->IsHeapObject() ? "YES" : "NO");
202 OS::PrintErr("AFTER IN VMISOLATE HEAP OBJECT: %s\n",
203 after_obj->ptr()->InVMIsolateHeap() ? "YES" : "NO");
204
205 if (before_obj->IsHeapObject()) {
206 OS::PrintErr("BEFORE OBJECT CLASS ID=%" Pd "\n", before_obj->GetClassId());
207 const Object& obj = Object::Handle(before_obj);
208 OS::PrintErr("BEFORE OBJECT AS STRING=%s\n", obj.ToCString());
209 }
210
211 if (after_obj->IsHeapObject()) {
212 OS::PrintErr("AFTER OBJECT CLASS ID=%" Pd "\n", after_obj->GetClassId());
213 const Object& obj = Object::Handle(after_obj);
214 OS::PrintErr("AFTER OBJECT AS STRING=%s\n", obj.ToCString());
215 }
216}
217
218void Become::ElementsForwardIdentity(const Array& before, const Array& after) {
219 Thread* thread = Thread::Current();
220 auto heap = thread->isolate_group()->heap();
221
222 TIMELINE_FUNCTION_GC_DURATION(thread, "Become::ElementsForwardIdentity");
223 HeapIterationScope his(thread);
224
225 // Setup forwarding pointers.
226 ASSERT(before.Length() == after.Length());
227 for (intptr_t i = 0; i < before.Length(); i++) {
228 ObjectPtr before_obj = before.At(i);
229 ObjectPtr after_obj = after.At(i);
230
231 if (before_obj == after_obj) {
232 FATAL("become: Cannot self-forward");
233 }
234 if (!before_obj->IsHeapObject()) {
235 CrashDump(before_obj, after_obj);
236 FATAL("become: Cannot forward immediates");
237 }
238 if (!after_obj->IsHeapObject()) {
239 CrashDump(before_obj, after_obj);
240 FATAL("become: Cannot become immediates");
241 }
242 if (before_obj->ptr()->InVMIsolateHeap()) {
243 CrashDump(before_obj, after_obj);
244 FATAL("become: Cannot forward VM heap objects");
245 }
246 if (before_obj->IsForwardingCorpse() && !IsDummyObject(before_obj)) {
247 FATAL("become: Cannot forward to multiple targets");
248 }
249 if (after_obj->IsForwardingCorpse()) {
250 // The Smalltalk become does allow this, and for very special cases
251 // it is important (shape changes to Class or Mixin), but as these
252 // cases do not arise in Dart, better to prohibit it.
253 FATAL("become: No indirect chains of forwarding");
254 }
255
256 ForwardObjectTo(before_obj, after_obj);
257 heap->ForwardWeakEntries(before_obj, after_obj);
258#if defined(HASH_IN_OBJECT_HEADER)
259 Object::SetCachedHash(after_obj, Object::GetCachedHash(before_obj));
260#endif
261 }
262
263 FollowForwardingPointers(thread);
264
265#if defined(DEBUG)
266 for (intptr_t i = 0; i < before.Length(); i++) {
267 ASSERT(before.At(i) == after.At(i));
268 }
269#endif
270}
271
272void Become::FollowForwardingPointers(Thread* thread) {
273 // N.B.: We forward the heap before forwarding the stack. This limits the
274 // amount of following of forwarding pointers needed to get at stack maps.
275 auto isolate_group = thread->isolate_group();
276 Heap* heap = isolate_group->heap();
277
278 // Clear the store buffer; will be rebuilt as we forward the heap.
279 isolate_group->ReleaseStoreBuffers();
280 isolate_group->store_buffer()->Reset();
281
282 ForwardPointersVisitor pointer_visitor(thread);
283
284 {
285 // Heap pointers.
286 WritableCodeLiteralsScope writable_code(heap);
287 ForwardHeapPointersVisitor object_visitor(&pointer_visitor);
288 heap->VisitObjects(&object_visitor);
289 pointer_visitor.VisitingObject(NULL);
290 }
291
292 // C++ pointers.
293 isolate_group->VisitObjectPointers(&pointer_visitor,
294 ValidationPolicy::kValidateFrames);
295#ifndef PRODUCT
296 isolate_group->ForEachIsolate(
297 [&](Isolate* isolate) {
298 ObjectIdRing* ring = isolate->object_id_ring();
299 if (ring != nullptr) {
300 ring->VisitPointers(&pointer_visitor);
301 }
302 },
303 /*at_safepoint=*/true);
304#endif // !PRODUCT
305
306 // Weak persistent handles.
307 ForwardHeapPointersHandleVisitor handle_visitor(thread);
308 isolate_group->VisitWeakPersistentHandles(&handle_visitor);
309}
310
311} // namespace dart
312