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