• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 #ifndef ECMASCRIPT_MEM_PARALLEL_EVACUATOR_INL_H
17 #define ECMASCRIPT_MEM_PARALLEL_EVACUATOR_INL_H
18 
19 #include "ecmascript/mem/parallel_evacuator.h"
20 
21 #include "ecmascript/mem/heap.h"
22 #include "ecmascript/mem/mark_word.h"
23 #include "ecmascript/mem/region-inl.h"
24 #include "ecmascript/taskpool/taskpool.h"
25 
26 namespace panda::ecmascript {
27 // Move regions with a survival rate of more than 75% to new space
28 // Move regions when young space overshoot size is larger than max capacity.
IsWholeRegionEvacuate(Region * region)29 bool ParallelEvacuator::IsWholeRegionEvacuate(Region *region)
30 {
31     if ((static_cast<double>(region->AliveObject()) / region->GetSize()) > MIN_OBJECT_SURVIVAL_RATE &&
32         !region->HasAgeMark()) {
33         return true;
34     }
35     if (heap_->GetFromSpaceDuringEvacuation()->CommittedSizeIsLarge() && !region->HasAgeMark()) {
36         return true;
37     }
38     return false;
39 }
40 
WholeRegionEvacuate(Region * region)41 bool ParallelEvacuator::WholeRegionEvacuate(Region *region)
42 {
43     if (region->IsFreshRegion()) {
44         ASSERT(region->InYoungSpace());
45         return heap_->MoveYoungRegionSync(region);
46     }
47     bool isInYoung = region->InYoungSpace();
48     bool isBelowAgeMark = region->BelowAgeMark();
49     if (isInYoung && !isBelowAgeMark && IsWholeRegionEvacuate(region) && heap_->MoveYoungRegionSync(region)) {
50         return true;
51     }
52     return false;
53 }
54 
55 template <typename Callback>
VisitBodyInObj(TaggedObject * root,ObjectSlot start,ObjectSlot end,Callback callback)56 bool ParallelEvacuator::VisitBodyInObj(
57     TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback)
58 {
59     auto hclass = root->GetClass();
60     ASSERT(!hclass->IsAllTaggedProp());
61     int index = 0;
62     TaggedObject *dst = hclass->GetLayout().GetTaggedObject();
63     auto layout = LayoutInfo::UncheckCast(dst);
64     ObjectSlot realEnd = start;
65     realEnd += layout->GetPropertiesCapacity();
66     end = end > realEnd ? realEnd : end;
67     for (ObjectSlot slot = start; slot < end; slot++) {
68         auto attr = layout->GetAttr(index++);
69         if (attr.IsTaggedRep()) {
70             callback(slot);
71         }
72     }
73     return true;
74 }
75 
UpdateNewToEdenObjectSlot(ObjectSlot & slot)76 bool ParallelEvacuator::UpdateNewToEdenObjectSlot(ObjectSlot &slot)
77 {
78     JSTaggedValue value(slot.GetTaggedType());
79     if (!value.IsHeapObject()) {
80         return false;
81     }
82     TaggedObject *object = value.GetHeapObject();
83     Region *valueRegion = Region::ObjectAddressToRange(object);
84 
85     // It is only update edenSpace object when iterate NewToEdenRSet
86     if (!valueRegion->InEdenSpace()) {
87         return false;
88     }
89     MarkWord markWord(object);
90     if (markWord.IsForwardingAddress()) {
91         TaggedObject *dst = markWord.ToForwardingAddress();
92         if (value.IsWeakForHeapObject()) {
93             dst = JSTaggedValue(dst).CreateAndGetWeakRef().GetRawTaggedObject();
94         }
95         slot.Update(dst);
96     } else {
97         if (value.IsWeakForHeapObject()) {
98             slot.Clear();
99         }
100     }
101     return false;
102 }
103 
UpdateForwardedOldToNewObjectSlot(TaggedObject * object,ObjectSlot & slot,bool isWeak)104 bool ParallelEvacuator::UpdateForwardedOldToNewObjectSlot(TaggedObject *object, ObjectSlot &slot, bool isWeak)
105 {
106     MarkWord markWord(object);
107     if (markWord.IsForwardingAddress()) {
108         TaggedObject *dst = markWord.ToForwardingAddress();
109         if (isWeak) {
110             dst = JSTaggedValue(dst).CreateAndGetWeakRef().GetRawTaggedObject();
111         }
112         slot.Update(dst);
113         Region *dstRegion = Region::ObjectAddressToRange(dst);
114         // Keep oldToNewRSet when object is YoungSpace
115         if (dstRegion->InYoungSpace()) {
116             return true;
117         }
118     } else if (isWeak) {
119         slot.Clear();
120     }
121     return false;
122 }
123 
124 template<bool IsEdenGC>
UpdateOldToNewObjectSlot(ObjectSlot & slot)125 bool ParallelEvacuator::UpdateOldToNewObjectSlot(ObjectSlot &slot)
126 {
127     JSTaggedValue value(slot.GetTaggedType());
128     if (!value.IsHeapObject()) {
129         return false;
130     }
131     TaggedObject *object = value.GetHeapObject();
132     Region *valueRegion = Region::ObjectAddressToRange(object);
133     if constexpr (IsEdenGC) {
134         // only object in EdenSpace will be collect in EdenGC
135         if (valueRegion->InEdenSpace()) {
136             return UpdateForwardedOldToNewObjectSlot(object, slot, value.IsWeakForHeapObject());
137         } else {
138             // Keep oldToNewRSet when object is YoungSpace
139             return valueRegion->InYoungSpace();
140         }
141     } else {
142         // It is only update old to new object when iterate OldToNewRSet
143         if (valueRegion->InGeneralNewSpace()) {
144             if (!valueRegion->InNewToNewSet()) {
145                 return UpdateForwardedOldToNewObjectSlot(object, slot, value.IsWeakForHeapObject());
146             }
147             // move region from fromspace to tospace
148             if (valueRegion->Test(object)) {
149                 return true;
150             }
151             if (value.IsWeakForHeapObject()) {
152                 slot.Clear();
153             }
154         }
155     }
156     return false;
157 }
158 
UpdateObjectSlot(ObjectSlot & slot)159 void ParallelEvacuator::UpdateObjectSlot(ObjectSlot &slot)
160 {
161     JSTaggedValue value(slot.GetTaggedType());
162     if (value.IsHeapObject()) {
163         if (value.IsInSharedHeap()) {
164             return;
165         }
166         if (value.IsWeakForHeapObject()) {
167             return UpdateWeakObjectSlot(value.GetTaggedWeakRef(), slot);
168         }
169         TaggedObject *object = value.GetTaggedObject();
170         MarkWord markWord(object);
171         if (markWord.IsForwardingAddress()) {
172             TaggedObject *dst = markWord.ToForwardingAddress();
173             slot.Update(dst);
174         }
175     }
176 }
177 
UpdateWeakObjectSlot(TaggedObject * value,ObjectSlot & slot)178 void ParallelEvacuator::UpdateWeakObjectSlot(TaggedObject *value, ObjectSlot &slot)
179 {
180     Region *objectRegion = Region::ObjectAddressToRange(value);
181     if (objectRegion->InSharedHeap()) {
182         return;
183     }
184 
185     TaggedObject *dst = UpdateAddressAfterEvacation(value);
186     if (dst == value) {
187         return;
188     }
189     if (dst == nullptr) {
190         slot.Clear();
191         return;
192     }
193     auto weakRef = JSTaggedValue(dst).CreateAndGetWeakRef().GetRawTaggedObject();
194     slot.Update(weakRef);
195 }
196 
197 template<TriggerGCType gcType>
UpdateObjectSlotOpt(ObjectSlot & slot)198 void ParallelEvacuator::UpdateObjectSlotOpt(ObjectSlot &slot)
199 {
200     JSTaggedValue value(slot.GetTaggedType());
201     if (value.IsHeapObject()) {
202         if (UpdateWeakObjectSlotOpt<gcType>(value, slot)) {
203             return;
204         }
205         MarkWord markWord(value.GetTaggedObject());
206         if (markWord.IsForwardingAddress()) {
207             auto dst = reinterpret_cast<JSTaggedType>(markWord.ToForwardingAddress());
208             slot.Update(dst);
209         }
210     }
211 }
212 
213 template<TriggerGCType gcType>
UpdateWeakObjectSlotOpt(JSTaggedValue value,ObjectSlot & slot)214 bool ParallelEvacuator::UpdateWeakObjectSlotOpt(JSTaggedValue value, ObjectSlot &slot)
215 {
216     // if need to update slot as non-weak then return FALSE, else return TRUE
217     Region *objectRegion = Region::ObjectAddressToRange(value.GetRawData());
218     ASSERT(objectRegion != nullptr);
219     if constexpr (gcType == TriggerGCType::YOUNG_GC) {
220         if (!objectRegion->InGeneralNewSpace()) {
221             return true;
222         }
223     } else if constexpr (gcType == TriggerGCType::OLD_GC) {
224         if (!objectRegion->InGeneralNewSpaceOrCSet()) {
225             if (value.IsWeakForHeapObject() && !objectRegion->InSharedHeap() &&
226                     (objectRegion->GetMarkGCBitset() == nullptr || !objectRegion->Test(value.GetRawData()))) {
227                 slot.Clear();
228             }
229             return true;
230         }
231     } else {
232         LOG_GC(FATAL) << "UpdateWeakObjectSlotOpt: not support gcType yet";
233         UNREACHABLE();
234     }
235     if (objectRegion->InNewToNewSet()) {
236         if (value.IsWeakForHeapObject() && !objectRegion->Test(value.GetRawData())) {
237             slot.Clear();
238         }
239         return true;
240     }
241     if (value.IsWeakForHeapObject()) {
242         MarkWord markWord(value.GetWeakReferent());
243         if (markWord.IsForwardingAddress()) {
244             auto dst = static_cast<JSTaggedType>(ToUintPtr(markWord.ToForwardingAddress()));
245             slot.Update(JSTaggedValue(dst).CreateAndGetWeakRef().GetRawData());
246         } else {
247             slot.Clear();
248         }
249         return true;
250     }
251     return false;
252 }
253 
UpdateLocalToShareRSet(TaggedObject * object,JSHClass * cls)254 void ParallelEvacuator::UpdateLocalToShareRSet(TaggedObject *object, JSHClass *cls)
255 {
256     Region *region = Region::ObjectAddressToRange(object);
257     ASSERT(!region->InSharedHeap());
258     auto callbackWithCSet = [this, region](TaggedObject *root, ObjectSlot start, ObjectSlot end, VisitObjectArea area) {
259         if (area == VisitObjectArea::IN_OBJECT) {
260             if (VisitBodyInObj(root, start, end, [&](ObjectSlot slot) { SetLocalToShareRSet(slot, region); })) {
261                 return;
262             };
263         }
264         for (ObjectSlot slot = start; slot < end; slot++) {
265             SetLocalToShareRSet(slot, region);
266         }
267     };
268     ObjectXRay::VisitObjectBody<VisitType::OLD_GC_VISIT>(object, cls, callbackWithCSet);
269 }
270 
SetLocalToShareRSet(ObjectSlot slot,Region * region)271 void ParallelEvacuator::SetLocalToShareRSet(ObjectSlot slot, Region *region)
272 {
273     ASSERT(!region->InSharedHeap());
274     JSTaggedType value = slot.GetTaggedType();
275     if (!JSTaggedValue(value).IsHeapObject()) {
276         return;
277     }
278     Region *valueRegion = Region::ObjectAddressToRange(value);
279     if (valueRegion->InSharedSweepableSpace()) {
280         region->AtomicInsertLocalToShareRSet(slot.SlotAddress());
281     }
282 }
283 
284 template<bool SetEdenObject>
SetObjectFieldRSet(TaggedObject * object,JSHClass * cls)285 void ParallelEvacuator::SetObjectFieldRSet(TaggedObject *object, JSHClass *cls)
286 {
287     Region *region = Region::ObjectAddressToRange(object);
288     auto callbackWithCSet = [this, region](TaggedObject *root, ObjectSlot start, ObjectSlot end, VisitObjectArea area) {
289         if (area == VisitObjectArea::IN_OBJECT) {
290             if (VisitBodyInObj(root, start, end,
291                                [&](ObjectSlot slot) { SetObjectRSet<SetEdenObject>(slot, region); })) {
292                 return;
293             };
294         }
295         for (ObjectSlot slot = start; slot < end; slot++) {
296             SetObjectRSet<SetEdenObject>(slot, region);
297         }
298     };
299     ObjectXRay::VisitObjectBody<VisitType::OLD_GC_VISIT>(object, cls, callbackWithCSet);
300 }
301 
302 template<bool SetEdenObject>
SetObjectRSet(ObjectSlot slot,Region * region)303 void ParallelEvacuator::SetObjectRSet(ObjectSlot slot, Region *region)
304 {
305     JSTaggedType value = slot.GetTaggedType();
306     if (!JSTaggedValue(value).IsHeapObject()) {
307         return;
308     }
309     Region *valueRegion = Region::ObjectAddressToRange(value);
310     if constexpr (SetEdenObject) {
311         if (region->InYoungSpace() && valueRegion->InEdenSpace()) {
312             region->AtomicInsertNewToEdenRSet(slot.SlotAddress());
313         } else if (valueRegion->InSharedSweepableSpace()) {
314             region->AtomicInsertLocalToShareRSet(slot.SlotAddress());
315         }
316     } else {
317         if (valueRegion->InGeneralNewSpace()) {
318             region->InsertOldToNewRSet(slot.SlotAddress());
319         }  else if (valueRegion->InSharedSweepableSpace()) {
320             region->InsertLocalToShareRSet(slot.SlotAddress());
321         } else if (valueRegion->InCollectSet() || JSTaggedValue(value).IsWeakForHeapObject()) {
322             region->InsertCrossRegionRSet(slot.SlotAddress());
323         }
324     }
325 }
326 
GetWorkloadSafe()327 std::unique_ptr<ParallelEvacuator::Workload> ParallelEvacuator::GetWorkloadSafe()
328 {
329     LockHolder holder(mutex_);
330     std::unique_ptr<Workload> workload;
331     if (!workloads_.empty()) {
332         workload = std::move(workloads_.back());
333         workloads_.pop_back();
334     }
335     return workload;
336 }
337 
AddWorkload(std::unique_ptr<Workload> workload)338 void ParallelEvacuator::AddWorkload(std::unique_ptr<Workload> workload)
339 {
340     workloads_.emplace_back(std::move(workload));
341 }
342 
UpdateAddressAfterEvacation(TaggedObject * oldAddress)343 TaggedObject* ParallelEvacuator::UpdateAddressAfterEvacation(TaggedObject *oldAddress)
344 {
345     Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast<TaggedObject *>(oldAddress));
346     if (!objectRegion) {
347         return nullptr;
348     }
349     if (heap_->IsEdenMark()) {
350         if (!objectRegion->InEdenSpace()) {
351             return oldAddress;
352         }
353         MarkWord markWord(oldAddress);
354         if (markWord.IsForwardingAddress()) {
355             return markWord.ToForwardingAddress();
356         }
357         return nullptr;
358     }
359     if (objectRegion->InGeneralNewSpaceOrCSet()) {
360         if (objectRegion->InNewToNewSet()) {
361             if (objectRegion->Test(oldAddress)) {
362                 return oldAddress;
363             }
364         } else {
365             MarkWord markWord(oldAddress);
366             if (markWord.IsForwardingAddress()) {
367                 return markWord.ToForwardingAddress();
368             }
369         }
370         return nullptr;
371     }
372     if (heap_->IsConcurrentFullMark()) {
373         if (objectRegion->GetMarkGCBitset() == nullptr || !objectRegion->Test(oldAddress)) {
374             return nullptr;
375         }
376     }
377     return oldAddress;
378 }
379 
CalculateEvacuationThreadNum()380 int ParallelEvacuator::CalculateEvacuationThreadNum()
381 {
382     uint32_t length = workloads_.size();
383     uint32_t regionPerThread = 8;
384     uint32_t maxThreadNum = std::min(heap_->GetMaxEvacuateTaskCount(),
385         Taskpool::GetCurrentTaskpool()->GetTotalThreadNum());
386     return static_cast<int>(std::min(std::max(1U, length / regionPerThread), maxThreadNum));
387 }
388 
CalculateUpdateThreadNum()389 int ParallelEvacuator::CalculateUpdateThreadNum()
390 {
391     uint32_t length = workloads_.size();
392     double regionPerThread = 1.0 / 4;
393     length = std::pow(length, regionPerThread);
394     uint32_t maxThreadNum = std::min(heap_->GetMaxEvacuateTaskCount(),
395         Taskpool::GetCurrentTaskpool()->GetTotalThreadNum());
396     return static_cast<int>(std::min(std::max(1U, length), maxThreadNum));
397 }
398 }  // namespace panda::ecmascript
399 #endif  // ECMASCRIPT_MEM_PARALLEL_EVACUATOR_INL_H
400