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_MEMDESC_H
17#define ECMASCRIPT_MEM_JIT_FORT_MEMDESC_H
18
19#include <deque>
20#include "ecmascript/js_tagged_value.h"
21#include "ecmascript/base/asan_interface.h"
22#include "ecmascript/platform/mutex.h"
23
24namespace panda::ecmascript {
25
26// Before Jit Fort, FreeList allocator uses FreeObject to link
27// together free memory blocks in heap regions where each
28// free memory block is a FreeObject with size of the block and
29// a pointer to the next free block in the heap region. Its usage
30// requires a mutable heap region and does not work with Jit Fort space
31// which is immutbale execept for access by CodeSigner.
32//
33// When JIT Fort is enabled, FreeObject usage is replaced by MemDesc
34// which serves same purpose as FreeObjects, but is stored outside of
35// JitFort memory space.
36//
37// To reuse FreeList allocator code for JitFort, related classes
38// (allocator/FreeObjectList/FreeObjectSet, etc) had to be changed into
39// template classes to support both FreeObject and MemDesc targets,
40// and MemDesc has to support same methods as FreeObject, and use the
41// same null pointer value forlink pointer that FreeObject uses, i.e.
42// NULL_POINTER with a value of 0x5 instead of 0.
43//
44#define INVALID_OBJPTR ((uintptr_t) JSTaggedValue::NULL_POINTER)
45
46class MemDesc {
47public:
48    MemDesc() = default;
49    ~MemDesc() = default;
50
51    static MemDesc *Cast(uintptr_t object)
52    {
53        return reinterpret_cast<MemDesc *>(object);
54    }
55
56    inline uintptr_t GetBegin() const
57    {
58        return mem_;
59    }
60
61    inline uintptr_t GetEnd() const
62    {
63        return mem_ + size_;
64    }
65
66    inline void SetMem(uintptr_t mem)
67    {
68        mem_ = mem;
69    }
70
71    inline void SetSize(size_t size)
72    {
73        size_ = size;
74    }
75
76    inline void SetNext(MemDesc *desc)
77    {
78        next_ = desc;
79    }
80
81    inline MemDesc *GetNext()
82    {
83        return next_;
84    }
85
86    inline uint32_t Available() const
87    {
88        return size_;
89    }
90
91    inline bool IsFreeObject() const
92    {
93        return true; // for compatibility with FreeObject
94    }
95
96    inline void SetAvailable(uint32_t size)
97    {
98        size_ = size;
99    }
100
101    inline void AsanPoisonFreeObject() const
102    {
103        ASAN_POISON_MEMORY_REGION((const volatile void *)mem_, size_);
104    }
105
106    inline void AsanUnPoisonFreeObject() const
107    {
108        ASAN_UNPOISON_MEMORY_REGION((const volatile void *)mem_, size_);
109    }
110
111    inline void SetInstalled(bool installed)
112    {
113        installed_.store(installed, std::memory_order_release);
114    }
115
116    inline bool IsInstalled()
117    {
118        return installed_.load(std::memory_order_acquire);
119    }
120
121private:
122    uintptr_t mem_ {0};
123    size_t size_ {0};
124    std::atomic<bool> installed_ {false};
125    MemDesc *next_ {MemDesc::Cast(INVALID_OBJPTR)};
126};
127
128class MemDescPool {
129public:
130    MemDescPool(uintptr_t fortBegin, size_t fortSize);
131    ~MemDescPool();
132
133    static inline bool IsEmpty(MemDesc* list)
134    {
135        return (list == nullptr || list == MemDesc::Cast(INVALID_OBJPTR));
136    }
137
138    inline MemDesc *GetDescFromPool()
139    {
140        LockHolder lock(lock_);
141        return GetDesc();
142    }
143
144    inline void ReturnDescToPool(MemDesc *desc)
145    {
146        LockHolder lock(lock_);
147        Add(desc);
148        returned_++;
149    }
150
151    inline uintptr_t JitFortBegin()
152    {
153        return fortBegin_;
154    }
155
156    inline size_t JitFortSize()
157    {
158        return fortSize_;
159    }
160
161private:
162    MemDesc *GetDesc();
163    void Add(MemDesc *);
164    void Expand();
165
166    static constexpr size_t MEMDESCS_PER_BLOCK = 100;
167    MemDesc *freeList_ {nullptr};
168    std::deque<void *> memDescBlocks_;
169    size_t allocated_ {0};
170    size_t returned_ {0};
171    size_t highwater_ {0};
172    Mutex lock_;
173
174    uintptr_t fortBegin_;
175    size_t fortSize_;
176};
177
178}  // namespace panda::ecmascript
179
180#endif  // ECMASCRIPT_MEM_JIT_FORT_MEMDESC_H
181