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 
18 namespace panda::ecmascript {
Marker(Heap *heap)19 Marker::Marker(Heap *heap) : heap_(heap), workManager_(heap->GetWorkManager()) {}
20 
MarkRoots(uint32_t threadId, VMRootVisitType type)21 void 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 
ProcessNewToEden(uint32_t threadId)37 void Marker::ProcessNewToEden(uint32_t threadId)
38 {
39     heap_->EnumerateNewSpaceRegions([this, threadId](Region *region) {this->HandleNewToEdenRSet(threadId, region);});
40     ProcessMarkStack(threadId);
41 }
42 
ProcessNewToEdenNoMarkStack(uint32_t threadId)43 void Marker::ProcessNewToEdenNoMarkStack(uint32_t threadId)
44 {
45     heap_->EnumerateNewSpaceRegions([this, threadId](Region *region) {this->HandleNewToEdenRSet(threadId, region);});
46 }
47 
ProcessOldToNew(uint32_t threadId)48 void Marker::ProcessOldToNew(uint32_t threadId)
49 {
50     heap_->EnumerateOldSpaceRegions([this, threadId](Region *region) {
51         this->HandleOldToNewRSet(threadId, region);
52     });
53     ProcessMarkStack(threadId);
54 }
55 
ProcessOldToNewNoMarkStack(uint32_t threadId)56 void Marker::ProcessOldToNewNoMarkStack(uint32_t threadId)
57 {
58     heap_->EnumerateOldSpaceRegions([this, threadId](Region *region) {
59         this->HandleOldToNewRSet(threadId, region);
60     });
61 }
62 
ProcessOldToNew(uint32_t threadId, Region *region)63 void 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 
ProcessSnapshotRSet(uint32_t threadId)71 void Marker::ProcessSnapshotRSet(uint32_t threadId)
72 {
73     heap_->EnumerateSnapshotSpaceRegions([this, threadId](Region *region) {
74         this->HandleOldToNewRSet(threadId, region);
75     });
76     ProcessMarkStack(threadId);
77 }
78 
ProcessSnapshotRSetNoMarkStack(uint32_t threadId)79 void Marker::ProcessSnapshotRSetNoMarkStack(uint32_t threadId)
80 {
81     heap_->EnumerateSnapshotSpaceRegions([this, threadId](Region *region) {
82         this->HandleOldToNewRSet(threadId, region);
83     });
84 }
85 
MarkJitCodeMap(uint32_t threadId)86 void 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 
ProcessMarkStack(uint32_t threadId)116 void 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 
ProcessIncrementalMarkStack(uint32_t threadId, uint32_t markStepSize)156 void 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 
Initialize()202 void SemiGCMarker::Initialize()
203 {
204     waterLine_ = heap_->GetNewSpace()->GetWaterLine();
205 }
206 
ProcessMarkStack(uint32_t threadId)207 void 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 
ProcessMarkStack(uint32_t threadId)228 void 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 
MarkJitCodeMap(uint32_t threadId)251 void 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 
HandleVisitJitCodeMap(uint32_t threadId, std::map<JSTaggedType, JitCodeVector *> &jitCodeMaps)265 void 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 
AllocateForwardAddress(uint32_t threadId, size_t size, JSHClass *hclass, TaggedObject *object)301 uintptr_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