• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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