• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 PANDA_RUNTIME_MEM_GC_CROSSING_MAP_H_
17 #define PANDA_RUNTIME_MEM_GC_CROSSING_MAP_H_
18 
19 #include <array>
20 #include <limits>
21 
22 #include "libpandabase/mem/mem.h"
23 #include "libpandabase/utils/logger.h"
24 #include "runtime/include/mem/allocator.h"
25 #include "runtime/mem/runslots.h"
26 
27 namespace panda::mem {
28 
29 static constexpr uint64_t PANDA_CROSSING_MAP_COVERAGE = PANDA_MAX_HEAP_SIZE;
30 // If enabled - we will manage elements, which cross map borders.
31 static constexpr bool PANDA_CROSSING_MAP_MANAGE_CROSSED_BORDER = true;
32 static constexpr size_t PANDA_CROSSING_MAP_GRANULARITY = PAGE_SIZE;
33 // because IteratingOverObjectsInRange is depended on it
34 static_assert(PANDA_CROSSING_MAP_GRANULARITY == PAGE_SIZE);
35 
36 //  CrossingMap structure is a double linked array:
37 //
38 //  Each static array has a link to dynamic map
39 //  which will be dynamically allocated/deallocated via internal allocator:
40 //   |-------|-------|-------|-------|-------|-------|
41 //   |       |       |       |       |       |       |
42 //   |-------|-------|-------|-------|-------|-------|
43 //       |       |       |       |       |       |
44 //       |       |       |       |       |       |
45 //    nullptr    |    nullptr nullptr nullptr nullptr
46 //               |
47 //               |
48 //               |
49 //               |
50 //      |-----|-----|-----|
51 //      |     |     |     |
52 //      |-----|-----|-----|
53 //          dynamic map
54 //
55 
56 // Each page from PANDA_CROSSING_MAP_COVERAGE heap space has its element in crossing map.
57 // This element (or map) can be used to get the first object address, which starts inside this page (if it exists)
58 // or an object address, which crosses the borders of this page.
59 class CrossingMap {
60 public:
61     /**
62      * \brief Create a new instance of a Crossing map.
63      * @param internal_allocator - pointer to the internal allocator.
64      * @param start_addr - first bit of the memory which must be covered by the Crossing Map.
65      * @param size - size of the memory which must be covered by the Crossing Map.
66      */
67     explicit CrossingMap(InternalAllocatorPtr internal_allocator, uintptr_t start_addr, size_t size);
68     ~CrossingMap();
69     NO_COPY_SEMANTIC(CrossingMap);
70     NO_MOVE_SEMANTIC(CrossingMap);
71 
72     void Initialize();
73     void Destroy();
74 
75     /**
76      * \brief Add object to the Crossing map.
77      * @param obj_addr - pointer to the object (object header).
78      * @param obj_size - size of the object.
79      */
80     void AddObject(const void *obj_addr, size_t obj_size);
81 
82     /**
83      * \brief Remove object from the Crossing map.
84      *  Crossing map doesn't know about existed objects (it knows only about first).
85      *  Therefore, during removing we need to send next and previous object parameters.
86      * @param obj_addr - pointer to the removing object (object header).
87      * @param obj_size - size of the removing object.
88      * @param next_obj_addr - pointer to the next object (object header). It can be nullptr.
89      * @param prev_obj_addr - pointer to the previous object (object header). It can be nullptr.
90      * @param prev_obj_size - size of the previous object.
91      *        It is used check if previous object crosses the borders of the current map.
92      */
93     void RemoveObject(const void *obj_addr, size_t obj_size, const void *next_obj_addr = nullptr,
94                       const void *prev_obj_addr = nullptr, size_t prev_obj_size = 0);
95 
96     /**
97      * \brief Find and return the first object, which starts in an interval inclusively
98      * or an object, which crosses the interval border.
99      * It is essential to check the previous object of the returned object to make sure that
100      * we find the first object, which crosses the border of this interval.
101      * @param start_addr - pointer to the first byte of the interval.
102      * @param end_addr - pointer to the last byte of the interval.
103      * @return Returns the first object which starts inside an interval,
104      *  or an object which crosses a border of this interval
105      *  or nullptr
106      */
107     void *FindFirstObject(const void *start_addr, const void *end_addr);
108 
109     /**
110      * \brief Initialize a Crossing map for the corresponding memory ranges.
111      * @param start_addr - pointer to the first byte of the interval.
112      * @param size - size of the interval.
113      */
114     void InitializeCrossingMapForMemory(const void *start_addr, size_t size);
115 
116     /**
117      * \brief Remove a Crossing map for the corresponding memory ranges.
118      * @param start_addr - pointer to the first byte of the interval.
119      * @param size - size of the interval.
120      */
121     void RemoveCrossingMapForMemory(const void *start_addr, size_t size);
122 
123 private:
124     static constexpr bool CROSSING_MAP_MANAGE_CROSSED_BORDER = PANDA_CROSSING_MAP_MANAGE_CROSSED_BORDER;
125     static constexpr size_t CROSSING_MAP_GRANULARITY = PANDA_CROSSING_MAP_GRANULARITY;
126     // How much memory we manage via one element of the static array.
127     static constexpr size_t CROSSING_MAP_STATIC_ARRAY_GRANULARITY = PANDA_POOL_ALIGNMENT_IN_BYTES;
128     static constexpr Alignment CROSSING_MAP_OBJ_ALIGNMENT = DEFAULT_ALIGNMENT;
129     using CROSSING_MAP_TYPE = uint16_t;
130 
131     static_assert(CROSSING_MAP_STATIC_ARRAY_GRANULARITY % CROSSING_MAP_GRANULARITY == 0);
132     static constexpr size_t CROSSING_MAP_COUNT_IN_STATIC_ARRAY_ELEMENT =
133         CROSSING_MAP_STATIC_ARRAY_GRANULARITY / CROSSING_MAP_GRANULARITY;
134 
135     // Each element of the crossing map consists of two fields:
136     //
137     // |.... Offset ....|.... Status ....|
138     //
139     // According Status bits, we can use the offset value in such a way:
140     //
141     // - if Status == Uninitialized, there is no element in this Page at all.
142     //
143     // - if Status == Initialized, the Offset value is an offset in words of the first element on this Page.
144     //   We can start our range iteration from this element.
145     //
146     // - if Status == Crossed border, the Offset value is an offset in crossing map array to a page where the object,
147     //   which crossed the Page border, is stored.
148     //
149     // - if Status == Initialized and Crossed border, the Offset value is an offset in words of the first element on
150     //   this Page, and also we know what there is an object, which crossed the Page border.
151     class CrossingMapElement {
152     public:
153         enum STATE {
154             // This element of crossing map hasn't been initialized yet.
155             STATE_UNINITIALIZED,
156             // There are no objects which start in this page,
157             // but there is an object which starts before this page and crosses page border.
158             STATE_CROSSED_BORDER,
159             // We have some object that starts inside this page,
160             // but there are no objects which cross page border.
161             STATE_INITIALIZED,
162             // We have some object that starts inside this page,
163             // and there is an object which crosses page border.
164             STATE_INITIALIZED_AND_CROSSED_BORDERS,
165         };
166 
GetMaxOffsetValue()167         static constexpr size_t GetMaxOffsetValue()
168         {
169             return MAX_OFFSET_VALUE;
170         }
171 
GetState()172         STATE GetState()
173         {
174             switch (value_ & STATUS_MASK) {
175                 case STATUS_UNINITIALIZED:
176                     return (value_ | 0U) == 0U ? STATE_UNINITIALIZED : STATE_CROSSED_BORDER;
177                 case STATUS_INITIALIZED:
178                     return STATE_INITIALIZED;
179                 case STATUS_INITIALIZED_AND_CROSSED_BORDERS:
180                     return STATE_INITIALIZED_AND_CROSSED_BORDERS;
181                 default:
182                     LOG(FATAL, ALLOC) << "CrossingMapElement: Undefined map state";
183                     return STATE_UNINITIALIZED;
184             }
185         }
186 
GetOffset()187         CROSSING_MAP_TYPE GetOffset()
188         {
189             return value_ >> STATUS_SIZE;
190         }
191 
SetUninitialized()192         void SetUninitialized()
193         {
194             value_ = STATUS_UNINITIALIZED;
195         }
196 
SetInitialized(CROSSING_MAP_TYPE offset)197         void SetInitialized(CROSSING_MAP_TYPE offset)
198         {
199             ASSERT(offset <= MAX_OFFSET_VALUE);
200             value_ = (offset << STATUS_SIZE);
201             value_ |= STATUS_INITIALIZED;
202         }
203 
SetInitializedAndCrossedBorder(CROSSING_MAP_TYPE offset)204         void SetInitializedAndCrossedBorder(CROSSING_MAP_TYPE offset)
205         {
206             ASSERT(offset <= MAX_OFFSET_VALUE);
207             value_ = (offset << STATUS_SIZE);
208             value_ |= STATUS_INITIALIZED_AND_CROSSED_BORDERS;
209         }
210 
SetCrossedBorder(CROSSING_MAP_TYPE offset)211         void SetCrossedBorder(CROSSING_MAP_TYPE offset)
212         {
213             ASSERT(offset <= MAX_OFFSET_VALUE);
214             value_ = (offset << STATUS_SIZE);
215             value_ |= STATUS_CROSSED_BORDER;
216         }
217 
218     private:
219         enum MAP_REPRESENTATION : CROSSING_MAP_TYPE {
220             STATUS_UNINITIALIZED = 0U,
221             STATUS_CROSSED_BORDER = 0U,
222             STATUS_INITIALIZED = 1U,
223             STATUS_INITIALIZED_AND_CROSSED_BORDERS = 2U,
224             STATUS_SIZE = 2U,
225             STATUS_MASK = (1U << STATUS_SIZE) - 1U,
226         };
227 
228         static_assert(std::is_unsigned<CROSSING_MAP_TYPE>::value);
229         static constexpr size_t MAX_OFFSET_VALUE = std::numeric_limits<CROSSING_MAP_TYPE>::max() >> STATUS_SIZE;
230 
231         // We must be sure that we can use such type for all possible obj offsets inside one page.
232         static_assert((CROSSING_MAP_GRANULARITY >> CROSSING_MAP_OBJ_ALIGNMENT) <= MAX_OFFSET_VALUE);
233 
234         CROSSING_MAP_TYPE value_ {STATUS_UNINITIALIZED};
235     };
236     // It is a pointer to an array of Crossing Map elements.
237     using StaticArrayPtr = CrossingMapElement **;
238 
GetMapNumFromAddr(const void * addr)239     size_t GetMapNumFromAddr(const void *addr)
240     {
241         ASSERT(ToUintPtr(addr) >= start_addr_);
242         size_t map_num = (ToUintPtr(addr) - start_addr_) / CROSSING_MAP_GRANULARITY;
243         ASSERT(map_num < map_elements_count_);
244         return map_num;
245     }
246 
GetAddrFromOffset(size_t map_num,size_t offset)247     void *GetAddrFromOffset(size_t map_num, size_t offset)
248     {
249         ASSERT(map_num < map_elements_count_);
250         return ToVoidPtr(start_addr_ + map_num * CROSSING_MAP_GRANULARITY + (offset << CROSSING_MAP_OBJ_ALIGNMENT));
251     }
252 
GetOffsetFromAddr(const void * addr)253     size_t GetOffsetFromAddr(const void *addr)
254     {
255         ASSERT(ToUintPtr(addr) >= start_addr_);
256         size_t offset = (ToUintPtr(addr) - start_addr_) % CROSSING_MAP_GRANULARITY;
257         ASSERT((offset & (GetAlignmentInBytes(CROSSING_MAP_OBJ_ALIGNMENT) - 1U)) == 0U);
258         return offset >> CROSSING_MAP_OBJ_ALIGNMENT;
259     }
260 
261     void UpdateCrossedBorderOnAdding(size_t first_crossed_border_map, size_t last_crossed_border_map);
262     void UpdateCrossedBorderOnRemoving(size_t crossed_border_map);
263     void *FindObjInMap(size_t map_num);
264 
GetMapElement(size_t map_num)265     CrossingMapElement *GetMapElement(size_t map_num)
266     {
267         ASSERT(map_num < map_elements_count_);
268         size_t static_array_num = map_num / CROSSING_MAP_COUNT_IN_STATIC_ARRAY_ELEMENT;
269         size_t relative_map_num = map_num % CROSSING_MAP_COUNT_IN_STATIC_ARRAY_ELEMENT;
270         ASSERT(GetStaticArrayElement(static_array_num) != nullptr);
271         return static_cast<CrossingMapElement *>(ToVoidPtr(
272             (ToUintPtr(GetStaticArrayElement(static_array_num)) + relative_map_num * sizeof(CrossingMapElement))));
273     }
274 
GetStaticArrayElement(size_t static_array_num)275     CrossingMapElement *GetStaticArrayElement(size_t static_array_num)
276     {
277         ASSERT(static_array_num < static_array_elements_count_);
278         auto element_pointer = static_cast<StaticArrayPtr>(
279             ToVoidPtr((ToUintPtr(static_array_) + static_array_num * sizeof(StaticArrayPtr))));
280         return *element_pointer;
281     }
282 
SetStaticArrayElement(size_t static_array_num,CrossingMapElement * value)283     void SetStaticArrayElement(size_t static_array_num, CrossingMapElement *value)
284     {
285         ASSERT(static_array_num < static_array_elements_count_);
286         void *element = ToVoidPtr(ToUintPtr(static_array_) + static_array_num * sizeof(StaticArrayPtr));
287         *static_cast<StaticArrayPtr>(element) = value;
288     }
289 
GetStaticArrayNumFromAddr(const void * addr)290     size_t GetStaticArrayNumFromAddr(const void *addr)
291     {
292         ASSERT(ToUintPtr(addr) >= start_addr_);
293         size_t static_array_num = (ToUintPtr(addr) - start_addr_) / CROSSING_MAP_STATIC_ARRAY_GRANULARITY;
294         ASSERT(static_array_num < static_array_elements_count_);
295         return static_array_num;
296     }
297 
298     StaticArrayPtr static_array_ {nullptr};
299     uintptr_t start_addr_ {0};
300     size_t map_elements_count_ {0};
301     size_t static_array_elements_count_ {0};
302     InternalAllocatorPtr internal_allocator_ {nullptr};
303     friend class CrossingMapTest;
304 };
305 
306 }  // namespace panda::mem
307 
308 #endif  // PANDA_RUNTIME_MEM_GC_CROSSING_MAP_H_
309