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 #ifndef ECMASCRIPT_MEM_SPARSE_SPACE_H 17 #define ECMASCRIPT_MEM_SPARSE_SPACE_H 18 19 #include "ecmascript/mem/space-inl.h" 20 #include "ecmascript/mem/mem_common.h" 21 #include "ecmascript/mem/jit_fort.h" 22 23 #ifdef ECMASCRIPT_SUPPORT_HEAPSAMPLING 24 #define CHECK_OBJECT_AND_INC_OBJ_SIZE(size) \ 25 if (object != 0) { \ 26 IncreaseLiveObjectSize(size); \ 27 if (!heap_->IsConcurrentFullMark() || heap_->IsReadyToConcurrentMark()) { \ 28 Region::ObjectAddressToRange(object)->IncreaseAliveObject(size); \ 29 } \ 30 InvokeAllocationInspector(object, size, size); \ 31 return object; \ 32 } 33 #else 34 #define CHECK_OBJECT_AND_INC_OBJ_SIZE(size) \ 35 if (object != 0) { \ 36 IncreaseLiveObjectSize(size); \ 37 if (!heap_->IsConcurrentFullMark() || heap_->IsReadyToConcurrentMark()) { \ 38 Region::ObjectAddressToRange(object)->IncreaseAliveObject(size); \ 39 } \ 40 return object; \ 41 } 42 #endif 43 44 enum class SweepState : uint8_t { 45 NO_SWEEP, 46 SWEEPING, 47 SWEPT 48 }; 49 50 namespace panda::ecmascript { 51 class LocalSpace; 52 class SemiSpace; 53 54 class SparseSpace : public Space { 55 public: 56 SparseSpace(Heap *heap, MemSpaceType type, size_t initialCapacity, size_t maximumCapacity); ~SparseSpace()57 ~SparseSpace() override 58 { 59 delete allocator_; 60 } 61 NO_COPY_SEMANTIC(SparseSpace); 62 NO_MOVE_SEMANTIC(SparseSpace); 63 64 void Initialize() override; 65 void Reset(); 66 void ResetTopPointer(uintptr_t top); 67 68 uintptr_t Allocate(size_t size, bool allowGC = true); 69 bool Expand(); 70 71 // For sweeping 72 virtual void PrepareSweeping(); 73 virtual void Sweep(); 74 virtual void AsyncSweep(bool isMain); 75 76 bool TryFillSweptRegion(); 77 // Ensure All region finished sweeping 78 bool FinishFillSweptRegion(); 79 80 void AddSweepingRegion(Region *region); 81 void SortSweepingRegion(); 82 Region *GetSweepingRegionSafe(); 83 void AddSweptRegionSafe(Region *region); 84 Region *GetSweptRegionSafe(); 85 Region *TryToGetSuitableSweptRegion(size_t size); 86 87 void FreeRegion(Region *current, bool isMain = true); 88 void FreeLiveRange(Region *current, uintptr_t freeStart, uintptr_t freeEnd, bool isMain); 89 90 void DetachFreeObjectSet(Region *region); 91 92 void IterateOverObjects(const std::function<void(TaggedObject *object)> &objectVisitor) const; 93 void IterateOldToNewOverObjects( 94 const std::function<void(TaggedObject *object, JSTaggedValue value)> &visitor) const; 95 96 size_t GetHeapObjectSize() const; 97 98 void IncreaseAllocatedSize(size_t size); 99 IncreaseLiveObjectSize(size_t size)100 void IncreaseLiveObjectSize(size_t size) 101 { 102 liveObjectSize_ += size; 103 } 104 DecreaseLiveObjectSize(size_t size)105 void DecreaseLiveObjectSize(size_t size) 106 { 107 liveObjectSize_ -= size; 108 } 109 SetOvershootSize(size_t size)110 void SetOvershootSize(size_t size) 111 { 112 overshootSize_ = size; 113 } 114 IncreaseOvershootSize(size_t size)115 void IncreaseOvershootSize(size_t size) 116 { 117 overshootSize_ += size; 118 } 119 GetOvershootSize()120 size_t GetOvershootSize() const 121 { 122 return overshootSize_; 123 } 124 AdjustOvershootSize()125 void AdjustOvershootSize() 126 { 127 if (overshootSize_ > 0 && maximumCapacity_ > committedSize_) { 128 size_t size = maximumCapacity_ - committedSize_; 129 overshootSize_ = overshootSize_ > size ? overshootSize_ - size : 0; 130 } 131 } 132 CommittedSizeExceed()133 bool CommittedSizeExceed() const 134 { 135 return committedSize_ >= maximumCapacity_ + overshootSize_ + outOfMemoryOvershootSize_; 136 } 137 138 size_t GetTotalAllocatedSize() const; 139 140 void InvokeAllocationInspector(Address object, size_t size, size_t alignedSize); 141 142 protected: 143 FreeListAllocator<FreeObject> *allocator_; 144 SweepState sweepState_ = SweepState::NO_SWEEP; 145 Heap *localHeap_ {nullptr}; 146 size_t liveObjectSize_ {0}; 147 uintptr_t AllocateAfterSweepingCompleted(size_t size); 148 149 private: 150 Mutex lock_; 151 std::vector<Region *> sweepingList_; 152 std::vector<Region *> sweptList_; 153 size_t overshootSize_ {0}; 154 }; 155 156 class OldSpace : public SparseSpace { 157 public: 158 OldSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity); 159 ~OldSpace() override = default; 160 NO_COPY_SEMANTIC(OldSpace); 161 NO_MOVE_SEMANTIC(OldSpace); 162 163 Region *TrySweepToGetSuitableRegion(size_t size); 164 Region *TryToGetExclusiveRegion(size_t size); 165 166 uintptr_t AllocateFast(size_t size); 167 uintptr_t AllocateSlow(size_t size, bool tryFast); 168 // CSet 169 void SelectCSet(); 170 void CheckRegionSize(); 171 void RevertCSet(); 172 void ReclaimCSet(); 173 174 bool SwapRegion(Region *region, SemiSpace *fromSpace); 175 176 void PrepareSweepNewToOldRegions(); 177 void SweepNewToOldRegions(); 178 GetSelectedRegionNumber()179 unsigned long GetSelectedRegionNumber() const 180 { 181 return std::max(committedSize_ / PARTIAL_GC_MAX_COLLECT_REGION_RATE, PARTIAL_GC_INITIAL_COLLECT_REGION_SIZE); 182 } 183 GetMergeSize()184 size_t GetMergeSize() const 185 { 186 return mergeSize_; 187 } 188 IncreaseMergeSize(size_t size)189 void IncreaseMergeSize(size_t size) 190 { 191 mergeSize_ += size; 192 } 193 ResetMergeSize()194 void ResetMergeSize() 195 { 196 mergeSize_ = 0; 197 } 198 IncreaseCommittedOverSizeLimit(size_t size)199 void IncreaseCommittedOverSizeLimit(size_t size) 200 { 201 committedOverSizeLimit_ += size; 202 } 203 ResetCommittedOverSizeLimit()204 void ResetCommittedOverSizeLimit() 205 { 206 DecreaseOutOfMemoryOvershootSize(committedOverSizeLimit_); 207 committedOverSizeLimit_ = 0; 208 } 209 210 template<class Callback> EnumerateCollectRegionSet(const Callback & cb)211 void EnumerateCollectRegionSet(const Callback &cb) const 212 { 213 for (Region *current : collectRegionSet_) { 214 if (current != nullptr) { 215 cb(current); 216 } 217 } 218 } 219 GetCollectSetRegionCount()220 size_t GetCollectSetRegionCount() const 221 { 222 return collectRegionSet_.size(); 223 } 224 225 void Merge(LocalSpace *localSpace); 226 private: 227 static constexpr int64_t PARTIAL_GC_MAX_EVACUATION_SIZE_FOREGROUND = 2_MB; 228 static constexpr int64_t PARTIAL_GC_MAX_EVACUATION_SIZE_BACKGROUND = 6_MB; 229 static constexpr unsigned long long PARTIAL_GC_MAX_COLLECT_REGION_RATE = 2_MB; 230 static constexpr unsigned long long PARTIAL_GC_INITIAL_COLLECT_REGION_SIZE = 24; 231 static constexpr size_t PARTIAL_GC_MIN_COLLECT_REGION_SIZE = 5; 232 233 void FreeRegionFromSpace(Region *region); 234 235 CVector<Region *> collectRegionSet_; 236 Mutex lock_; 237 size_t mergeSize_ {0}; 238 size_t committedOverSizeLimit_ {0}; 239 }; 240 241 class NonMovableSpace : public SparseSpace { 242 public: 243 NonMovableSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity); 244 ~NonMovableSpace() override = default; 245 NO_COPY_SEMANTIC(NonMovableSpace); 246 NO_MOVE_SEMANTIC(NonMovableSpace); 247 248 uintptr_t CheckAndAllocate(size_t size); 249 }; 250 251 class AppSpawnSpace : public SparseSpace { 252 public: 253 AppSpawnSpace(Heap *heap, size_t initialCapacity); 254 ~AppSpawnSpace() override = default; 255 NO_COPY_SEMANTIC(AppSpawnSpace); 256 NO_MOVE_SEMANTIC(AppSpawnSpace); 257 258 uintptr_t AllocateSync(size_t size); 259 260 void IterateOverMarkedObjects(const std::function<void(TaggedObject *object)> &visitor) const; 261 private: 262 Mutex mutex_; 263 }; 264 265 class LocalSpace : public SparseSpace { 266 public: 267 LocalSpace() = delete; 268 LocalSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity); 269 ~LocalSpace() override = default; 270 NO_COPY_SEMANTIC(LocalSpace); 271 NO_MOVE_SEMANTIC(LocalSpace); 272 273 uintptr_t Allocate(size_t size, bool isExpand = true); 274 bool AddRegionToList(Region *region); 275 void FreeBumpPoint(); 276 void Stop(); 277 private: 278 void ForceExpandInGC(); 279 }; 280 281 class MachineCode; 282 struct MachineCodeDesc; 283 class MachineCodeSpace : public SparseSpace { 284 public: 285 MachineCodeSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity); 286 ~MachineCodeSpace() override; 287 NO_COPY_SEMANTIC(MachineCodeSpace); 288 NO_MOVE_SEMANTIC(MachineCodeSpace); // Note: Expand() left for define 289 uintptr_t GetMachineCodeObject(uintptr_t pc); 290 size_t CheckMachineCodeObject(uintptr_t curPtr, uintptr_t &machineCode, uintptr_t pc); 291 void AsyncSweep(bool isMain) override; 292 void Sweep() override; 293 void PrepareSweeping() override; 294 // Used by CMCGC to clear the marking bits in Young GC. 295 void ClearMarkBits(); 296 uintptr_t Allocate(size_t size, bool allowGC = true); 297 uintptr_t Allocate(size_t size, MachineCodeDesc *desc, bool allowGC = true); 298 uintptr_t PUBLIC_API JitFortAllocate(MachineCodeDesc *desc); 299 void StoreMachineCodeObjectLocation(uintptr_t start, uintptr_t end, uintptr_t address); 300 MarkJitFortMemAlive(MachineCode * obj)301 inline void MarkJitFortMemAlive(MachineCode *obj) 302 { 303 if (jitFort_) { 304 jitFort_->MarkJitFortMemAlive(obj); 305 } 306 } 307 MarkJitFortMemInstalled(MachineCode * obj)308 inline void MarkJitFortMemInstalled(MachineCode *obj) 309 { 310 if (jitFort_) { 311 jitFort_->MarkJitFortMemInstalled(obj); 312 } 313 } 314 IsSweeping()315 inline bool IsSweeping() 316 { 317 return sweepState_ == SweepState::SWEEPING ; 318 } 319 GetJitFort()320 inline JitFort *GetJitFort() 321 { 322 return jitFort_; 323 } 324 InJitFortRange(uintptr_t address)325 bool InJitFortRange(uintptr_t address) const 326 { 327 if (jitFort_) { 328 return jitFort_->InRange(address); 329 } 330 return false; 331 } 332 333 // interval map is used to cache the location of machinecode object on heap. 334 // This support GetMachineCodeObject 335 struct IntervalMap { 336 using TextStart = uintptr_t ; 337 using TextEnd = uintptr_t; 338 using MachineCodeAddress = uintptr_t; 339 // For <MachineCode> text at [<start, end>), store <start> as key, and <end, MachineCode> as value. 340 // Invariant is that intervals never overlap. 341 std::map<TextStart, std::pair<TextEnd, MachineCodeAddress>> intervals; 342 343 // Getter should only happen in enumroot GetMachineCodeObjectIntervalMap344 MachineCodeAddress GetMachineCodeObject(uintptr_t pc) 345 { 346 // first interval whose start > pc 347 auto it = intervals.upper_bound(pc); 348 if (it == intervals.begin()) { 349 UNREACHABLE(); 350 } 351 -- it; 352 auto start = it->first; 353 auto end = it->second.first; 354 if (pc >= start && pc < end) { 355 return it->second.second; 356 } 357 UNREACHABLE(); 358 } 359 InsertMachineCodeObjectIntervalMap360 void InsertMachineCodeObject(TextStart start, TextEnd end, MachineCodeAddress obj) 361 { 362 LOG_JIT(INFO) << "Insert MachineCodeObject 0x" << std::hex << obj << " , code range 0x" << std::hex << start 363 << " to 0x" << std::hex << end; 364 // first interval whose start > our start 365 auto it = intervals.upper_bound(start); 366 // find first overlapping interval, if any 367 if (it != intervals.begin()) { 368 auto prev = std::prev(it); 369 auto prevEnd = prev->second.first; 370 if (prevEnd > start) { 371 it = prev; 372 } 373 } 374 // Remove all overlapping intervals, if any 375 while (it != intervals.end()) { 376 if (it->first < end) { 377 LOG_JIT(INFO) << "MachoneCodeObject at 0x" << std::hex << it->second.second 378 << " was freed: 0x" << std::hex << it->first 379 << " - 0x" << std::hex << it->second.first; 380 it = intervals.erase(it); 381 } else { 382 break; 383 } 384 } 385 intervals[start] = {end, obj}; 386 } 387 }; 388 389 private: 390 IntervalMap machineCodeObjectLocations; 391 JitFort *jitFort_ {nullptr}; 392 Mutex asyncSweepMutex_; 393 friend class Heap; 394 friend class ConcurrentSweeper; 395 }; 396 } // namespace panda::ecmascript 397 #endif // ECMASCRIPT_MEM_SPARSE_SPACE_H 398