1/*
2 * Copyright (c) 2021 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#include "ecmascript/mem/incremental_marker.h"
16#include "ecmascript/mem/parallel_marker-inl.h"
17
18namespace panda::ecmascript {
19Marker::Marker(Heap *heap) : heap_(heap), workManager_(heap->GetWorkManager()) {}
20
21void Marker::MarkRoots(uint32_t threadId, VMRootVisitType type)
22{
23    TRACE_GC(GCStats::Scope::ScopeId::MarkRoots, heap_->GetEcmaVM()->GetEcmaGCStats());
24    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::MarkRoots");
25    ObjectXRay::VisitVMRoots(
26        heap_->GetEcmaVM(),
27        [this, threadId](Root type, ObjectSlot slot) {this->HandleRoots(threadId, type, slot);},
28        [this, threadId](Root type, ObjectSlot start, ObjectSlot end) {
29            this->HandleRangeRoots(threadId, type, start, end);
30        },
31        [this](Root type, ObjectSlot base, ObjectSlot derived, uintptr_t baseOldObject) {
32            this->HandleDerivedRoots(type, base, derived, baseOldObject);
33        }, type);
34    workManager_->PushWorkNodeToGlobal(threadId, false);
35}
36
37void Marker::ProcessNewToEden(uint32_t threadId)
38{
39    heap_->EnumerateNewSpaceRegions([this, threadId](Region *region) {this->HandleNewToEdenRSet(threadId, region);});
40    ProcessMarkStack(threadId);
41}
42
43void Marker::ProcessNewToEdenNoMarkStack(uint32_t threadId)
44{
45    heap_->EnumerateNewSpaceRegions([this, threadId](Region *region) {this->HandleNewToEdenRSet(threadId, region);});
46}
47
48void Marker::ProcessOldToNew(uint32_t threadId)
49{
50    heap_->EnumerateOldSpaceRegions([this, threadId](Region *region) {
51        this->HandleOldToNewRSet(threadId, region);
52    });
53    ProcessMarkStack(threadId);
54}
55
56void Marker::ProcessOldToNewNoMarkStack(uint32_t threadId)
57{
58    heap_->EnumerateOldSpaceRegions([this, threadId](Region *region) {
59        this->HandleOldToNewRSet(threadId, region);
60    });
61}
62
63void Marker::ProcessOldToNew(uint32_t threadId, Region *region)
64{
65    heap_->EnumerateOldSpaceRegions([this, threadId](Region *region) {
66        this->HandleOldToNewRSet(threadId, region);
67        }, region);
68    ProcessMarkStack(threadId);
69}
70
71void Marker::ProcessSnapshotRSet(uint32_t threadId)
72{
73    heap_->EnumerateSnapshotSpaceRegions([this, threadId](Region *region) {
74        this->HandleOldToNewRSet(threadId, region);
75    });
76    ProcessMarkStack(threadId);
77}
78
79void Marker::ProcessSnapshotRSetNoMarkStack(uint32_t threadId)
80{
81    heap_->EnumerateSnapshotSpaceRegions([this, threadId](Region *region) {
82        this->HandleOldToNewRSet(threadId, region);
83    });
84}
85
86void NonMovableMarker::MarkJitCodeMap(uint32_t threadId)
87{
88    // To keep MachineCode objects alive (for dump) before JsError object be free, we have to know which JsError is
89    // alive first. So this method must be call after all other mark work finish.
90    TRACE_GC(GCStats::Scope::ScopeId::MarkRoots, heap_->GetEcmaVM()->GetEcmaGCStats());
91    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::MarkJitCodeMap");
92    if (!heap_->IsFullMark()) {
93        return;
94    }
95    JitCodeMapVisitor visitor = [this, threadId](std::map<JSTaggedType, JitCodeVector *> &jitCodeMaps) {
96        auto it = jitCodeMaps.begin();
97        while (it != jitCodeMaps.end()) {
98            JSTaggedType jsError = it->first;
99            Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(jsError));
100            if (!objectRegion->Test(reinterpret_cast<TaggedObject *>(jsError))) {
101                ++it;
102                continue;
103            }
104            for (auto &jitCodeMap : *(it->second)) {
105                auto &jitCode = std::get<0>(jitCodeMap);
106                MarkObject(threadId, jitCode);
107            }
108            ++it;
109        }
110    };
111    ObjectXRay::VisitJitCodeMap(heap_->GetEcmaVM(), visitor);
112    ProcessMarkStack(threadId);
113    heap_->WaitRunningTaskFinished();
114}
115
116void NonMovableMarker::ProcessMarkStack(uint32_t threadId)
117{
118    bool isFullMark = heap_->IsConcurrentFullMark();
119    auto cb = [&](ObjectSlot s, Region *rootRegion, bool needBarrier) {
120        MarkValue(threadId, s, rootRegion, needBarrier);
121    };
122    EcmaObjectRangeVisitor visitor = [this, threadId, isFullMark, cb](TaggedObject *root, ObjectSlot start,
123                                                                      ObjectSlot end, VisitObjectArea area) {
124        Region *rootRegion = Region::ObjectAddressToRange(root);
125        bool needBarrier = isFullMark && !rootRegion->InGeneralNewSpaceOrCSet();
126        if (area == VisitObjectArea::IN_OBJECT) {
127            if (VisitBodyInObj(root, start, end, needBarrier, cb)) {
128                return;
129            }
130        }
131        for (ObjectSlot slot = start; slot < end; slot++) {
132            MarkValue(threadId, slot, rootRegion, needBarrier);
133        }
134    };
135    SemiSpace *newSpace = heap_->GetNewSpace();
136    TaggedObject *obj = nullptr;
137    while (workManager_->Pop(threadId, &obj)) {
138        Region *region = Region::ObjectAddressToRange(obj);
139        if (region->IsHalfFreshRegion()) {
140            ASSERT(region->InYoungSpace());
141            if (newSpace->IsFreshObjectInHalfFreshRegion(obj)) {
142                // Fresh object do not need to visit body.
143                continue;
144            }
145        }
146
147        JSHClass *jsHclass = obj->SynchronizedGetClass();
148        ASSERT(!region->IsFreshRegion());
149        auto size = jsHclass->SizeFromJSHClass(obj);
150        region->IncreaseAliveObjectSafe(size);
151        MarkObject(threadId, jsHclass);
152        ObjectXRay::VisitObjectBody<VisitType::OLD_GC_VISIT>(obj, jsHclass, visitor);
153    }
154}
155
156void NonMovableMarker::ProcessIncrementalMarkStack(uint32_t threadId, uint32_t markStepSize)
157{
158    bool isFullMark = heap_->IsConcurrentFullMark();
159    uint32_t visitAddrNum = 0;
160    auto cb = [&](ObjectSlot s, Region *rootRegion, bool needBarrier) {
161        MarkValue(threadId, s, rootRegion, needBarrier);
162    };
163    EcmaObjectRangeVisitor visitor = [this, threadId, isFullMark, &visitAddrNum, cb](TaggedObject *root,
164                                                                                     ObjectSlot start,
165                                                                                     ObjectSlot end,
166                                                                                     VisitObjectArea area) {
167        Region *rootRegion = Region::ObjectAddressToRange(root);
168        visitAddrNum += end.SlotAddress() - start.SlotAddress();
169        bool needBarrier = isFullMark && !rootRegion->InGeneralNewSpaceOrCSet();
170        if (area == VisitObjectArea::IN_OBJECT) {
171            if (VisitBodyInObj(root, start, end, needBarrier, cb)) {
172                return;
173            }
174        }
175        for (ObjectSlot slot = start; slot < end; slot++) {
176            MarkValue(threadId, slot, rootRegion, needBarrier);
177        }
178    };
179    TaggedObject *obj = nullptr;
180    double startTime = heap_->GetIncrementalMarker()->GetCurrentTimeInMs();
181    double costTime = startTime;
182    while (workManager_->Pop(threadId, &obj)) {
183        JSHClass *jsHclass = obj->GetClass();
184        Region *region = Region::ObjectAddressToRange(obj);
185        auto size = jsHclass->SizeFromJSHClass(obj);
186        region->IncreaseAliveObjectSafe(size);
187        MarkObject(threadId, jsHclass);
188        ObjectXRay::VisitObjectBody<VisitType::OLD_GC_VISIT>(obj, jsHclass, visitor);
189        if (heap_->GetIncrementalMarker()->IsTriggeredIncrementalMark() && visitAddrNum >= markStepSize) {
190            costTime = heap_->GetIncrementalMarker()->GetCurrentTimeInMs() - startTime;
191            heap_->GetIncrementalMarker()->UpdateMarkingSpeed(visitAddrNum, costTime);
192            return;
193        }
194    }
195    if (heap_->GetJSThread()->IsMarking() && heap_->GetIncrementalMarker()->IsTriggeredIncrementalMark()) {
196        costTime = heap_->GetIncrementalMarker()->GetCurrentTimeInMs() - startTime;
197        heap_->GetIncrementalMarker()->UpdateMarkingSpeed(visitAddrNum, costTime);
198        heap_->GetIncrementalMarker()->SetMarkingFinished(true);
199    }
200}
201
202void SemiGCMarker::Initialize()
203{
204    waterLine_ = heap_->GetNewSpace()->GetWaterLine();
205}
206
207void SemiGCMarker::ProcessMarkStack(uint32_t threadId)
208{
209    auto cb = [&](ObjectSlot s, TaggedObject *root) { MarkValue(threadId, root, s); };
210    EcmaObjectRangeVisitor visitor = [this, threadId, cb](TaggedObject *root, ObjectSlot start,
211                                                          ObjectSlot end, VisitObjectArea area) {
212        if (area == VisitObjectArea::IN_OBJECT) {
213            if (VisitBodyInObj(root, start, end, cb)) {
214                return;
215            }
216        }
217        for (ObjectSlot slot = start; slot < end; slot++) {
218            MarkValue(threadId, root, slot);
219        }
220    };
221    TaggedObject *obj = nullptr;
222    while (workManager_->Pop(threadId, &obj)) {
223        auto jsHclass = obj->GetClass();
224        ObjectXRay::VisitObjectBody<VisitType::SEMI_GC_VISIT>(obj, jsHclass, visitor);
225    }
226}
227
228void CompressGCMarker::ProcessMarkStack(uint32_t threadId)
229{
230    auto cb = [&](ObjectSlot s, [[maybe_unused]] TaggedObject *root) { MarkValue(threadId, s); };
231    EcmaObjectRangeVisitor visitor = [this, threadId, cb](TaggedObject *root, ObjectSlot start,
232                                                          ObjectSlot end, VisitObjectArea area) {
233        if (area == VisitObjectArea::IN_OBJECT) {
234            if (VisitBodyInObj(root, start, end, cb)) {
235                return;
236            }
237        }
238        for (ObjectSlot slot = start; slot < end; slot++) {
239            MarkValue(threadId, slot);
240        }
241    };
242    TaggedObject *obj = nullptr;
243    while (workManager_->Pop(threadId, &obj)) {
244        auto jsHClass = obj->GetClass();
245        ObjectSlot objectSlot(ToUintPtr(obj));
246        MarkObject(threadId, jsHClass, objectSlot);
247        ObjectXRay::VisitObjectBody<VisitType::OLD_GC_VISIT>(obj, jsHClass, visitor);
248    }
249}
250
251void CompressGCMarker::MarkJitCodeMap(uint32_t threadId)
252{
253    // To keep MachineCode objects alive (for dump) before JsError object be free, we have to know which JsError is
254    // alive first. So this method must be call after all other mark work finish.
255    TRACE_GC(GCStats::Scope::ScopeId::MarkRoots, heap_->GetEcmaVM()->GetEcmaGCStats());
256    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "GC::MarkJitCodeMap");
257    ObjectXRay::VisitJitCodeMap(heap_->GetEcmaVM(),
258        [this, threadId](std::map<JSTaggedType, JitCodeVector *> &jitCodeMaps) {
259            this->HandleVisitJitCodeMap(threadId, jitCodeMaps);
260        });
261    ProcessMarkStack(threadId);
262    heap_->WaitRunningTaskFinished();
263}
264
265void CompressGCMarker::HandleVisitJitCodeMap(uint32_t threadId, std::map<JSTaggedType, JitCodeVector *> &jitCodeMaps)
266{
267    auto it = jitCodeMaps.begin();
268    while (it != jitCodeMaps.end()) {
269        JSTaggedType jsError = it->first;
270        auto jsErrorObj = reinterpret_cast<TaggedObject *>(jsError);
271        Region *objectRegion = Region::ObjectAddressToRange(jsErrorObj);
272        auto jitCodeVec = it->second;
273        if (!NeedEvacuate(objectRegion)) {
274            if (!objectRegion->InSharedHeap() && !objectRegion->Test(jsErrorObj)) {
275                delete it->second;
276                it = jitCodeMaps.erase(it);
277                continue;
278            }
279        } else {
280            MarkWord markWord(jsErrorObj);
281            if (markWord.IsForwardingAddress()) {
282                TaggedObject *dst = markWord.ToForwardingAddress();
283                jitCodeMaps.emplace(JSTaggedValue(dst).GetRawData(), it->second);
284                it = jitCodeMaps.erase(it);
285            } else {
286                delete it->second;
287                it = jitCodeMaps.erase(it);
288                continue;
289            }
290        }
291        for (auto &jitCodeMap : *jitCodeVec) {
292            auto &jitCode = std::get<0>(jitCodeMap);
293            auto obj = static_cast<TaggedObject *>(jitCode);
294            // jitcode is MachineCode, and MachineCode is in the MachineCode space, will not be evacute.
295            MarkObject(threadId, obj, ObjectSlot(reinterpret_cast<uintptr_t>(&jitCode)));
296        }
297        ++it;
298    }
299}
300
301uintptr_t CompressGCMarker::AllocateForwardAddress(uint32_t threadId, size_t size, JSHClass *hclass,
302                                                   TaggedObject *object)
303{
304    if (!isAppSpawn_) {
305        bool isPromoted = true;
306        return AllocateDstSpace(threadId, size, isPromoted);
307    }
308    if (Heap::ShouldMoveToRoSpace(hclass, object)) {
309        return AllocateReadOnlySpace(size);
310    } else {
311        return AllocateAppSpawnSpace(size);
312    }
313}
314}  // namespace panda::ecmascript
315