1/*
2 * Copyright (c) 2021 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_ALLOCATOR_INL_H
17#define ECMASCRIPT_MEM_ALLOCATOR_INL_H
18
19#include <cstdlib>
20#include <type_traits>
21
22#include "ecmascript/free_object.h"
23#include "ecmascript/mem/allocator.h"
24#include "ecmascript/mem/heap.h"
25
26namespace panda::ecmascript {
27BumpPointerAllocator::BumpPointerAllocator(uintptr_t begin, uintptr_t end) : begin_(begin), top_(begin), end_(end) {}
28
29void BumpPointerAllocator::Reset()
30{
31    begin_ = 0;
32    top_ = 0;
33    end_ = 0;
34}
35
36void BumpPointerAllocator::Reset(uintptr_t begin, uintptr_t end)
37{
38    begin_ = begin;
39    top_ = begin;
40    end_ = end;
41#ifdef ARK_ASAN_ON
42    ASAN_POISON_MEMORY_REGION(reinterpret_cast<void *>(top_), (end_ - top_));
43#endif
44}
45
46void BumpPointerAllocator::Reset(uintptr_t begin, uintptr_t end, uintptr_t top)
47{
48    begin_ = begin;
49    top_ = top;
50    end_ = end;
51#ifdef ARK_ASAN_ON
52    ASAN_POISON_MEMORY_REGION(reinterpret_cast<void *>(top_), (end_ - top_));
53#endif
54}
55
56void BumpPointerAllocator::ResetTopPointer(uintptr_t top)
57{
58    top_ = top;
59}
60
61uintptr_t BumpPointerAllocator::Allocate(size_t size)
62{
63    ASSERT(size != 0);
64    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
65    if (UNLIKELY(top_ + size > end_)) {
66        return 0;
67    }
68    uintptr_t result = top_;
69    // It need to mark unpoison when object being allocated.
70    ASAN_UNPOISON_MEMORY_REGION(reinterpret_cast<void *>(result), size);
71    top_ += size;
72    return result;
73}
74
75template <typename T>
76FreeListAllocator<T>::FreeListAllocator(BaseHeap *heap) : heap_(heap)
77{
78    freeList_ = std::make_unique<FreeObjectList<T>>();
79}
80
81template <typename T>
82void FreeListAllocator<T>::Initialize(Region *region)
83{
84    bpAllocator_.Reset(region->GetBegin(), region->GetEnd());
85}
86
87template <typename T>
88void FreeListAllocator<T>::Reset(BaseHeap *heap)
89{
90    heap_ = heap;
91    freeList_ = std::make_unique<FreeObjectList<T>>();
92    FreeBumpPoint();
93}
94
95template <typename T>
96void FreeListAllocator<T>::AddFree(Region *region)
97{
98    auto begin = region->GetBegin();
99    auto end = region->GetEnd();
100    FreeBumpPoint();
101    bpAllocator_.Reset(begin, end);
102}
103
104template <typename T>
105uintptr_t FreeListAllocator<T>::Allocate(size_t size)
106{
107    auto ret = bpAllocator_.Allocate(size);
108    if (LIKELY(ret != 0)) {
109        allocationSizeAccumulator_ += size;
110        return ret;
111    }
112    T *object = freeList_->Allocate(size);
113    if (object != nullptr) {
114        ret = Allocate(object, size);
115    }
116    return ret;
117}
118
119
120template <typename T>
121uintptr_t FreeListAllocator<T>::Allocate(T *object, size_t size)
122{
123    uintptr_t begin = object->GetBegin();
124    uintptr_t end = object->GetEnd();
125    uintptr_t remainSize = end - begin - size;
126    ASSERT(remainSize >= 0);
127    if constexpr (std::is_same<T, MemDesc>::value) {
128        memDescPool_->ReturnDescToPool(object);
129    }
130    // Keep a longest freeObject between bump-pointer and free object that just allocated
131    allocationSizeAccumulator_ += size;
132    if (remainSize <= bpAllocator_.Available()) {
133        Free(begin + size, remainSize);
134        return begin;
135    } else {
136        FreeBumpPoint();
137        bpAllocator_.Reset(begin, end);
138        auto ret = bpAllocator_.Allocate(size);
139        return ret;
140    }
141}
142
143template <typename T>
144void FreeListAllocator<T>::FreeBumpPoint()
145{
146    auto begin = bpAllocator_.GetTop();
147    auto size = bpAllocator_.Available();
148    bpAllocator_.Reset();
149    Free(begin, size, true);
150}
151
152template <typename T>
153void FreeListAllocator<T>::FillBumpPointer()
154{
155    if constexpr (std::is_same<T, MemDesc>::value) {
156        return;
157    }
158    size_t size = bpAllocator_.Available();
159    if (size != 0) {
160        FreeObject::FillFreeObject(heap_, bpAllocator_.GetTop(), size);
161    }
162}
163
164template <typename T>
165void FreeListAllocator<T>::ResetBumpPointer(uintptr_t begin, uintptr_t end, uintptr_t top)
166{
167    bpAllocator_.Reset(begin, end, top);
168}
169
170template <typename T>
171void FreeListAllocator<T>::ResetTopPointer(uintptr_t top)
172{
173    bpAllocator_.ResetTopPointer(top);
174}
175
176// The object will be marked with poison after being put into the freelist when is_asan is true.
177template <typename T>
178void FreeListAllocator<T>::Free(uintptr_t begin, size_t size, bool isAdd)
179{
180    ASSERT(size >= 0);
181    if (size != 0) {
182        if constexpr (!std::is_same<T, MemDesc>::value) {
183            ASSERT(heap_ != nullptr);
184            T::FillFreeObject(heap_, begin, size);
185        }
186
187        ASAN_UNPOISON_MEMORY_REGION(reinterpret_cast<void *>(begin), size);
188        freeList_->Free(begin, size, isAdd);
189#ifdef ARK_ASAN_ON
190        ASAN_POISON_MEMORY_REGION(reinterpret_cast<void *>(begin), size);
191#endif
192    }
193}
194
195template <typename T>
196uintptr_t FreeListAllocator<T>::LookupSuitableFreeObject(size_t size)
197{
198    auto freeObject = freeList_->LookupSuitableFreeObject(size);
199    if (freeObject != nullptr) {
200        return freeObject->GetBegin();
201    }
202    return 0;
203}
204
205template <typename T>
206void FreeListAllocator<T>::RebuildFreeList()
207{
208    bpAllocator_.Reset();
209    freeList_->Rebuild();
210}
211
212template <typename T>
213inline void FreeListAllocator<T>::CollectFreeObjectSet(Region *region)
214{
215    region->EnumerateFreeObjectSets([&](FreeObjectSet<T> *set) {
216        if (set == nullptr || set->Empty()) {
217            return;
218        }
219        freeList_->AddSet(set);
220    });
221    freeList_->IncreaseWastedSize(region->GetWastedSize());
222}
223
224template <typename T>
225inline bool FreeListAllocator<T>::MatchFreeObjectSet(Region *region, size_t size)
226{
227    bool ret = false;
228    region->REnumerateFreeObjectSets([&](FreeObjectSet<T> *set) {
229        if (set == nullptr || set->Empty()) {
230            return true;
231        }
232        ret = freeList_->MatchFreeObjectInSet(set, size);
233        return false;
234    });
235    return ret;
236}
237
238template <typename T>
239inline void FreeListAllocator<T>::DetachFreeObjectSet(Region *region)
240{
241    region->EnumerateFreeObjectSets([&](FreeObjectSet<T> *set) {
242        if (set == nullptr || set->Empty()) {
243            return;
244        }
245        freeList_->RemoveSet(set);
246    });
247    freeList_->DecreaseWastedSize(region->GetWastedSize());
248}
249
250template <typename T>
251size_t FreeListAllocator<T>::GetAvailableSize() const
252{
253    return freeList_->GetFreeObjectSize() + bpAllocator_.Available();
254}
255
256template <typename T>
257size_t FreeListAllocator<T>::GetWastedSize() const
258{
259    return freeList_->GetWastedSize();
260}
261}  // namespace panda::ecmascript
262#endif  // ECMASCRIPT_MEM_ALLOCATOR_INL_H
263