1 /*
2 * Copyright (c) 2025 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 #include "ecmascript/mem/shared_heap/shared_gc_evacuator.h"
17
18 #include "ecmascript/mem/object_xray.h"
19 #include "ecmascript/mem/tlab_allocator-inl.h"
20
21 namespace panda::ecmascript {
Evacuate()22 void SharedGCEvacuator::Evacuate()
23 {
24 EvacuateRegions();
25 UpdateReference();
26 }
27
EvacuateRegions()28 void SharedGCEvacuator::EvacuateRegions()
29 {
30 TRACE_GC(GCStats::Scope::ScopeId::Evacuate, sHeap_->GetEcmaGCStats());
31 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedGCEvacuator::EvacuateRegions;cset count: "
32 + std::to_string(sHeap_->GetOldSpace()->GetCollectSetRegionCount()));
33 auto sTlabAllocator = new SharedTlabAllocator(sHeap_);
34 auto inHeapProfiler = sHeap_->InHeapProfiler();
35 sHeap_->GetOldSpace()->EnumerateCollectRegionSet([this, inHeapProfiler, sTlabAllocator](Region *region) {
36 region->IterateAllMarkedBits([this, inHeapProfiler, sTlabAllocator](void *mem) {
37 auto header = reinterpret_cast<TaggedObject *>(mem);
38 JSHClass *klass = header->GetClass();
39 size_t size = klass->SizeFromJSHClass(header);
40 uintptr_t address = sTlabAllocator->Allocate(size, SHARED_COMPRESS_SPACE);
41 ASSERT(address != 0);
42 if (memcpy_s(ToVoidPtr(address), size, ToVoidPtr(ToUintPtr(mem)), size) != EOK) { // LOCV_EXCL_BR_LINE
43 LOG_ECMA_MEM(FATAL) << "memcpy_s failed";
44 UNREACHABLE();
45 }
46 if (UNLIKELY(inHeapProfiler)) {
47 sHeap_->OnMoveEvent(reinterpret_cast<intptr_t>(mem), reinterpret_cast<TaggedObject *>(address), size);
48 }
49 Barriers::SetPrimitive(header, 0, MarkWord::FromForwardingAddress(address));
50 ProcessObjectField(reinterpret_cast<TaggedObject *>(address), klass);
51 });
52 sHeap_->GetOldSpace()->DecreaseCommitted(region->GetCapacity());
53 sHeap_->GetOldSpace()->DecreaseObjectSize(region->GetSize());
54 });
55 sTlabAllocator->Finalize();
56 delete sTlabAllocator;
57 }
58
UpdateReference()59 void SharedGCEvacuator::UpdateReference()
60 {
61 TRACE_GC(GCStats::Scope::ScopeId::UpdateReference, sHeap_->GetEcmaGCStats());
62 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedGCEvacuator::UpdateReference");
63 Runtime *runtime = Runtime::GetInstance();
64 runtime->GCIterateThreadList([this](JSThread *thread) {
65 ASSERT(!thread->IsInRunningState());
66 auto heap = const_cast<Heap*>(thread->GetEcmaVM()->GetHeap());
67 heap->GetSweeper()->EnsureAllTaskFinished();
68 heap->EnumerateRegions([this](Region *region) {
69 AddWorkload(std::make_unique<UpdateLocalReferenceWorkload>(this, region));
70 });
71 });
72 sHeap_->EnumerateOldSpaceRegions([this](Region *region) {
73 AddWorkload(std::make_unique<UpdateSharedReferenceWorkload>(this, region));
74 });
75 if (sHeap_->IsParallelGCEnabled()) {
76 LockHolder holder(lock_);
77 parallel_ = CalculateUpdateThreadNum();
78 auto dTid = DaemonThread::GetInstance()->GetThreadId();
79 for (int i = 0; i < parallel_; i++) {
80 Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<UpdateReferenceTask>(dTid, this));
81 }
82 }
83 runtime->IterateSharedRoot(rootVisitor_);
84 runtime->GCIterateThreadList([this](JSThread *thread) {
85 ASSERT(!thread->IsInRunningState());
86 auto vm = thread->GetEcmaVM();
87 ObjectXRay::VisitVMRoots(vm, rootVisitor_, VMRootVisitType::UPDATE_ROOT);
88 });
89 ProcessWorkloads(true);
90 WaitFinished();
91 }
92
Process(bool isMain)93 void SharedGCEvacuator::UpdateLocalReferenceWorkload::Process([[maybe_unused]]bool isMain)
94 {
95 region_->IterateAllLocalToShareBits([this](void *mem) {
96 ObjectSlot slot(ToUintPtr(mem));
97 return evacuator_->UpdateObjectSlot(slot);
98 });
99 }
100
Process(bool isMain)101 void SharedGCEvacuator::UpdateSharedReferenceWorkload::Process([[maybe_unused]]bool isMain)
102 {
103 region_->IterateAllCrossRegionBits([this](void *mem) {
104 ObjectSlot slot(ToUintPtr(mem));
105 evacuator_->UpdateObjectSlot(slot);
106 });
107 }
108
Run(uint32_t threadIndex)109 bool SharedGCEvacuator::UpdateReferenceTask::Run([[maybe_unused]] uint32_t threadIndex)
110 {
111 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedGCEvacuator::UpdateReferenceTask");
112 evacuator_->ProcessWorkloads(false);
113 return true;
114 }
115
WaitFinished()116 void SharedGCEvacuator::WaitFinished()
117 {
118 if (parallel_ > 0) {
119 LockHolder holder(lock_);
120 while (parallel_ > 0) {
121 condition_.Wait(&lock_);
122 }
123 }
124 }
125
CalculateUpdateThreadNum()126 int SharedGCEvacuator::CalculateUpdateThreadNum()
127 {
128 uint32_t count = workloads_.size();
129 constexpr double RATIO = 1.0 / 4;
130 count = std::pow(count, RATIO);
131 return static_cast<int>(std::min(std::max(1U, count), Taskpool::GetCurrentTaskpool()->GetTotalThreadNum()));
132 }
133
UpdateObjectSlot(ObjectSlot slot)134 bool SharedGCEvacuator::UpdateObjectSlot(ObjectSlot slot)
135 {
136 JSTaggedValue value(slot.GetTaggedType());
137 if (!value.IsHeapObject()) {
138 return false;
139 }
140 Region *region = Region::ObjectAddressToRange(slot.GetTaggedType());
141 if (!region->InSCollectSet()) {
142 return true;
143 }
144 ASSERT(region->InSharedHeap());
145 bool isWeak = value.IsWeakForHeapObject();
146 TaggedObject *object = value.GetHeapObject();
147 MarkWord markWord(object);
148 if (markWord.IsForwardingAddress()) {
149 TaggedObject *dst = markWord.ToForwardingAddress();
150 if (isWeak) {
151 dst = JSTaggedValue(dst).CreateAndGetWeakRef().GetRawTaggedObject();
152 }
153 slot.Update(dst);
154 return true;
155 } else {
156 slot.Clear();
157 return false;
158 }
159 }
160
VisitObjectRangeImpl(TaggedObject * root,ObjectSlot start,ObjectSlot end,VisitObjectArea area)161 void SharedGCEvacuator::ObjectFieldCSetVisitor::VisitObjectRangeImpl(TaggedObject *root, ObjectSlot start,
162 ObjectSlot end, VisitObjectArea area)
163 {
164 Region *rootRegion = Region::ObjectAddressToRange(root);
165 if (UNLIKELY(area == VisitObjectArea::IN_OBJECT)) {
166 JSHClass *hclass = root->GetClass();
167 ASSERT(!hclass->IsAllTaggedProp());
168 int index = 0;
169 TaggedObject *dst = hclass->GetLayout().GetTaggedObject();
170 LayoutInfo *layout = LayoutInfo::UncheckCast(dst);
171 ObjectSlot realEnd = start;
172 realEnd += layout->GetPropertiesCapacity();
173 end = end > realEnd ? realEnd : end;
174 for (ObjectSlot slot = start; slot < end; slot++) {
175 auto attr = layout->GetAttr(index++);
176 if (attr.IsTaggedRep()) {
177 evacuator_->UpdateCrossRegionRSet(slot, rootRegion);
178 }
179 }
180 return;
181 }
182 for (ObjectSlot slot = start; slot < end; slot++) {
183 evacuator_->UpdateCrossRegionRSet(slot, rootRegion);
184 }
185 }
186
ProcessObjectField(TaggedObject * object,JSHClass * hclass)187 void SharedGCEvacuator::ProcessObjectField(TaggedObject *object, JSHClass *hclass)
188 {
189 ObjectXRay::VisitObjectBody<VisitType::OLD_GC_VISIT>(object, hclass, objectFieldCSetVisitor_);
190 }
191
UpdateCrossRegionRSet(ObjectSlot slot,Region * objectRegion)192 void SharedGCEvacuator::UpdateCrossRegionRSet(ObjectSlot slot, Region *objectRegion)
193 {
194 JSTaggedType value = slot.GetTaggedType();
195 if (!JSTaggedValue(value).IsHeapObject()) {
196 return;
197 }
198 Region *valueRegion = Region::ObjectAddressToRange(value);
199 if (valueRegion->InSCollectSet()) {
200 objectRegion->InsertCrossRegionRSet(slot.SlotAddress());
201 }
202 }
203
VisitRoot(Root type,ObjectSlot slot)204 void SharedGCEvacuator::UpdateRootVisitor::VisitRoot([[maybe_unused]] Root type, ObjectSlot slot)
205 {
206 UpdateObjectSlotRoot(slot);
207 }
208
VisitRangeRoot(Root type,ObjectSlot start,ObjectSlot end)209 void SharedGCEvacuator::UpdateRootVisitor::VisitRangeRoot([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end)
210 {
211 for (ObjectSlot slot = start; slot < end; slot++) {
212 UpdateObjectSlotRoot(slot);
213 }
214 }
215
VisitBaseAndDerivedRoot(Root type,ObjectSlot base,ObjectSlot derived,uintptr_t baseOldObject)216 void SharedGCEvacuator::UpdateRootVisitor::VisitBaseAndDerivedRoot([[maybe_unused]] Root type, ObjectSlot base,
217 ObjectSlot derived, uintptr_t baseOldObject)
218 {
219 if (JSTaggedValue(base.GetTaggedType()).IsHeapObject()) {
220 derived.Update(base.GetTaggedType() + derived.GetTaggedType() - baseOldObject);
221 }
222 }
223
UpdateObjectSlotRoot(ObjectSlot slot)224 void SharedGCEvacuator::UpdateRootVisitor::UpdateObjectSlotRoot(ObjectSlot slot)
225 {
226 JSTaggedValue value(slot.GetTaggedType());
227 if (value.IsHeapObject()) {
228 Region *region = Region::ObjectAddressToRange(slot.GetTaggedType());
229 if (region->InSCollectSet()) {
230 ASSERT(region->InSharedHeap());
231 ASSERT(!value.IsWeakForHeapObject());
232 TaggedObject *object = value.GetHeapObject();
233 MarkWord markWord(object);
234 if (markWord.IsForwardingAddress()) {
235 TaggedObject *dst = markWord.ToForwardingAddress();
236 slot.Update(dst);
237 } else {
238 slot.Clear();
239 }
240 }
241 }
242 }
243 } // namespace panda::ecmascript