#include "../PPCRecompiler.h" #include "../PPCRecompilerIml.h" #include "IMLRegisterAllocatorRanges.h" #include "util/helpers/MemoryPool.h" uint32 IMLRA_GetNextIterationIndex(); IMLRegID raLivenessRange::GetVirtualRegister() const { return virtualRegister; } sint32 raLivenessRange::GetPhysicalRegister() const { return physicalRegister; } IMLName raLivenessRange::GetName() const { return name; } void raLivenessRange::SetPhysicalRegister(IMLPhysReg physicalRegister) { this->physicalRegister = physicalRegister; } void raLivenessRange::SetPhysicalRegisterForCluster(IMLPhysReg physicalRegister) { auto clusterRanges = GetAllSubrangesInCluster(); for(auto& range : clusterRanges) range->physicalRegister = physicalRegister; } boost::container::small_vector raLivenessRange::GetAllSubrangesInCluster() { uint32 iterationIndex = IMLRA_GetNextIterationIndex(); boost::container::small_vector subranges; subranges.push_back(this); this->lastIterationIndex = iterationIndex; size_t i = 0; while(isubrangeBranchTaken && cur->subrangeBranchTaken->lastIterationIndex != iterationIndex) { cur->subrangeBranchTaken->lastIterationIndex = iterationIndex; subranges.push_back(cur->subrangeBranchTaken); } if(cur->subrangeBranchNotTaken && cur->subrangeBranchNotTaken->lastIterationIndex != iterationIndex) { cur->subrangeBranchNotTaken->lastIterationIndex = iterationIndex; subranges.push_back(cur->subrangeBranchNotTaken); } // check predecessors for(auto& prev : cur->previousRanges) { if(prev->lastIterationIndex != iterationIndex) { prev->lastIterationIndex = iterationIndex; subranges.push_back(prev); } } } return subranges; } void raLivenessRange::GetAllowedRegistersExRecursive(raLivenessRange* range, uint32 iterationIndex, IMLPhysRegisterSet& allowedRegs) { range->lastIterationIndex = iterationIndex; for (auto& it : range->list_fixedRegRequirements) allowedRegs &= it.allowedReg; // check successors if (range->subrangeBranchTaken && range->subrangeBranchTaken->lastIterationIndex != iterationIndex) GetAllowedRegistersExRecursive(range->subrangeBranchTaken, iterationIndex, allowedRegs); if (range->subrangeBranchNotTaken && range->subrangeBranchNotTaken->lastIterationIndex != iterationIndex) GetAllowedRegistersExRecursive(range->subrangeBranchNotTaken, iterationIndex, allowedRegs); // check predecessors for (auto& prev : range->previousRanges) { if (prev->lastIterationIndex != iterationIndex) GetAllowedRegistersExRecursive(prev, iterationIndex, allowedRegs); } }; bool raLivenessRange::GetAllowedRegistersEx(IMLPhysRegisterSet& allowedRegisters) { uint32 iterationIndex = IMLRA_GetNextIterationIndex(); allowedRegisters.SetAllAvailable(); GetAllowedRegistersExRecursive(this, iterationIndex, allowedRegisters); return !allowedRegisters.HasAllAvailable(); } IMLPhysRegisterSet raLivenessRange::GetAllowedRegisters(IMLPhysRegisterSet regPool) { IMLPhysRegisterSet fixedRegRequirements = regPool; if(interval.ExtendsPreviousSegment() || interval.ExtendsIntoNextSegment()) { auto clusterRanges = GetAllSubrangesInCluster(); for(auto& subrange : clusterRanges) { for(auto& fixedRegLoc : subrange->list_fixedRegRequirements) fixedRegRequirements &= fixedRegLoc.allowedReg; } return fixedRegRequirements; } for(auto& fixedRegLoc : list_fixedRegRequirements) fixedRegRequirements &= fixedRegLoc.allowedReg; return fixedRegRequirements; } void PPCRecRARange_addLink_perVirtualGPR(std::unordered_map& root, raLivenessRange* subrange) { IMLRegID regId = subrange->GetVirtualRegister(); auto it = root.find(regId); if (it == root.end()) { // new single element root.try_emplace(regId, subrange); subrange->link_sameVirtualRegister.prev = nullptr; subrange->link_sameVirtualRegister.next = nullptr; } else { // insert in first position raLivenessRange* priorFirst = it->second; subrange->link_sameVirtualRegister.next = priorFirst; it->second = subrange; subrange->link_sameVirtualRegister.prev = nullptr; priorFirst->link_sameVirtualRegister.prev = subrange; } } void PPCRecRARange_addLink_allSegmentRanges(raLivenessRange** root, raLivenessRange* subrange) { subrange->link_allSegmentRanges.next = *root; if (*root) (*root)->link_allSegmentRanges.prev = subrange; subrange->link_allSegmentRanges.prev = nullptr; *root = subrange; } void PPCRecRARange_removeLink_perVirtualGPR(std::unordered_map& root, raLivenessRange* subrange) { #ifdef CEMU_DEBUG_ASSERT raLivenessRange* cur = root.find(subrange->GetVirtualRegister())->second; bool hasRangeFound = false; while(cur) { if(cur == subrange) { hasRangeFound = true; break; } cur = cur->link_sameVirtualRegister.next; } cemu_assert_debug(hasRangeFound); #endif IMLRegID regId = subrange->GetVirtualRegister(); raLivenessRange* nextRange = subrange->link_sameVirtualRegister.next; raLivenessRange* prevRange = subrange->link_sameVirtualRegister.prev; raLivenessRange* newBase = prevRange ? prevRange : nextRange; if (prevRange) prevRange->link_sameVirtualRegister.next = subrange->link_sameVirtualRegister.next; if (nextRange) nextRange->link_sameVirtualRegister.prev = subrange->link_sameVirtualRegister.prev; if (!prevRange) { if (nextRange) { root.find(regId)->second = nextRange; } else { cemu_assert_debug(root.find(regId)->second == subrange); root.erase(regId); } } #ifdef CEMU_DEBUG_ASSERT subrange->link_sameVirtualRegister.prev = (raLivenessRange*)1; subrange->link_sameVirtualRegister.next = (raLivenessRange*)1; #endif } void PPCRecRARange_removeLink_allSegmentRanges(raLivenessRange** root, raLivenessRange* subrange) { raLivenessRange* tempPrev = subrange->link_allSegmentRanges.prev; if (subrange->link_allSegmentRanges.prev) subrange->link_allSegmentRanges.prev->link_allSegmentRanges.next = subrange->link_allSegmentRanges.next; else (*root) = subrange->link_allSegmentRanges.next; if (subrange->link_allSegmentRanges.next) subrange->link_allSegmentRanges.next->link_allSegmentRanges.prev = tempPrev; #ifdef CEMU_DEBUG_ASSERT subrange->link_allSegmentRanges.prev = (raLivenessRange*)1; subrange->link_allSegmentRanges.next = (raLivenessRange*)1; #endif } MemoryPoolPermanentObjects memPool_livenessSubrange(4096); // startPosition and endPosition are inclusive raLivenessRange* IMLRA_CreateRange(ppcImlGenContext_t* ppcImlGenContext, IMLSegment* imlSegment, IMLRegID virtualRegister, IMLName name, raInstructionEdge startPosition, raInstructionEdge endPosition) { raLivenessRange* range = memPool_livenessSubrange.acquireObj(); range->previousRanges.clear(); range->list_accessLocations.clear(); range->list_fixedRegRequirements.clear(); range->imlSegment = imlSegment; cemu_assert_debug(startPosition <= endPosition); range->interval.start = startPosition; range->interval.end = endPosition; // register mapping range->virtualRegister = virtualRegister; range->name = name; range->physicalRegister = -1; // default values range->hasStore = false; range->hasStoreDelayed = false; range->lastIterationIndex = 0; range->subrangeBranchNotTaken = nullptr; range->subrangeBranchTaken = nullptr; cemu_assert_debug(range->previousRanges.empty()); range->_noLoad = false; // add to segment linked lists PPCRecRARange_addLink_perVirtualGPR(imlSegment->raInfo.linkedList_perVirtualRegister, range); PPCRecRARange_addLink_allSegmentRanges(&imlSegment->raInfo.linkedList_allSubranges, range); return range; } void _unlinkSubrange(raLivenessRange* range) { IMLSegment* imlSegment = range->imlSegment; PPCRecRARange_removeLink_perVirtualGPR(imlSegment->raInfo.linkedList_perVirtualRegister, range); PPCRecRARange_removeLink_allSegmentRanges(&imlSegment->raInfo.linkedList_allSubranges, range); // unlink reverse references if(range->subrangeBranchTaken) range->subrangeBranchTaken->previousRanges.erase(std::find(range->subrangeBranchTaken->previousRanges.begin(), range->subrangeBranchTaken->previousRanges.end(), range)); if(range->subrangeBranchNotTaken) range->subrangeBranchNotTaken->previousRanges.erase(std::find(range->subrangeBranchNotTaken->previousRanges.begin(), range->subrangeBranchNotTaken->previousRanges.end(), range)); range->subrangeBranchTaken = (raLivenessRange*)(uintptr_t)-1; range->subrangeBranchNotTaken = (raLivenessRange*)(uintptr_t)-1; // remove forward references for(auto& prev : range->previousRanges) { if(prev->subrangeBranchTaken == range) prev->subrangeBranchTaken = nullptr; if(prev->subrangeBranchNotTaken == range) prev->subrangeBranchNotTaken = nullptr; } range->previousRanges.clear(); } void IMLRA_DeleteRange(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange* range) { _unlinkSubrange(range); range->list_accessLocations.clear(); range->list_fixedRegRequirements.clear(); memPool_livenessSubrange.releaseObj(range); } void IMLRA_DeleteRangeCluster(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange* range) { auto clusterRanges = range->GetAllSubrangesInCluster(); for (auto& subrange : clusterRanges) IMLRA_DeleteRange(ppcImlGenContext, subrange); } void IMLRA_DeleteAllRanges(ppcImlGenContext_t* ppcImlGenContext) { for(auto& seg : ppcImlGenContext->segmentList2) { raLivenessRange* cur; while(cur = seg->raInfo.linkedList_allSubranges) IMLRA_DeleteRange(ppcImlGenContext, cur); seg->raInfo.linkedList_allSubranges = nullptr; seg->raInfo.linkedList_perVirtualRegister.clear(); } } void IMLRA_MergeSubranges(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange* subrange, raLivenessRange* absorbedSubrange) { #ifdef CEMU_DEBUG_ASSERT PPCRecRA_debugValidateSubrange(subrange); PPCRecRA_debugValidateSubrange(absorbedSubrange); if (subrange->imlSegment != absorbedSubrange->imlSegment) assert_dbg(); cemu_assert_debug(subrange->interval.end == absorbedSubrange->interval.start); if (subrange->subrangeBranchTaken || subrange->subrangeBranchNotTaken) assert_dbg(); if (subrange == absorbedSubrange) assert_dbg(); #endif // update references subrange->subrangeBranchTaken = absorbedSubrange->subrangeBranchTaken; subrange->subrangeBranchNotTaken = absorbedSubrange->subrangeBranchNotTaken; absorbedSubrange->subrangeBranchTaken = nullptr; absorbedSubrange->subrangeBranchNotTaken = nullptr; if(subrange->subrangeBranchTaken) *std::find(subrange->subrangeBranchTaken->previousRanges.begin(), subrange->subrangeBranchTaken->previousRanges.end(), absorbedSubrange) = subrange; if(subrange->subrangeBranchNotTaken) *std::find(subrange->subrangeBranchNotTaken->previousRanges.begin(), subrange->subrangeBranchNotTaken->previousRanges.end(), absorbedSubrange) = subrange; // merge usage locations for (auto& accessLoc : absorbedSubrange->list_accessLocations) subrange->list_accessLocations.push_back(accessLoc); absorbedSubrange->list_accessLocations.clear(); // merge fixed reg locations #ifdef CEMU_DEBUG_ASSERT if(!subrange->list_fixedRegRequirements.empty() && !absorbedSubrange->list_fixedRegRequirements.empty()) { cemu_assert_debug(subrange->list_fixedRegRequirements.back().pos < absorbedSubrange->list_fixedRegRequirements.front().pos); } #endif for (auto& fixedReg : absorbedSubrange->list_fixedRegRequirements) subrange->list_fixedRegRequirements.push_back(fixedReg); absorbedSubrange->list_fixedRegRequirements.clear(); subrange->interval.end = absorbedSubrange->interval.end; PPCRecRA_debugValidateSubrange(subrange); IMLRA_DeleteRange(ppcImlGenContext, absorbedSubrange); } // remove all inter-segment connections from the range cluster and split it into local ranges. Ranges are trimmed and if they have no access location they will be removed void IMLRA_ExplodeRangeCluster(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange* originRange) { cemu_assert_debug(originRange->interval.ExtendsPreviousSegment() || originRange->interval.ExtendsIntoNextSegment()); // only call this on ranges that span multiple segments auto clusterRanges = originRange->GetAllSubrangesInCluster(); for (auto& subrange : clusterRanges) { if (subrange->list_accessLocations.empty()) continue; raInterval interval; interval.SetInterval(subrange->list_accessLocations.front().pos, subrange->list_accessLocations.back().pos); raLivenessRange* newSubrange = IMLRA_CreateRange(ppcImlGenContext, subrange->imlSegment, subrange->GetVirtualRegister(), subrange->GetName(), interval.start, interval.end); // copy locations and fixed reg indices newSubrange->list_accessLocations = subrange->list_accessLocations; newSubrange->list_fixedRegRequirements = subrange->list_fixedRegRequirements; if(originRange->HasPhysicalRegister()) { cemu_assert_debug(subrange->list_fixedRegRequirements.empty()); // avoid unassigning a register from a range with a fixed register requirement } // validate if(!newSubrange->list_accessLocations.empty()) { cemu_assert_debug(newSubrange->list_accessLocations.front().pos >= newSubrange->interval.start); cemu_assert_debug(newSubrange->list_accessLocations.back().pos <= newSubrange->interval.end); } if(!newSubrange->list_fixedRegRequirements.empty()) { cemu_assert_debug(newSubrange->list_fixedRegRequirements.front().pos >= newSubrange->interval.start); // fixed register requirements outside of the actual access range probably means there is a mistake in GetInstructionFixedRegisters() cemu_assert_debug(newSubrange->list_fixedRegRequirements.back().pos <= newSubrange->interval.end); } } // delete the original range cluster IMLRA_DeleteRangeCluster(ppcImlGenContext, originRange); } #ifdef CEMU_DEBUG_ASSERT void PPCRecRA_debugValidateSubrange(raLivenessRange* range) { // validate subrange if (range->subrangeBranchTaken && range->subrangeBranchTaken->imlSegment != range->imlSegment->nextSegmentBranchTaken) assert_dbg(); if (range->subrangeBranchNotTaken && range->subrangeBranchNotTaken->imlSegment != range->imlSegment->nextSegmentBranchNotTaken) assert_dbg(); if(range->subrangeBranchTaken || range->subrangeBranchNotTaken) { cemu_assert_debug(range->interval.end.ConnectsToNextSegment()); } if(!range->previousRanges.empty()) { cemu_assert_debug(range->interval.start.ConnectsToPreviousSegment()); } // validate locations if (!range->list_accessLocations.empty()) { cemu_assert_debug(range->list_accessLocations.front().pos >= range->interval.start); cemu_assert_debug(range->list_accessLocations.back().pos <= range->interval.end); } // validate fixed reg requirements if (!range->list_fixedRegRequirements.empty()) { cemu_assert_debug(range->list_fixedRegRequirements.front().pos >= range->interval.start); cemu_assert_debug(range->list_fixedRegRequirements.back().pos <= range->interval.end); for(sint32 i = 0; i < (sint32)range->list_fixedRegRequirements.size()-1; i++) cemu_assert_debug(range->list_fixedRegRequirements[i].pos < range->list_fixedRegRequirements[i+1].pos); } } #else void PPCRecRA_debugValidateSubrange(raLivenessRange* range) {} #endif // trim start and end of range to match first and last read/write locations // does not trim start/endpoints which extend into the next/previous segment void IMLRA_TrimRangeToUse(raLivenessRange* range) { if(range->list_accessLocations.empty()) { // special case where we trim ranges extending from other segments to a single instruction edge cemu_assert_debug(!range->interval.start.IsInstructionIndex() || !range->interval.end.IsInstructionIndex()); if(range->interval.start.IsInstructionIndex()) range->interval.start = range->interval.end; if(range->interval.end.IsInstructionIndex()) range->interval.end = range->interval.start; return; } // trim start and end raInterval prevInterval = range->interval; if(range->interval.start.IsInstructionIndex()) range->interval.start = range->list_accessLocations.front().pos; if(range->interval.end.IsInstructionIndex()) range->interval.end = range->list_accessLocations.back().pos; // extra checks #ifdef CEMU_DEBUG_ASSERT cemu_assert_debug(range->interval.start <= range->interval.end); for(auto& loc : range->list_accessLocations) { cemu_assert_debug(range->interval.ContainsEdge(loc.pos)); } cemu_assert_debug(prevInterval.ContainsWholeInterval(range->interval)); #endif } // split range at the given position // After the split there will be two ranges: // head -> subrange is shortened to end at splitIndex (exclusive) // tail -> a new subrange that ranges from splitIndex (inclusive) to the end of the original subrange // if head has a physical register assigned it will not carry over to tail // The return value is the tail range // If trimToUsage is true, the end of the head subrange and the start of the tail subrange will be shrunk to fit the read/write locations within. If there are no locations then the range will be deleted raLivenessRange* IMLRA_SplitRange(ppcImlGenContext_t* ppcImlGenContext, raLivenessRange*& subrange, raInstructionEdge splitPosition, bool trimToUsage) { cemu_assert_debug(splitPosition.IsInstructionIndex()); cemu_assert_debug(!subrange->interval.IsNextSegmentOnly() && !subrange->interval.IsPreviousSegmentOnly()); cemu_assert_debug(subrange->interval.ContainsEdge(splitPosition)); // determine new intervals raInterval headInterval, tailInterval; headInterval.SetInterval(subrange->interval.start, splitPosition-1); tailInterval.SetInterval(splitPosition, subrange->interval.end); cemu_assert_debug(headInterval.start <= headInterval.end); cemu_assert_debug(tailInterval.start <= tailInterval.end); // create tail raLivenessRange* tailSubrange = IMLRA_CreateRange(ppcImlGenContext, subrange->imlSegment, subrange->GetVirtualRegister(), subrange->GetName(), tailInterval.start, tailInterval.end); tailSubrange->SetPhysicalRegister(subrange->GetPhysicalRegister()); // carry over branch targets and update reverse references tailSubrange->subrangeBranchTaken = subrange->subrangeBranchTaken; tailSubrange->subrangeBranchNotTaken = subrange->subrangeBranchNotTaken; subrange->subrangeBranchTaken = nullptr; subrange->subrangeBranchNotTaken = nullptr; if(tailSubrange->subrangeBranchTaken) *std::find(tailSubrange->subrangeBranchTaken->previousRanges.begin(), tailSubrange->subrangeBranchTaken->previousRanges.end(), subrange) = tailSubrange; if(tailSubrange->subrangeBranchNotTaken) *std::find(tailSubrange->subrangeBranchNotTaken->previousRanges.begin(), tailSubrange->subrangeBranchNotTaken->previousRanges.end(), subrange) = tailSubrange; // we assume that list_locations is ordered by instruction index and contains no duplicate indices, so lets check that here just in case #ifdef CEMU_DEBUG_ASSERT if(subrange->list_accessLocations.size() > 1) { for(size_t i=0; ilist_accessLocations.size()-1; i++) { cemu_assert_debug(subrange->list_accessLocations[i].pos < subrange->list_accessLocations[i+1].pos); } } #endif // split locations auto it = std::lower_bound( subrange->list_accessLocations.begin(), subrange->list_accessLocations.end(), splitPosition, [](const raAccessLocation& accessLoc, raInstructionEdge value) { return accessLoc.pos < value; } ); size_t originalCount = subrange->list_accessLocations.size(); tailSubrange->list_accessLocations.insert(tailSubrange->list_accessLocations.end(), it, subrange->list_accessLocations.end()); subrange->list_accessLocations.erase(it, subrange->list_accessLocations.end()); cemu_assert_debug(subrange->list_accessLocations.empty() || subrange->list_accessLocations.back().pos < splitPosition); cemu_assert_debug(tailSubrange->list_accessLocations.empty() || tailSubrange->list_accessLocations.front().pos >= splitPosition); cemu_assert_debug(subrange->list_accessLocations.size() + tailSubrange->list_accessLocations.size() == originalCount); // split fixed reg requirements for (sint32 i = 0; i < subrange->list_fixedRegRequirements.size(); i++) { raFixedRegRequirement* fixedReg = subrange->list_fixedRegRequirements.data() + i; if (tailInterval.ContainsEdge(fixedReg->pos)) { tailSubrange->list_fixedRegRequirements.push_back(*fixedReg); } } // remove tail fixed reg requirements from head for (sint32 i = 0; i < subrange->list_fixedRegRequirements.size(); i++) { raFixedRegRequirement* fixedReg = subrange->list_fixedRegRequirements.data() + i; if (!headInterval.ContainsEdge(fixedReg->pos)) { subrange->list_fixedRegRequirements.resize(i); break; } } // adjust intervals subrange->interval = headInterval; tailSubrange->interval = tailInterval; // trim to hole if(trimToUsage) { if(subrange->list_accessLocations.empty() && (subrange->interval.start.IsInstructionIndex() && subrange->interval.end.IsInstructionIndex())) { IMLRA_DeleteRange(ppcImlGenContext, subrange); subrange = nullptr; } else { IMLRA_TrimRangeToUse(subrange); } if(tailSubrange->list_accessLocations.empty() && (tailSubrange->interval.start.IsInstructionIndex() && tailSubrange->interval.end.IsInstructionIndex())) { IMLRA_DeleteRange(ppcImlGenContext, tailSubrange); tailSubrange = nullptr; } else { IMLRA_TrimRangeToUse(tailSubrange); } } // validation cemu_assert_debug(!subrange || subrange->interval.start <= subrange->interval.end); cemu_assert_debug(!tailSubrange || tailSubrange->interval.start <= tailSubrange->interval.end); cemu_assert_debug(!tailSubrange || tailSubrange->interval.start >= splitPosition); if (!trimToUsage) cemu_assert_debug(!tailSubrange || tailSubrange->interval.start == splitPosition); if(subrange) PPCRecRA_debugValidateSubrange(subrange); if(tailSubrange) PPCRecRA_debugValidateSubrange(tailSubrange); return tailSubrange; } sint32 IMLRA_GetSegmentReadWriteCost(IMLSegment* imlSegment) { sint32 v = imlSegment->loopDepth + 1; v *= 5; return v*v; // 25, 100, 225, 400 } // calculate additional cost of range that it would have after calling _ExplodeRange() on it sint32 IMLRA_CalculateAdditionalCostOfRangeExplode(raLivenessRange* subrange) { auto ranges = subrange->GetAllSubrangesInCluster(); sint32 cost = 0;//-PPCRecRARange_estimateTotalCost(ranges); for (auto& subrange : ranges) { if (subrange->list_accessLocations.empty()) continue; // this range would be deleted and thus has no cost sint32 segmentLoadStoreCost = IMLRA_GetSegmentReadWriteCost(subrange->imlSegment); bool hasAdditionalLoad = subrange->interval.ExtendsPreviousSegment(); bool hasAdditionalStore = subrange->interval.ExtendsIntoNextSegment(); if(hasAdditionalLoad && subrange->list_accessLocations.front().IsWrite()) // if written before read then a load isn't necessary { cemu_assert_debug(!subrange->list_accessLocations.front().IsRead()); cost += segmentLoadStoreCost; } if(hasAdditionalStore) { bool hasWrite = std::find_if(subrange->list_accessLocations.begin(), subrange->list_accessLocations.end(), [](const raAccessLocation& loc) { return loc.IsWrite(); }) != subrange->list_accessLocations.end(); if(!hasWrite) // ranges which don't modify their value do not need to be stored cost += segmentLoadStoreCost; } } // todo - properly calculating all the data-flow dependency based costs is more complex so this currently is an approximation return cost; } sint32 IMLRA_CalculateAdditionalCostAfterSplit(raLivenessRange* subrange, raInstructionEdge splitPosition) { // validation #ifdef CEMU_DEBUG_ASSERT if (subrange->interval.ExtendsIntoNextSegment()) assert_dbg(); #endif cemu_assert_debug(splitPosition.IsInstructionIndex()); sint32 cost = 0; // find split position in location list if (subrange->list_accessLocations.empty()) return 0; if (splitPosition <= subrange->list_accessLocations.front().pos) return 0; if (splitPosition > subrange->list_accessLocations.back().pos) return 0; size_t firstTailLocationIndex = 0; for (size_t i = 0; i < subrange->list_accessLocations.size(); i++) { if (subrange->list_accessLocations[i].pos >= splitPosition) { firstTailLocationIndex = i; break; } } std::span headLocations{subrange->list_accessLocations.data(), firstTailLocationIndex}; std::span tailLocations{subrange->list_accessLocations.data() + firstTailLocationIndex, subrange->list_accessLocations.size() - firstTailLocationIndex}; cemu_assert_debug(headLocations.empty() || headLocations.back().pos < splitPosition); cemu_assert_debug(tailLocations.empty() || tailLocations.front().pos >= splitPosition); sint32 segmentLoadStoreCost = IMLRA_GetSegmentReadWriteCost(subrange->imlSegment); auto CalculateCostFromLocationRange = [segmentLoadStoreCost](std::span locations, bool trackLoadCost = true, bool trackStoreCost = true) -> sint32 { if(locations.empty()) return 0; sint32 cost = 0; if(locations.front().IsRead() && trackLoadCost) cost += segmentLoadStoreCost; // not overwritten, so there is a load cost bool hasWrite = std::find_if(locations.begin(), locations.end(), [](const raAccessLocation& loc) { return loc.IsWrite(); }) != locations.end(); if(hasWrite && trackStoreCost) cost += segmentLoadStoreCost; // modified, so there is a store cost return cost; }; sint32 baseCost = CalculateCostFromLocationRange(subrange->list_accessLocations); bool tailOverwritesValue = !tailLocations.empty() && !tailLocations.front().IsRead() && tailLocations.front().IsWrite(); sint32 newCost = CalculateCostFromLocationRange(headLocations) + CalculateCostFromLocationRange(tailLocations, !tailOverwritesValue, true); cemu_assert_debug(newCost >= baseCost); cost = newCost - baseCost; return cost; }