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_SPARSE_SPACE_H
17#define ECMASCRIPT_MEM_SPARSE_SPACE_H
18
19#include "ecmascript/mem/space-inl.h"
20#include "ecmascript/mem/mem_common.h"
21#include "ecmascript/mem/jit_fort.h"
22
23#ifdef ECMASCRIPT_SUPPORT_HEAPSAMPLING
24#define CHECK_OBJECT_AND_INC_OBJ_SIZE(size)                                             \
25    if (object != 0) {                                                                  \
26        IncreaseLiveObjectSize(size);                                                   \
27        if (!heap_->IsConcurrentFullMark() || heap_->IsReadyToConcurrentMark()) {       \
28            Region::ObjectAddressToRange(object)->IncreaseAliveObject(size);            \
29        }                                                                               \
30        InvokeAllocationInspector(object, size, size);                                  \
31        return object;                                                                  \
32    }
33#else
34#define CHECK_OBJECT_AND_INC_OBJ_SIZE(size)                                             \
35    if (object != 0) {                                                                  \
36        IncreaseLiveObjectSize(size);                                                   \
37        if (!heap_->IsConcurrentFullMark() || heap_->IsReadyToConcurrentMark()) {       \
38            Region::ObjectAddressToRange(object)->IncreaseAliveObject(size);            \
39        }                                                                               \
40        return object;                                                                  \
41    }
42#endif
43
44enum class SweepState : uint8_t {
45    NO_SWEEP,
46    SWEEPING,
47    SWEPT
48};
49
50namespace panda::ecmascript {
51class LocalSpace;
52
53class SparseSpace : public Space {
54public:
55    SparseSpace(Heap *heap, MemSpaceType type, size_t initialCapacity, size_t maximumCapacity);
56    ~SparseSpace() override
57    {
58        delete allocator_;
59    }
60    NO_COPY_SEMANTIC(SparseSpace);
61    NO_MOVE_SEMANTIC(SparseSpace);
62
63    void Initialize() override;
64    void Reset();
65    void ResetTopPointer(uintptr_t top);
66
67    uintptr_t Allocate(size_t size, bool allowGC = true);
68    bool Expand();
69
70    // For sweeping
71    virtual void PrepareSweeping();
72    virtual void Sweep();
73    virtual void AsyncSweep(bool isMain);
74
75    bool TryFillSweptRegion();
76    // Ensure All region finished sweeping
77    bool FinishFillSweptRegion();
78
79    void AddSweepingRegion(Region *region);
80    void SortSweepingRegion();
81    Region *GetSweepingRegionSafe();
82    void AddSweptRegionSafe(Region *region);
83    Region *GetSweptRegionSafe();
84    void FreeRegionFromSpace(Region *region);
85    Region *TryToGetSuitableSweptRegion(size_t size);
86
87    void FreeRegion(Region *current, bool isMain = true);
88    void FreeLiveRange(Region *current, uintptr_t freeStart, uintptr_t freeEnd, bool isMain);
89
90    void DetachFreeObjectSet(Region *region);
91
92    void IterateOverObjects(const std::function<void(TaggedObject *object)> &objectVisitor) const;
93    void IterateOldToNewOverObjects(
94        const std::function<void(TaggedObject *object, JSTaggedValue value)> &visitor) const;
95
96    size_t GetHeapObjectSize() const;
97
98    void IncreaseAllocatedSize(size_t size);
99
100    void IncreaseLiveObjectSize(size_t size)
101    {
102        liveObjectSize_ += size;
103    }
104
105    void DecreaseLiveObjectSize(size_t size)
106    {
107        liveObjectSize_ -= size;
108    }
109
110    void SetOvershootSize(size_t size)
111    {
112        overshootSize_ = size;
113    }
114
115    void IncreaseOvershootSize(size_t size)
116    {
117        overshootSize_ += size;
118    }
119
120    size_t GetOvershootSize() const
121    {
122        return overshootSize_;
123    }
124
125    void AdjustOvershootSize()
126    {
127        if (overshootSize_ > 0 && maximumCapacity_ > committedSize_) {
128            size_t size = maximumCapacity_ - committedSize_;
129            overshootSize_ = overshootSize_ > size ? overshootSize_ - size : 0;
130        }
131    }
132
133    bool CommittedSizeExceed() const
134    {
135        return committedSize_ >= maximumCapacity_ + overshootSize_ + outOfMemoryOvershootSize_;
136    }
137
138    size_t GetTotalAllocatedSize() const;
139
140    void InvokeAllocationInspector(Address object, size_t size, size_t alignedSize);
141
142protected:
143    FreeListAllocator<FreeObject> *allocator_;
144    SweepState sweepState_ = SweepState::NO_SWEEP;
145    Heap *localHeap_ {nullptr};
146    size_t liveObjectSize_ {0};
147    uintptr_t AllocateAfterSweepingCompleted(size_t size);
148
149private:
150    Mutex lock_;
151    std::vector<Region *> sweepingList_;
152    std::vector<Region *> sweptList_;
153    size_t overshootSize_ {0};
154};
155
156class OldSpace : public SparseSpace {
157public:
158    OldSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity);
159    ~OldSpace() override = default;
160    NO_COPY_SEMANTIC(OldSpace);
161    NO_MOVE_SEMANTIC(OldSpace);
162
163    Region *TrySweepToGetSuitableRegion(size_t size);
164    Region *TryToGetExclusiveRegion(size_t size);
165
166    // CSet
167    void SelectCSet();
168    void CheckRegionSize();
169    void RevertCSet();
170    void ReclaimCSet();
171
172    unsigned long GetSelectedRegionNumber() const
173    {
174        return std::max(committedSize_ / PARTIAL_GC_MAX_COLLECT_REGION_RATE, PARTIAL_GC_INITIAL_COLLECT_REGION_SIZE);
175    }
176
177    size_t GetMergeSize() const
178    {
179        return mergeSize_;
180    }
181
182    void IncreaseMergeSize(size_t size)
183    {
184        mergeSize_ += size;
185    }
186
187    void ResetMergeSize()
188    {
189        mergeSize_ = 0;
190    }
191
192    void IncreaseCommittedOverSizeLimit(size_t size)
193    {
194        committedOverSizeLimit_ += size;
195    }
196
197    void ResetCommittedOverSizeLimit()
198    {
199        DecreaseOutOfMemoryOvershootSize(committedOverSizeLimit_);
200        committedOverSizeLimit_ = 0;
201    }
202
203    template<class Callback>
204    void EnumerateCollectRegionSet(const Callback &cb) const
205    {
206        for (Region *current : collectRegionSet_) {
207            if (current != nullptr) {
208                cb(current);
209            }
210        }
211    }
212
213    size_t GetCollectSetRegionCount() const
214    {
215        return collectRegionSet_.size();
216    }
217
218    void Merge(LocalSpace *localSpace);
219private:
220    static constexpr int64_t PARTIAL_GC_MAX_EVACUATION_SIZE_FOREGROUND = 2_MB;
221    static constexpr int64_t PARTIAL_GC_MAX_EVACUATION_SIZE_BACKGROUND = 6_MB;
222    static constexpr unsigned long long PARTIAL_GC_MAX_COLLECT_REGION_RATE = 2_MB;
223    static constexpr unsigned long long PARTIAL_GC_INITIAL_COLLECT_REGION_SIZE = 24;
224    static constexpr size_t PARTIAL_GC_MIN_COLLECT_REGION_SIZE = 5;
225
226    CVector<Region *> collectRegionSet_;
227    Mutex lock_;
228    size_t mergeSize_ {0};
229    size_t committedOverSizeLimit_ {0};
230};
231
232class NonMovableSpace : public SparseSpace {
233public:
234    NonMovableSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity);
235    ~NonMovableSpace() override = default;
236    NO_COPY_SEMANTIC(NonMovableSpace);
237    NO_MOVE_SEMANTIC(NonMovableSpace);
238
239    uintptr_t  CheckAndAllocate(size_t size);
240};
241
242class AppSpawnSpace : public SparseSpace {
243public:
244    AppSpawnSpace(Heap *heap, size_t initialCapacity);
245    ~AppSpawnSpace() override = default;
246    NO_COPY_SEMANTIC(AppSpawnSpace);
247    NO_MOVE_SEMANTIC(AppSpawnSpace);
248
249    void IterateOverMarkedObjects(const std::function<void(TaggedObject *object)> &visitor) const;
250};
251
252class LocalSpace : public SparseSpace {
253public:
254    LocalSpace() = delete;
255    LocalSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity);
256    ~LocalSpace() override = default;
257    NO_COPY_SEMANTIC(LocalSpace);
258    NO_MOVE_SEMANTIC(LocalSpace);
259
260    uintptr_t Allocate(size_t size, bool isExpand = true);
261    bool AddRegionToList(Region *region);
262    void FreeBumpPoint();
263    void Stop();
264};
265
266class MachineCode;
267struct MachineCodeDesc;
268class MachineCodeSpace : public SparseSpace {
269public:
270    MachineCodeSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity);
271    ~MachineCodeSpace() override;
272    NO_COPY_SEMANTIC(MachineCodeSpace);
273    NO_MOVE_SEMANTIC(MachineCodeSpace);  // Note: Expand() left for define
274    uintptr_t GetMachineCodeObject(uintptr_t pc);
275    size_t CheckMachineCodeObject(uintptr_t curPtr, uintptr_t &machineCode, uintptr_t pc);
276    void AsyncSweep(bool isMain) override;
277    void Sweep() override;
278    void PrepareSweeping() override;
279    uintptr_t Allocate(size_t size, bool allowGC = true);
280    uintptr_t Allocate(size_t size, MachineCodeDesc *desc, bool allowGC = true);
281    uintptr_t PUBLIC_API JitFortAllocate(MachineCodeDesc *desc);
282
283    inline void MarkJitFortMemAlive(MachineCode *obj)
284    {
285        if (jitFort_) {
286            jitFort_->MarkJitFortMemAlive(obj);
287        }
288    }
289
290    inline void MarkJitFortMemInstalled(MachineCode *obj)
291    {
292        if (jitFort_) {
293            jitFort_->MarkJitFortMemInstalled(obj);
294        }
295    }
296
297    inline bool IsSweeping()
298    {
299        return sweepState_ == SweepState::SWEEPING ;
300    }
301
302    inline JitFort *GetJitFort()
303    {
304        return jitFort_;
305    }
306
307    bool InJitFortRange(uintptr_t address) const
308    {
309        if (jitFort_) {
310            return jitFort_->InRange(address);
311        }
312        return false;
313    }
314
315private:
316    JitFort *jitFort_ {nullptr};
317    Mutex asyncSweepMutex_;
318    friend class Heap;
319    friend class ConcurrentSweeper;
320};
321}  // namespace panda::ecmascript
322#endif  // ECMASCRIPT_MEM_SPARSE_SPACE_H
323