1/*
2 * Copyright (c) 2024 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_JIT_FORT_H
17#define ECMASCRIPT_MEM_JIT_FORT_H
18
19#include <array>
20
21#include "ecmascript/mem/mem_common.h"
22#include "ecmascript/mem/region.h"
23#include "ecmascript/mem/machine_code.h"
24
25namespace panda::ecmascript {
26
27class JitFortRegion;
28class JitFortMemDescPool;
29template <typename T>
30class FreeListAllocator;
31
32class JitFort {
33public:
34    JitFort();
35    ~JitFort();
36    NO_COPY_SEMANTIC(JitFort);
37    NO_MOVE_SEMANTIC(JitFort);
38
39    void InitRegions();
40    bool AddRegion();
41    uintptr_t Allocate(MachineCodeDesc *desc);
42
43    inline JitFortRegion *GetRegionList()
44    {
45        return regionList_.GetFirst();
46    }
47
48    inline uintptr_t JitFortBegin()
49    {
50        return jitFortBegin_;
51    }
52
53    inline size_t JitFortSize()
54    {
55        return jitFortSize_;
56    }
57
58    bool InRange(uintptr_t address) const;
59    void CollectFreeRanges(JitFortRegion  *region);
60    void UpdateFreeSpace();
61
62    JitFortRegion *ObjectAddressToRange(uintptr_t objAddress);
63    static void InitJitFortResource();
64    void PrepareSweeping();
65    void AsyncSweep();
66    void Sweep();
67    void MarkJitFortMemAlive(MachineCode *obj);
68    void MarkJitFortMemAwaitInstall(uintptr_t addr, size_t size);
69    void MarkJitFortMemInstalled(MachineCode *obj);
70    void FreeRegion(JitFortRegion *region);
71    uint32_t AddrToFortRegionIdx(uint64_t addr);
72    size_t FortAllocSize(size_t instrSize);
73    PUBLIC_API static bool IsResourceAvailable();
74
75private:
76    static bool isResourceAvailable_;
77    FreeListAllocator<MemDesc> *allocator_ {nullptr};
78
79    // Fort memory space
80    static constexpr int MAP_JITFORT = 0x1000;
81    static constexpr size_t JIT_FORT_REG_SPACE_MAX = 4_MB;
82    static constexpr size_t JIT_FORT_HUGE_SPACE_MAX = 2_MB;
83    static constexpr size_t JIT_FORT_MEM_DESC_MAX = 40_KB;
84    MemMap jitFortMem_;
85    uintptr_t jitFortBegin_ {0};
86    size_t jitFortSize_ {0};
87
88    // Fort regions
89    static constexpr uint32_t FORT_BUF_ALIGN = 32;
90    static constexpr uint32_t FORT_BUF_ALIGN_LOG2 = base::MathHelper::GetIntLog2(FORT_BUF_ALIGN);
91    static constexpr size_t FORT_BUF_ADDR_MASK = FORT_BUF_ALIGN - 1;
92    static constexpr size_t MAX_JIT_FORT_REGIONS = JIT_FORT_REG_SPACE_MAX/DEFAULT_REGION_SIZE;
93    std::array<JitFortRegion *, MAX_JIT_FORT_REGIONS>regions_;
94    size_t nextFreeRegionIdx_ {0};
95    EcmaList<JitFortRegion> regionList_ {}; // regions in use by Jit Fort allocator
96
97    MemDescPool *memDescPool_ {nullptr};
98
99    bool freeListUpdated_ {false};  // use atomic if not mutext protected
100    Mutex mutex_;
101    Mutex liveJitCodeBlksLock_;
102    std::atomic<bool> isSweeping_ {false};
103    friend class HugeMachineCodeSpace;
104};
105
106class JitFortGCBitset : public GCBitset {
107public:
108    JitFortGCBitset() = default;
109    ~JitFortGCBitset() = default;
110
111    NO_COPY_SEMANTIC(JitFortGCBitset);
112    NO_MOVE_SEMANTIC(JitFortGCBitset);
113
114    template <typename Visitor>
115    void IterateMarkedBitsConst(uintptr_t regionAddr, size_t bitsetSize, Visitor visitor);
116    void MarkStartAddr(bool awaitInstall, uintptr_t startAddr, uint32_t index, uint32_t &word);
117    void MarkEndAddr(bool awaitInstall, uintptr_t endAddr, uint32_t index, uint32_t &word);
118
119    size_t WordCount(size_t size) const
120    {
121        return size >> BYTE_PER_WORD_LOG2;
122    }
123
124    inline void ClearMark(uintptr_t addr)
125    {
126        ClearBit((addr & DEFAULT_REGION_MASK) >> TAGGED_TYPE_SIZE_LOG);
127    }
128
129    inline bool Test(uintptr_t addr)
130    {
131        return TestBit((addr & DEFAULT_REGION_MASK) >> TAGGED_TYPE_SIZE_LOG);
132    }
133};
134
135class JitFortRegion : public Region {
136public:
137    JitFortRegion(NativeAreaAllocator *allocator, uintptr_t allocateBase, uintptr_t end,
138        RegionSpaceFlag spaceType, MemDescPool *pool) : Region(allocator, allocateBase, end, spaceType),
139        memDescPool_(pool)
140    {
141        markGCBitset_ = new(reinterpret_cast<void *>(gcBitSet_)) JitFortGCBitset();
142        markGCBitset_->Clear(bitsetSize_);
143        InitializeFreeObjectSets();
144    }
145
146    void InitializeFreeObjectSets()
147    {
148        fortFreeObjectSets_ = Span<FreeObjectSet<MemDesc> *>(new FreeObjectSet<MemDesc>
149            *[FreeObjectList<MemDesc>::NumberOfSets()](), FreeObjectList<MemDesc>::NumberOfSets());
150    }
151
152    void DestroyFreeObjectSets()
153    {
154        for (auto set : fortFreeObjectSets_) {
155            delete set;
156        }
157        delete[] fortFreeObjectSets_.data();
158    }
159
160    FreeObjectSet<MemDesc> *GetFreeObjectSet(SetType type)
161    {
162        // Thread safe
163        if (fortFreeObjectSets_[type] == nullptr) {
164            fortFreeObjectSets_[type] = new FreeObjectSet<MemDesc>(type, memDescPool_);
165        }
166        return fortFreeObjectSets_[type];
167    }
168
169    inline void LinkNext(JitFortRegion *next)
170    {
171        next_ = next;
172    }
173
174    inline JitFortRegion *GetNext() const
175    {
176        return next_;
177    }
178
179    inline void LinkPrev(JitFortRegion *prev)
180    {
181        prev_ = prev;
182    }
183
184    inline JitFortRegion *GetPrev() const
185    {
186        return prev_;
187    }
188
189    inline JitFortGCBitset *GetGCBitset()
190    {
191        return markGCBitset_;
192    }
193
194    inline size_t GetGCBitsetSize()
195    {
196        return bitsetSize_;
197    }
198
199    inline bool AtomicMark(void *address)
200    {
201        auto addrPtr = reinterpret_cast<uintptr_t>(address);
202        ASSERT(InRange(addrPtr));
203        return markGCBitset_->SetBit<AccessType::ATOMIC>(
204            (addrPtr & DEFAULT_REGION_MASK) >> TAGGED_TYPE_SIZE_LOG);
205    }
206
207private:
208    Span<FreeObjectSet<MemDesc> *> fortFreeObjectSets_;
209    JitFortRegion *next_ {nullptr};
210    JitFortRegion *prev_ {nullptr};
211    MemDescPool *memDescPool_ {nullptr};
212
213    static constexpr int FORT_REGION_BITSET_SIZE = 4096;
214    size_t bitsetSize_ {FORT_REGION_BITSET_SIZE};
215    alignas(uint64_t) uint8_t gcBitSet_[FORT_REGION_BITSET_SIZE];
216    alignas(uint64_t) JitFortGCBitset *markGCBitset_ {nullptr};
217};
218
219}  // namespace panda::ecmascript
220#endif  // ECMASCRIPT_MEM_SPARSE_SPACE_H
221