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
MarkObject(uint32_t threadId,TaggedObject * object)30 inline void NonMovableMarker::MarkObject(uint32_t threadId, TaggedObject *object)
31 {
32 Region *objectRegion = Region::ObjectAddressToRange(object);
33
34 if (!heap_->IsFullMark() && !objectRegion->InYoungSpace()) {
35 return;
36 }
37
38 if (objectRegion->AtomicMark(object)) {
39 workManager_->Push(threadId, object, objectRegion);
40 }
41 }
42
HandleRoots(uint32_t threadId,Root type,ObjectSlot slot)43 inline void NonMovableMarker::HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot)
44 {
45 JSTaggedValue value(slot.GetTaggedType());
46 if (value.IsHeapObject()) {
47 MarkObject(threadId, value.GetTaggedObject());
48 }
49 }
50
HandleRangeRoots(uint32_t threadId,Root type,ObjectSlot start,ObjectSlot end)51 inline void NonMovableMarker::HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start,
52 ObjectSlot end)
53 {
54 for (ObjectSlot slot = start; slot < end; slot++) {
55 JSTaggedValue value(slot.GetTaggedType());
56 if (value.IsHeapObject()) {
57 if (value.IsWeakForHeapObject()) {
58 LOG_ECMA_MEM(FATAL) << "Weak Reference in NonMovableMarker roots";
59 }
60 MarkObject(threadId, value.GetTaggedObject());
61 }
62 }
63 }
64
HandleDerivedRoots(Root type,ObjectSlot base,ObjectSlot derived,uintptr_t baseOldObject)65 inline void NonMovableMarker::HandleDerivedRoots([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot base,
66 [[maybe_unused]] ObjectSlot derived,
67 [[maybe_unused]] uintptr_t baseOldObject)
68 {
69 // It is only used to update the derived value. The mark of partial GC does not need to update slot
70 }
71
HandleOldToNewRSet(uint32_t threadId,Region * region)72 inline void NonMovableMarker::HandleOldToNewRSet(uint32_t threadId, Region *region)
73 {
74 region->IterateAllOldToNewBits([this, threadId, ®ion](void *mem) -> bool {
75 ObjectSlot slot(ToUintPtr(mem));
76 JSTaggedValue value(slot.GetTaggedType());
77 if (value.IsHeapObject()) {
78 if (value.IsInvalidValue()) {
79 LOG_ECMA_MEM(INFO) << "HandleOldToNew found an invalid value: " << value.GetRawData()
80 << " " << slot.GetTaggedType();
81 return true;
82 }
83 if (value.IsWeakForHeapObject()) {
84 RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(mem), region);
85 } else {
86 MarkObject(threadId, value.GetTaggedObject());
87 }
88 if (value.GetRawData() != slot.GetTaggedType()) {
89 LOG_ECMA_MEM(INFO) << "HandleOldToNew mark an overdue value : " << value.GetRawData() << " "
90 << slot.GetTaggedType() << " "
91 << *reinterpret_cast<JSTaggedType*>(value.GetRawData());
92 }
93 }
94 return true;
95 });
96 }
97
RecordWeakReference(uint32_t threadId,JSTaggedType * ref,Region * objectRegion)98 inline void NonMovableMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *ref, Region *objectRegion)
99 {
100 auto value = JSTaggedValue(*ref);
101 Region *valueRegion = Region::ObjectAddressToRange(value.GetTaggedWeakRef());
102 if (!objectRegion->InYoungSpaceOrCSet() && !valueRegion->InYoungSpaceOrCSet()) {
103 workManager_->PushWeakReference(threadId, ref);
104 }
105 }
106
HandleRoots(uint32_t threadId,Root type,ObjectSlot slot)107 inline void MovableMarker::HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot)
108 {
109 JSTaggedValue value(slot.GetTaggedType());
110 if (value.IsHeapObject()) {
111 MarkObject(threadId, value.GetTaggedObject(), slot);
112 }
113 }
114
HandleRangeRoots(uint32_t threadId,Root type,ObjectSlot start,ObjectSlot end)115 inline void MovableMarker::HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start,
116 ObjectSlot end)
117 {
118 for (ObjectSlot slot = start; slot < end; slot++) {
119 JSTaggedValue value(slot.GetTaggedType());
120 if (value.IsHeapObject()) {
121 if (value.IsWeakForHeapObject()) {
122 Region *objectRegion = Region::ObjectAddressToRange(start.SlotAddress());
123 RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(slot.SlotAddress()), objectRegion);
124 } else {
125 MarkObject(threadId, value.GetTaggedObject(), slot);
126 }
127 }
128 }
129 }
130
HandleDerivedRoots(Root type,ObjectSlot base,ObjectSlot derived,uintptr_t baseOldObject)131 inline void MovableMarker::HandleDerivedRoots([[maybe_unused]] Root type, ObjectSlot base,
132 ObjectSlot derived, uintptr_t baseOldObject)
133 {
134 if (JSTaggedValue(base.GetTaggedType()).IsHeapObject()) {
135 derived.Update(base.GetTaggedType() + derived.GetTaggedType() - baseOldObject);
136 }
137 }
138
HandleOldToNewRSet(uint32_t threadId,Region * region)139 inline void MovableMarker::HandleOldToNewRSet(uint32_t threadId, Region *region)
140 {
141 region->IterateAllOldToNewBits([this, threadId, ®ion](void *mem) -> bool {
142 ObjectSlot slot(ToUintPtr(mem));
143 JSTaggedValue value(slot.GetTaggedType());
144 if (value.IsHeapObject()) {
145 if (value.IsWeakForHeapObject()) {
146 RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(mem), region);
147 return true;
148 }
149 auto slotStatus = MarkObject(threadId, value.GetTaggedObject(), slot);
150 if (slotStatus == SlotStatus::CLEAR_SLOT) {
151 return false;
152 }
153 }
154 return true;
155 });
156 }
157
AllocateDstSpace(uint32_t threadId,size_t size,bool & shouldPromote)158 inline uintptr_t MovableMarker::AllocateDstSpace(uint32_t threadId, size_t size, bool &shouldPromote)
159 {
160 uintptr_t forwardAddress = 0;
161 if (shouldPromote) {
162 forwardAddress = workManager_->GetTlabAllocator(threadId)->Allocate(size, COMPRESS_SPACE);
163 if (UNLIKELY(forwardAddress == 0)) {
164 LOG_ECMA_MEM(FATAL) << "EvacuateObject alloc failed: "
165 << " size: " << size;
166 UNREACHABLE();
167 }
168 } else {
169 forwardAddress = workManager_->GetTlabAllocator(threadId)->Allocate(size, SEMI_SPACE);
170 if (UNLIKELY(forwardAddress == 0)) {
171 forwardAddress = workManager_->GetTlabAllocator(threadId)->Allocate(size, COMPRESS_SPACE);
172 if (UNLIKELY(forwardAddress == 0)) {
173 LOG_ECMA_MEM(FATAL) << "EvacuateObject alloc failed: "
174 << " size: " << size;
175 UNREACHABLE();
176 }
177 shouldPromote = true;
178 }
179 }
180 return forwardAddress;
181 }
182
UpdateForwardAddressIfSuccess(uint32_t threadId,TaggedObject * object,JSHClass * klass,uintptr_t toAddress,size_t size,const MarkWord & markWord,ObjectSlot slot,bool isPromoted)183 inline void MovableMarker::UpdateForwardAddressIfSuccess(uint32_t threadId, TaggedObject *object, JSHClass *klass,
184 uintptr_t toAddress, size_t size, const MarkWord &markWord, ObjectSlot slot, bool isPromoted)
185 {
186 if (memcpy_s(ToVoidPtr(toAddress + HEAD_SIZE), size - HEAD_SIZE, ToVoidPtr(ToUintPtr(object) + HEAD_SIZE),
187 size - HEAD_SIZE) != EOK) {
188 LOG_FULL(FATAL) << "memcpy_s failed";
189 }
190 workManager_->IncreaseAliveSize(threadId, size);
191 if (isPromoted) {
192 workManager_->IncreasePromotedSize(threadId, size);
193 }
194
195 *reinterpret_cast<MarkWordType *>(toAddress) = markWord.GetValue();
196 heap_->OnMoveEvent(reinterpret_cast<intptr_t>(object), reinterpret_cast<TaggedObject *>(toAddress), size);
197 if (klass->HasReferenceField()) {
198 workManager_->Push(threadId, reinterpret_cast<TaggedObject *>(toAddress));
199 }
200 slot.Update(reinterpret_cast<TaggedObject *>(toAddress));
201 }
202
UpdateForwardAddressIfFailed(TaggedObject * object,uintptr_t toAddress,size_t size,ObjectSlot slot)203 inline bool MovableMarker::UpdateForwardAddressIfFailed(TaggedObject *object, uintptr_t toAddress, size_t size,
204 ObjectSlot slot)
205 {
206 FreeObject::FillFreeObject(heap_->GetEcmaVM(), toAddress, size);
207 TaggedObject *dst = MarkWord(object).ToForwardingAddress();
208 slot.Update(dst);
209 return Region::ObjectAddressToRange(dst)->InYoungSpace();
210 }
211
MarkObject(uint32_t threadId,TaggedObject * object,ObjectSlot slot)212 inline SlotStatus SemiGCMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot)
213 {
214 Region *objectRegion = Region::ObjectAddressToRange(object);
215 if (!objectRegion->InYoungSpace()) {
216 return SlotStatus::CLEAR_SLOT;
217 }
218
219 MarkWord markWord(object);
220 if (markWord.IsForwardingAddress()) {
221 TaggedObject *dst = markWord.ToForwardingAddress();
222 slot.Update(dst);
223 Region *valueRegion = Region::ObjectAddressToRange(dst);
224 return valueRegion->InYoungSpace() ? SlotStatus::KEEP_SLOT : SlotStatus::CLEAR_SLOT;
225 }
226 return EvacuateObject(threadId, object, markWord, slot);
227 }
228
EvacuateObject(uint32_t threadId,TaggedObject * object,const MarkWord & markWord,ObjectSlot slot)229 inline SlotStatus SemiGCMarker::EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord,
230 ObjectSlot slot)
231 {
232 JSHClass *klass = markWord.GetJSHClass();
233 size_t size = klass->SizeFromJSHClass(object);
234 bool isPromoted = ShouldBePromoted(object);
235
236 uintptr_t forwardAddress = AllocateDstSpace(threadId, size, isPromoted);
237 bool result = Barriers::AtomicSetPrimitive(object, 0, markWord.GetValue(),
238 MarkWord::FromForwardingAddress(forwardAddress));
239 if (result) {
240 UpdateForwardAddressIfSuccess(threadId, object, klass, forwardAddress, size, markWord, slot, isPromoted);
241 return isPromoted ? SlotStatus::CLEAR_SLOT : SlotStatus::KEEP_SLOT;
242 }
243 bool keepSlot = UpdateForwardAddressIfFailed(object, forwardAddress, size, slot);
244 return keepSlot ? SlotStatus::KEEP_SLOT : SlotStatus::CLEAR_SLOT;
245 }
246
ShouldBePromoted(TaggedObject * object)247 inline bool SemiGCMarker::ShouldBePromoted(TaggedObject *object)
248 {
249 Region *region = Region::ObjectAddressToRange(object);
250 return (region->BelowAgeMark() || (region->HasAgeMark() && ToUintPtr(object) < waterLine_));
251 }
252
RecordWeakReference(uint32_t threadId,JSTaggedType * ref,Region * objectRegion)253 inline void SemiGCMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *ref,
254 [[maybe_unused]] Region *objectRegion)
255 {
256 auto value = JSTaggedValue(*ref);
257 Region *valueRegion = Region::ObjectAddressToRange(value.GetTaggedWeakRef());
258 if (valueRegion->InYoungSpace()) {
259 workManager_->PushWeakReference(threadId, ref);
260 }
261 }
262
MarkObject(uint32_t threadId,TaggedObject * object,ObjectSlot slot)263 inline SlotStatus CompressGCMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot)
264 {
265 Region *objectRegion = Region::ObjectAddressToRange(object);
266 if (!NeedEvacuate(objectRegion)) {
267 if (objectRegion->AtomicMark(object)) {
268 workManager_->Push(threadId, object);
269 }
270 return SlotStatus::CLEAR_SLOT;
271 }
272
273 MarkWord markWord(object);
274 if (markWord.IsForwardingAddress()) {
275 TaggedObject *dst = markWord.ToForwardingAddress();
276 slot.Update(dst);
277 return SlotStatus::CLEAR_SLOT;
278 }
279 return EvacuateObject(threadId, object, markWord, slot);
280 }
281
AllocateReadOnlySpace(size_t size)282 inline uintptr_t CompressGCMarker::AllocateReadOnlySpace(size_t size)
283 {
284 os::memory::LockHolder lock(mutex_);
285 uintptr_t forwardAddress = heap_->GetReadOnlySpace()->Allocate(size);
286 if (UNLIKELY(forwardAddress == 0)) {
287 LOG_ECMA_MEM(FATAL) << "Evacuate Read only Object: alloc failed: "
288 << " size: " << size;
289 UNREACHABLE();
290 }
291 return forwardAddress;
292 }
293
AllocateAppSpawnSpace(size_t size)294 inline uintptr_t CompressGCMarker::AllocateAppSpawnSpace(size_t size)
295 {
296 os::memory::LockHolder lock(mutex_);
297 uintptr_t forwardAddress = heap_->GetAppSpawnSpace()->Allocate(size);
298 if (UNLIKELY(forwardAddress == 0)) {
299 LOG_ECMA_MEM(FATAL) << "Evacuate AppSpawn Object: alloc failed: "
300 << " size: " << size;
301 UNREACHABLE();
302 }
303 return forwardAddress;
304 }
305
EvacuateObject(uint32_t threadId,TaggedObject * object,const MarkWord & markWord,ObjectSlot slot)306 inline SlotStatus CompressGCMarker::EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord,
307 ObjectSlot slot)
308 {
309 JSHClass *klass = markWord.GetJSHClass();
310 size_t size = klass->SizeFromJSHClass(object);
311 uintptr_t forwardAddress = AllocateForwardAddress(threadId, size, klass, object);
312 bool result = Barriers::AtomicSetPrimitive(object, 0, markWord.GetValue(),
313 MarkWord::FromForwardingAddress(forwardAddress));
314 if (result) {
315 UpdateForwardAddressIfSuccess(threadId, object, klass, forwardAddress, size, markWord, slot);
316 if (isAppSpawn_ && klass->IsString()) {
317 // calculate and set hashcode for read-only ecmastring in advance
318 EcmaStringAccessor(reinterpret_cast<TaggedObject *>(forwardAddress)).GetHashcode();
319 }
320 return SlotStatus::CLEAR_SLOT;
321 }
322 UpdateForwardAddressIfFailed(object, forwardAddress, size, slot);
323 return SlotStatus::CLEAR_SLOT;
324 }
325
RecordWeakReference(uint32_t threadId,JSTaggedType * ref,Region * objectRegion)326 inline void CompressGCMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *ref,
327 [[maybe_unused]] Region *objectRegion)
328 {
329 workManager_->PushWeakReference(threadId, ref);
330 }
331
NeedEvacuate(Region * region)332 inline bool CompressGCMarker::NeedEvacuate(Region *region)
333 {
334 if (isAppSpawn_) {
335 return !region->InHugeObjectSpace() && !region->InReadOnlySpace() && !region->InNonMovableSpace();
336 }
337 return region->InYoungOrOldSpace();
338 }
339 } // namespace panda::ecmascript
340 #endif // ECMASCRIPT_MEM_PARALLEL_MARKER_INL_H
341