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> ®ion) {
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, ®ion, &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> ®ion) {
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