• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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/parallel_evacuator-inl.h"
17 
18 #include "ecmascript/js_weak_container.h"
19 #include "ecmascript/linked_hash_table.h"
20 #include "ecmascript/mem/parallel_evacuator_visitor-inl.h"
21 #include "ecmascript/mem/tlab_allocator-inl.h"
22 #include "ecmascript/mem/work_manager-inl.h"
23 #include "ecmascript/runtime_call_id.h"
24 
25 namespace panda::ecmascript {
Initialize()26 void ParallelEvacuator::Initialize()
27 {
28     MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), ParallelEvacuatorInitialize);
29     waterLine_ = heap_->GetNewSpace()->GetWaterLine();
30     heap_->SwapNewSpace();
31     allocator_ = new TlabAllocator(heap_);
32     promotedSize_ = 0;
33     hasNewToOldRegions_ = false;
34 }
35 
Finalize()36 void ParallelEvacuator::Finalize()
37 {
38     TRACE_GC(GCStats::Scope::ScopeId::Finalize, heap_->GetEcmaVM()->GetEcmaGCStats());
39     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::Finalize", "");
40     MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), ParallelEvacuatorFinalize);
41     delete allocator_;
42     evacuateWorkloadSet_.Clear();
43     updateWorkloadSet_.Clear();
44 }
45 
Evacuate()46 void ParallelEvacuator::Evacuate()
47 {
48     Initialize();
49     EvacuateSpace();
50     UpdateReference();
51     SweepNewToOldRegions();
52     Finalize();
53 }
54 
SweepNewToOldRegions()55 void ParallelEvacuator::SweepNewToOldRegions()
56 {
57     TRACE_GC(GCStats::Scope::ScopeId::SweepNewToOldRegions, heap_->GetEcmaVM()->GetEcmaGCStats());
58     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::SweepNewToOldRegions", "");
59     if (!hasNewToOldRegions_) {
60         return;
61     }
62     heap_->GetSweeper()->SweepNewToOldRegions();
63     if (!heap_->IsConcurrentFullMark()) {
64         heap_->GetSweeper()->PostTask();
65     }
66 }
67 
UpdateTrackInfo()68 void ParallelEvacuator::UpdateTrackInfo()
69 {
70     for (uint32_t i = 0; i <= common::MAX_TASKPOOL_THREAD_NUM; i++) {
71         auto &trackInfoSet = ArrayTrackInfoSet(i);
72         for (auto &each : trackInfoSet) {
73             auto trackInfoVal = JSTaggedValue(each);
74             if (!trackInfoVal.IsHeapObject() || !trackInfoVal.IsWeak()) {
75                 continue;
76             }
77             auto trackInfo = trackInfoVal.GetWeakReferentUnChecked();
78             trackInfo = UpdateAddressAfterEvacation(trackInfo);
79             if (trackInfo) {
80                 heap_->GetEcmaVM()->GetPGOProfiler()->UpdateTrackSpaceFlag(trackInfo, RegionSpaceFlag::IN_OLD_SPACE);
81             }
82         }
83         trackInfoSet.clear();
84     }
85 }
86 
ProcessFromSpaceEvacuation()87 void ParallelEvacuator::ProcessFromSpaceEvacuation()
88 {
89     std::vector<std::pair<size_t, Region*>> sortRegion;
90     sortRegion.reserve(heap_->GetFromSpaceDuringEvacuation()->GetRegionCount());
91     heap_->GetFromSpaceDuringEvacuation()->EnumerateRegions([this, &sortRegion](Region *current) {
92         if (current->IsFreshRegion() && TryWholeRegionEvacuate(current, RegionEvacuateType::REGION_NEW_TO_NEW)) {
93             return;
94         }
95         sortRegion.emplace_back(current->AliveObject(), current);
96     });
97     std::sort(sortRegion.begin(), sortRegion.end());
98     for (auto iter = sortRegion.rbegin(); iter != sortRegion.rend(); iter++) {
99         Region *region = iter->second;
100         auto type = SelectRegionEvacuateType(region);
101         if (TryWholeRegionEvacuate(region, type)) {
102             continue;
103         }
104         evacuateWorkloadSet_.Add(std::make_unique<EvacuateWorkload>(this, region));
105     }
106 }
107 
EvacuateSpace()108 void ParallelEvacuator::EvacuateSpace()
109 {
110     TRACE_GC(GCStats::Scope::ScopeId::EvacuateSpace, heap_->GetEcmaVM()->GetEcmaGCStats());
111     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::EvacuateSpace", "");
112     MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), ParallelEvacuator);
113     {
114         // Process JSWeakMap before evacuate
115         // fixme: process in parallel
116         uint32_t totalThreadCount = common::Taskpool::GetCurrentTaskpool()->GetTotalThreadNum() + 1;
117         for (uint32_t i = 0; i < totalThreadCount; ++i) {
118             if (heap_->IsYoungMark()) {
119                 UpdateRecordJSWeakMap<TriggerGCType::YOUNG_GC>(i);
120             } else {
121                 UpdateRecordJSWeakMap<TriggerGCType::OLD_GC>(i);
122             }
123         }
124     }
125     auto &workloadSet = evacuateWorkloadSet_;
126     if (heap_->IsConcurrentFullMark() || heap_->IsYoungMark()) {
127         ProcessFromSpaceEvacuation();
128         heap_->GetOldSpace()->EnumerateCollectRegionSet([this, &workloadSet](Region *current) {
129             workloadSet.Add(std::make_unique<EvacuateWorkload>(this, current));
130         });
131     }
132     workloadSet.PrepareWorkloads();
133     if (heap_->IsParallelGCEnabled()) {
134         LockHolder holder(mutex_);
135         parallel_ = CalculateEvacuationThreadNum();
136         ASSERT(parallel_ >= 0);
137         evacuateTaskNum_ = static_cast<uint32_t>(parallel_);
138         for (uint32_t i = 1; i <= evacuateTaskNum_; i++) {
139             common::Taskpool::GetCurrentTaskpool()->PostTask(
140                 std::make_unique<EvacuationTask>(heap_->GetJSThread()->GetThreadId(), i, this));
141         }
142     } else {
143         evacuateTaskNum_ = 0;
144     }
145     {
146         GCStats::Scope sp2(GCStats::Scope::ScopeId::EvacuateRegion, heap_->GetEcmaVM()->GetEcmaGCStats());
147         EvacuateSpace(allocator_, MAIN_THREAD_INDEX, 0, true);
148     }
149 
150     {
151         GCStats::Scope sp2(GCStats::Scope::ScopeId::WaitFinish, heap_->GetEcmaVM()->GetEcmaGCStats());
152         WaitFinished();
153     }
154 }
155 
EvacuateSpace(TlabAllocator * allocator,uint32_t threadIndex,uint32_t idOrder,bool isMain)156 bool ParallelEvacuator::EvacuateSpace(TlabAllocator *allocator, uint32_t threadIndex, uint32_t idOrder, bool isMain)
157 {
158     UpdateRecordWeakReferenceInParallel(idOrder);
159 
160     auto &arrayTrackInfoSet = ArrayTrackInfoSet(threadIndex);
161     DrainWorkloads(evacuateWorkloadSet_, [&](std::unique_ptr<Workload> &region) {
162         EvacuateRegion(allocator, region->GetRegion(), arrayTrackInfoSet);
163     });
164     allocator->Finalize();
165     if (!isMain) {
166         LockHolder holder(mutex_);
167         if (--parallel_ <= 0) {
168             condition_.SignalAll();
169         }
170     }
171     return true;
172 }
173 
UpdateRecordWeakReferenceInParallel(uint32_t idOrder)174 void ParallelEvacuator::UpdateRecordWeakReferenceInParallel(uint32_t idOrder)
175 {
176     auto totalThreadCount = common::Taskpool::GetCurrentTaskpool()->GetTotalThreadNum() + 1;
177     for (uint32_t i = idOrder; i < totalThreadCount; i += (evacuateTaskNum_ + 1)) {
178         UpdateRecordWeakReference(i);
179     }
180 }
181 
UpdateRecordWeakReference(uint32_t threadId)182 void ParallelEvacuator::UpdateRecordWeakReference(uint32_t threadId)
183 {
184     ProcessQueue *queue = heap_->GetWorkManager()->GetWorkNodeHolder(threadId)->GetWeakReferenceQueue();
185     while (true) {
186         auto obj = queue->PopBack();
187         if (UNLIKELY(obj == nullptr)) {
188             break;
189         }
190         ObjectSlot slot(ToUintPtr(obj));
191         JSTaggedType value = slot.GetTaggedType();
192         if (JSTaggedValue(value).IsWeak()) {
193             ASSERT(heap_->IsConcurrentFullMark());
194             Region *objectRegion = Region::ObjectAddressToRange(value);
195             if (!objectRegion->InYoungSpaceOrCSet() && !objectRegion->InSharedHeap() &&
196                     (objectRegion->GetMarkGCBitset() == nullptr || !objectRegion->Test(value))) {
197                 slot.Clear();
198             }
199         }
200     }
201 }
202 
203 template <TriggerGCType gcType>
UpdateRecordJSWeakMap(uint32_t threadId)204 void ParallelEvacuator::UpdateRecordJSWeakMap(uint32_t threadId)
205 {
206     std::function<bool(JSTaggedValue)> visitor = [](JSTaggedValue key) {
207         ASSERT(!key.IsHole());
208         if (key.IsUndefined()) {    // Dead key, and set to undefined by GC
209             return true;
210         }
211         ASSERT(key.IsHeapObject() && key.IsWeak());
212         uintptr_t addr = ToUintPtr(key.GetTaggedWeakRef());
213         Region *keyRegion = Region::ObjectAddressToRange(addr);
214 
215         if constexpr (gcType == TriggerGCType::YOUNG_GC) {
216             return keyRegion->InYoungSpace() && !keyRegion->Test(addr);
217         } else {
218             static_assert(gcType == TriggerGCType::OLD_GC);
219             if (keyRegion->InSharedHeap()) {
220                 return false;
221             }
222             if (keyRegion->GetMarkGCBitset() == nullptr) {
223                 return true;
224             }
225             return !keyRegion->Test(addr);
226         }
227     };
228 
229     JSWeakMapProcessQueue *queue = heap_->GetWorkManager()->GetWorkNodeHolder(threadId)->GetJSWeakMapQueue();
230     while (true) {
231         TaggedObject *obj = queue->PopBack();
232         if (UNLIKELY(obj == nullptr)) {
233             break;
234         }
235         JSWeakMap *weakMap = JSWeakMap::Cast(obj);
236         JSThread *thread = heap_->GetJSThread();
237         JSTaggedValue maybeMap = weakMap->GetLinkedMap(thread);
238         if (maybeMap.IsUndefined()) {
239             continue;
240         }
241         LinkedHashMap *map = LinkedHashMap::Cast(maybeMap.GetTaggedObject());
242         map->ClearAllDeadEntries(thread, visitor);
243     }
244 }
245 
EvacuateRegion(TlabAllocator * allocator,Region * region,std::unordered_set<JSTaggedType> & trackSet)246 void ParallelEvacuator::EvacuateRegion(TlabAllocator *allocator, Region *region,
247                                        std::unordered_set<JSTaggedType> &trackSet)
248 {
249     bool isInOldGen = region->InOldSpace();
250     bool isBelowAgeMark = region->BelowAgeMark();
251     bool pgoEnabled = heap_->GetJSThread()->IsPGOProfilerEnable();
252     bool inHeapProfiler = heap_->InHeapProfiler();
253     size_t promotedSize = 0;
254     auto thread = heap_->GetJSThread();
255     region->IterateAllMarkedBits([this, &region, &isInOldGen, &isBelowAgeMark, &pgoEnabled,
256                                   &promotedSize, &allocator, &trackSet, inHeapProfiler, &thread](void *mem) {
257         ASSERT(region->InRange(ToUintPtr(mem)));
258         auto header = reinterpret_cast<TaggedObject *>(mem);
259         auto klass = header->GetClass();
260         auto size = header->GetSize();
261 
262         uintptr_t address = 0;
263         bool actualPromoted = false;
264         bool hasAgeMark = isBelowAgeMark || (region->HasAgeMark() && ToUintPtr(mem) < waterLine_);
265         if (hasAgeMark) {
266             address = allocator->Allocate(size, OLD_SPACE);
267             actualPromoted = true;
268             promotedSize += size;
269         } else if (isInOldGen) {
270             address = allocator->Allocate(size, OLD_SPACE);
271             actualPromoted = true;
272         } else {
273             address = allocator->Allocate(size, SEMI_SPACE);
274             if (address == 0) {
275                 address = allocator->Allocate(size, OLD_SPACE);
276                 actualPromoted = true;
277                 promotedSize += size;
278             }
279         }
280         ASSERT(address != 0);
281 
282         if (memcpy_s(ToVoidPtr(address), size, ToVoidPtr(ToUintPtr(mem)), size) != EOK) { // LOCV_EXCL_BR_LINE
283             LOG_FULL(FATAL) << "memcpy_s failed";
284         }
285         if (inHeapProfiler) {
286             heap_->OnMoveEvent(reinterpret_cast<uintptr_t>(mem), reinterpret_cast<TaggedObject *>(address), size);
287         }
288         if (pgoEnabled) {
289             if (actualPromoted && klass->IsJSArray()) {
290                 auto trackInfo = JSArray::Cast(header)->GetTrackInfo(thread);
291                 trackSet.emplace(trackInfo.GetRawData());
292             }
293         }
294         Barriers::SetPrimitive(header, 0, MarkWord::FromForwardingAddress(address));
295 
296         if (actualPromoted) {
297             SetObjectFieldRSet(reinterpret_cast<TaggedObject *>(address), klass);
298         }
299     });
300     promotedSize_.fetch_add(promotedSize);
301 }
302 
UpdateReference()303 void ParallelEvacuator::UpdateReference()
304 {
305     TRACE_GC(GCStats::Scope::ScopeId::UpdateReference, heap_->GetEcmaVM()->GetEcmaGCStats());
306     MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), ParallelUpdateReference);
307     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::UpdateReference", "");
308     // Update reference pointers
309     uint32_t youngeRegionMoveCount = 0;
310     uint32_t youngeRegionCopyCount = 0;
311     uint32_t oldRegionCount = 0;
312     auto &workloadSet = updateWorkloadSet_;
313     heap_->GetNewSpace()->EnumerateRegions([&](Region *current) {
314         if (current->InNewToNewSet()) {
315             workloadSet.Add(
316                 std::make_unique<UpdateAndSweepNewRegionWorkload>(
317                     this, current, heap_->IsYoungMark()));
318             youngeRegionMoveCount++;
319         } else {
320             workloadSet.Add(
321                 std::make_unique<UpdateNewRegionWorkload>(this, current, heap_->IsYoungMark()));
322             youngeRegionCopyCount++;
323         }
324     });
325     heap_->EnumerateOldSpaceRegions([this, &oldRegionCount, &workloadSet](Region *current) {
326         if (current->InCollectSet()) {
327             return;
328         }
329         if (current->InNewToOldSet()) {
330             hasNewToOldRegions_ = true;
331             promotedSize_.fetch_add(current->AliveObject(), std::memory_order_relaxed);
332             workloadSet.Add(std::make_unique<UpdateNewToOldEvacuationWorkload>(this, current, heap_->IsYoungMark()));
333         } else {
334             workloadSet.Add(std::make_unique<UpdateRSetWorkload>(this, current));
335         }
336         oldRegionCount++;
337     });
338     heap_->EnumerateSnapshotSpaceRegions([this, &workloadSet](Region *current) {
339         workloadSet.Add(std::make_unique<UpdateRSetWorkload>(this, current));
340     });
341     workloadSet.PrepareWorkloads();
342     LOG_GC(DEBUG) << "UpdatePointers statistic: younge space region compact moving count:"
343                         << youngeRegionMoveCount
344                         << "younge space region compact coping count:" << youngeRegionCopyCount
345                         << "old space region count:" << oldRegionCount;
346 
347     if (heap_->IsParallelGCEnabled()) {
348         LockHolder holder(mutex_);
349         parallel_ = CalculateUpdateThreadNum();
350         for (int i = 0; i < parallel_; i++) {
351             common::Taskpool::GetCurrentTaskpool()->PostTask(
352                 std::make_unique<UpdateReferenceTask>(heap_->GetJSThread()->GetThreadId(), this));
353         }
354     }
355     {
356         GCStats::Scope sp2(GCStats::Scope::ScopeId::UpdateRoot, heap_->GetEcmaVM()->GetEcmaGCStats());
357         UpdateRoot();
358     }
359 
360     {
361         GCStats::Scope sp2(GCStats::Scope::ScopeId::UpdateWeekRef, heap_->GetEcmaVM()->GetEcmaGCStats());
362         if (heap_->IsYoungMark()) {
363             UpdateWeakReferenceOpt<TriggerGCType::YOUNG_GC>();
364         } else {
365             UpdateWeakReferenceOpt<TriggerGCType::OLD_GC>();
366         }
367     }
368     {
369         GCStats::Scope sp2(GCStats::Scope::ScopeId::ProceeWorkload, heap_->GetEcmaVM()->GetEcmaGCStats());
370         ProcessWorkloads(true);
371     }
372     WaitFinished();
373 
374     if (heap_->GetJSThread()->IsPGOProfilerEnable()) {
375         UpdateTrackInfo();
376     }
377 }
378 
UpdateRoot()379 void ParallelEvacuator::UpdateRoot()
380 {
381     MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), UpdateRoot);
382     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::UpdateRoot", "");
383 
384     ObjectXRay::VisitVMRoots(heap_->GetEcmaVM(), updateRootVisitor_);
385 }
386 
387 template<TriggerGCType gcType>
UpdateWeakReferenceOpt()388 void ParallelEvacuator::UpdateWeakReferenceOpt()
389 {
390     MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), UpdateWeakReferenceOpt);
391     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::UpdateWeakReferenceOpt", "");
392     WeakRootVisitor gcUpdateWeak = [](TaggedObject *header) -> TaggedObject* {
393         Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(header));
394         ASSERT(objectRegion != nullptr);
395         if constexpr (gcType == TriggerGCType::YOUNG_GC) {
396             if (!objectRegion->InYoungSpace()) {
397                 if (objectRegion->InNewToOldSet() && !objectRegion->Test(header)) {
398                     return nullptr;
399                 }
400                 return header;
401             }
402         } else if constexpr (gcType == TriggerGCType::OLD_GC) {
403             if (!objectRegion->InYoungSpaceOrCSet()) {
404                 if (!objectRegion->InSharedHeap() && (objectRegion->GetMarkGCBitset() == nullptr ||
405                                               !objectRegion->Test(header))) {
406                     return nullptr;
407                 }
408                 return header;
409             }
410         } else { // LOCV_EXCL_BR_LINE
411             LOG_GC(FATAL) << "WeakRootVisitor: not support gcType yet";
412             UNREACHABLE();
413         }
414         if (objectRegion->InNewToNewSet()) {
415             if (objectRegion->Test(header)) {
416                 return header;
417             }
418         } else {
419             MarkWord markWord(header);
420             if (markWord.IsForwardingAddress()) {
421                 return markWord.ToForwardingAddress();
422             }
423         }
424         return nullptr;
425     };
426 
427     heap_->GetEcmaVM()->GetJSThread()->IterateWeakEcmaGlobalStorage(gcUpdateWeak);
428     heap_->GetEcmaVM()->ProcessReferences(gcUpdateWeak);
429     heap_->GetEcmaVM()->ProcessSnapShotEnv(gcUpdateWeak);
430     heap_->GetEcmaVM()->GetJSThread()->UpdateJitCodeMapReference(gcUpdateWeak);
431 }
432 
UpdateRSet(Region * region)433 void ParallelEvacuator::UpdateRSet(Region *region)
434 {
435     auto cb = [this](void *mem) -> bool {
436         ObjectSlot slot(ToUintPtr(mem));
437         return UpdateOldToNewObjectSlot(slot);
438     };
439 
440     if (heap_->GetSweeper()->IsSweeping()) {
441         if (region->IsGCFlagSet(RegionGCFlags::HAS_BEEN_SWEPT)) {
442             // Region is safe while update remember set
443             region->MergeOldToNewRSetForCS();
444             region->MergeLocalToShareRSetForCS();
445         } else {
446             region->AtomicIterateAllSweepingRSetBits(cb);
447         }
448     }
449     region->IterateAllOldToNewBits(cb);
450     if (heap_->IsYoungMark()) {
451         return;
452     }
453     region->IterateAllCrossRegionBits([this](void *mem) {
454         ObjectSlot slot(ToUintPtr(mem));
455         UpdateCrossRegionObjectSlot(slot);
456     });
457     region->DeleteCrossRegionRSet();
458 }
459 
460 template<TriggerGCType gcType, bool needUpdateLocalToShare>
UpdateNewRegionReference(Region * region)461 void ParallelEvacuator::UpdateNewRegionReference(Region *region)
462 {
463     UpdateNewObjectFieldVisitor<gcType, needUpdateLocalToShare> updateFieldVisitor(this);
464     Region *current = heap_->GetNewSpace()->GetCurrentRegion();
465     auto curPtr = region->GetBegin();
466     uintptr_t endPtr = 0;
467     if (region == current) {
468         auto top = heap_->GetNewSpace()->GetTop();
469         endPtr = curPtr + region->GetAllocatedBytes(top);
470     } else {
471         endPtr = curPtr + region->GetAllocatedBytes();
472     }
473 
474     size_t objSize = 0;
475     while (curPtr < endPtr) {
476         auto freeObject = FreeObject::Cast(curPtr);
477         // If curPtr is freeObject, It must to mark unpoison first.
478         ASAN_UNPOISON_MEMORY_REGION(reinterpret_cast<void *>(freeObject), TaggedObject::TaggedObjectSize());
479         if (!freeObject->IsFreeObject()) {
480             auto obj = reinterpret_cast<TaggedObject *>(curPtr);
481             auto klass = obj->GetClass();
482             UpdateNewObjectField<gcType, needUpdateLocalToShare>(obj, klass, updateFieldVisitor);
483             objSize = obj->GetSize();
484         } else {
485             freeObject->AsanUnPoisonFreeObject();
486             objSize = freeObject->Available();
487             freeObject->AsanPoisonFreeObject();
488         }
489         curPtr += objSize;
490         CHECK_OBJECT_SIZE(objSize);
491     }
492     CHECK_REGION_END(curPtr, endPtr);
493 }
494 
495 template<TriggerGCType gcType, bool needUpdateLocalToShare>
UpdateAndSweepNewRegionReference(Region * region)496 void ParallelEvacuator::UpdateAndSweepNewRegionReference(Region *region)
497 {
498     UpdateNewObjectFieldVisitor<gcType, needUpdateLocalToShare> updateFieldVisitor(this);
499     uintptr_t freeStart = region->GetBegin();
500     uintptr_t freeEnd = freeStart + region->GetAllocatedBytes();
501     region->IterateAllMarkedBits([&](void *mem) {
502         ASSERT(region->InRange(ToUintPtr(mem)));
503         auto header = reinterpret_cast<TaggedObject *>(mem);
504         JSHClass *klass = header->GetClass();
505         UpdateNewObjectField<gcType, needUpdateLocalToShare>(header, klass, updateFieldVisitor);
506 
507         uintptr_t freeEnd = ToUintPtr(mem);
508         if (freeStart != freeEnd) {
509             size_t freeSize = freeEnd - freeStart;
510             FreeObject::FillFreeObject(heap_, freeStart, freeSize);
511             region->ClearLocalToShareRSetInRange(freeStart, freeEnd);
512         }
513 
514         freeStart = freeEnd + header->GetSize();
515     });
516     CHECK_REGION_END(freeStart, freeEnd);
517     if (freeStart < freeEnd) {
518         FreeObject::FillFreeObject(heap_, freeStart, freeEnd - freeStart);
519         region->ClearLocalToShareRSetInRange(freeStart, freeEnd);
520     }
521 }
522 
523 template <TriggerGCType gcType, bool needUpdateLocalToShare>
UpdateNewObjectFieldVisitor(ParallelEvacuator * evacuator)524 ParallelEvacuator::UpdateNewObjectFieldVisitor<gcType, needUpdateLocalToShare>::UpdateNewObjectFieldVisitor(
525     ParallelEvacuator *evacuator) : evacuator_(evacuator) {}
526 
527 template <TriggerGCType gcType, bool needUpdateLocalToShare>
VisitObjectRangeImpl(BaseObject * rootObject,uintptr_t startAddr,uintptr_t endAddr,VisitObjectArea area)528 void ParallelEvacuator::UpdateNewObjectFieldVisitor<gcType, needUpdateLocalToShare>::VisitObjectRangeImpl(
529     BaseObject *rootObject, uintptr_t startAddr, uintptr_t endAddr, VisitObjectArea area)
530 {
531     ObjectSlot start(startAddr);
532     ObjectSlot end(endAddr);
533     if (UNLIKELY(area == VisitObjectArea::IN_OBJECT)) {
534         auto root = TaggedObject::Cast(rootObject);
535         JSThread *thread = evacuator_->heap_->GetJSThread();
536         JSHClass *hclass = root->GetClass();
537         ASSERT(!hclass->IsAllTaggedProp());
538         int index = 0;
539         TaggedObject *dst = hclass->GetLayout(thread).GetTaggedObject();
540         LayoutInfo *layout = LayoutInfo::UncheckCast(dst);
541         ObjectSlot realEnd = start;
542         realEnd += layout->GetPropertiesCapacity();
543         end = end > realEnd ? realEnd : end;
544         for (ObjectSlot slot = start; slot < end; slot++) {
545             auto attr = layout->GetAttr(thread, index++);
546             if (attr.IsTaggedRep()) {
547                 evacuator_->UpdateNewObjectSlot<gcType, needUpdateLocalToShare>(slot);
548             }
549         }
550         return;
551     }
552     for (ObjectSlot slot = start; slot < end; slot++) {
553         evacuator_->UpdateNewObjectSlot<gcType, needUpdateLocalToShare>(slot);
554     }
555 }
556 
557 template<TriggerGCType gcType, bool needUpdateLocalToShare>
UpdateNewObjectField(TaggedObject * object,JSHClass * cls,UpdateNewObjectFieldVisitor<gcType,needUpdateLocalToShare> & updateFieldVisitor)558 void ParallelEvacuator::UpdateNewObjectField(TaggedObject *object, JSHClass *cls,
559     UpdateNewObjectFieldVisitor<gcType, needUpdateLocalToShare> &updateFieldVisitor)
560 {
561     ObjectXRay::VisitObjectBody<VisitType::OLD_GC_VISIT>(object, cls, updateFieldVisitor);
562 }
563 
564 template<TriggerGCType gcType>
UpdateNewToOldEvacuationReference(Region * region,uint32_t threadIndex)565 void ParallelEvacuator::UpdateNewToOldEvacuationReference(Region *region, uint32_t threadIndex)
566 {
567     std::unordered_set<JSTaggedType> *sets = &arrayTrackInfoSets_[threadIndex];
568     NewToOldEvacuationVisitor<gcType> visitor(heap_, sets, this);
569     region->IterateAllMarkedBits(visitor);
570 }
571 
WaitFinished()572 void ParallelEvacuator::WaitFinished()
573 {
574     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::WaitFinished", "");
575     MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), WaitUpdateFinished);
576     if (parallel_ > 0) {
577         LockHolder holder(mutex_);
578         while (parallel_ > 0) {
579             condition_.Wait(&mutex_);
580         }
581     }
582 }
583 
ProcessWorkloads(bool isMain,uint32_t threadIndex)584 bool ParallelEvacuator::ProcessWorkloads(bool isMain, uint32_t threadIndex)
585 {
586     ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "GC::ProcessWorkloads", "");
587     DrainWorkloads(updateWorkloadSet_, [&](std::unique_ptr<Workload> &region) {
588         region->Process(isMain, threadIndex);
589         });
590     if (!isMain) {
591         LockHolder holder(mutex_);
592         if (--parallel_ <= 0) {
593             condition_.SignalAll();
594         }
595     }
596     return true;
597 }
598 
599 template <typename WorkloadCallback>
DrainWorkloads(WorkloadSet & workloadSet,WorkloadCallback callback)600 void ParallelEvacuator::DrainWorkloads(WorkloadSet &workloadSet, WorkloadCallback callback)
601 {
602     std::unique_ptr<Workload> region;
603     while (workloadSet.HasRemaningWorkload()) {
604         std::optional<size_t> index = workloadSet.GetNextIndex();
605         if (!index.has_value()) {
606             return;
607         }
608         size_t count = workloadSet.GetWorkloadCount();
609         size_t finishedCount = 0;
610         for (size_t i = index.value(); i < count; i++) {
611             region = workloadSet.TryGetWorkload(i);
612             if (region == nullptr) {
613                 break;
614             }
615             callback(region);
616             finishedCount++;
617         }
618         if (finishedCount && workloadSet.FetchSubAndCheckWorkloadCount(finishedCount)) {
619             return;
620         }
621     }
622 }
623 
PrepareWorkloads()624 void ParallelEvacuator::WorkloadSet::PrepareWorkloads()
625 {
626     size_t size = workloads_.size();
627     remainingWorkloadNum_.store(size, std::memory_order_relaxed);
628     /*
629     Construct indexList_ containing starting indices for multi-threaded acquire workload.
630     The construction method starts with the interval [0, size] and recursively
631     selects midpoints as starting indices for subintervals.
632     The first starting index is 0 to ensure no workloads are missed.
633     */
634     indexList_.reserve(size);
635     indexList_.emplace_back(0);
636     std::vector<std::pair<size_t, size_t>> pairList{{0, size}};
637     pairList.reserve(size);
638     while (!pairList.empty()) {
639         auto [start, end] = pairList.back();
640         pairList.pop_back();
641         size_t mid = (start + end) >> 1;
642         indexList_.emplace_back(mid);
643         if (end - mid > 1U) {
644             pairList.emplace_back(mid, end);
645         }
646         if (mid - start > 1U) {
647             pairList.emplace_back(start, mid);
648         }
649     }
650 }
651 
GetNextIndex()652 std::optional<size_t> ParallelEvacuator::WorkloadSet::GetNextIndex()
653 {
654     size_t cursor = indexCursor_.fetch_add(1, std::memory_order_relaxed);
655     if (cursor >= indexList_.size()) {
656         return std::nullopt;
657     }
658     return indexList_[cursor];
659 }
660 
TryGetWorkload(size_t index)661 std::unique_ptr<ParallelEvacuator::Workload> ParallelEvacuator::WorkloadSet::TryGetWorkload(size_t index)
662 {
663     std::unique_ptr<Workload> workload;
664     if (workloads_.at(index).first.TryAcquire()) {
665         workload = std::move(workloads_[index].second);
666     }
667     return workload;
668 }
669 
Clear()670 void ParallelEvacuator::WorkloadSet::Clear()
671 {
672     workloads_.clear();
673     indexList_.clear();
674     indexCursor_.store(0, std::memory_order_relaxed);
675     remainingWorkloadNum_.store(0, std::memory_order_relaxed);
676 }
677 
EvacuationTask(int32_t id,uint32_t idOrder,ParallelEvacuator * evacuator)678 ParallelEvacuator::EvacuationTask::EvacuationTask(int32_t id, uint32_t idOrder, ParallelEvacuator *evacuator)
679     : common::Task(id), idOrder_(idOrder), evacuator_(evacuator)
680 {
681     allocator_ = new TlabAllocator(evacuator->heap_);
682 }
683 
~EvacuationTask()684 ParallelEvacuator::EvacuationTask::~EvacuationTask()
685 {
686     delete allocator_;
687 }
688 
Run(uint32_t threadIndex)689 bool ParallelEvacuator::EvacuationTask::Run(uint32_t threadIndex)
690 {
691     return evacuator_->EvacuateSpace(allocator_, threadIndex, idOrder_);
692 }
693 
Run(uint32_t threadIndex)694 bool ParallelEvacuator::UpdateReferenceTask::Run([[maybe_unused]] uint32_t threadIndex)
695 {
696     evacuator_->ProcessWorkloads(false, threadIndex);
697     return true;
698 }
699 
Process(bool isMain,uint32_t threadIndex)700 bool ParallelEvacuator::EvacuateWorkload::Process([[maybe_unused]] bool isMain, [[maybe_unused]] uint32_t threadIndex)
701 {
702     return true;
703 }
704 
Process(bool isMain,uint32_t threadIndex)705 bool ParallelEvacuator::UpdateRSetWorkload::Process([[maybe_unused]] bool isMain,
706                                                     [[maybe_unused]] uint32_t threadIndex)
707 {
708     GetEvacuator()->UpdateRSet(GetRegion());
709     return true;
710 }
711 
Process(bool isMain,uint32_t threadIndex)712 bool ParallelEvacuator::UpdateNewRegionWorkload::Process([[maybe_unused]] bool isMain,
713                                                          [[maybe_unused]] uint32_t threadIndex)
714 {
715     if (isYoungGC_) {
716         GetEvacuator()->UpdateNewRegionReference<TriggerGCType::YOUNG_GC, true>(GetRegion());
717     } else {
718         GetEvacuator()->UpdateNewRegionReference<TriggerGCType::OLD_GC, true>(GetRegion());
719     }
720     return true;
721 }
722 
Process(bool isMain,uint32_t threadIndex)723 bool ParallelEvacuator::UpdateAndSweepNewRegionWorkload::Process([[maybe_unused]] bool isMain,
724                                                                  [[maybe_unused]] uint32_t threadIndex)
725 {
726     if (isYoungGC_) {
727         GetEvacuator()->UpdateAndSweepNewRegionReference<TriggerGCType::YOUNG_GC, false>(GetRegion());
728     } else {
729         GetEvacuator()->UpdateAndSweepNewRegionReference<TriggerGCType::OLD_GC, false>(GetRegion());
730     }
731     return true;
732 }
733 
Process(bool isMain,uint32_t threadIndex)734 bool ParallelEvacuator::UpdateNewToOldEvacuationWorkload::Process([[maybe_unused]] bool isMain, uint32_t threadIndex)
735 {
736     if (isYoungGC_) {
737         GetEvacuator()->UpdateNewToOldEvacuationReference<TriggerGCType::YOUNG_GC>(GetRegion(), threadIndex);
738     } else {
739         GetEvacuator()->UpdateNewToOldEvacuationReference<TriggerGCType::OLD_GC>(GetRegion(), threadIndex);
740     }
741     return true;
742 }
743 }  // namespace panda::ecmascript
744