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 
24 namespace 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 
46 class MemDesc {
47 public:
48     MemDesc() = default;
49     ~MemDesc() = default;
50 
Cast(uintptr_t object)51     static MemDesc *Cast(uintptr_t object)
52     {
53         return reinterpret_cast<MemDesc *>(object);
54     }
55 
GetBegin() const56     inline uintptr_t GetBegin() const
57     {
58         return mem_;
59     }
60 
GetEnd() const61     inline uintptr_t GetEnd() const
62     {
63         return mem_ + size_;
64     }
65 
SetMem(uintptr_t mem)66     inline void SetMem(uintptr_t mem)
67     {
68         mem_ = mem;
69     }
70 
SetSize(size_t size)71     inline void SetSize(size_t size)
72     {
73         size_ = size;
74     }
75 
SetNext(MemDesc *desc)76     inline void SetNext(MemDesc *desc)
77     {
78         next_ = desc;
79     }
80 
GetNext()81     inline MemDesc *GetNext()
82     {
83         return next_;
84     }
85 
Available() const86     inline uint32_t Available() const
87     {
88         return size_;
89     }
90 
IsFreeObject() const91     inline bool IsFreeObject() const
92     {
93         return true; // for compatibility with FreeObject
94     }
95 
SetAvailable(uint32_t size)96     inline void SetAvailable(uint32_t size)
97     {
98         size_ = size;
99     }
100 
AsanPoisonFreeObject() const101     inline void AsanPoisonFreeObject() const
102     {
103         ASAN_POISON_MEMORY_REGION((const volatile void *)mem_, size_);
104     }
105 
AsanUnPoisonFreeObject() const106     inline void AsanUnPoisonFreeObject() const
107     {
108         ASAN_UNPOISON_MEMORY_REGION((const volatile void *)mem_, size_);
109     }
110 
SetInstalled(bool installed)111     inline void SetInstalled(bool installed)
112     {
113         installed_.store(installed, std::memory_order_release);
114     }
115 
IsInstalled()116     inline bool IsInstalled()
117     {
118         return installed_.load(std::memory_order_acquire);
119     }
120 
121 private:
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 
128 class MemDescPool {
129 public:
130     MemDescPool(uintptr_t fortBegin, size_t fortSize);
131     ~MemDescPool();
132 
IsEmpty(MemDesc* list)133     static inline bool IsEmpty(MemDesc* list)
134     {
135         return (list == nullptr || list == MemDesc::Cast(INVALID_OBJPTR));
136     }
137 
GetDescFromPool()138     inline MemDesc *GetDescFromPool()
139     {
140         LockHolder lock(lock_);
141         return GetDesc();
142     }
143 
ReturnDescToPool(MemDesc *desc)144     inline void ReturnDescToPool(MemDesc *desc)
145     {
146         LockHolder lock(lock_);
147         Add(desc);
148         returned_++;
149     }
150 
JitFortBegin()151     inline uintptr_t JitFortBegin()
152     {
153         return fortBegin_;
154     }
155 
JitFortSize()156     inline size_t JitFortSize()
157     {
158         return fortSize_;
159     }
160 
161 private:
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