| //===-- GCNRematStrategy.cpp - Rematerialization strategy -*- C++ -*-------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| /// \file | |
| /// Implements the rematerialization strategy. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "GCNRematStrategy.h" | |
| #include "GCNRegPressure.h" | |
| #include "SIMachineFunctionInfo.h" | |
| #include "llvm/ADT/BitVector.h" | |
| #include "llvm/CodeGen/MachineBasicBlock.h" | |
| #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" | |
| #include "llvm/CodeGen/RegisterPressure.h" | |
| #include "llvm/CodeGen/TargetRegisterInfo.h" | |
| #include "llvm/MC/LaneBitmask.h" | |
| #include "llvm/Support/Debug.h" | |
| #define DEBUG_TYPE "gcn-rematerialization-strategy" | |
| using namespace llvm; | |
| using RegSubRegPair = TargetInstrInfo::RegSubRegPair; | |
| using RegisterIdx = Rematerializer::RegisterIdx; | |
| GCNRematStrategy::RematRegions::RematRegions( | |
| ArrayRef<RegionBoundaries> Regions, | |
| MutableArrayRef<GCNRPTracker::LiveRegSet> LiveIns, | |
| MutableArrayRef<GCNRPTracker::LiveRegSet> LiveOuts, LiveIntervals &LIS, | |
| const MachineBlockFrequencyInfo &MBFI, const MachineRegisterInfo &MRI) | |
| : Bounds(Regions), LiveIns(LiveIns), LiveOuts(LiveOuts), | |
| Frequencies(Regions.size(), 0), LIS(LIS), MRI(MRI) { | |
| assert(LiveIns.size() == Regions.size()); | |
| assert(LiveOuts.size() == Regions.size()); | |
| // Derive frequency of each region. Frequencies are scaled down if they are | |
| // high to avoid overflow/underflow when combining them. | |
| MinFreq = MBFI.getEntryFreq().getFrequency(); | |
| MaxFreq = 0; | |
| for (unsigned RegionIdx : seq(Regions.size())) { | |
| MachineBasicBlock *MBB = Regions[RegionIdx].first->getParent(); | |
| uint64_t BlockFreq = MBFI.getBlockFreq(MBB).getFrequency(); | |
| Frequencies[RegionIdx] = BlockFreq; | |
| if (BlockFreq && BlockFreq < MinFreq) | |
| MinFreq = BlockFreq; | |
| else if (BlockFreq > MaxFreq) | |
| MaxFreq = BlockFreq; | |
| } | |
| if (!MinFreq) | |
| return; | |
| // Scale everything down if frequencies are high. | |
| if (MinFreq >= FreqScaleFactor * FreqScaleFactor) { | |
| for (uint64_t &Freq : Frequencies) | |
| Freq /= FreqScaleFactor; | |
| MinFreq /= FreqScaleFactor; | |
| MaxFreq /= FreqScaleFactor; | |
| } | |
| } | |
| GCNRegPressure GCNRematStrategy::RematRegions::computeMaxRegPressure( | |
| unsigned RegionIdx) const { | |
| assert(RegionIdx < Bounds.size() && "out of bounds"); | |
| const auto &[Begin, End] = Bounds[RegionIdx]; | |
| if (Begin == End) | |
| return llvm::getRegPressure(MRI, LiveIns[RegionIdx]); | |
| GCNDownwardRPTracker RPTracker(LIS); | |
| RPTracker.advance(Begin, End, &LiveIns[RegionIdx]); | |
| return RPTracker.moveMaxPressure(); | |
| } | |
| void GCNRematStrategy::RematRegions::removeFromLiveMaps( | |
| Register Reg, const BitVector &LiveIn, const BitVector &LiveOut) { | |
| for (unsigned I : LiveIn.set_bits()) | |
| LiveIns[I].erase(Reg); | |
| for (unsigned I : LiveOut.set_bits()) | |
| LiveOuts[I].erase(Reg); | |
| } | |
| void GCNRematStrategy::RematRegions::addToLiveMaps(Register Reg, | |
| LaneBitmask Mask, | |
| const BitVector &LiveIn, | |
| const BitVector &LiveOut) { | |
| std::pair<Register, LaneBitmask> LiveReg(Reg, Mask); | |
| for (unsigned I : LiveIn.set_bits()) | |
| LiveIns[I].insert(LiveReg); | |
| for (unsigned I : LiveOut.set_bits()) | |
| LiveOuts[I].insert(LiveReg); | |
| } | |
| GCNRematStrategy::RematTargets::RematTargets( | |
| unsigned MaxSGPRs, unsigned MaxVGPRs, bool SpillReduction, | |
| const MachineFunction &MF, const RematRegions &Regions, | |
| ArrayRef<GCNRegPressure> *MaxPressure) | |
| : ExcessRP(Regions.size()), Regions(Regions), | |
| SpillReduction(SpillReduction) { | |
| RPTargets.reserve(Regions.size()); | |
| for (unsigned RegionIdx : seq(Regions.size())) { | |
| if (MaxPressure) { | |
| RPTargets.emplace_back(MaxSGPRs, MaxVGPRs, MF, (*MaxPressure)[RegionIdx]); | |
| } else { | |
| RPTargets.emplace_back(MaxSGPRs, MaxVGPRs, MF, | |
| Regions.computeMaxRegPressure(RegionIdx)); | |
| } | |
| if (!RPTargets.back().satisfied()) | |
| ExcessRP.set(RegionIdx); | |
| } | |
| } | |
| void GCNRematStrategy::RematTargets::updateRPTargets( | |
| const BitVector &UpdateRegions, const GCNRegPressure &RPSave) { | |
| for (unsigned I : UpdateRegions.set_bits()) { | |
| RPTargets[I].saveRP(RPSave); | |
| if (ExcessRP[I] && RPTargets[I].satisfied()) { | |
| LLVM_DEBUG(dbgs() << " [" << I << "] Target reached!\n"); | |
| ExcessRP.reset(I); | |
| } | |
| } | |
| } | |
| bool GCNRematStrategy::RematTargets::updateAndVerifyRPTargets( | |
| const BitVector &UpdateRegions) { | |
| bool TooOptimistic = false; | |
| for (unsigned RegionIdx : UpdateRegions.set_bits()) { | |
| GCNRPTarget &Target = RPTargets[RegionIdx]; | |
| Target.setRP(Regions.computeMaxRegPressure(RegionIdx)); | |
| // Since we were optimistic in assessing RP decreases in these regions, we | |
| // may need to remark the target as a target region if RP didn't decrease | |
| // as expected. | |
| if (!ExcessRP.test(RegionIdx) && !Target.satisfied()) { | |
| LLVM_DEBUG(dbgs() << " [" << RegionIdx << "] Incorrect RP estimation\n"); | |
| TooOptimistic = true; | |
| ExcessRP.set(RegionIdx); | |
| } | |
| } | |
| return TooOptimistic; | |
| } | |
| void GCNRematStrategy::RematCandidate::init(const RematRegions &RF, | |
| const Rematerializer &Remater, | |
| const MachineRegisterInfo &MRI) { | |
| const Rematerializer::Reg &Reg = Remater.getReg(RegIdx); | |
| Register DefReg = Reg.getDefReg(); | |
| Live |= LiveIn; | |
| Live |= LiveOut; | |
| for (unsigned I : Live.set_bits()) { | |
| // If the register is both unused and live-through in the region, the | |
| // latter's RP is guaranteed to decrease. | |
| if (!LiveIn[I] || !LiveOut[I] || Reg.Uses.contains(I)) | |
| UnpredictableRPSave.set(I); | |
| } | |
| RPSave.inc(DefReg, LaneBitmask::getNone(), Reg.Mask, MRI); | |
| // Estimate the difference in instruction execution frequency that will be | |
| // induced by the rematerialization. A rematerialization from the least | |
| // frequent region to the most frequent region should yield the greatest | |
| // frequency penalty (most negative frequency difference). When the register | |
| // has users in its defining region the original instruction(s) won't be | |
| // deleted so there is no frequency gain in the defining region. | |
| int64_t DefOrMin = | |
| Reg.hasUsersInDefRegion() ? 0 : RF.getFrequencyOrMin(Reg.DefRegion); | |
| int64_t UseOrMax = 0; | |
| for (const auto &[UseRegion, _] : Reg.Uses) { | |
| if (Reg.DefRegion == UseRegion) | |
| continue; | |
| UseOrMax += RF.getFrequencyOrMax(UseRegion); | |
| } | |
| FreqDiff = DefOrMin - UseOrMax; | |
| } | |
| void GCNRematStrategy::RematCandidate::update(const RematTargets &Targets) { | |
| MaxFreq = RegionImpact = 0; | |
| for (unsigned RegionIdx : Targets.ExcessRP.set_bits()) { | |
| if (!Live[RegionIdx]) | |
| continue; | |
| // The rematerialization must contribute positively in at least one | |
| // register class with usage above the RP target for this region to | |
| // contribute to the score. | |
| const GCNRPTarget &RegionTarget = Targets.RPTargets[RegionIdx]; | |
| const unsigned NumRegsBenefit = RegionTarget.getNumRegsBenefit(RPSave); | |
| if (!NumRegsBenefit) | |
| continue; | |
| // Regions in which RP is guaranteed to decrease have more weight. | |
| RegionImpact += (UnpredictableRPSave[RegionIdx] ? 1 : 2) * NumRegsBenefit; | |
| if (Targets.SpillReduction) { | |
| uint64_t Freq = Targets.Regions.getFrequency(RegionIdx); | |
| if (UnpredictableRPSave[RegionIdx]) { | |
| // Apply a frequency penalty in regions in which we are not sure that RP | |
| // will decrease. | |
| Freq /= 2; | |
| } | |
| MaxFreq = std::max(MaxFreq, Freq); | |
| } | |
| } | |
| } | |
| bool GCNRematStrategy::RematCandidate::maybeBeneficial( | |
| const RematTargets &Targets) const { | |
| for (unsigned I : Targets.ExcessRP.set_bits()) { | |
| if (Live[I] && Targets.RPTargets[I].isSaveBeneficial(RPSave)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| void GCNRematStrategy::RematCandidate::rematerialize( | |
| Rematerializer &Remater) const { | |
| const Rematerializer::Reg &Reg = Remater.getReg(RegIdx); | |
| // All rematerializable dependencies are re-used for all rematerializations. | |
| Rematerializer::DependencyReuseInfo DRI; | |
| for (RegisterIdx DepRegIdx : Reg.Dependencies) | |
| DRI.reuse(DepRegIdx); | |
| // Rematerialize once per using region, except in the defining region. | |
| SmallVector<unsigned, 8> RematRegions; | |
| RematRegions.reserve(Reg.Uses.size()); | |
| for (const auto &[UseRegion, _] : Reg.Uses) { | |
| if (Reg.DefRegion != UseRegion) | |
| RematRegions.push_back(UseRegion); | |
| } | |
| for (const unsigned UseRegion : RematRegions) { | |
| Rematerializer::DependencyReuseInfo RegionDRI(DRI); | |
| Remater.rematerializeToRegion(RegIdx, UseRegion, RegionDRI); | |
| } | |
| } | |
| void GCNRematStrategy::rematerializeFor( | |
| unsigned MaxSGPRs, unsigned MaxVGPRs, bool SpillReduction, | |
| BitVector &AffectedRegions, MutableArrayRef<GCNRegPressure> *MaxPressure) { | |
| assert(AffectedRegions.size() == Regions.size()); | |
| assert(!MaxPressure || MaxPressure->size() == Regions.size()); | |
| RematTargets Targets(MaxSGPRs, MaxVGPRs, SpillReduction, MF, Regions, | |
| MaxPressure); | |
| if (Targets.allAchieved()) | |
| return; | |
| if (!CandidatesCreated) { | |
| Remater.analyze(); | |
| createCandidates(); | |
| } | |
| LLVM_DEBUG({ | |
| dbgs() << "Rematerializing across regions to reduce RP below target in " | |
| "function "; | |
| MF.getFunction().printAsOperand(dbgs(), /*PrintType=*/false); | |
| dbgs() << '\n' | |
| << Candidates.size() | |
| << " rematerialization candidates were identified in the function\n" | |
| << Targets.print() << '\n'; | |
| MF.print(dbgs(), LIS.getSlotIndexes()); | |
| }); | |
| // Keep track of all useful candidates. | |
| SmallVector<unsigned, 8> CandidateOrder; | |
| for (auto [CandIdx, Cand] : enumerate(Candidates)) { | |
| Cand.update(Targets); | |
| if (!Cand.hasNullScore()) | |
| CandidateOrder.push_back(CandIdx); | |
| } | |
| // Rematerialize registers in successive rounds until all RP targets are | |
| // satisifed or until we run out of rematerialization candidates. | |
| BitVector RecomputeRP(Regions.size()); | |
| for (;;) { | |
| RecomputeRP.reset(); | |
| // Sort candidates in increasing score order. | |
| sort(CandidateOrder, [&](unsigned LHSIndex, unsigned RHSIndex) { | |
| return Candidates[LHSIndex] < Candidates[RHSIndex]; | |
| }); | |
| LLVM_DEBUG({ | |
| dbgs() << "==== NEW REMAT ROUND ====\n" | |
| << "Candidates with non-null score, in rematerialization order:\n"; | |
| for (const RematCandidate &Cand : reverse(Candidates)) { | |
| dbgs() << " " << Cand.print() << " | " | |
| << Remater.printRematReg(Cand.RegIdx) << '\n'; | |
| } | |
| dbgs() << Targets.print(); | |
| }); | |
| // Rematerialize registers in decreasing score order until we estimate | |
| // that all RP targets are satisfied or until rematerialization candidates | |
| // are no longer useful to decrease RP. | |
| while (!CandidateOrder.empty()) { | |
| const RematCandidate &Cand = Candidates[CandidateOrder.back()]; | |
| const Rematerializer::Reg &Reg = Remater.getReg(Cand.RegIdx); | |
| // When previous rematerializations in this round have already satisfied | |
| // RP targets in all regions this rematerialization can impact, we have a | |
| // good indication that our scores have diverged significantly from | |
| // reality, in which case we interrupt this round and re-score. This also | |
| // ensures that every rematerialization we perform is possibly impactful | |
| // in at least one target region. | |
| if (!Cand.maybeBeneficial(Targets)) { | |
| LLVM_DEBUG(dbgs() << "Interrupt round on stale score for " | |
| << Cand.print() << " | " | |
| << Remater.printRematReg(Cand.RegIdx)); | |
| break; | |
| } | |
| CandidateOrder.pop_back(); | |
| #ifdef EXPENSIVE_CHECKS | |
| // All uses outside the defining region are known to be available / live | |
| // at the remat points. Thus, the uses should already be live in to | |
| // those using regions. | |
| const TargetRegisterInfo &TRI = *MRI.getTargetRegisterInfo(); | |
| for (const MachineInstr *DefMI : Reg.Defs) { | |
| for (const MachineOperand &MO : DefMI->operands()) { | |
| // Exclude the defined register. We are rematerializing all | |
| // instructions defining it so we don't care that its value is | |
| // available at the remat point. | |
| if (!MO.isReg() || !MO.getReg() || !MO.readsReg() || MO.isDef()) | |
| continue; | |
| Register UseReg = MO.getReg(); | |
| if (!UseReg.isVirtual()) | |
| continue; | |
| LiveInterval &LI = LIS.getInterval(UseReg); | |
| LaneBitmask LM = MRI.getMaxLaneMaskForVReg(MO.getReg()); | |
| if (LI.hasSubRanges() && MO.getSubReg()) | |
| LM = TRI.getSubRegIndexLaneMask(MO.getSubReg()); | |
| for (const auto &[UseRegion, _] : Reg.Uses) { | |
| if (UseRegion == Reg.DefRegion) | |
| continue; | |
| LaneBitmask LiveInMask = Regions.getLiveIns(UseRegion).at(UseReg); | |
| LaneBitmask UncoveredLanes = LM & ~(LiveInMask & LM); | |
| // If this register has lanes not covered by the LiveIns, be sure | |
| // they do not map to any subrange. ref: | |
| // machine-scheduler-sink-trivial-remats.mir::omitted_subrange | |
| if (UncoveredLanes.any()) { | |
| assert(LI.hasSubRanges()); | |
| for (LiveInterval::SubRange &SR : LI.subranges()) | |
| assert((SR.LaneMask & UncoveredLanes).none()); | |
| } | |
| } | |
| } | |
| } | |
| #endif | |
| // Remove the register from all regions where it is a live-in or live-out, | |
| // then rematerialize the register. | |
| LLVM_DEBUG(dbgs() << "** REMAT " << Remater.printRematReg(Cand.RegIdx) | |
| << '\n'); | |
| Regions.removeFromLiveMaps(Reg.getDefReg(), Cand.LiveIn, Cand.LiveOut); | |
| if (Recorder) { | |
| Recorder->LiveMapUpdates.emplace_back(Cand.RegIdx, Cand.LiveIn, | |
| Cand.LiveOut); | |
| } | |
| Cand.rematerialize(Remater); | |
| // Adjust RP targets. The save is guaranteed in regions in which the | |
| // register is live-through and unused but optimistic in all other regions | |
| // where the register is live. | |
| Targets.updateRPTargets(Cand.Live, Cand.RPSave); | |
| RecomputeRP |= Cand.UnpredictableRPSave; | |
| AffectedRegions |= Cand.Live; | |
| if (Targets.allAchieved()) { | |
| LLVM_DEBUG(dbgs() << "All targets cleared, verifying...\n"); | |
| break; | |
| } | |
| } | |
| if (!Targets.updateAndVerifyRPTargets(RecomputeRP) && | |
| Targets.allAchieved()) { | |
| LLVM_DEBUG(dbgs() << "Objectives achieved!\n"); | |
| break; | |
| } | |
| // Update the score of remaining candidates and filter out those that have | |
| // become useless from the vector. Candidates never become useful after | |
| // having been useless for a round, so we can freely drop them without | |
| // losing any future rematerialization opportunity. | |
| unsigned NumUsefulCandidates = 0; | |
| for (unsigned CandIdx : CandidateOrder) { | |
| RematCandidate &Candidate = Candidates[CandIdx]; | |
| Candidate.update(Targets); | |
| if (!Candidate.hasNullScore()) | |
| CandidateOrder[NumUsefulCandidates++] = CandIdx; | |
| } | |
| if (NumUsefulCandidates == 0) { | |
| LLVM_DEBUG(dbgs() << "Stop on exhausted rematerialization candidates\n"); | |
| break; | |
| } | |
| CandidateOrder.truncate(NumUsefulCandidates); | |
| } | |
| if (!MaxPressure) | |
| return; | |
| for (unsigned RegionIdx : AffectedRegions.set_bits()) | |
| (*MaxPressure)[RegionIdx] = Targets.RPTargets[RegionIdx].getCurrentRP(); | |
| } | |
| void GCNRematStrategy::createCandidates() { | |
| CandidatesCreated = true; | |
| // Set of registers already marked for potential remterialization; used to | |
| // avoid rematerialization chains. | |
| SmallSet<Register, 4> MarkedRegs; | |
| // Map registers to candidate indices. Use ~0u as null value since 0 is a | |
| // valid index. | |
| IndexedMap<unsigned, VirtReg2IndexFunctor> DefRegToCandIdx(~0u); | |
| DefRegToCandIdx.resize(MRI.getNumVirtRegs()); | |
| const unsigned NumRegions = Regions.size(); | |
| for (unsigned RegIdx = 0, E = Remater.getNumRegs(); RegIdx < E; ++RegIdx) { | |
| const Rematerializer::Reg &CandReg = Remater.getReg(RegIdx); | |
| if (!CandReg.hasUsersOutsideDefRegion()) | |
| continue; | |
| // Rematerialization moves the defining instruction into the region of its | |
| // uses, which may sit under different control dependencies (e.g., across a | |
| // change of EXEC). Convergent operations must not be made control-dependent | |
| // on additional values, so they cannot be safely relocated this way. This | |
| // mirrors the check MachineSink performs before sinking an instruction. | |
| if (any_of(CandReg.Defs, | |
| [](const MachineInstr *DefMI) { return DefMI->isConvergent(); })) | |
| continue; | |
| // Rematerializing a load into multiple using regions duplicates the | |
| // memory access itself. Conservatively restrict rematerialization of loads | |
| // to a single region. | |
| if (CandReg.Uses.size() > 1 && | |
| any_of(CandReg.Defs, | |
| [](const MachineInstr *DefMI) { return DefMI->mayLoad(); })) | |
| continue; | |
| // We further filter the registers that we can rematerialize based on our | |
| // current tracking capabilities in the strategy. This ensures that we are | |
| // not extending any live range while rematerializing, and that | |
| // rematerializing any register candidate will not affect other candidates. | |
| // All register dependencies should not already be considered | |
| // rematerializable. | |
| if (llvm::any_of(CandReg.Dependencies, [&](RegisterIdx DepRegIdx) { | |
| return MarkedRegs.contains(Remater.getReg(DepRegIdx).getDefReg()); | |
| })) | |
| continue; | |
| if (!candidateHasValidUsers(RegIdx, MarkedRegs)) | |
| continue; | |
| Register DefReg = CandReg.getDefReg(); | |
| MarkedRegs.insert(DefReg); | |
| DefRegToCandIdx[DefReg] = static_cast<unsigned>(Candidates.size()); | |
| Candidates.emplace_back(RegIdx, NumRegions); | |
| } | |
| // Initialize the LiveIn and LiveOut sets of all candidates. This is more | |
| // efficient than querying those structures for each candidate separately. | |
| for (unsigned RegionIdx = 0; RegionIdx < NumRegions; ++RegionIdx) { | |
| for (const auto &[Reg, Mask] : Regions.getLiveIns(RegionIdx)) { | |
| if (Register::isPhysicalRegister(Reg)) | |
| continue; | |
| unsigned CandIdx = DefRegToCandIdx[Reg]; | |
| if (CandIdx != ~0u) | |
| Candidates[CandIdx].LiveIn.set(RegionIdx); | |
| } | |
| for (const auto &[Reg, Mask] : Regions.getLiveOuts(RegionIdx)) { | |
| if (Register::isPhysicalRegister(Reg)) | |
| continue; | |
| unsigned CandIdx = DefRegToCandIdx[Reg]; | |
| if (CandIdx != ~0u) | |
| Candidates[CandIdx].LiveOut.set(RegionIdx); | |
| } | |
| } | |
| for (RematCandidate &Cand : Candidates) | |
| Cand.init(Regions, Remater, MRI); | |
| } | |
| bool GCNRematStrategy::candidateHasValidUsers( | |
| RegisterIdx CandIdx, const SmallSet<Register, 4> &MarkedRegs) const { | |
| const Rematerializer::Reg &CandReg = Remater.getReg(CandIdx); | |
| SlotIndex RefIdx = | |
| LIS.getInstructionIndex(*CandReg.getLastDef()).getRegSlot(true); | |
| for (const auto &[UseRegion, Users] : CandReg.Uses) { | |
| // Users cannot themselves be rematerializable. | |
| if (llvm::any_of(Users, [&](const MachineInstr *UserMI) { | |
| assert(UserMI->getNumOperands() > 0 && | |
| "user must have at least one operand"); | |
| // A user is rematerializable if its first register operand (its | |
| // register def) was marked already. | |
| const MachineOperand &DefMO = UserMI->getOperand(0); | |
| return DefMO.isReg() && MarkedRegs.contains(DefMO.getReg()); | |
| })) | |
| return false; | |
| MachineInstr *FirstUseMI = CandReg.getRegionUseBounds(UseRegion, LIS).first; | |
| assert(FirstUseMI && "there must be a user in the region"); | |
| SlotIndex FirstUseIdx = | |
| LIS.getInstructionIndex(*FirstUseMI).getRegSlot(true); | |
| // All dependencies must be available at the first use in the region. | |
| if (llvm::any_of(CandReg.Dependencies, [&](RegisterIdx DepRegIdx) { | |
| const Rematerializer::Reg &DepReg = Remater.getReg(DepRegIdx); | |
| Register DepDefReg = DepReg.getDefReg(); | |
| return !Remater.isRegIdenticalAtUses(DepDefReg, DepReg.Mask, RefIdx, | |
| {FirstUseIdx}); | |
| })) | |
| return false; | |
| if (llvm::any_of(Remater.getUnrematableDeps(CandIdx), | |
| [&](const std::pair<Register, LaneBitmask> &RegAndMask) { | |
| const auto &[Reg, Mask] = RegAndMask; | |
| return !Remater.isRegIdenticalAtUses(Reg, Mask, RefIdx, | |
| {FirstUseIdx}); | |
| })) | |
| return false; | |
| } | |
| return true; | |
| } | |
| void GCNRematStrategy::rollbackRecordedRemats() { | |
| // Roll back changes made by the strategy. | |
| assert(Recorder && "recorder should be defined"); | |
| Recorder->Listener.rollback(Remater); | |
| for (const auto &[RegIdx, LiveIn, LiveOut] : Recorder->LiveMapUpdates) { | |
| const Rematerializer::Reg &Reg = Remater.getReg(RegIdx); | |
| Regions.addToLiveMaps(Reg.getDefReg(), Reg.Mask, LiveIn, LiveOut); | |
| } | |
| } | |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | |
| Printable GCNRematStrategy::RematCandidate::print() const { | |
| return Printable([&](raw_ostream &OS) { | |
| OS << '(' << MaxFreq << ", " << FreqDiff << ", " << RegionImpact << ')'; | |
| }); | |
| } | |
| Printable GCNRematStrategy::RematTargets::print() const { | |
| return Printable([&](raw_ostream &OS) { | |
| if (allAchieved()) { | |
| OS << "No target regions\n"; | |
| return; | |
| } | |
| OS << "Target regions:\n"; | |
| for (unsigned I : ExcessRP.set_bits()) | |
| OS << " [" << I << "] " << RPTargets[I] << '\n'; | |
| }); | |
| } | |
| #endif |