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