• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef ECMASCRIPT_MEM_REGION_H
17 #define ECMASCRIPT_MEM_REGION_H
18 
19 #include "ecmascript/base/aligned_struct.h"
20 #include "ecmascript/base/asan_interface.h"
21 #include "ecmascript/js_tagged_value.h"
22 #include "ecmascript/mem/free_object_list.h"
23 #include "ecmascript/mem/gc_bitset.h"
24 #include "ecmascript/mem/remembered_set.h"
25 #include "ecmascript/mem/mem_common.h"
26 #include "ecmascript/platform/map.h"
27 
28 #include "libpandabase/os/mutex.h"
29 #include "libpandabase/utils/aligned_storage.h"
30 
31 #include "securec.h"
32 
33 namespace panda {
34 namespace ecmascript {
35 class JSThread;
36 
37 enum RegionSpaceFlag {
38     UNINITIALIZED = 0,
39     // We should avoid using the lower 3 bits (bits 0 to 2).
40     // If ZAP_MEM is enabled, the value of the lower 3 bits conflicts with the INVALID_VALUE.
41 
42     // Bits 3 to 7 are reserved to denote the space where the region is located.
43     IN_YOUNG_SPACE = 0x08,
44     IN_SNAPSHOT_SPACE = 0x09,
45     IN_HUGE_OBJECT_SPACE = 0x0A,
46     IN_OLD_SPACE = 0x0B,
47     IN_NON_MOVABLE_SPACE = 0x0C,
48     IN_MACHINE_CODE_SPACE = 0x0D,
49     IN_READ_ONLY_SPACE = 0x0E,
50     IN_APPSPAWN_SPACE = 0X0F,
51 
52     VALID_SPACE_MASK = 0xFF,
53 };
54 
55 enum RegionGCFlags {
56     // We should avoid using the lower 3 bits (bits 0 to 2).
57     // If ZAP_MEM is enabled, the value of the lower 3 bits conflicts with the INVALID_VALUE.
58 
59     // Below flags are used for GC, and each flag has a dedicated bit starting from the 3rd bit.
60     NEVER_EVACUATE = 1 << 3,
61     HAS_AGE_MARK = 1 << 4,
62     BELOW_AGE_MARK = 1 << 5,
63     IN_COLLECT_SET = 1 << 6,
64     IN_NEW_TO_NEW_SET = 1 << 7,
65     // Bits 8 to 10 (the lower 3 bits for the next byte) are also excluded for the sake of
66     // INVALID_VALUE in ZAP_MEM.
67     HAS_BEEN_SWEPT = 1 << 11,
68     NEED_RELOCATE = 1 << 12,
69 };
70 
ToSpaceTypeName(uint8_t space)71 static inline std::string ToSpaceTypeName(uint8_t space)
72 {
73     switch (space) {
74         case RegionSpaceFlag::IN_YOUNG_SPACE:
75             return "young space";
76         case RegionSpaceFlag::IN_SNAPSHOT_SPACE:
77             return "snapshot space";
78         case RegionSpaceFlag::IN_HUGE_OBJECT_SPACE:
79             return "huge object space";
80         case RegionSpaceFlag::IN_OLD_SPACE:
81             return "old space";
82         case RegionSpaceFlag::IN_NON_MOVABLE_SPACE:
83             return "non movable space";
84         case RegionSpaceFlag::IN_MACHINE_CODE_SPACE:
85             return "machine code space";
86         case RegionSpaceFlag::IN_READ_ONLY_SPACE:
87             return "read only space";
88         case RegionSpaceFlag::IN_APPSPAWN_SPACE:
89             return "appspawn space";
90         default:
91             return "invalid space";
92     }
93 }
94 
95 // |---------------------------------------------------------------------------------------|
96 // |                                   Region (256 kb)                                     |
97 // |---------------------------------|--------------------------------|--------------------|
98 // |     Head (sizeof(Region))       |         Mark bitset (4kb)      |      Data          |
99 // |---------------------------------|--------------------------------|--------------------|
100 
101 class Region {
102 public:
Region(JSThread * thread,uintptr_t allocateBase,uintptr_t begin,uintptr_t end,RegionSpaceFlag spaceType)103     Region(JSThread *thread, uintptr_t allocateBase, uintptr_t begin, uintptr_t end, RegionSpaceFlag spaceType)
104         : packedData_(begin, end, spaceType),
105           thread_(thread),
106           allocateBase_(allocateBase),
107           end_(end),
108           highWaterMark_(end),
109           aliveObject_(0),
110           wasted_(0),
111           snapshotData_(0)
112     {
113         lock_ = new os::memory::Mutex();
114     }
115 
116     ~Region() = default;
117 
118     NO_COPY_SEMANTIC(Region);
119     NO_MOVE_SEMANTIC(Region);
120 
LinkNext(Region * next)121     void LinkNext(Region *next)
122     {
123         next_ = next;
124     }
125 
GetNext()126     Region *GetNext() const
127     {
128         return next_;
129     }
130 
LinkPrev(Region * prev)131     void LinkPrev(Region *prev)
132     {
133         prev_ = prev;
134     }
135 
GetPrev()136     Region *GetPrev() const
137     {
138         return prev_;
139     }
140 
GetBegin()141     uintptr_t GetBegin() const
142     {
143         return packedData_.begin_;
144     }
145 
GetEnd()146     uintptr_t GetEnd() const
147     {
148         return end_;
149     }
150 
GetHighWaterMark()151     uintptr_t GetHighWaterMark() const
152     {
153         return highWaterMark_;
154     }
155 
GetCapacity()156     size_t GetCapacity() const
157     {
158         return end_ - allocateBase_;
159     }
160 
GetSize()161     size_t GetSize() const
162     {
163         return end_ - packedData_.begin_;
164     }
165 
GetJSThread()166     JSThread *GetJSThread() const
167     {
168         return thread_;
169     }
170 
IsGCFlagSet(RegionGCFlags flag)171     bool IsGCFlagSet(RegionGCFlags flag) const
172     {
173         return (packedData_.flags_.gcFlags_ & flag) == flag;
174     }
175 
SetGCFlag(RegionGCFlags flag)176     void SetGCFlag(RegionGCFlags flag)
177     {
178         packedData_.flags_.gcFlags_ |= flag;
179     }
180 
ClearGCFlag(RegionGCFlags flag)181     void ClearGCFlag(RegionGCFlags flag)
182     {
183         // NOLINTNEXTLINE(hicpp-signed-bitwise)
184         packedData_.flags_.gcFlags_ &= ~flag;
185     }
186 
GetSpaceTypeName()187     std::string GetSpaceTypeName()
188     {
189         return ToSpaceTypeName(packedData_.flags_.spaceFlag_);
190     }
191 
192     // Mark bitset
193     GCBitset *GetMarkGCBitset() const;
194     bool AtomicMark(void *address);
195     void ClearMark(void *address);
196     bool Test(void *addr) const;
197     template <typename Visitor>
198     void IterateAllMarkedBits(Visitor visitor) const;
199     void ClearMarkGCBitset();
200     // Cross region remembered set
201     void InsertCrossRegionRSet(uintptr_t addr);
202     void AtomicInsertCrossRegionRSet(uintptr_t addr);
203     template <typename Visitor>
204     void IterateAllCrossRegionBits(Visitor visitor) const;
205     void ClearCrossRegionRSet();
206     void ClearCrossRegionRSetInRange(uintptr_t start, uintptr_t end);
207     void AtomicClearCrossRegionRSetInRange(uintptr_t start, uintptr_t end);
208     void DeleteCrossRegionRSet();
209     // Old to new remembered set
210     void InsertOldToNewRSet(uintptr_t addr);
211     void ClearOldToNewRSet(uintptr_t addr);
212     template <typename Visitor>
213     void IterateAllOldToNewBits(Visitor visitor);
214     void ClearOldToNewRSet();
215     void ClearOldToNewRSetInRange(uintptr_t start, uintptr_t end);
216     void DeleteOldToNewRSet();
217 
218     void AtomicClearSweepingRSetInRange(uintptr_t start, uintptr_t end);
219     void ClearSweepingRSetInRange(uintptr_t start, uintptr_t end);
220     void DeleteSweepingRSet();
221     template <typename Visitor>
222     void AtomicIterateAllSweepingRSetBits(Visitor visitor);
223     template <typename Visitor>
224     void IterateAllSweepingRSetBits(Visitor visitor);
225 
ObjectAddressToRange(TaggedObject * obj)226     static Region *ObjectAddressToRange(TaggedObject *obj)
227     {
228         return reinterpret_cast<Region *>(ToUintPtr(obj) & ~DEFAULT_REGION_MASK);
229     }
230 
ObjectAddressToRange(uintptr_t objAddress)231     static Region *ObjectAddressToRange(uintptr_t objAddress)
232     {
233         return reinterpret_cast<Region *>(objAddress & ~DEFAULT_REGION_MASK);
234     }
235 
ClearMembers()236     void ClearMembers()
237     {
238         if (lock_ != nullptr) {
239             delete lock_;
240             lock_ = nullptr;
241         }
242     }
243 
Invalidate()244     void Invalidate()
245     {
246         ASAN_UNPOISON_MEMORY_REGION(reinterpret_cast<void *>(GetBegin()), GetSize());
247         packedData_.flags_.spaceFlag_ = RegionSpaceFlag::UNINITIALIZED;
248     }
249 
InYoungSpace()250     bool InYoungSpace() const
251     {
252         return packedData_.flags_.spaceFlag_ == RegionSpaceFlag::IN_YOUNG_SPACE;
253     }
254 
InOldSpace()255     bool InOldSpace() const
256     {
257         return packedData_.flags_.spaceFlag_ == RegionSpaceFlag::IN_OLD_SPACE;
258     }
259 
InYoungOrOldSpace()260     bool InYoungOrOldSpace() const
261     {
262         return InYoungSpace() || InOldSpace();
263     }
264 
InHugeObjectSpace()265     bool InHugeObjectSpace() const
266     {
267         return packedData_.flags_.spaceFlag_ == RegionSpaceFlag::IN_HUGE_OBJECT_SPACE;
268     }
269 
InMachineCodeSpace()270     bool InMachineCodeSpace() const
271     {
272         return packedData_.flags_.spaceFlag_ == RegionSpaceFlag::IN_MACHINE_CODE_SPACE;
273     }
274 
InNonMovableSpace()275     bool InNonMovableSpace() const
276     {
277         return packedData_.flags_.spaceFlag_  == RegionSpaceFlag::IN_NON_MOVABLE_SPACE;
278     }
279 
InSnapshotSpace()280     bool InSnapshotSpace() const
281     {
282         return packedData_.flags_.spaceFlag_ == RegionSpaceFlag::IN_SNAPSHOT_SPACE;
283     }
284 
InReadOnlySpace()285     bool InReadOnlySpace() const
286     {
287         return packedData_.flags_.spaceFlag_ == RegionSpaceFlag::IN_READ_ONLY_SPACE;
288     }
289 
InAppSpawnSpace()290     bool InAppSpawnSpace() const
291     {
292         return packedData_.flags_.spaceFlag_ == RegionSpaceFlag::IN_APPSPAWN_SPACE;
293     }
294 
InHeapSpace()295     bool InHeapSpace() const
296     {
297         uint8_t space = packedData_.flags_.spaceFlag_;
298         return (space == RegionSpaceFlag::IN_YOUNG_SPACE ||
299                 space == RegionSpaceFlag::IN_OLD_SPACE ||
300                 space == RegionSpaceFlag::IN_HUGE_OBJECT_SPACE ||
301                 space == RegionSpaceFlag::IN_MACHINE_CODE_SPACE ||
302                 space == RegionSpaceFlag::IN_NON_MOVABLE_SPACE ||
303                 space == RegionSpaceFlag::IN_SNAPSHOT_SPACE ||
304                 space == RegionSpaceFlag::IN_READ_ONLY_SPACE ||
305                 space == RegionSpaceFlag::IN_APPSPAWN_SPACE);
306     }
307 
InCollectSet()308     bool InCollectSet() const
309     {
310         return IsGCFlagSet(RegionGCFlags::IN_COLLECT_SET);
311     }
312 
InYoungSpaceOrCSet()313     bool InYoungSpaceOrCSet() const
314     {
315         return InYoungSpace() || InCollectSet();
316     }
317 
InNewToNewSet()318     bool InNewToNewSet() const
319     {
320         return IsGCFlagSet(RegionGCFlags::IN_NEW_TO_NEW_SET);
321     }
322 
HasAgeMark()323     bool HasAgeMark() const
324     {
325         return IsGCFlagSet(RegionGCFlags::HAS_AGE_MARK);
326     }
327 
BelowAgeMark()328     bool BelowAgeMark() const
329     {
330         return IsGCFlagSet(RegionGCFlags::BELOW_AGE_MARK);
331     }
332 
NeedRelocate()333     bool NeedRelocate() const
334     {
335         return IsGCFlagSet(RegionGCFlags::NEED_RELOCATE);
336     }
337 
SetSwept()338     void SetSwept()
339     {
340         SetGCFlag(RegionGCFlags::HAS_BEEN_SWEPT);
341     }
342 
ResetSwept()343     void ResetSwept()
344     {
345         ClearGCFlag(RegionGCFlags::HAS_BEEN_SWEPT);
346     }
347 
InRange(uintptr_t address)348     bool InRange(uintptr_t address) const
349     {
350         return address >= packedData_.begin_ && address <= end_;
351     }
352 
GetAllocateBase()353     uintptr_t GetAllocateBase() const
354     {
355         return allocateBase_;
356     }
357 
358     size_t GetAllocatedBytes(uintptr_t top = 0)
359     {
360         ASSERT(top == 0 || InRange(top));
361         return (top == 0) ? (highWaterMark_ - packedData_.begin_) : (top - packedData_.begin_);
362     }
363 
SetHighWaterMark(uintptr_t mark)364     void SetHighWaterMark(uintptr_t mark)
365     {
366         ASSERT(InRange(mark));
367         highWaterMark_ = mark;
368     }
369 
SetReadOnlyAndMarked()370     void SetReadOnlyAndMarked()
371     {
372         packedData_.markGCBitset_->SetAllBits(packedData_.bitsetSize_);
373         PageProtect(reinterpret_cast<void *>(allocateBase_), GetCapacity(), PAGE_PROT_READ);
374     }
375 
ClearReadOnly()376     void ClearReadOnly()
377     {
378         PageProtect(reinterpret_cast<void *>(allocateBase_), GetCapacity(), PAGE_PROT_READWRITE);
379     }
380 
InitializeFreeObjectSets()381     void InitializeFreeObjectSets()
382     {
383         freeObjectSets_ = Span<FreeObjectSet *>(new FreeObjectSet *[FreeObjectList::NumberOfSets()](),
384             FreeObjectList::NumberOfSets());
385     }
386 
DestroyFreeObjectSets()387     void DestroyFreeObjectSets()
388     {
389         for (auto set : freeObjectSets_) {
390             delete set;
391         }
392         delete[] freeObjectSets_.data();
393     }
394 
GetFreeObjectSet(SetType type)395     FreeObjectSet *GetFreeObjectSet(SetType type)
396     {
397         // Thread safe
398         if (freeObjectSets_[type] == nullptr) {
399             freeObjectSets_[type] = new FreeObjectSet(type);
400         }
401         return freeObjectSets_[type];
402     }
403 
404     template<class Callback>
EnumerateFreeObjectSets(Callback cb)405     void EnumerateFreeObjectSets(Callback cb)
406     {
407         for (auto set : freeObjectSets_) {
408             cb(set);
409         }
410     }
411 
412     template<class Callback>
REnumerateFreeObjectSets(Callback cb)413     void REnumerateFreeObjectSets(Callback cb)
414     {
415         auto last = freeObjectSets_.crbegin();
416         auto first = freeObjectSets_.crend();
417         for (; last != first; last++) {
418             if (!cb(*last)) {
419                 break;
420             }
421         }
422     }
423 
424     inline bool IsMarking() const;
425 
IncreaseAliveObjectSafe(size_t size)426     void IncreaseAliveObjectSafe(size_t size)
427     {
428         ASSERT(aliveObject_ + size <= GetSize());
429         aliveObject_ += size;
430     }
431 
IncreaseAliveObject(size_t size)432     void IncreaseAliveObject(size_t size)
433     {
434         ASSERT(aliveObject_ + size <= GetSize());
435         aliveObject_.fetch_add(size, std::memory_order_relaxed);
436     }
437 
ResetAliveObject()438     void ResetAliveObject()
439     {
440         aliveObject_ = 0;
441     }
442 
AliveObject()443     size_t AliveObject() const
444     {
445         return aliveObject_.load(std::memory_order_relaxed);
446     }
447 
MostObjectAlive()448     bool MostObjectAlive() const
449     {
450         return aliveObject_ > MOST_OBJECT_ALIVE_THRESHOLD_PERCENT * GetSize();
451     }
452 
ResetWasted()453     void ResetWasted()
454     {
455         wasted_ = 0;
456     }
457 
IncreaseWasted(uint64_t size)458     void IncreaseWasted(uint64_t size)
459     {
460         wasted_ += size;
461     }
462 
GetWastedSize()463     uint64_t GetWastedSize()
464     {
465         return wasted_;
466     }
467 
GetSnapshotData()468     uint64_t GetSnapshotData()
469     {
470         return snapshotData_;
471     }
472 
SetSnapshotData(uint64_t value)473     void SetSnapshotData(uint64_t value)
474     {
475         snapshotData_ = value;
476     }
477 
SwapRSetForConcurrentSweeping()478     void SwapRSetForConcurrentSweeping()
479     {
480         sweepingRSet_ = packedData_.oldToNewSet_;
481         packedData_.oldToNewSet_ = nullptr;
482     }
483 
484     // should call in js-thread
485     void MergeRSetForConcurrentSweeping();
486 
487     struct alignas(JSTaggedValue::TaggedTypeSize()) PackedPtr : public base::AlignedPointer {
488         uint8_t spaceFlag_;
489         uint16_t  gcFlags_;
490     };
491 
492     struct PackedData : public base::AlignedStruct<JSTaggedValue::TaggedTypeSize(),
493                                                  base::AlignedPointer,
494                                                  base::AlignedPointer,
495                                                  base::AlignedPointer,
496                                                  base::AlignedPointer,
497                                                  base::AlignedSize> {
498         enum class Index : size_t {
499             FlagIndex = 0,
500             MarkGCBitSetIndex,
501             OldToNewSetIndex,
502             BeginIndex,
503             BitSetSizeIndex,
504             NumOfMembers
505         };
506 
507         static_assert(static_cast<size_t>(Index::NumOfMembers) == NumOfTypes);
508 
PackedDataPackedData509         inline PackedData(uintptr_t begin, uintptr_t end, RegionSpaceFlag spaceType)
510         {
511             flags_.spaceFlag_ = spaceType;
512             flags_.gcFlags_ = 0;
513             bitsetSize_ = (spaceType == RegionSpaceFlag::IN_HUGE_OBJECT_SPACE) ?
514                 GCBitset::BYTE_PER_WORD : GCBitset::SizeOfGCBitset(end - begin);
515             markGCBitset_ = new (ToVoidPtr(begin)) GCBitset();
516             markGCBitset_->Clear(bitsetSize_);
517             begin_ = AlignUp(begin + bitsetSize_, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
518             // The object region marked with poison until it is allocated if is_asan is true
519             ASAN_POISON_MEMORY_REGION(reinterpret_cast<void *>(begin_), (end - begin_));
520         }
521 
GetFlagOffsetPackedData522         static size_t GetFlagOffset(bool isArch32)
523         {
524             return GetOffset<static_cast<size_t>(Index::FlagIndex)>(isArch32);
525         }
526 
GetGCBitsetOffsetPackedData527         static size_t GetGCBitsetOffset(bool isArch32)
528         {
529             return GetOffset<static_cast<size_t>(Index::MarkGCBitSetIndex)>(isArch32);
530         }
531 
GetOldToNewSetOffsetPackedData532         static size_t GetOldToNewSetOffset(bool isArch32)
533         {
534             return GetOffset<static_cast<size_t>(Index::OldToNewSetIndex)>(isArch32);
535         }
536 
GetBeginOffsetPackedData537         static size_t GetBeginOffset(bool isArch32)
538         {
539             return GetOffset<static_cast<size_t>(Index::BeginIndex)>(isArch32);
540         }
541 
542         alignas(EAS) PackedPtr flags_;
543         alignas(EAS) GCBitset *markGCBitset_ {nullptr};
544         alignas(EAS) RememberedSet *oldToNewSet_ {nullptr};
545         alignas(EAS) uintptr_t begin_ {0};
546         alignas(EAS) size_t bitsetSize_ {0};
547     };
548     STATIC_ASSERT_EQ_ARCH(sizeof(PackedData), PackedData::SizeArch32, PackedData::SizeArch64);
549 
550 private:
551     static constexpr double MOST_OBJECT_ALIVE_THRESHOLD_PERCENT = 0.8;
552 
553     RememberedSet *CreateRememberedSet();
554     RememberedSet *GetOrCreateCrossRegionRememberedSet();
555     RememberedSet *GetOrCreateOldToNewRememberedSet();
556 
557     PackedData packedData_;
558     /*
559      * The thread instance here is used by the GC barriers to get marking related information
560      * and perform marking related operations. The barriers will indirectly access such information
561      * via. the objects' associated regions.
562      * fixme: Figure out a more elegant solution to bridging the barrier
563      * and the information / operations it depends on. Then we can get rid of this from the region,
564      * and consequently, the region allocator, the spaces using the region allocator, etc.
565      */
566     JSThread *thread_;
567 
568     uintptr_t allocateBase_;
569     uintptr_t end_;
570     uintptr_t highWaterMark_;
571     std::atomic_size_t aliveObject_ {0};
572     Region *next_ {nullptr};
573     Region *prev_ {nullptr};
574 
575     RememberedSet *crossRegionSet_ {nullptr};
576     RememberedSet *sweepingRSet_ {nullptr};
577     Span<FreeObjectSet *> freeObjectSets_;
578     os::memory::Mutex *lock_ {nullptr};
579     uint64_t wasted_;
580     // snapshotdata_ is used to encode the region for snapshot. Its upper 32 bits are used to store the size of
581     // the huge object, and the lower 32 bits are used to store the region index
582     uint64_t snapshotData_;
583 
584     friend class Snapshot;
585     friend class SnapshotProcessor;
586 };
587 }  // namespace ecmascript
588 }  // namespace panda
589 #endif  // ECMASCRIPT_MEM_REGION_H
590