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