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