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_TLAB_ALLOCATOR_INL_H
17#define ECMASCRIPT_MEM_TLAB_ALLOCATOR_INL_H
18
19#include "ecmascript/mem/tlab_allocator.h"
20
21#include "ecmascript/free_object.h"
22#include "ecmascript/mem/full_gc.h"
23#include "ecmascript/mem/heap-inl.h"
24
25namespace panda::ecmascript {
26static constexpr size_t MIN_BUFFER_SIZE = 31_KB;
27static constexpr size_t SMALL_OBJECT_SIZE = 8_KB;
28
29TlabAllocator::TlabAllocator(Heap *heap)
30    : heap_(heap), enableExpandYoung_(true), enableStealOldRegion_(true)
31{
32    size_t maxOldSpaceCapacity = heap->GetOldSpace()->GetMaximumCapacity();
33    localSpace_ = new LocalSpace(heap, maxOldSpaceCapacity, maxOldSpaceCapacity);
34    youngAllocator_.Reset();
35}
36
37inline void TlabAllocator::Finalize()
38{
39    if (youngAllocator_.Available() != 0) {
40        FreeObject::FillFreeObject(heap_, youngAllocator_.GetTop(), youngAllocator_.Available());
41        youngAllocator_.Reset();
42    }
43
44    heap_->MergeToOldSpaceSync(localSpace_);
45}
46
47uintptr_t TlabAllocator::Allocate(size_t size, MemSpaceType space)
48{
49    uintptr_t result = 0;
50    switch (space) {
51        case SEMI_SPACE:
52            result = AllocateInYoungSpace(size);
53            break;
54        case OLD_SPACE:
55            result = AllocateInOldSpace(size);
56            break;
57        case COMPRESS_SPACE:
58            result = AllocateInCompressSpace(size);
59            break;
60        default:
61            LOG_ECMA(FATAL) << "this branch is unreachable";
62            UNREACHABLE();
63    }
64    return result;
65}
66
67uintptr_t TlabAllocator::AllocateInYoungSpace(size_t size)
68{
69    ASSERT(AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)) == size);
70    if (UNLIKELY(size > SMALL_OBJECT_SIZE)) {
71        uintptr_t address = heap_->AllocateYoungSync(size);
72        return address;
73    }
74    uintptr_t result = youngAllocator_.Allocate(size);
75    if (result != 0) {
76        return result;
77    }
78    if (!enableExpandYoung_ || !ExpandYoung()) {
79        enableExpandYoung_ = false;
80        return 0;
81    }
82    return youngAllocator_.Allocate(size);
83}
84
85uintptr_t TlabAllocator::AllocateInCompressSpace(size_t size)
86{
87    ASSERT(AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)) == size);
88    size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
89    uintptr_t result = localSpace_->Allocate(size, true);
90    ASSERT(result != 0);
91    return result;
92}
93
94uintptr_t TlabAllocator::AllocateInOldSpace(size_t size)
95{
96    ASSERT(AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)) == size);
97    size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
98    // 1. Allocate from freelist in compress allocator
99    uintptr_t result = localSpace_->Allocate(size, false);
100    if (result == 0) {
101        // 2. Expand region from old space
102        if (enableStealOldRegion_) {
103            enableStealOldRegion_ = ExpandCompressFromOld(size);
104        }
105        result = localSpace_->Allocate(size, true);
106    }
107    ASSERT(result != 0);
108    return result;
109}
110
111bool TlabAllocator::ExpandYoung()
112{
113    uintptr_t buffer = heap_->AllocateYoungSync(MIN_BUFFER_SIZE);
114    if (buffer == 0) {
115        if (youngAllocator_.Available() != 0) {
116            FreeObject::FillFreeObject(heap_, youngAllocator_.GetTop(), youngAllocator_.Available());
117        }
118        return false;
119    }
120    uintptr_t end = buffer + MIN_BUFFER_SIZE;
121
122    if (buffer == youngAllocator_.GetEnd()) {
123        buffer = youngAllocator_.GetTop();
124    } else {
125        if (youngAllocator_.Available() != 0) {
126            FreeObject::FillFreeObject(heap_, youngAllocator_.GetTop(), youngAllocator_.Available());
127        }
128    }
129    youngAllocator_.Reset(buffer, end);
130    return true;
131}
132
133bool TlabAllocator::ExpandCompressFromOld(size_t size)
134{
135    auto region = heap_->GetOldSpace()->TryToGetExclusiveRegion(size);
136    if (region != nullptr) {
137        localSpace_->AddRegionToList(region);
138        return true;
139    }
140    return false;
141}
142
143SharedTlabAllocator::SharedTlabAllocator(SharedHeap *sHeap)
144    : sHeap_(sHeap)
145{
146    size_t maxOldSpaceCapacity = sHeap->GetOldSpace()->GetMaximumCapacity();
147    sLocalSpace_ = new SharedLocalSpace(sHeap, maxOldSpaceCapacity, maxOldSpaceCapacity);
148}
149
150inline void SharedTlabAllocator::Finalize()
151{
152    sHeap_->MergeToOldSpaceSync(sLocalSpace_);
153}
154
155uintptr_t SharedTlabAllocator::Allocate(size_t size, MemSpaceType space)
156{
157    uintptr_t result = 0;
158    switch (space) {
159        case SHARED_COMPRESS_SPACE:
160            result = AllocateInCompressSpace(size);
161            break;
162        default:
163            LOG_ECMA(FATAL) << "this branch is unreachable";
164            UNREACHABLE();
165    }
166    return result;
167}
168
169uintptr_t SharedTlabAllocator::AllocateInCompressSpace(size_t size)
170{
171    ASSERT(AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)) == size);
172    size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
173    uintptr_t result = sLocalSpace_->Allocate(size, true);
174    ASSERT(result != 0);
175    return result;
176}
177
178}  // namespace panda::ecmascript
179#endif  // ECMASCRIPT_MEM_TLAB_ALLOCATOR_INL_H
180