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#include "ecmascript/mem/mem_map_allocator.h"
17#include "ecmascript/platform/os.h"
18
19namespace panda::ecmascript {
20MemMapAllocator *MemMapAllocator::GetInstance()
21{
22    static MemMapAllocator *vmAllocator_ = new MemMapAllocator();
23    return vmAllocator_;
24}
25
26void MemMapAllocator::InitializeRegularRegionMap([[maybe_unused]] size_t alignment)
27{
28#if defined(PANDA_TARGET_64) && !WIN_OR_MAC_OR_IOS_PLATFORM
29    size_t initialRegularObjectCapacity = std::min(capacity_ / 2, INITIAL_REGULAR_OBJECT_CAPACITY);
30    size_t i = 0;
31    while (i < MEM_MAP_RETRY_NUM) {
32        void *addr = reinterpret_cast<void *>(ToUintPtr(RandomGenerateBigAddr(REGULAR_OBJECT_MEM_MAP_BEGIN_ADDR)) +
33            i * STEP_INCREASE_MEM_MAP_ADDR);
34        MemMap memMap = PageMap(initialRegularObjectCapacity, PAGE_PROT_NONE, alignment, addr);
35        if (ToUintPtr(memMap.GetMem()) >= ToUintPtr(addr)) {
36            PageTag(memMap.GetMem(), memMap.GetSize(), PageTagType::MEMPOOL_CACHE);
37            PageRelease(memMap.GetMem(), memMap.GetSize());
38            memMapPool_.InsertMemMap(memMap);
39            memMapPool_.SplitMemMapToCache(memMap);
40            break;
41        } else {
42            PageUnmap(memMap);
43            LOG_ECMA(ERROR) << "Regular object mem map big addr fail: " << errno;
44        }
45        i++;
46    }
47#endif
48}
49
50void MemMapAllocator::InitializeHugeRegionMap(size_t alignment)
51{
52    size_t initialHugeObjectCapacity = std::min(capacity_ / 2, INITIAL_HUGE_OBJECT_CAPACITY);
53#if defined(PANDA_TARGET_64) && !WIN_OR_MAC_OR_IOS_PLATFORM
54    size_t i = 0;
55    while (i <= MEM_MAP_RETRY_NUM) {
56        void *addr = reinterpret_cast<void *>(ToUintPtr(RandomGenerateBigAddr(HUGE_OBJECT_MEM_MAP_BEGIN_ADDR)) +
57            i * STEP_INCREASE_MEM_MAP_ADDR);
58        MemMap memMap = PageMap(initialHugeObjectCapacity, PAGE_PROT_NONE, alignment, addr);
59        if (ToUintPtr(memMap.GetMem()) >= ToUintPtr(addr) || i == MEM_MAP_RETRY_NUM) {
60            PageTag(memMap.GetMem(), memMap.GetSize(), PageTagType::MEMPOOL_CACHE);
61            PageRelease(memMap.GetMem(), memMap.GetSize());
62            memMapFreeList_.Initialize(memMap, capacity_);
63            break;
64        } else {
65            PageUnmap(memMap);
66            LOG_ECMA(ERROR) << "Huge object mem map big addr fail: " << errno;
67        }
68        i++;
69    }
70#else
71    MemMap hugeMemMap = PageMap(initialHugeObjectCapacity, PAGE_PROT_NONE, alignment);
72    PageTag(hugeMemMap.GetMem(), hugeMemMap.GetSize(), PageTagType::MEMPOOL_CACHE);
73    PageRelease(hugeMemMap.GetMem(), hugeMemMap.GetSize());
74    memMapFreeList_.Initialize(hugeMemMap, capacity_);
75#endif
76}
77
78static bool PageProtectMem(bool machineCodeSpace, void *mem, size_t size, [[maybe_unused]] bool isEnableJitFort)
79{
80    int prot = machineCodeSpace ? PAGE_PROT_EXEC_READWRITE : PAGE_PROT_READWRITE;
81
82    if (!machineCodeSpace) {
83        return PageProtect(mem, size, prot);
84    }
85
86    // MachineCode and HugeMachineCode space pages:
87#if defined(PANDA_TARGET_ARM64) && defined(PANDA_TARGET_OHOS)
88    if (isEnableJitFort) {
89        // if JitFort enabled, Jit code will be in JitFort space, so only need READWRITE here
90        return PageProtect(mem, size, PAGE_PROT_READWRITE);
91    } else {
92        // else Jit code will be in MachineCode space, need EXEC_READWRITE and MAP_EXECUTABLE (0x1000)
93        void *addr = PageMapExecFortSpace(mem, size, PAGE_PROT_EXEC_READWRITE);
94        if (addr != mem) {
95            return false;
96        }
97        return true;
98    }
99#else
100    // not running phone kernel. Jit code will be MachineCode space
101    return PageProtect(mem, size, PAGE_PROT_EXEC_READWRITE);
102#endif
103}
104
105MemMap MemMapAllocator::Allocate(const uint32_t threadId, size_t size, size_t alignment,
106                                 const std::string &spaceName, bool regular, bool isMachineCode, bool isEnableJitFort)
107{
108    MemMap mem;
109    PageTagType type = isMachineCode ? PageTagType::MACHINE_CODE : PageTagType::HEAP;
110
111    if (regular) {
112        mem = memMapPool_.GetRegularMemFromCommitted(size);
113        if (mem.GetMem() != nullptr) {
114            bool res = PageProtectMem(isMachineCode, mem.GetMem(), mem.GetSize(), isEnableJitFort);
115            if (!res) {
116                return MemMap();
117            }
118            PageTag(mem.GetMem(), size, type, spaceName, threadId);
119            return mem;
120        }
121        if (UNLIKELY(memMapTotalSize_ + size > capacity_)) {
122            LOG_GC(ERROR) << "memory map overflow";
123            return MemMap();
124        }
125        mem = memMapPool_.GetMemFromCache(size);
126        if (mem.GetMem() != nullptr) {
127            memMapTotalSize_ += size;
128            bool res = PageProtectMem(isMachineCode, mem.GetMem(), mem.GetSize(), isEnableJitFort);
129            if (!res) {
130                return MemMap();
131            }
132            PageTag(mem.GetMem(), size, type, spaceName, threadId);
133            return mem;
134        }
135        mem = PageMap(REGULAR_REGION_MMAP_SIZE, PAGE_PROT_NONE, alignment);
136        memMapPool_.InsertMemMap(mem);
137        mem = memMapPool_.SplitMemFromCache(mem);
138    } else {
139        if (UNLIKELY(memMapTotalSize_ + size > capacity_)) {
140            LOG_GC(ERROR) << "memory map overflow";
141            return MemMap();
142        }
143        mem = memMapFreeList_.GetMemFromList(size);
144    }
145    if (mem.GetMem() != nullptr) {
146        bool res = PageProtectMem(isMachineCode, mem.GetMem(), mem.GetSize(), isEnableJitFort);
147        if (!res) {
148            return MemMap();
149        }
150        PageTag(mem.GetMem(), mem.GetSize(), type, spaceName, threadId);
151        memMapTotalSize_ += mem.GetSize();
152    }
153    return mem;
154}
155
156void MemMapAllocator::CacheOrFree(void *mem, size_t size, bool isRegular, size_t cachedSize)
157{
158    if (isRegular && !memMapPool_.IsRegularCommittedFull(cachedSize)) {
159        // Cache regions to accelerate allocation.
160        // Clear ThreadId tag and tag the mem with ARKTS HEAP.
161        PageClearTag(mem, size);
162        PageTag(mem, size, PageTagType::HEAP);
163        memMapPool_.AddMemToCommittedCache(mem, size);
164        return;
165    }
166    Free(mem, size, isRegular);
167    if (isRegular && memMapPool_.ShouldFreeMore(cachedSize) > 0) {
168        int freeNum = memMapPool_.ShouldFreeMore(cachedSize);
169        for (int i = 0; i < freeNum; i++) {
170            void *freeMem = memMapPool_.GetRegularMemFromCommitted(size).GetMem();
171            if (freeMem != nullptr) {
172                Free(freeMem, size, isRegular);
173            } else {
174                return;
175            }
176        }
177    }
178}
179
180void MemMapAllocator::Free(void *mem, size_t size, bool isRegular)
181{
182    memMapTotalSize_ -= size;
183    PageTag(mem, size, PageTagType::MEMPOOL_CACHE);
184    if (!PageProtect(mem, size, PAGE_PROT_NONE)) {
185        return;
186    }
187    PageRelease(mem, size);
188    if (isRegular) {
189        memMapPool_.AddMemToCache(mem, size);
190    } else {
191        memMapFreeList_.AddMemToList(MemMap(mem, size));
192    }
193}
194
195void MemMapAllocator::AdapterSuitablePoolCapacity()
196{
197    size_t physicalSize = PhysicalSize();
198    capacity_ = std::min<size_t>(physicalSize * DEFAULT_CAPACITY_RATE, MAX_MEM_POOL_CAPACITY);
199    LOG_GC(INFO) << "Ark Auto adapter memory pool capacity:" << capacity_;
200}
201}  // namespace panda::ecmascript
202