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