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