• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "ecmascript/mem/heap-inl.h"
17 #include "ecmascript/mem/jit_fort.h"
18 #include "ecmascript/jit/jit.h"
19 #include "ecmascript/platform/os.h"
20 #if defined(JIT_ENABLE_CODE_SIGN) && !defined(JIT_FORT_DISABLE)
21 #include <sys/ioctl.h>
22 #include <sys/prctl.h>
23 
24 #define XPM_JITFORT_ENABLE_OPCODE 3
25 #define XPM_MAGIC 'x'
26 #define XPM_SET_JITFORT_ENABLE _IOW(XPM_MAGIC, XPM_JITFORT_ENABLE_OPCODE, unsigned long)
27 #endif
28 
29 namespace panda::ecmascript {
30 
31 template <>
FreeListAllocator(BaseHeap * heap,MemDescPool * pool,JitFort * fort)32 FreeListAllocator<MemDesc>::FreeListAllocator(BaseHeap *heap, MemDescPool *pool, JitFort *fort)
33     : memDescPool_(pool), heap_(heap)
34 {
35     freeList_ = std::make_unique<FreeObjectList<MemDesc>>(fort);
36 }
37 
JitFort()38 JitFort::JitFort()
39 {
40     jitFortMem_ = PageMap(JIT_FORT_REG_SPACE_MAX,
41                           PageProtectProt(Jit::GetInstance()->IsDisableCodeSign() || !IsResourceAvailable()),
42                           DEFAULT_REGION_SIZE, nullptr, MAP_JITFORT);
43     jitFortBegin_ = reinterpret_cast<uintptr_t>(jitFortMem_.GetMem());
44     jitFortSize_ = JIT_FORT_REG_SPACE_MAX;
45     memDescPool_ = new MemDescPool(jitFortBegin_, jitFortSize_);
46     allocator_ = new FreeListAllocator<MemDesc>(nullptr, memDescPool_, this);
47     InitRegions();
48     PrctlSetVMA(jitFortMem_.GetMem(), jitFortSize_, "ArkTS Code Jit");
49     LOG_JIT(DEBUG) << "JitFort Begin " << (void *)JitFortBegin() << " end " << (void *)(JitFortBegin() + JitFortSize());
50 }
51 
~JitFort()52 JitFort::~JitFort()
53 {
54     constexpr size_t numRegions = JIT_FORT_REG_SPACE_MAX / DEFAULT_REGION_SIZE;
55     for (size_t i = 0; i < numRegions; i++) {
56         if (regions_[i] != nullptr) {
57             regions_[i]->DestroyFreeObjectSets();
58             delete regions_[i];
59         }
60     }
61     if (allocator_ != nullptr) {
62         delete allocator_;
63     }
64     if (memDescPool_ != nullptr) {
65         delete memDescPool_;
66     }
67     PageUnmap(jitFortMem_);
68 }
69 
InitRegions()70 void JitFort::InitRegions()
71 {
72     constexpr size_t numRegions = JIT_FORT_REG_SPACE_MAX / DEFAULT_REGION_SIZE;
73     for (size_t i = 0; i < numRegions; i++) {
74         uintptr_t mem = reinterpret_cast<uintptr_t>(jitFortMem_.GetMem()) + i*DEFAULT_REGION_SIZE;
75         uintptr_t end = mem + DEFAULT_REGION_SIZE;
76         JitFortRegion *region = new JitFortRegion(nullptr, mem, end, RegionSpaceFlag::IN_MACHINE_CODE_SPACE,
77             memDescPool_);
78         regions_[i] = region;
79     }
80     AddRegion();
81 }
82 
AddRegion()83 bool JitFort::AddRegion()
84 {
85     if (nextFreeRegionIdx_ < MAX_JIT_FORT_REGIONS) {
86         allocator_->AddFree(regions_[nextFreeRegionIdx_]);
87         regionList_.AddNode(regions_[nextFreeRegionIdx_]);
88         nextFreeRegionIdx_++;
89         return true;
90     }
91     return false;
92 }
93 
94 // Fort buf allocation is in multiples of FORT_BUF_ALIGN
FortAllocSize(size_t instrSize)95 size_t JitFort::FortAllocSize(size_t instrSize)
96 {
97     return(AlignUp(instrSize, FORT_BUF_ALIGN));
98 }
99 
Allocate(MachineCodeDesc * desc)100 uintptr_t JitFort::Allocate(MachineCodeDesc *desc)
101 {
102     LockHolder lock(mutex_);
103 
104     size_t size = FortAllocSize(desc->instructionsSize);
105     auto ret = allocator_->Allocate(size);
106     if (ret == ToUintPtr(nullptr)) {
107         if (AddRegion()) {
108             LOG_JIT(DEBUG) << "JitFort: Allocate - AddRegion";
109             ret = allocator_->Allocate(size);
110         }
111     }
112     if (ret == ToUintPtr(nullptr)) {
113         LOG_JIT(DEBUG) << "JitFort:: Allocate return nullptr for size " << size;
114         return ret;
115     }
116     // Record allocation to keep it from being collected by the next
117     // JitFort::UpdateFreeSpace in case corresponding Machine code object is not
118     // marked for sweep yet by then.
119     ASSERT((ret & FORT_BUF_ADDR_MASK) == 0);
120     MarkJitFortMemAwaitInstall(ret, size);
121     LOG_JIT(DEBUG) << "JitFort:: Allocate " << (void *)ret << " - " << (void *)(ret+size-1) <<
122         " size " << size << " instructionsSize " << desc->instructionsSize;
123     return ret;
124 }
125 
126 // Called by GC thread during Mark to mark Fort buf alive.
127 // Encoding details in GCBitset for marking live Fort buffers:
128 //   Begin of a live Fort buf:
129 //     - 10: a live Jit Fort buf that has been installed
130 //     - 11: a live Jit Fort buf that has not been installed yet
131 //   End of a live Fort buf:
132 //     - 01
133 // This encoding requires 4 GCBitset bits to mark a live JitFort
134 // buffer, which makes the minimum Fort buffer allocation size 32 bytes.
MarkJitFortMemAlive(MachineCode * obj)135 void JitFort::MarkJitFortMemAlive(MachineCode *obj)
136 {
137     size_t size = FortAllocSize(obj->GetInstructionsSize());
138     uintptr_t addr = obj->GetText();
139     uintptr_t endAddr = addr + size - 1;
140     uint32_t regionIdx = AddrToFortRegionIdx(addr);
141     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(addr));
142     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(endAddr));
143     LOG_JIT(DEBUG) << "MarkFortMemAlive: addr " << (void *)addr << " size " << size
144         << " regionIdx " << regionIdx << " instructionsSize " << obj->GetInstructionsSize();
145 }
146 
147 // Called by Jit Compile thread during JitFort Allocate to mark Fort buf
148 // awaiting install. Need mutex (JitGCLockHolder) for region gcBitset access.
149 // See JitFort::MarkJitFortMemAlive comments for mark bit encoding in GC bitset.
MarkJitFortMemAwaitInstall(uintptr_t addr,size_t size)150 void JitFort::MarkJitFortMemAwaitInstall(uintptr_t addr, size_t size)
151 {
152     uintptr_t endAddr = addr + size - 1;
153     uint32_t regionIdx = AddrToFortRegionIdx(addr);
154     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(addr));
155     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(addr + sizeof(uint64_t))); // mark next bit
156     regions_[regionIdx]->AtomicMark(reinterpret_cast<void *>(endAddr));
157     LOG_JIT(DEBUG) << "MarkFortMemAwaitInstall: addr " << (void *)addr << " size " << size
158         << " regionIdx " << regionIdx;
159 }
160 
161 // Called by JS/Main thread during SafePoint to clear Fort buf AwaitInstall bit
162 // See JitFort::MarkJitFortMemAlive comments for mark bit encoding in GC bitset.
MarkJitFortMemInstalled(MachineCode * obj)163 void JitFort::MarkJitFortMemInstalled(MachineCode *obj)
164 {
165     size_t size = FortAllocSize(obj->GetInstructionsSize());
166     uintptr_t addr = obj->GetText();
167     uint32_t regionIdx = AddrToFortRegionIdx(addr);
168     regions_[regionIdx]->GetGCBitset()->ClearMark(addr + sizeof(uint64_t)); // clear next bit
169     LOG_JIT(DEBUG) << "MarkFortMemInstalled: addr " << (void *)addr << " size " << size
170         << " regionIdx " << regionIdx << " instructionsSize " << obj->GetInstructionsSize();
171 }
172 
AddrToFortRegionIdx(uint64_t addr)173 uint32_t JitFort::AddrToFortRegionIdx(uint64_t addr)
174 {
175     ASSERT(InRange(addr));
176     uint32_t regionIdx = ((addr - JitFortBegin()) & ~(DEFAULT_REGION_MASK)) >> REGION_SIZE_LOG2;
177     ASSERT(regionIdx < MAX_JIT_FORT_REGIONS);
178     return regionIdx;
179 }
180 
181 /*
182  * Called from GC worker thread duing Old/Full GC Sweep (AsyncSweep). Mutex is needed
183  * to ensure exclusive access to JitFort memory by GC thread when it frees JitFort mem
184  * blocks, and by Jit compiled thread when it allocates Fort mem.
185  */
UpdateFreeSpace()186 void JitFort::UpdateFreeSpace()
187 {
188     if (!Jit::GetInstance()->IsEnableJitFort()) {
189         return;
190     }
191 
192     LockHolder lock(mutex_);
193 
194     if (!regionList_.GetLength()) { // LCOV_EXCL_BR_LINE
195         return;
196     }
197     LOG_JIT(DEBUG) << "UpdateFreeSpace enter: " << "Fort space allocated: "
198         << allocator_->GetAllocatedSize()
199         << " available: " << allocator_->GetAvailableSize();
200     allocator_->RebuildFreeList();
201     JitFortRegion *region = regionList_.GetFirst();
202     while (region) {
203         FreeRegion(region);
204         region = region->GetNext();
205     }
206     LOG_JIT(DEBUG) << "UpdateFreeSpace exit: allocator_->GetAvailableSize  "
207         << allocator_->GetAvailableSize();
208 }
209 
FreeRegion(JitFortRegion * region)210 void JitFort::FreeRegion(JitFortRegion *region)
211 {
212     LOG_JIT(DEBUG) << "JitFort FreeRegion " << (void*)(region->GetBegin());
213 
214     uintptr_t freeStart = region->GetBegin();
215     region->GetGCBitset()->IterateMarkedBitsConst(
216         region->GetBegin(), region->GetGCBitsetSize(),
217         [this, &region, &freeStart](void *mem, size_t size) {
218             ASSERT(region->InRange(ToUintPtr(mem)));
219             (void) region;
220             uintptr_t freeEnd = ToUintPtr(mem);
221             if (freeStart != freeEnd) {
222                 allocator_->Free(freeStart, freeEnd - freeStart, true);
223             }
224             freeStart = freeEnd + size;
225         });
226     uintptr_t freeEnd = region->GetEnd();
227     if (freeStart != freeEnd) {
228         allocator_->Free(freeStart, freeEnd - freeStart, true);
229     }
230 }
231 
InRange(uintptr_t address) const232 bool JitFort::InRange(uintptr_t address) const
233 {
234     return address >= jitFortBegin_ && (jitFortBegin_ + jitFortSize_) > 1 &&
235         address <= (jitFortBegin_ + jitFortSize_ - 1);
236 }
237 
PrepareSweeping()238 void JitFort::PrepareSweeping()
239 {
240     isSweeping_.store(false, std::memory_order_release);
241 }
242 
243 // concurrent sweep - only one of the AsyncSweep task will do JitFort sweep
AsyncSweep()244 void JitFort::AsyncSweep()
245 {
246     bool expect = false;
247     if (isSweeping_.compare_exchange_strong(expect, true, std::memory_order_seq_cst)) {
248         LOG_JIT(DEBUG) << "JitFort::AsyncSweep";
249         UpdateFreeSpace();
250     }
251 }
252 
253 // non-concurrent sweep
Sweep()254 void JitFort::Sweep()
255 {
256     LOG_JIT(DEBUG) << "JitFort::Sweep";
257     UpdateFreeSpace();
258 }
259 
260 // Used by JitFort::UpdateFreeSpace call path to find corresponding
261 // JitFortRegion for a free block in JitFort space, in order to put the blk into
262 // the corresponding free set of the JitFortRegion the free block belongs.
ObjectAddressToRange(uintptr_t addr)263 JitFortRegion *JitFort::ObjectAddressToRange(uintptr_t addr)
264 {
265     return regions_[AddrToFortRegionIdx(addr)];
266 }
267 
MarkStartAddr(bool awaitInstall,uintptr_t startAddr,uint32_t index,uint32_t & word)268 void JitFortGCBitset::MarkStartAddr(bool awaitInstall, uintptr_t startAddr, uint32_t index, uint32_t &word)
269 {
270     if (!awaitInstall) {
271         ClearMark(startAddr);
272         word &= ~(1u << index);
273     } else {
274         word &= ~(1u << index);
275         word &= ~(1u << (index+1));
276     }
277 }
278 
MarkEndAddr(bool awaitInstall,uintptr_t endAddr,uint32_t index,uint32_t & word)279 void JitFortGCBitset::MarkEndAddr(bool awaitInstall, uintptr_t endAddr, uint32_t index, uint32_t &word)
280 {
281     if (!awaitInstall) {
282         ClearMark(endAddr - 1);
283     }
284     word &= ~(1u << index);
285 }
286 
287 // See JitFort::MarkJitFortMemAlive comments for mark bit encoding in JitFort GC bitset.
288 template <typename Visitor>
IterateMarkedBitsConst(uintptr_t regionAddr,size_t bitsetSize,Visitor visitor)289 void JitFortGCBitset::IterateMarkedBitsConst(uintptr_t regionAddr, size_t bitsetSize, Visitor visitor)
290 {
291     bool awaitInstall = false;
292     uintptr_t startAddr = 0;
293     uintptr_t endAddr = 0;
294 
295     auto words = Words();
296     uint32_t wordCount = WordCount(bitsetSize);
297     uint32_t index = BIT_PER_WORD;
298     for (uint32_t i = 0; i < wordCount; i++) {
299         uint32_t word = words[i];
300         while (word != 0) {
301             index = static_cast<uint32_t>(__builtin_ctz(word));
302             ASSERT(index < BIT_PER_WORD);
303             if (!startAddr) {
304                 startAddr = regionAddr + (index << TAGGED_TYPE_SIZE_LOG);
305                 awaitInstall = Test(regionAddr + ((index+1) << TAGGED_TYPE_SIZE_LOG));
306                 MarkStartAddr(awaitInstall, startAddr, index, word);
307             } else {
308                 endAddr = regionAddr + ((index+1) << TAGGED_TYPE_SIZE_LOG);
309                 LOG_JIT(DEBUG) << "Live Jit Mem " << (void *)startAddr << " size " << endAddr-startAddr;
310                 visitor(reinterpret_cast<void *>(startAddr), endAddr - startAddr);
311                 MarkEndAddr(awaitInstall, endAddr, index, word);
312                 awaitInstall = false;
313                 startAddr = 0;
314             }
315         }
316         regionAddr += TAGGED_TYPE_SIZE * BIT_PER_WORD;
317     }
318 }
319 
320 bool JitFort::isResourceAvailable_ = true;
IsResourceAvailable()321 bool JitFort::IsResourceAvailable()
322 {
323     return isResourceAvailable_;
324 }
InitJitFortResource()325 void JitFort::InitJitFortResource()
326 {
327 #if defined(JIT_ENABLE_CODE_SIGN) && !defined(JIT_FORT_DISABLE)
328     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "JIT::InitJitFortResource");
329     if (!Jit::GetInstance()->IsAppJit()) {
330         int fd = open("/dev/xpm", O_RDWR);
331         if (fd < 0) {
332             isResourceAvailable_ = false;
333             LOG_JIT(ERROR) << "Failed to init jitfort resource, open xpm failed: " << strerror(errno);
334             return;
335         }
336         int rc = ioctl(fd, XPM_SET_JITFORT_ENABLE, 0);
337         if (rc < 0) {
338             isResourceAvailable_ = false;
339             LOG_JIT(ERROR) << "Failed to init jitfort resource, enable xpm failed: " << strerror(errno);
340             close(fd);
341             return;
342         }
343         close(fd);
344     }
345     constexpr int prSetJitFort = 0x6a6974;
346     constexpr int jitFortInit = 5;
347     int res = prctl(prSetJitFort, jitFortInit, 0);
348     if (res < 0) {
349         isResourceAvailable_ = false;
350         LOG_JIT(ERROR) << "Failed to init jitfort resource: " << strerror(errno);
351         return;
352     }
353     res = prctl(prSetJitFort, jitFortInit, 0);
354     if (res >= 0 || errno != EEXIST) {
355         isResourceAvailable_ = false;
356         LOG_JIT(ERROR) << "jitfort not support";
357     }
358 #endif
359 }
360 
MemDescPool(uintptr_t fortBegin,size_t fortSize)361 MemDescPool::MemDescPool(uintptr_t fortBegin, size_t fortSize)
362     : fortBegin_(fortBegin), fortSize_(fortSize)
363 {
364     Expand();
365 }
366 
~MemDescPool()367 MemDescPool::~MemDescPool()
368 {
369     for (const auto& block : memDescBlocks_) {
370         if (block) {
371             free(block);
372         }
373     }
374 }
375 
GetDesc()376 MemDesc *MemDescPool::GetDesc()
377 {
378     if (IsEmpty(freeList_)) {
379         Expand();
380     }
381     if (!IsEmpty(freeList_)) { // LCOV_EXCL_BR_LINE
382         MemDesc *res = freeList_;
383         freeList_ = freeList_->GetNext();
384         allocated_++;
385         if (allocated_-returned_ > highwater_) {
386             highwater_ = allocated_ - returned_;
387         }
388         return res;
389     }
390     return nullptr;
391 }
392 
Expand()393 void MemDescPool::Expand()
394 {
395     void *block = malloc(sizeof(MemDesc) * MEMDESCS_PER_BLOCK);
396     if (block) {
397         memDescBlocks_.push_back(block);
398         for (size_t i = 0; i < MEMDESCS_PER_BLOCK; ++i) {
399             Add(new (ToVoidPtr(reinterpret_cast<uintptr_t>(block) + i*sizeof(MemDesc))) MemDesc());
400         }
401     }
402 }
403 
Add(MemDesc * desc)404 void MemDescPool::Add(MemDesc *desc)
405 {
406     desc->SetNext(freeList_);
407     freeList_ = desc;
408 }
409 
410 }  // namespace panda::ecmascript
411