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#include "ecmascript/mem/shared_heap/shared_full_gc.h"
17#include "ecmascript/mem/shared_heap/shared_concurrent_marker.h"
18#include "ecmascript/mem/shared_heap/shared_concurrent_sweeper.h"
19#include "ecmascript/mem/shared_heap/shared_gc_marker-inl.h"
20#include "ecmascript/runtime.h"
21
22namespace panda::ecmascript {
23void SharedFullGC::RunPhases()
24{
25    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedFullGC::RunPhases"
26        + std::to_string(static_cast<int>(sHeap_->GetEcmaGCStats()->GetGCReason()))
27        + ";Sensitive" + std::to_string(static_cast<int>(sHeap_->GetSensitiveStatus()))
28        + ";IsInBackground" + std::to_string(sHeap_->IsInBackground())
29        + ";Startup" + std::to_string(sHeap_->OnStartupEvent())
30        + ";Old" + std::to_string(sHeap_->GetOldSpace()->GetCommittedSize())
31        + ";huge" + std::to_string(sHeap_->GetHugeObjectSpace()->GetCommittedSize())
32        + ";NonMov" + std::to_string(sHeap_->GetNonMovableSpace()->GetCommittedSize())
33        + ";TotCommit" + std::to_string(sHeap_->GetCommittedSize()));
34    TRACE_GC(GCStats::Scope::ScopeId::TotalGC, sHeap_->GetEcmaGCStats());
35    Initialize();
36    Mark();
37    Sweep();
38    Finish();
39}
40
41void SharedFullGC::Initialize()
42{
43    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedFullGC::Initialize");
44    TRACE_GC(GCStats::Scope::ScopeId::Initialize, sHeap_->GetEcmaGCStats());
45    sHeap_->Prepare(true);
46    if (UNLIKELY(sHeap_->CheckOngoingConcurrentMarking())) {
47        // Concurrent shared mark should always trigger shared gc without moving.
48        sHeap_->GetConcurrentMarker()->Reset(true);
49    }
50    sHeap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
51        current->ClearMarkGCBitset();
52    });
53    sHeap_->EnumerateOldSpaceRegions([](Region *current) {
54        ASSERT(current->InSharedSweepableSpace());
55        current->ResetAliveObject();
56    });
57    sWorkManager_->Initialize(TriggerGCType::SHARED_FULL_GC, SharedParallelMarkPhase::SHARED_COMPRESS_TASK);
58}
59
60void SharedFullGC::Mark()
61{
62    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedFullGC::Mark");
63    TRACE_GC(GCStats::Scope::ScopeId::Mark, sHeap_->GetEcmaGCStats());
64    SharedGCMovableMarker *marker = sHeap_->GetSharedGCMovableMarker();
65
66    marker->MarkRoots(DAEMON_THREAD_INDEX, SharedMarkType::NOT_CONCURRENT_MARK, VMRootVisitType::UPDATE_ROOT);
67    marker->DoMark<SharedMarkType::NOT_CONCURRENT_MARK>(DAEMON_THREAD_INDEX);
68    marker->MergeBackAndResetRSetWorkListHandler();
69    sHeap_->WaitRunningTaskFinished();
70}
71
72void SharedFullGC::Sweep()
73{
74    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedFullGC::Sweep");
75    TRACE_GC(GCStats::Scope::ScopeId::Sweep, sHeap_->GetEcmaGCStats());
76    UpdateRecordWeakReference();
77    WeakRootVisitor gcUpdateWeak = [](TaggedObject *header) {
78        Region *objectRegion = Region::ObjectAddressToRange(header);
79        if (!objectRegion) {
80            LOG_GC(ERROR) << "SharedFullGC updateWeakReference: region is nullptr, header is " << header;
81            return reinterpret_cast<TaggedObject *>(ToUintPtr(nullptr));
82        }
83        if (objectRegion->InSharedOldSpace()) {
84            MarkWord markWord(header);
85            if (markWord.IsForwardingAddress()) {
86                return markWord.ToForwardingAddress();
87            }
88            return reinterpret_cast<TaggedObject *>(ToUintPtr(nullptr));
89        }
90        if (!objectRegion->InSharedSweepableSpace() || objectRegion->Test(header)) {
91            return header;
92        }
93        return reinterpret_cast<TaggedObject *>(ToUintPtr(nullptr));
94    };
95    auto stringTableCleaner = Runtime::GetInstance()->GetEcmaStringTable()->GetCleaner();
96    stringTableCleaner->PostSweepWeakRefTask(gcUpdateWeak);
97    Runtime::GetInstance()->ProcessNativeDeleteInSharedGC(gcUpdateWeak);
98    Runtime::GetInstance()->ProcessSharedNativeDelete(gcUpdateWeak);
99
100    Runtime::GetInstance()->GCIterateThreadList([&](JSThread *thread) {
101        ASSERT(!thread->IsInRunningState());
102        thread->IterateWeakEcmaGlobalStorage(gcUpdateWeak, GCKind::SHARED_GC);
103        const_cast<Heap*>(thread->GetEcmaVM()->GetHeap())->ResetTlab();
104        thread->ClearContextCachedConstantPool();
105    });
106
107    stringTableCleaner->JoinAndWaitSweepWeakRefTask(gcUpdateWeak);
108    sHeap_->GetSweeper()->Sweep(true);
109    sHeap_->GetSweeper()->PostTask(true);
110}
111
112void SharedFullGC::Finish()
113{
114    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SharedFullGC::Finish");
115    TRACE_GC(GCStats::Scope::ScopeId::Finish, sHeap_->GetEcmaGCStats());
116    sHeap_->SwapOldSpace();
117    sWorkManager_->Finish();
118    if (!isAppspawn_) {
119        sHeap_->Reclaim(TriggerGCType::SHARED_FULL_GC);
120    } else {
121        sHeap_->ReclaimForAppSpawn();
122    }
123
124    sHeap_->GetSweeper()->TryFillSweptRegion();
125}
126
127void SharedFullGC::UpdateRecordWeakReference()
128{
129    auto totalThreadCount = Taskpool::GetCurrentTaskpool()->GetTotalThreadNum() + 1;
130    for (uint32_t i = 0; i < totalThreadCount; i++) {
131        ProcessQueue *queue = sHeap_->GetWorkManager()->GetWeakReferenceQueue(i);
132
133        while (true) {
134            auto obj = queue->PopBack();
135            if (UNLIKELY(obj == nullptr)) {
136                break;
137            }
138            ObjectSlot slot(ToUintPtr(obj));
139            JSTaggedValue value(slot.GetTaggedType());
140            ASSERT(value.IsWeak());
141            auto header = value.GetTaggedWeakRef();
142            Region *objectRegion = Region::ObjectAddressToRange(header);
143            if (!objectRegion->InSharedOldSpace()) {
144                if (!objectRegion->Test(header)) {
145                    slot.Clear();
146                }
147            } else {
148                MarkWord markWord(header);
149                if (markWord.IsForwardingAddress()) {
150                    TaggedObject *dst = markWord.ToForwardingAddress();
151                    auto weakRef = JSTaggedValue(JSTaggedValue(dst).CreateAndGetWeakRef()).GetRawTaggedObject();
152                    slot.Update(weakRef);
153                } else {
154                    slot.Clear();
155                }
156            }
157        }
158    }
159}
160
161bool SharedFullGC::HasEvacuated(Region *region)
162{
163    auto marker = reinterpret_cast<SharedGCMovableMarker *>(sHeap_->GetSharedGCMovableMarker());
164    return marker->NeedEvacuate(region);
165}
166
167void SharedFullGC::ResetWorkManager(SharedGCWorkManager *sWorkManager)
168{
169    sWorkManager_ = sWorkManager;
170}
171}  // namespace panda::ecmascript
172