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