1 /*
2  * Copyright (c) 2024 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_SHARED_HEAP_SHARED_GC_MARKER_INL_H
17 #define ECMASCRIPT_MEM_SHARED_HEAP_SHARED_GC_MARKER_INL_H
18 
19 #include "ecmascript/mem/shared_heap/shared_gc_marker.h"
20 
21 #include "ecmascript/js_hclass-inl.h"
22 #include "ecmascript/mem/heap-inl.h"
23 #include "ecmascript/mem/region-inl.h"
24 #include "ecmascript/mem/tlab_allocator-inl.h"
25 
26 namespace panda::ecmascript {
MarkObject(uint32_t threadId, TaggedObject *object, [[maybe_unused]] ObjectSlot &slot)27 inline void SharedGCMarker::MarkObject(uint32_t threadId, TaggedObject *object, [[maybe_unused]] ObjectSlot &slot)
28 {
29     Region *objectRegion = Region::ObjectAddressToRange(object);
30     ASSERT(objectRegion->InSharedHeap());
31     if (!objectRegion->InSharedReadOnlySpace() && objectRegion->AtomicMark(object)) {
32         ASSERT(objectRegion->InSharedSweepableSpace());
33         sWorkManager_->Push(threadId, object);
34     }
35 }
36 
MarkObjectFromJSThread(WorkNode *&localBuffer, TaggedObject *object)37 inline void SharedGCMarkerBase::MarkObjectFromJSThread(WorkNode *&localBuffer, TaggedObject *object)
38 {
39     Region *objectRegion = Region::ObjectAddressToRange(object);
40     ASSERT(objectRegion->InSharedHeap());
41     if (!objectRegion->InSharedReadOnlySpace() && objectRegion->AtomicMark(object)) {
42         sWorkManager_->PushToLocalMarkingBuffer(localBuffer, object);
43     }
44 }
45 
MarkValue(uint32_t threadId, ObjectSlot &slot)46 inline void SharedGCMarker::MarkValue(uint32_t threadId, ObjectSlot &slot)
47 {
48     JSTaggedValue value(slot.GetTaggedType());
49     if (value.IsInSharedSweepableSpace()) {
50         if (!value.IsWeakForHeapObject()) {
51             MarkObject(threadId, value.GetTaggedObject(), slot);
52         } else {
53             RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(slot.SlotAddress()));
54         }
55     }
56 }
57 
HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot)58 inline void SharedGCMarkerBase::HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot)
59 {
60     JSTaggedValue value(slot.GetTaggedType());
61     if (value.IsInSharedSweepableSpace()) {
62         MarkObject(threadId, value.GetTaggedObject(), slot);
63     }
64 }
65 
HandleLocalRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot)66 inline void SharedGCMarkerBase::HandleLocalRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot)
67 {
68     JSTaggedValue value(slot.GetTaggedType());
69     if (value.IsInSharedSweepableSpace()) {
70         MarkObject(threadId, value.GetTaggedObject(), slot);
71     }
72 }
73 
HandleLocalRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end)74 inline void SharedGCMarkerBase::HandleLocalRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start,
75     ObjectSlot end)
76 {
77     for (ObjectSlot slot = start; slot < end; slot++) {
78         JSTaggedValue value(slot.GetTaggedType());
79         if (value.IsInSharedSweepableSpace()) {
80             if (value.IsWeakForHeapObject()) {
81                 LOG_ECMA_MEM(FATAL) << "Weak Reference in SharedGCMarker roots";
82             }
83             MarkObject(threadId, value.GetTaggedObject(), slot);
84         }
85     }
86 }
87 
HandleLocalDerivedRoots([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot base, [[maybe_unused]] ObjectSlot derived, [[maybe_unused]] uintptr_t baseOldObject)88 void SharedGCMarker::HandleLocalDerivedRoots([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot base,
89                                              [[maybe_unused]] ObjectSlot derived,
90                                              [[maybe_unused]] uintptr_t baseOldObject)
91 {
92     // It is only used to update the derived value. The mark of share GC does not need to update slot
93 }
94 
HandleLocalDerivedRoots([[maybe_unused]] Root type, ObjectSlot base, ObjectSlot derived, uintptr_t baseOldObject)95 void SharedGCMovableMarker::HandleLocalDerivedRoots([[maybe_unused]] Root type, ObjectSlot base,
96                                                     ObjectSlot derived, uintptr_t baseOldObject)
97 {
98     if (JSTaggedValue(base.GetTaggedType()).IsHeapObject()) {
99         derived.Update(base.GetTaggedType() + derived.GetTaggedType() - baseOldObject);
100     }
101 }
102 
103 template <typename Callback>
VisitBodyInObj(TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback)104 ARK_INLINE bool SharedGCMarkerBase::VisitBodyInObj(TaggedObject *root, ObjectSlot start, ObjectSlot end,
105                                                    Callback callback)
106 {
107     auto hclass = root->SynchronizedGetClass();
108     int index = 0;
109     auto layout = LayoutInfo::UncheckCast(hclass->GetLayout().GetTaggedObject());
110     ObjectSlot realEnd = start;
111     realEnd += layout->GetPropertiesCapacity();
112     end = end > realEnd ? realEnd : end;
113     for (ObjectSlot slot = start; slot < end; slot++) {
114         auto attr = layout->GetAttr(index++);
115         if (attr.IsTaggedRep()) {
116             callback(slot);
117         }
118     }
119     return true;
120 }
121 
RecordWeakReference(uint32_t threadId, JSTaggedType *slot)122 inline void SharedGCMarkerBase::RecordWeakReference(uint32_t threadId, JSTaggedType *slot)
123 {
124     sWorkManager_->PushWeakReference(threadId, slot);
125 }
126 
RecordObject(JSTaggedValue value, uint32_t threadId, void *mem)127 inline void SharedGCMarkerBase::RecordObject(JSTaggedValue value, uint32_t threadId, void *mem)
128 {
129     if (value.IsWeakForHeapObject()) {
130         RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(mem));
131     } else {
132         ObjectSlot slot(ToUintPtr(mem));
133         MarkObject(threadId, value.GetTaggedObject(), slot);
134     }
135 }
136 
137 template<SharedMarkType markType>
GetVisitor(JSTaggedValue value, uint32_t threadId, void *mem)138 inline bool SharedGCMarkerBase::GetVisitor(JSTaggedValue value, uint32_t threadId, void *mem)
139 {
140     if (value.IsInSharedSweepableSpace()) {
141         if constexpr (markType == SharedMarkType::CONCURRENT_MARK_INITIAL_MARK) {
142             // For now if record weak references from local to share in marking root, the slots
143             // may be invalid due to LocalGC, so just mark them as strong-reference.
144             ObjectSlot slot(ToUintPtr(mem));
145             MarkObject(threadId, value.GetHeapObject(), slot);
146         } else {
147             static_assert(markType == SharedMarkType::NOT_CONCURRENT_MARK);
148             RecordObject(value, threadId, mem);
149         }
150         return true;
151     }
152     return false;
153 }
154 
155 template<SharedMarkType markType>
GenerateRSetVisitor(uint32_t threadId)156 inline auto SharedGCMarkerBase::GenerateRSetVisitor(uint32_t threadId)
157 {
158     auto visitor = [this, threadId](void *mem) -> bool {
159         ObjectSlot slot(ToUintPtr(mem));
160         JSTaggedValue value(slot.GetTaggedType());
161         return GetVisitor<markType>(value, threadId, mem);
162     };
163     return visitor;
164 }
165 
166 template<SharedMarkType markType>
ProcessVisitorOfDoMark(uint32_t threadId)167 inline void SharedGCMarkerBase::ProcessVisitorOfDoMark(uint32_t threadId)
168 {
169     auto rSetVisitor = GenerateRSetVisitor<markType>(threadId);
170     auto visitor = [rSetVisitor](Region *region, RememberedSet *rSet) {
171         rSet->IterateAllMarkedBits(ToUintPtr(region), rSetVisitor);
172     };
173     for (RSetWorkListHandler *handler : rSetHandlers_) {
174         ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedGCMarker::ProcessRSet");
175         handler->ProcessAll(visitor);
176     }
177 }
178 
179 template<SharedMarkType markType>
DoMark(uint32_t threadId)180 inline void SharedGCMarkerBase::DoMark(uint32_t threadId)
181 {
182     if constexpr (markType != SharedMarkType::CONCURRENT_MARK_REMARK) {
183         ProcessVisitorOfDoMark<markType>(threadId);
184     }
185     ProcessMarkStack(threadId);
186 }
187 
MarkObjectOfProcessVisitor(void *mem, WorkNode *&localBuffer)188 inline bool SharedGCMarkerBase::MarkObjectOfProcessVisitor(void *mem, WorkNode *&localBuffer)
189 {
190     ObjectSlot slot(ToUintPtr(mem));
191     JSTaggedValue value(slot.GetTaggedType());
192     if (value.IsInSharedSweepableSpace()) {
193         // For now if record weak references from local to share in marking root, the slots
194         // may be invalid due to LocalGC, so just mark them as strong-reference.
195         MarkObjectFromJSThread(localBuffer, value.GetHeapObject());
196         return true;
197     }
198 
199     // clear bit.
200     return false;
201 }
202 
ProcessVisitor(RSetWorkListHandler *handler)203 inline void SharedGCMarkerBase::ProcessVisitor(RSetWorkListHandler *handler)
204 {
205     WorkNode *&localBuffer = handler->GetHeap()->GetMarkingObjectLocalBuffer();
206     auto rSetVisitor = [this, &localBuffer](void *mem) -> bool {
207         return MarkObjectOfProcessVisitor(mem, localBuffer);
208     };
209     auto visitor = [rSetVisitor](Region *region, RememberedSet *rSet) {
210         rSet->IterateAllMarkedBits(ToUintPtr(region), rSetVisitor);
211     };
212     handler->ProcessAll(visitor);
213 }
214 
ProcessThenMergeBackRSetFromBoundJSThread(RSetWorkListHandler *handler)215 inline void SharedGCMarkerBase::ProcessThenMergeBackRSetFromBoundJSThread(RSetWorkListHandler *handler)
216 {
217     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedGCMarker::ProcessRSet");
218     ASSERT(JSThread::GetCurrent() == handler->GetHeap()->GetEcmaVM()->GetJSThread());
219     ASSERT(JSThread::GetCurrent()->IsInRunningState());
220     ProcessVisitor(handler);
221     handler->WaitFinishedThenMergeBack();
222 }
223 
MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot &slot)224 void SharedGCMovableMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot &slot)
225 {
226     Region *objectRegion = Region::ObjectAddressToRange(object);
227     ASSERT(objectRegion->InSharedHeap());
228     if (!NeedEvacuate(objectRegion)) {
229         if (!objectRegion->InSharedReadOnlySpace() && objectRegion->AtomicMark(object)) {
230             auto hclass = object->GetClass();
231             auto size = hclass->SizeFromJSHClass(object);
232             objectRegion->IncreaseAliveObject(size);
233             sWorkManager_->Push(threadId, object);
234         }
235         return;
236     }
237 
238     MarkWord markWord(object);
239     if (markWord.IsForwardingAddress()) {
240         TaggedObject *dst = markWord.ToForwardingAddress();
241         slot.Update(dst);
242         return;
243     }
244     return EvacuateObject(threadId, object, markWord, slot);
245 }
246 
MarkValue(uint32_t threadId, ObjectSlot &slot)247 void SharedGCMovableMarker::MarkValue(uint32_t threadId, ObjectSlot &slot)
248 {
249     JSTaggedValue value(slot.GetTaggedType());
250     if (value.IsInSharedSweepableSpace()) {
251         if (!value.IsWeakForHeapObject()) {
252             MarkObject(threadId, value.GetTaggedObject(), slot);
253         } else {
254             RecordWeakReference(threadId, reinterpret_cast<JSTaggedType *>(slot.SlotAddress()));
255         }
256     }
257 }
258 
NeedEvacuate(Region *region)259 bool SharedGCMovableMarker::NeedEvacuate(Region *region)
260 {
261     return region->InSharedOldSpace();
262 }
263 
EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, ObjectSlot slot)264 void SharedGCMovableMarker::EvacuateObject(uint32_t threadId, TaggedObject *object,
265     const MarkWord &markWord, ObjectSlot slot)
266 {
267     JSHClass *klass = markWord.GetJSHClass();
268     size_t size = klass->SizeFromJSHClass(object);
269     uintptr_t forwardAddress = AllocateForwardAddress(threadId, size);
270     RawCopyObject(ToUintPtr(object), forwardAddress, size, markWord);
271 
272     auto oldValue = markWord.GetValue();
273     auto result = Barriers::AtomicSetPrimitive(object, 0, oldValue,
274                                                MarkWord::FromForwardingAddress(forwardAddress));
275     if (result == oldValue) {
276         UpdateForwardAddressIfSuccess(threadId, object, klass, forwardAddress, size, slot);
277         return;
278     }
279     UpdateForwardAddressIfFailed(object, forwardAddress, size, slot);
280 }
281 
AllocateDstSpace(uint32_t threadId, size_t size)282 uintptr_t SharedGCMovableMarker::AllocateDstSpace(uint32_t threadId, size_t size)
283 {
284     uintptr_t forwardAddress = 0;
285     forwardAddress = sWorkManager_->GetTlabAllocator(threadId)->Allocate(size, SHARED_COMPRESS_SPACE);
286     if (UNLIKELY(forwardAddress == 0)) {
287         LOG_ECMA_MEM(FATAL) << "EvacuateObject alloc failed: "
288                             << " size: " << size;
289         UNREACHABLE();
290     }
291     return forwardAddress;
292 }
293 
RawCopyObject(uintptr_t fromAddress, uintptr_t toAddress, size_t size, const MarkWord &markWord)294 inline void SharedGCMovableMarker::RawCopyObject(uintptr_t fromAddress, uintptr_t toAddress, size_t size,
295     const MarkWord &markWord)
296 {
297     if (memcpy_s(ToVoidPtr(toAddress + HEAD_SIZE), size - HEAD_SIZE, ToVoidPtr(fromAddress + HEAD_SIZE),
298         size - HEAD_SIZE) != EOK) {
299         LOG_FULL(FATAL) << "memcpy_s failed";
300     }
301     *reinterpret_cast<MarkWordType *>(toAddress) = markWord.GetValue();
302 }
303 
UpdateForwardAddressIfSuccess(uint32_t threadId, TaggedObject *object, JSHClass *klass, uintptr_t toAddress, size_t size, ObjectSlot slot)304 void SharedGCMovableMarker::UpdateForwardAddressIfSuccess(uint32_t threadId, TaggedObject *object, JSHClass *klass,
305                                                           uintptr_t toAddress, size_t size, ObjectSlot slot)
306 {
307     sWorkManager_->IncreaseAliveSize(threadId, size);
308     if (klass->HasReferenceField()) {
309         sWorkManager_->Push(threadId, reinterpret_cast<TaggedObject *>(toAddress));
310     }
311 
312     if (UNLIKELY(sHeap_->InHeapProfiler())) {
313         sHeap_->OnMoveEvent(reinterpret_cast<intptr_t>(object), reinterpret_cast<TaggedObject *>(toAddress), size);
314     }
315 
316     slot.Update(reinterpret_cast<TaggedObject *>(toAddress));
317 }
318 
UpdateForwardAddressIfFailed(TaggedObject *object, uintptr_t toAddress, size_t size, ObjectSlot slot)319 void SharedGCMovableMarker::UpdateForwardAddressIfFailed(TaggedObject *object, uintptr_t toAddress, size_t size,
320     ObjectSlot slot)
321 {
322     FreeObject::FillFreeObject(sHeap_, toAddress, size);
323     TaggedObject *dst = MarkWord(object).ToForwardingAddress();
324     slot.Update(dst);
325 }
326 
AllocateForwardAddress(uint32_t threadId, size_t size)327 uintptr_t SharedGCMovableMarker::AllocateForwardAddress(uint32_t threadId, size_t size)
328 {
329     return AllocateDstSpace(threadId, size);
330 }
331 }  // namespace panda::ecmascript
332 #endif  // ECMASCRIPT_MEM_SHARED_HEAP_SHARED_GC_MARKER_INL_H
333