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_PARALLEL_EVACUATOR_H 17 #define ECMASCRIPT_MEM_PARALLEL_EVACUATOR_H 18 19 #include <atomic> 20 #include <memory> 21 22 #include "ecmascript/js_hclass.h" 23 #include "ecmascript/mem/heap.h" 24 #include "ecmascript/mem/object_xray.h" 25 #include "ecmascript/mem/region.h" 26 #include "ecmascript/mem/space.h" 27 #include "ecmascript/mem/tagged_object.h" 28 #include "ecmascript/mem/tlab_allocator.h" 29 #include "ecmascript/taskpool/task.h" 30 31 #include "ecmascript/platform/mutex.h" 32 33 namespace panda::ecmascript { 34 enum RegionEvacuateType { 35 REGION_NEW_TO_NEW, 36 REGION_NEW_TO_OLD, 37 OBJECT_EVACUATE, 38 }; 39 class ParallelEvacuator { 40 public: 41 explicit ParallelEvacuator(Heap *heap); 42 ~ParallelEvacuator() = default; 43 void Initialize(); 44 void Finalize(); 45 void Evacuate(); 46 void SweepNewToOldRegions(); 47 GetPromotedSize()48 size_t GetPromotedSize() const 49 { 50 return promotedSize_; 51 } 52 private: 53 class UpdateRootVisitor final : public RootVisitor { 54 public: 55 explicit UpdateRootVisitor(ParallelEvacuator *evacuator); 56 ~UpdateRootVisitor() = default; 57 58 void VisitRoot([[maybe_unused]] Root type, ObjectSlot slot) override; 59 void VisitRangeRoot([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) override; 60 void VisitBaseAndDerivedRoot([[maybe_unused]] Root type, ObjectSlot base, ObjectSlot derived, 61 uintptr_t baseOldObject) override; 62 private: 63 ParallelEvacuator *evacuator_ {nullptr}; 64 }; 65 66 class SetObjectFieldRSetVisitor final : public EcmaObjectRangeVisitor<SetObjectFieldRSetVisitor> { 67 public: 68 explicit SetObjectFieldRSetVisitor(ParallelEvacuator *evacuator); 69 ~SetObjectFieldRSetVisitor() = default; 70 71 void VisitObjectRangeImpl(TaggedObject *root, ObjectSlot start, ObjectSlot end, VisitObjectArea area); 72 private: 73 ParallelEvacuator *evacuator_ {nullptr}; 74 }; 75 76 template <TriggerGCType gcType, bool needUpdateLocalToShare> 77 class UpdateNewObjectFieldVisitor final : 78 public EcmaObjectRangeVisitor<UpdateNewObjectFieldVisitor<gcType, needUpdateLocalToShare>> { 79 public: 80 explicit UpdateNewObjectFieldVisitor(ParallelEvacuator *evacuator); 81 ~UpdateNewObjectFieldVisitor() = default; 82 83 void VisitObjectRangeImpl(TaggedObject *root, ObjectSlot start, ObjectSlot end, VisitObjectArea area); 84 private: 85 ParallelEvacuator *evacuator_ {nullptr}; 86 }; 87 88 class EvacuationTask : public Task { 89 public: 90 EvacuationTask(int32_t id, uint32_t idOrder, ParallelEvacuator *evacuator); 91 ~EvacuationTask() override; 92 bool Run(uint32_t threadIndex) override; 93 94 NO_COPY_SEMANTIC(EvacuationTask); 95 NO_MOVE_SEMANTIC(EvacuationTask); 96 97 private: 98 uint32_t idOrder_; 99 ParallelEvacuator *evacuator_; 100 TlabAllocator *allocator_ {nullptr}; 101 }; 102 103 class UpdateReferenceTask : public Task { 104 public: UpdateReferenceTask(int32_t id,ParallelEvacuator * evacuator)105 UpdateReferenceTask(int32_t id, ParallelEvacuator *evacuator) : Task(id), evacuator_(evacuator) {}; 106 ~UpdateReferenceTask() override = default; 107 108 bool Run(uint32_t threadIndex) override; 109 110 NO_COPY_SEMANTIC(UpdateReferenceTask); 111 NO_MOVE_SEMANTIC(UpdateReferenceTask); 112 113 private: 114 ParallelEvacuator *evacuator_; 115 }; 116 117 class Workload { 118 public: Workload(ParallelEvacuator * evacuator,Region * region)119 Workload(ParallelEvacuator *evacuator, Region *region) : evacuator_(evacuator), region_(region) {}; 120 virtual ~Workload() = default; 121 virtual bool Process(bool isMain, uint32_t threadIndex) = 0; GetRegion()122 inline Region *GetRegion() const 123 { 124 return region_; 125 } 126 GetEvacuator()127 inline ParallelEvacuator *GetEvacuator() const 128 { 129 return evacuator_; 130 } 131 protected: 132 ParallelEvacuator *evacuator_; 133 Region *region_; 134 }; 135 136 class AcquireItem { 137 public: 138 AcquireItem() = default; AcquireItem(AcquireItem && other)139 AcquireItem(AcquireItem&& other) noexcept {(void)other;}; 140 bool TryAcquire(); 141 private: 142 std::atomic<bool> acquire_{false}; 143 }; 144 145 class WorkloadSet { 146 public: 147 inline void Add(std::unique_ptr<Workload> workload); 148 inline size_t GetWorkloadCount() const; 149 inline bool HasRemaningWorkload() const; 150 inline bool FetchSubAndCheckWorkloadCount(size_t finishedCount); 151 void PrepareWorkloads(); 152 std::optional<size_t> GetNextIndex(); 153 std::unique_ptr<ParallelEvacuator::Workload> TryGetWorkload(size_t index); 154 void Clear(); 155 private: 156 using WorkItem = std::pair<AcquireItem, std::unique_ptr<Workload>>; 157 std::vector<WorkItem> workloads_; 158 std::vector<size_t> indexList_; 159 std::atomic<size_t> indexCursor_ = 0; 160 std::atomic<size_t> remainingWorkloadNum_ = 0; 161 }; 162 163 class EvacuateWorkload : public Workload { 164 public: EvacuateWorkload(ParallelEvacuator * evacuator,Region * region)165 EvacuateWorkload(ParallelEvacuator *evacuator, Region *region) : Workload(evacuator, region) {} 166 ~EvacuateWorkload() override = default; 167 bool Process(bool isMain, uint32_t threadIndex) override; 168 }; 169 170 class UpdateRSetWorkload : public Workload { 171 public: UpdateRSetWorkload(ParallelEvacuator * evacuator,Region * region)172 UpdateRSetWorkload(ParallelEvacuator *evacuator, Region *region) 173 : Workload(evacuator, region) {} 174 ~UpdateRSetWorkload() override = default; 175 bool Process(bool isMain, uint32_t threadIndex) override; 176 }; 177 178 class UpdateNewRegionWorkload : public Workload { 179 public: UpdateNewRegionWorkload(ParallelEvacuator * evacuator,Region * region,bool isYoungGC)180 UpdateNewRegionWorkload(ParallelEvacuator *evacuator, Region *region, bool isYoungGC) 181 : Workload(evacuator, region), isYoungGC_(isYoungGC) {} 182 ~UpdateNewRegionWorkload() override = default; 183 bool Process(bool isMain, uint32_t threadIndex) override; 184 private: 185 bool isYoungGC_; 186 }; 187 188 class UpdateAndSweepNewRegionWorkload : public Workload { 189 public: UpdateAndSweepNewRegionWorkload(ParallelEvacuator * evacuator,Region * region,bool isYoungGC)190 UpdateAndSweepNewRegionWorkload(ParallelEvacuator *evacuator, Region *region, bool isYoungGC) 191 : Workload(evacuator, region), isYoungGC_(isYoungGC) {} 192 ~UpdateAndSweepNewRegionWorkload() override = default; 193 bool Process(bool isMain, uint32_t threadIndex) override; 194 private: 195 bool isYoungGC_; 196 }; 197 198 class UpdateNewToOldEvacuationWorkload : public Workload { 199 public: UpdateNewToOldEvacuationWorkload(ParallelEvacuator * evacuator,Region * region,bool isYoungGC)200 UpdateNewToOldEvacuationWorkload(ParallelEvacuator *evacuator, Region *region, bool isYoungGC) 201 : Workload(evacuator, region), isYoungGC_(isYoungGC) {} 202 ~UpdateNewToOldEvacuationWorkload() override = default; 203 bool Process(bool isMain, uint32_t threadIndex) override; 204 private: 205 bool isYoungGC_; 206 }; 207 208 template <typename WorkloadCallback> 209 void DrainWorkloads(WorkloadSet &workloadSet, WorkloadCallback callback); 210 ArrayTrackInfoSet(uint32_t threadIndex)211 std::unordered_set<JSTaggedType> &ArrayTrackInfoSet(uint32_t threadIndex) 212 { 213 return arrayTrackInfoSets_[threadIndex]; 214 } 215 216 TaggedObject* UpdateAddressAfterEvacation(TaggedObject *oldTrackInfo); 217 218 void UpdateTrackInfo(); 219 220 bool ProcessWorkloads(bool isMain = false, uint32_t threadIndex = 0); 221 222 void EvacuateSpace(); 223 bool EvacuateSpace(TlabAllocator *allocation, uint32_t threadIndex, uint32_t idOrder, bool isMain = false); 224 void UpdateRecordWeakReferenceInParallel(uint32_t idOrder); 225 void EvacuateRegion(TlabAllocator *allocator, Region *region, std::unordered_set<JSTaggedType> &trackSet); 226 inline void SetObjectFieldRSet(TaggedObject *object, JSHClass *cls); 227 inline void SetObjectRSet(ObjectSlot slot, Region *region); 228 229 void ProcessFromSpaceEvacuation(); 230 inline RegionEvacuateType SelectRegionEvacuateType(Region *region); 231 inline bool TryWholeRegionEvacuate(Region *region, RegionEvacuateType type); 232 inline void CompensateOvershootSizeIfHighAliveRate(Region* region); 233 void VerifyValue(TaggedObject *object, ObjectSlot slot); 234 void VerifyHeapObject(TaggedObject *object); 235 236 void UpdateReference(); 237 void UpdateRoot(); 238 template<TriggerGCType gcType> 239 void UpdateWeakReferenceOpt(); 240 void UpdateRSet(Region *region); 241 template<TriggerGCType gcType, bool needUpdateLocalToShare> 242 void UpdateNewRegionReference(Region *region); 243 template<TriggerGCType gcType, bool needUpdateLocalToShare> 244 void UpdateAndSweepNewRegionReference(Region *region); 245 template<TriggerGCType gcType, bool needUpdateLocalToShare> 246 void UpdateNewObjectField(TaggedObject *object, JSHClass *cls, 247 UpdateNewObjectFieldVisitor<gcType, needUpdateLocalToShare> &updateFieldVisito); 248 template<TriggerGCType gcType> 249 void UpdateNewToOldEvacuationReference(Region *region, uint32_t threadIndex); 250 251 inline bool UpdateForwardedOldToNewObjectSlot(TaggedObject *object, ObjectSlot &slot, bool isWeak); 252 inline bool UpdateOldToNewObjectSlot(ObjectSlot &slot); 253 inline void UpdateObjectSlot(ObjectSlot &slot); 254 inline void UpdateWeakObjectSlot(TaggedObject *object, ObjectSlot &slot); 255 inline void UpdateCrossRegionObjectSlot(ObjectSlot &slot); 256 template<TriggerGCType gcType, bool needUpdateLocalToShare> 257 inline void UpdateNewObjectSlot(ObjectSlot &slot); 258 inline void UpdateObjectSlotValue(JSTaggedValue value, ObjectSlot &slot); 259 260 inline int CalculateEvacuationThreadNum(); 261 inline int CalculateUpdateThreadNum(); 262 void WaitFinished(); 263 264 Heap *heap_; 265 UpdateRootVisitor updateRootVisitor_; 266 SetObjectFieldRSetVisitor setObjectFieldRSetVisitor_; 267 TlabAllocator *allocator_ {nullptr}; 268 269 uintptr_t waterLine_ = 0; 270 std::unordered_set<JSTaggedType> arrayTrackInfoSets_[MAX_TASKPOOL_THREAD_NUM + 1]; 271 bool hasNewToOldRegions_ {false}; 272 uint32_t evacuateTaskNum_ = 0; 273 std::atomic_int parallel_ = 0; 274 Mutex mutex_; 275 ConditionVariable condition_; 276 std::atomic<size_t> promotedSize_ = 0; 277 WorkloadSet evacuateWorkloadSet_; 278 WorkloadSet updateWorkloadSet_; 279 280 template<TriggerGCType gcType> 281 friend class SlotUpdateRangeVisitor; 282 }; 283 } // namespace panda::ecmascript 284 #endif // ECMASCRIPT_MEM_PARALLEL_EVACUATOR_H 285