1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef ECMASCRIPT_MEM_PARALLEL_MARKER_INL_H
17 #define ECMASCRIPT_MEM_PARALLEL_MARKER_INL_H
18
19 #include "ecmascript/mem/parallel_marker.h"
20
21 #include "ecmascript/js_hclass-inl.h"
22 #include "ecmascript/mem/gc_bitset.h"
23 #include "ecmascript/mem/heap.h"
24 #include "ecmascript/mem/region-inl.h"
25 #include "ecmascript/mem/tlab_allocator-inl.h"
26
27 namespace panda::ecmascript {
28 constexpr size_t HEAD_SIZE = TaggedObject::TaggedObjectSize();
29
30 template <typename Callback>
VisitBodyInObj(TaggedObject * root,ObjectSlot start,ObjectSlot end,Callback callback)31 inline bool NonMovableMarker::VisitBodyInObj(TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback)
32 {
33 auto hclass = root->SynchronizedGetClass();
34 if (hclass->IsAllTaggedProp()) {
35 return false;
36 }
37 int index = 0;
38 for (ObjectSlot slot = start; slot < end; slot++) {
39 auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
40 auto attr = layout->GetAttr(index++);
41 if (attr.IsTaggedRep()) {
42 callback(slot);
43 }
44 }
45 return true;
46 }
47
MarkValue(uint32_t threadId,ObjectSlot & slot,Region * rootRegion,bool needBarrier)48 inline void NonMovableMarker::MarkValue(uint32_t threadId, ObjectSlot &slot, Region *rootRegion, bool needBarrier)
49 {
50 JSTaggedValue value(slot.GetTaggedType());
51 if (value.IsHeapObject()) {
52 TaggedObject *obj = nullptr;
53 if (!value.IsWeakForHeapObject()) {
54 obj = value.GetTaggedObject();
55 MarkObject(threadId, obj);
56 } else {
57 RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(slot.SlotAddress()), rootRegion);
58 obj = value.GetWeakReferentUnChecked();
59 }
60 if (needBarrier) {
61 Region *valueRegion = Region::ObjectAddressToRange(obj);
62 if (valueRegion->InCollectSet()) {
63 rootRegion->AtomicInsertCrossRegionRSet(slot.SlotAddress());
64 }
65 }
66 }
67 }
68
MarkObject(uint32_t threadId,TaggedObject * object)69 inline void NonMovableMarker::MarkObject(uint32_t threadId, TaggedObject *object)
70 {
71 Region *objectRegion = Region::ObjectAddressToRange(object);
72
73 if (!heap_->IsFullMark() && !objectRegion->InYoungSpace()) {
74 return;
75 }
76
77 if (objectRegion->AtomicMark(object)) {
78 workManager_->Push(threadId, object, objectRegion);
79 }
80 }
81
HandleRoots(uint32_t threadId,Root type,ObjectSlot slot)82 inline void NonMovableMarker::HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot)
83 {
84 JSTaggedValue value(slot.GetTaggedType());
85 if (value.IsHeapObject()) {
86 MarkObject(threadId, value.GetTaggedObject());
87 }
88 }
89
HandleRangeRoots(uint32_t threadId,Root type,ObjectSlot start,ObjectSlot end)90 inline void NonMovableMarker::HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start,
91 ObjectSlot end)
92 {
93 for (ObjectSlot slot = start; slot < end; slot++) {
94 JSTaggedValue value(slot.GetTaggedType());
95 if (value.IsHeapObject()) {
96 if (value.IsWeakForHeapObject()) {
97 LOG_ECMA_MEM(FATAL) << "Weak Reference in NonMovableMarker roots";
98 }
99 MarkObject(threadId, value.GetTaggedObject());
100 }
101 }
102 }
103
HandleDerivedRoots(Root type,ObjectSlot base,ObjectSlot derived,uintptr_t baseOldObject)104 inline void NonMovableMarker::HandleDerivedRoots([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot base,
105 [[maybe_unused]] ObjectSlot derived,
106 [[maybe_unused]] uintptr_t baseOldObject)
107 {
108 // It is only used to update the derived value. The mark of partial GC does not need to update slot
109 }
110
HandleOldToNewRSet(uint32_t threadId,Region * region)111 inline void NonMovableMarker::HandleOldToNewRSet(uint32_t threadId, Region *region)
112 {
113 region->IterateAllOldToNewBits([this, threadId, ®ion](void *mem) -> bool {
114 ObjectSlot slot(ToUintPtr(mem));
115 JSTaggedValue value(slot.GetTaggedType());
116 if (value.IsHeapObject()) {
117 if (value.IsWeakForHeapObject()) {
118 RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(mem), region);
119 } else {
120 MarkObject(threadId, value.GetTaggedObject());
121 }
122 }
123 return true;
124 });
125 }
126
RecordWeakReference(uint32_t threadId,JSTaggedType * ref,Region * objectRegion)127 inline void NonMovableMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *ref, Region *objectRegion)
128 {
129 auto value = JSTaggedValue(*ref);
130 Region *valueRegion = Region::ObjectAddressToRange(value.GetTaggedWeakRef());
131 if (!objectRegion->InYoungSpaceOrCSet() && !valueRegion->InYoungSpaceOrCSet()) {
132 workManager_->PushWeakReference(threadId, ref);
133 }
134 }
135
136 template <typename Callback>
VisitBodyInObj(TaggedObject * root,ObjectSlot start,ObjectSlot end,Callback callback)137 inline bool MovableMarker::VisitBodyInObj(TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback)
138 {
139 auto hclass = root->GetClass();
140 if (hclass->IsAllTaggedProp()) {
141 return false;
142 }
143 int index = 0;
144 for (ObjectSlot slot = start; slot < end; slot++) {
145 TaggedObject *dst = hclass->GetLayout().GetTaggedObject();
146 MarkWord markWord(dst);
147 if (markWord.IsForwardingAddress()) {
148 dst = markWord.ToForwardingAddress();
149 }
150 auto layout = LayoutInfo::Cast(dst);
151 auto attr = layout->GetAttr(index++);
152 if (attr.IsTaggedRep()) {
153 callback(slot);
154 }
155 }
156 return true;
157 }
158
HandleRoots(uint32_t threadId,Root type,ObjectSlot slot)159 inline void MovableMarker::HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot)
160 {
161 JSTaggedValue value(slot.GetTaggedType());
162 if (value.IsHeapObject()) {
163 MarkObject(threadId, value.GetTaggedObject(), slot);
164 }
165 }
166
HandleRangeRoots(uint32_t threadId,Root type,ObjectSlot start,ObjectSlot end)167 inline void MovableMarker::HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start,
168 ObjectSlot end)
169 {
170 for (ObjectSlot slot = start; slot < end; slot++) {
171 JSTaggedValue value(slot.GetTaggedType());
172 if (value.IsHeapObject()) {
173 if (value.IsWeakForHeapObject()) {
174 Region *objectRegion = Region::ObjectAddressToRange(start.SlotAddress());
175 RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(slot.SlotAddress()), objectRegion);
176 } else {
177 MarkObject(threadId, value.GetTaggedObject(), slot);
178 }
179 }
180 }
181 }
182
HandleDerivedRoots(Root type,ObjectSlot base,ObjectSlot derived,uintptr_t baseOldObject)183 inline void MovableMarker::HandleDerivedRoots([[maybe_unused]] Root type, ObjectSlot base,
184 ObjectSlot derived, uintptr_t baseOldObject)
185 {
186 if (JSTaggedValue(base.GetTaggedType()).IsHeapObject()) {
187 derived.Update(base.GetTaggedType() + derived.GetTaggedType() - baseOldObject);
188 }
189 }
190
HandleOldToNewRSet(uint32_t threadId,Region * region)191 inline void MovableMarker::HandleOldToNewRSet(uint32_t threadId, Region *region)
192 {
193 region->IterateAllOldToNewBits([this, threadId, ®ion](void *mem) -> bool {
194 ObjectSlot slot(ToUintPtr(mem));
195 JSTaggedValue value(slot.GetTaggedType());
196 if (value.IsHeapObject()) {
197 if (value.IsWeakForHeapObject()) {
198 RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(mem), region);
199 return true;
200 }
201 auto slotStatus = MarkObject(threadId, value.GetTaggedObject(), slot);
202 if (slotStatus == SlotStatus::CLEAR_SLOT) {
203 return false;
204 }
205 }
206 return true;
207 });
208 }
209
AllocateDstSpace(uint32_t threadId,size_t size,bool & shouldPromote)210 inline uintptr_t MovableMarker::AllocateDstSpace(uint32_t threadId, size_t size, bool &shouldPromote)
211 {
212 uintptr_t forwardAddress = 0;
213 if (shouldPromote) {
214 forwardAddress = workManager_->GetTlabAllocator(threadId)->Allocate(size, COMPRESS_SPACE);
215 if (UNLIKELY(forwardAddress == 0)) {
216 LOG_ECMA_MEM(FATAL) << "EvacuateObject alloc failed: "
217 << " size: " << size;
218 UNREACHABLE();
219 }
220 } else {
221 forwardAddress = workManager_->GetTlabAllocator(threadId)->Allocate(size, SEMI_SPACE);
222 if (UNLIKELY(forwardAddress == 0)) {
223 forwardAddress = workManager_->GetTlabAllocator(threadId)->Allocate(size, COMPRESS_SPACE);
224 if (UNLIKELY(forwardAddress == 0)) {
225 LOG_ECMA_MEM(FATAL) << "EvacuateObject alloc failed: "
226 << " size: " << size;
227 UNREACHABLE();
228 }
229 shouldPromote = true;
230 }
231 }
232 return forwardAddress;
233 }
234
UpdateForwardAddressIfSuccess(uint32_t threadId,TaggedObject * object,JSHClass * klass,uintptr_t toAddress,size_t size,const MarkWord & markWord,ObjectSlot slot,bool isPromoted)235 inline void MovableMarker::UpdateForwardAddressIfSuccess(uint32_t threadId, TaggedObject *object, JSHClass *klass,
236 uintptr_t toAddress, size_t size, const MarkWord &markWord, ObjectSlot slot, bool isPromoted)
237 {
238 if (memcpy_s(ToVoidPtr(toAddress + HEAD_SIZE), size - HEAD_SIZE, ToVoidPtr(ToUintPtr(object) + HEAD_SIZE),
239 size - HEAD_SIZE) != EOK) {
240 LOG_FULL(FATAL) << "memcpy_s failed";
241 }
242 workManager_->IncreaseAliveSize(threadId, size);
243 if (isPromoted) {
244 workManager_->IncreasePromotedSize(threadId, size);
245 }
246
247 *reinterpret_cast<MarkWordType *>(toAddress) = markWord.GetValue();
248 heap_->OnMoveEvent(reinterpret_cast<intptr_t>(object), reinterpret_cast<TaggedObject *>(toAddress), size);
249 if (klass->HasReferenceField()) {
250 workManager_->Push(threadId, reinterpret_cast<TaggedObject *>(toAddress));
251 }
252 slot.Update(reinterpret_cast<TaggedObject *>(toAddress));
253 }
254
UpdateForwardAddressIfFailed(TaggedObject * object,uintptr_t toAddress,size_t size,ObjectSlot slot)255 inline bool MovableMarker::UpdateForwardAddressIfFailed(TaggedObject *object, uintptr_t toAddress, size_t size,
256 ObjectSlot slot)
257 {
258 FreeObject::FillFreeObject(heap_->GetEcmaVM(), toAddress, size);
259 TaggedObject *dst = MarkWord(object).ToForwardingAddress();
260 slot.Update(dst);
261 return Region::ObjectAddressToRange(dst)->InYoungSpace();
262 }
263
MarkValue(uint32_t threadId,TaggedObject * root,ObjectSlot slot)264 inline void SemiGCMarker::MarkValue(uint32_t threadId, TaggedObject *root, ObjectSlot slot)
265 {
266 JSTaggedValue value(slot.GetTaggedType());
267 if (value.IsHeapObject()) {
268 Region *rootRegion = Region::ObjectAddressToRange(root);
269 if (value.IsWeakForHeapObject()) {
270 RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(slot.SlotAddress()), rootRegion);
271 return;
272 }
273 auto slotStatus = MarkObject(threadId, value.GetTaggedObject(), slot);
274 if (!rootRegion->InYoungSpace() && slotStatus == SlotStatus::KEEP_SLOT) {
275 SlotNeedUpdate waitUpdate(reinterpret_cast<TaggedObject *>(root), slot);
276 workManager_->PushSlotNeedUpdate(threadId, waitUpdate);
277 }
278 }
279 }
280
MarkObject(uint32_t threadId,TaggedObject * object,ObjectSlot slot)281 inline SlotStatus SemiGCMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot)
282 {
283 Region *objectRegion = Region::ObjectAddressToRange(object);
284 if (!objectRegion->InYoungSpace()) {
285 return SlotStatus::CLEAR_SLOT;
286 }
287
288 MarkWord markWord(object);
289 if (markWord.IsForwardingAddress()) {
290 TaggedObject *dst = markWord.ToForwardingAddress();
291 slot.Update(dst);
292 Region *valueRegion = Region::ObjectAddressToRange(dst);
293 return valueRegion->InYoungSpace() ? SlotStatus::KEEP_SLOT : SlotStatus::CLEAR_SLOT;
294 }
295 return EvacuateObject(threadId, object, markWord, slot);
296 }
297
EvacuateObject(uint32_t threadId,TaggedObject * object,const MarkWord & markWord,ObjectSlot slot)298 inline SlotStatus SemiGCMarker::EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord,
299 ObjectSlot slot)
300 {
301 JSHClass *klass = markWord.GetJSHClass();
302 size_t size = klass->SizeFromJSHClass(object);
303 bool isPromoted = ShouldBePromoted(object);
304
305 uintptr_t forwardAddress = AllocateDstSpace(threadId, size, isPromoted);
306 bool result = Barriers::AtomicSetPrimitive(object, 0, markWord.GetValue(),
307 MarkWord::FromForwardingAddress(forwardAddress));
308 if (result) {
309 UpdateForwardAddressIfSuccess(threadId, object, klass, forwardAddress, size, markWord, slot, isPromoted);
310 return isPromoted ? SlotStatus::CLEAR_SLOT : SlotStatus::KEEP_SLOT;
311 }
312 bool keepSlot = UpdateForwardAddressIfFailed(object, forwardAddress, size, slot);
313 return keepSlot ? SlotStatus::KEEP_SLOT : SlotStatus::CLEAR_SLOT;
314 }
315
ShouldBePromoted(TaggedObject * object)316 inline bool SemiGCMarker::ShouldBePromoted(TaggedObject *object)
317 {
318 Region *region = Region::ObjectAddressToRange(object);
319 return (region->BelowAgeMark() || (region->HasAgeMark() && ToUintPtr(object) < waterLine_));
320 }
321
RecordWeakReference(uint32_t threadId,JSTaggedType * ref,Region * objectRegion)322 inline void SemiGCMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *ref,
323 [[maybe_unused]] Region *objectRegion)
324 {
325 auto value = JSTaggedValue(*ref);
326 Region *valueRegion = Region::ObjectAddressToRange(value.GetTaggedWeakRef());
327 if (valueRegion->InYoungSpace()) {
328 workManager_->PushWeakReference(threadId, ref);
329 }
330 }
331
MarkValue(uint32_t threadId,ObjectSlot slot)332 inline void CompressGCMarker::MarkValue(uint32_t threadId, ObjectSlot slot)
333 {
334 JSTaggedValue value(slot.GetTaggedType());
335 if (value.IsHeapObject()) {
336 if (value.IsWeakForHeapObject()) {
337 // It is unnecessary to use region pointer in compressGCMarker.
338 RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(slot.SlotAddress()));
339 return;
340 }
341 MarkObject(threadId, value.GetTaggedObject(), slot);
342 }
343 }
344
MarkObject(uint32_t threadId,TaggedObject * object,ObjectSlot slot)345 inline SlotStatus CompressGCMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot)
346 {
347 Region *objectRegion = Region::ObjectAddressToRange(object);
348 if (!NeedEvacuate(objectRegion)) {
349 if (objectRegion->AtomicMark(object)) {
350 workManager_->Push(threadId, object);
351 }
352 return SlotStatus::CLEAR_SLOT;
353 }
354
355 MarkWord markWord(object);
356 if (markWord.IsForwardingAddress()) {
357 TaggedObject *dst = markWord.ToForwardingAddress();
358 slot.Update(dst);
359 return SlotStatus::CLEAR_SLOT;
360 }
361 return EvacuateObject(threadId, object, markWord, slot);
362 }
363
AllocateReadOnlySpace(size_t size)364 inline uintptr_t CompressGCMarker::AllocateReadOnlySpace(size_t size)
365 {
366 os::memory::LockHolder lock(mutex_);
367 uintptr_t forwardAddress = heap_->GetReadOnlySpace()->Allocate(size);
368 if (UNLIKELY(forwardAddress == 0)) {
369 LOG_ECMA_MEM(FATAL) << "Evacuate Read only Object: alloc failed: "
370 << " size: " << size;
371 UNREACHABLE();
372 }
373 return forwardAddress;
374 }
375
AllocateAppSpawnSpace(size_t size)376 inline uintptr_t CompressGCMarker::AllocateAppSpawnSpace(size_t size)
377 {
378 os::memory::LockHolder lock(mutex_);
379 uintptr_t forwardAddress = heap_->GetAppSpawnSpace()->Allocate(size);
380 if (UNLIKELY(forwardAddress == 0)) {
381 LOG_ECMA_MEM(FATAL) << "Evacuate AppSpawn Object: alloc failed: "
382 << " size: " << size;
383 UNREACHABLE();
384 }
385 return forwardAddress;
386 }
387
EvacuateObject(uint32_t threadId,TaggedObject * object,const MarkWord & markWord,ObjectSlot slot)388 inline SlotStatus CompressGCMarker::EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord,
389 ObjectSlot slot)
390 {
391 JSHClass *klass = markWord.GetJSHClass();
392 size_t size = klass->SizeFromJSHClass(object);
393 uintptr_t forwardAddress = AllocateForwardAddress(threadId, size, klass, object);
394 bool result = Barriers::AtomicSetPrimitive(object, 0, markWord.GetValue(),
395 MarkWord::FromForwardingAddress(forwardAddress));
396 if (result) {
397 UpdateForwardAddressIfSuccess(threadId, object, klass, forwardAddress, size, markWord, slot);
398 if (isAppSpawn_ && klass->IsString()) {
399 // calculate and set hashcode for read-only ecmastring in advance
400 EcmaStringAccessor(reinterpret_cast<TaggedObject *>(forwardAddress)).GetHashcode();
401 }
402 return SlotStatus::CLEAR_SLOT;
403 }
404 UpdateForwardAddressIfFailed(object, forwardAddress, size, slot);
405 return SlotStatus::CLEAR_SLOT;
406 }
407
RecordWeakReference(uint32_t threadId,JSTaggedType * ref,Region * objectRegion)408 inline void CompressGCMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *ref,
409 [[maybe_unused]] Region *objectRegion)
410 {
411 workManager_->PushWeakReference(threadId, ref);
412 }
413
NeedEvacuate(Region * region)414 inline bool CompressGCMarker::NeedEvacuate(Region *region)
415 {
416 if (isAppSpawn_) {
417 return !region->InHugeObjectSpace() && !region->InReadOnlySpace() && !region->InNonMovableSpace();
418 }
419 return region->InYoungOrOldSpace();
420 }
421 } // namespace panda::ecmascript
422 #endif // ECMASCRIPT_MEM_PARALLEL_MARKER_INL_H
423