564 lines
26 KiB
C++
564 lines
26 KiB
C++
//=====-- Rematerializer.h - MIR rematerialization support ------*- 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
|
|
/// MIR-level target-independent rematerialization helpers.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CODEGEN_REMATERIALIZER_H
|
|
#define LLVM_CODEGEN_REMATERIALIZER_H
|
|
|
|
#include "llvm/ADT/MapVector.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/CodeGen/LiveIntervals.h"
|
|
#include "llvm/CodeGen/MachineBasicBlock.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/TargetInstrInfo.h"
|
|
#include "llvm/CodeGen/TargetOpcodes.h"
|
|
#include "llvm/CodeGen/TargetRegisterInfo.h"
|
|
#include <iterator>
|
|
|
|
namespace llvm {
|
|
|
|
/// MIR-level target-independent rematerializer. Provides an API to identify and
|
|
/// rematerialize registers within a machine function.
|
|
///
|
|
/// At the moment this supports rematerializing registers that meet all of the
|
|
/// following constraints.
|
|
/// 1. The register is virtual and has a single defining instruction.
|
|
/// 2. The single defining instruction is deemed rematerializable by the TII and
|
|
/// doesn't have any physical register use that is both non-constant and
|
|
/// non-ignorable.
|
|
/// 3. The register has at least one non-debug use that is inside or at a region
|
|
/// boundary (see below for what we consider to be a region).
|
|
///
|
|
/// Rematerializable registers (represented by \ref Rematerializer::Reg) form a
|
|
/// DAG of their own, with every register having incoming edges from all
|
|
/// rematerializable registers which are read by the instruction defining it. It
|
|
/// is possible to rematerialize registers with unrematerializable dependencies;
|
|
/// however the latter are not considered part of this DAG since their
|
|
/// position/identity never change and therefore do not require the same level
|
|
/// of tracking.
|
|
///
|
|
/// Each register has a "dependency DAG" which is defined as the subset of nodes
|
|
/// in the overall DAG that have at least one path to the register, which is
|
|
/// called the "root" register in this context. Semantically, these nodes are
|
|
/// the registers which are involved into the computation of the root register
|
|
/// i.e., all of its transitive dependencies. We use the term "root" because all
|
|
/// paths within the dependency DAG of a register terminate at it; however,
|
|
/// there may be multiple paths between a non-root node and the root node, so a
|
|
/// dependency DAG is not always a tree.
|
|
///
|
|
/// The API uses dense unsigned integers starting at 0 to reference
|
|
/// rematerializable registers. These indices are immutable i.e., even when
|
|
/// registers are deleted their respective integer handle remain valid. Method
|
|
/// which perform actual rematerializations should however be assumed to
|
|
/// invalidate addresses to \ref Rematerializer::Reg objects.
|
|
///
|
|
/// The rematerializer tracks def/use points of registers based on regions.
|
|
/// These are alike the regions the machine scheduler works on. A region is
|
|
/// simply a pair on MBB iterators encoding a range of machine instructions. The
|
|
/// first iterator (beginning of the region) is inclusive whereas the second
|
|
/// iterator (end of the region) is exclusive and can either point to a MBB's
|
|
/// end sentinel or an actual MI (not necessarily a terminator). Regions must be
|
|
/// non-empty, cannot overlap, and cannot contain terminators. However, they do
|
|
/// not have to cover the whole function.
|
|
///
|
|
/// The API uses dense unsigned integers starting at 0 to reference regions.
|
|
/// These map directly to the indices of the corresponding regions in the region
|
|
/// vector passed during construction.
|
|
///
|
|
/// The rematerializer supports rematerializing arbitrary complex DAGs of
|
|
/// registers to regions where these registers are used, with the option of
|
|
/// re-using non-root registers or their previous rematerializations instead of
|
|
/// rematerializing them again.
|
|
///
|
|
/// Throughout its lifetime, the rematerializer tracks new registers it creates
|
|
/// (which are rematerializable by construction) and their relations to other
|
|
/// registers. It performs DAG updates immediately on rematerialization but
|
|
/// defers/batches all necessary live interval updates to reduce the number of
|
|
/// expensive LIS queries when successively rematerializing many registers. \ref
|
|
/// Rematerializer::updateLiveIntervals performs all currently batched live
|
|
/// interval updates.
|
|
///
|
|
/// In its nomenclature, the rematerializer differentiates between "original
|
|
/// registers" (registers that were present when it analyzed the function) and
|
|
/// rematerializations of these original registers. Rematerializations have an
|
|
/// "origin" which is the index of the original regiser they were rematerialized
|
|
/// from (transitivity applies; a rematerialization and all of its own
|
|
/// rematerializations have the same origin). Semantically, only original
|
|
/// registers have rematerializations.
|
|
class Rematerializer {
|
|
public:
|
|
/// Index type for rematerializable registers.
|
|
using RegisterIdx = unsigned;
|
|
|
|
/// A rematerializable register defined by a single machine instruction.
|
|
///
|
|
/// A rematerializable register has a set of dependencies, which correspond
|
|
/// to the unique read register operands of its defining instruction.
|
|
/// They are identified by their machine operand index, and can themselves be
|
|
/// rematerializable. Operand indices corresponding to unrematerializable
|
|
/// dependencies are managed by and queried from the rematerializer.
|
|
///
|
|
/// A rematerializable register also has an arbitrary number of users in an
|
|
/// arbitrary number of regions, potentially including its own defining
|
|
/// region. When rematerializations lead to operand changes in users, a
|
|
/// register may find itself without any user left, at which point the
|
|
/// rematerializer deletes it (setting its defining MI to nullptr).
|
|
struct Reg {
|
|
/// Single MI defining the rematerializable register.
|
|
MachineInstr *DefMI;
|
|
/// Defining region of \p DefMI.
|
|
unsigned DefRegion;
|
|
/// The rematerializable register's lane bitmask.
|
|
LaneBitmask Mask;
|
|
|
|
using RegionUsers = SmallDenseSet<MachineInstr *, 4>;
|
|
/// Uses of the register, mapped by region.
|
|
SmallDenseMap<unsigned, RegionUsers, 2> Uses;
|
|
|
|
/// A read register operand of \p DefMI that is rematerializable (according
|
|
/// to the rematerializer).
|
|
struct Dependency {
|
|
/// The register's machine operand index in \p DefMI.
|
|
unsigned MOIdx;
|
|
/// The corresponding register's index in the rematerializer.
|
|
RegisterIdx RegIdx;
|
|
|
|
Dependency(unsigned MOIdx, RegisterIdx RegIdx)
|
|
: MOIdx(MOIdx), RegIdx(RegIdx) {}
|
|
};
|
|
/// This register's rematerializable dependencies, one per unique
|
|
/// rematerializable register operand.
|
|
SmallVector<Dependency, 2> Dependencies;
|
|
|
|
/// Returns the rematerializable register from its defining instruction.
|
|
Register getDefReg() const {
|
|
assert(DefMI && "defining instruction was deleted");
|
|
assert(DefMI->getOperand(0).isDef() && "not a register def");
|
|
return DefMI->getOperand(0).getReg();
|
|
}
|
|
|
|
bool hasUsersInDefRegion() const {
|
|
return !Uses.empty() && Uses.contains(DefRegion);
|
|
}
|
|
|
|
bool hasUsersOutsideDefRegion() const {
|
|
if (Uses.empty())
|
|
return false;
|
|
return Uses.size() > 1 || Uses.begin()->first != DefRegion;
|
|
}
|
|
|
|
/// Returns the first and last user of the register in region \p UseRegion.
|
|
/// If the register has no user in the region, returns a pair of nullptr's.
|
|
LLVM_ABI std::pair<MachineInstr *, MachineInstr *>
|
|
getRegionUseBounds(unsigned UseRegion, const LiveIntervals &LIS) const;
|
|
|
|
bool isAlive() const { return DefMI; }
|
|
|
|
private:
|
|
void addUser(MachineInstr *MI, unsigned Region);
|
|
void addUsers(const RegionUsers &NewUsers, unsigned Region);
|
|
void eraseUser(MachineInstr *MI, unsigned Region);
|
|
|
|
friend Rematerializer;
|
|
};
|
|
|
|
/// Rematerializer listener. Defines overridable hooks that allow to catch
|
|
/// specific events inside the rematerializer. All hooks do nothing by
|
|
/// default. Listeners can be added or removed at any time during the
|
|
/// rematerializer's lifetime.
|
|
class LLVM_ABI Listener {
|
|
public:
|
|
using RegisterIdx = Rematerializer::RegisterIdx;
|
|
|
|
/// Called just after register \p NewRegIdx is created (following a
|
|
/// rematerialization). At this point the rematerialization exists in the \p
|
|
/// Remater state and the MIR but does not yet have any user.
|
|
virtual void rematerializerNoteRegCreated(const Rematerializer &Remater,
|
|
RegisterIdx NewRegIdx) {}
|
|
|
|
/// Called juste before register \p RegIdx is deleted from the MIR. At this
|
|
/// point the register still exists in the MIR but no longer has any user.
|
|
virtual void rematerializerNoteRegDeleted(const Rematerializer &Remater,
|
|
RegisterIdx RegIdx) {}
|
|
|
|
virtual ~Listener() = default;
|
|
|
|
private:
|
|
virtual void anchor();
|
|
};
|
|
|
|
/// Error value for register indices.
|
|
static constexpr unsigned NoReg = ~0;
|
|
|
|
/// A region's boundaries i.e. a pair of instruction bundle iterators. The
|
|
/// lower boundary is inclusive, the upper boundary is exclusive.
|
|
using RegionBoundaries =
|
|
std::pair<MachineBasicBlock::iterator, MachineBasicBlock::iterator>;
|
|
|
|
using RematsOf = SmallDenseSet<RegisterIdx, 4>;
|
|
|
|
/// Simply initializes some internal state, does not identify
|
|
/// rematerialization candidates.
|
|
LLVM_ABI Rematerializer(MachineFunction &MF,
|
|
SmallVectorImpl<RegionBoundaries> &Regions,
|
|
LiveIntervals &LIS);
|
|
|
|
/// Goes through the whole MF and identifies all rematerializable registers.
|
|
/// Returns whether there is any rematerializable register in regions.
|
|
LLVM_ABI bool analyze();
|
|
|
|
/// Adds a new listener to the rematerializer.
|
|
void addListener(Listener *Listen) {
|
|
assert(Listen && "null listener");
|
|
if (!Listeners.insert(Listen).second)
|
|
llvm_unreachable("duplicate listener");
|
|
}
|
|
|
|
/// Removes a listener from the rematerializer.
|
|
void removeListener(Listener *Listen) {
|
|
if (!Listeners.erase(Listen))
|
|
llvm_unreachable("unknown listener");
|
|
}
|
|
|
|
/// Removes all listeners from the rematerializer.
|
|
void clearListeners() { Listeners.clear(); }
|
|
|
|
const Reg &getReg(RegisterIdx RegIdx) const {
|
|
assert(RegIdx < Regs.size() && "out of bounds");
|
|
return Regs[RegIdx];
|
|
};
|
|
ArrayRef<Reg> getRegs() const { return Regs; };
|
|
unsigned getNumRegs() const { return Regs.size(); };
|
|
|
|
const RegionBoundaries &getRegion(RegisterIdx RegionIdx) const {
|
|
assert(RegionIdx < Regions.size() && "out of bounds");
|
|
return Regions[RegionIdx];
|
|
}
|
|
unsigned getNumRegions() const { return Regions.size(); }
|
|
|
|
/// Whether register \p RegIdx is an original register.
|
|
bool isOriginalRegister(RegisterIdx RegIdx) const {
|
|
return !isRematerializedRegister(RegIdx);
|
|
}
|
|
/// Whether register \p RegIdx is a rematerialization of some original
|
|
/// register.
|
|
bool isRematerializedRegister(RegisterIdx RegIdx) const {
|
|
assert(RegIdx < Regs.size() && "out of bounds");
|
|
return RegIdx >= UnrematableOprds.size();
|
|
}
|
|
/// Returns the origin index of rematerializable register \p RegIdx.
|
|
RegisterIdx getOriginOf(RegisterIdx RematRegIdx) const {
|
|
assert(isRematerializedRegister(RematRegIdx) && "not a rematerialization");
|
|
return Origins[RematRegIdx - UnrematableOprds.size()];
|
|
}
|
|
/// If \p RegIdx is a rematerialization, returns its origin's index. If it is
|
|
/// an original register's index, returns the same index.
|
|
RegisterIdx getOriginOrSelf(RegisterIdx RegIdx) const {
|
|
if (isRematerializedRegister(RegIdx))
|
|
return getOriginOf(RegIdx);
|
|
return RegIdx;
|
|
}
|
|
/// Returns operand indices corresponding to unrematerializable operands for
|
|
/// any register \p RegIdx.
|
|
ArrayRef<unsigned> getUnrematableOprds(RegisterIdx RegIdx) const {
|
|
return UnrematableOprds[getOriginOrSelf(RegIdx)];
|
|
}
|
|
|
|
/// If \p MI's first operand defines a register and that register is a
|
|
/// rematerializable register tracked by the rematerializer, returns its
|
|
/// index in the \ref Regs vector. Otherwise returns \ref
|
|
/// Rematerializer::NoReg.
|
|
LLVM_ABI RegisterIdx getDefRegIdx(const MachineInstr &MI) const;
|
|
|
|
/// When rematerializating a register (called the "root" register in this
|
|
/// context) to a given position, we must decide what to do with all its
|
|
/// rematerializable dependencies (for unrematerializable dependencies, we
|
|
/// have no choice but to re-use the same register). For each rematerializable
|
|
/// dependency we can either
|
|
/// 1. rematerialize it along with the register,
|
|
/// 2. re-use it as-is, or
|
|
/// 3. re-use a pre-existing rematerialization of it.
|
|
/// In case 1, the same decision needs to be made for all of the dependency's
|
|
/// dependencies. In cases 2 and 3, the dependency's dependencies need not be
|
|
/// examined.
|
|
///
|
|
/// This struct allows to encode decisions of types (2) and (3) when
|
|
/// rematerialization of all of the root's dependency DAG is undesirable.
|
|
/// During rematerialization, registers in the root's dependency DAG which
|
|
/// have a path to the root made up exclusively of non-re-used registers will
|
|
/// be rematerialized along with the root.
|
|
struct DependencyReuseInfo {
|
|
/// Keys and values are rematerializable register indices.
|
|
///
|
|
/// Before rematerialization, this only contains entries for non-root
|
|
/// registers of the root's dependency DAG which should not be
|
|
/// rematerialized i.e., for which an existing register should be used
|
|
/// instead. These map each such non-root register to either the same
|
|
/// register (case 2, \ref DependencyReuseInfo::reuse) or to a
|
|
/// rematerialization of the key register (case 3, \ref
|
|
/// DependencyReuseInfo::useRemat).
|
|
///
|
|
/// After rematerialization, this contains additional entries for non-root
|
|
/// registers of the root's dependency DAG that needed to be rematerialized
|
|
/// along the root. These map each such non-root register to their
|
|
/// corresponding new rematerialization that is used in the rematerialized
|
|
/// root's dependency DAG. It follows that the difference in map size before
|
|
/// and after rematerialization indicates the number of non-root registers
|
|
/// that were rematerialized along the root.
|
|
SmallDenseMap<RegisterIdx, RegisterIdx, 4> DependencyMap;
|
|
|
|
DependencyReuseInfo &reuse(RegisterIdx DepIdx) {
|
|
DependencyMap.insert({DepIdx, DepIdx});
|
|
return *this;
|
|
}
|
|
DependencyReuseInfo &useRemat(RegisterIdx DepIdx, RegisterIdx DepRematIdx) {
|
|
DependencyMap.insert({DepIdx, DepRematIdx});
|
|
return *this;
|
|
}
|
|
DependencyReuseInfo &clear() {
|
|
DependencyMap.clear();
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
/// Rematerializes register \p RootIdx just before its first user inside
|
|
/// region \p UseRegion (or at the end of the region if it has no user),
|
|
/// transfers all its users in the region to the new register, and returns the
|
|
/// latter's index. The root's dependency DAG is rematerialized or re-used
|
|
/// according to \p DRI.
|
|
///
|
|
/// When the method returns, \p DRI contains additional entries for non-root
|
|
/// registers of the root's dependency DAG that needed to be rematerialized
|
|
/// along the root. References to \ref Rematerializer::Reg should be
|
|
/// considered invalidated by calls to this method.
|
|
LLVM_ABI RegisterIdx rematerializeToRegion(RegisterIdx RootIdx,
|
|
unsigned UseRegion,
|
|
DependencyReuseInfo &DRI);
|
|
|
|
/// Rematerializes register \p RootIdx before position \p InsertPos in \p
|
|
/// UseRegion and returns the new register's index. The root's dependency DAG
|
|
/// is rematerialized or re-used according to \p DRI.
|
|
///
|
|
/// When the method returns, \p DRI contains additional entries for non-root
|
|
/// registers of the root's dependency DAG that needed to be rematerialized
|
|
/// along the root. References to \ref Rematerializer::Reg should be
|
|
/// considered invalidated by calls to this method.
|
|
LLVM_ABI RegisterIdx rematerializeToPos(RegisterIdx RootIdx,
|
|
unsigned UseRegion,
|
|
MachineBasicBlock::iterator InsertPos,
|
|
DependencyReuseInfo &DRI);
|
|
|
|
/// Rematerializes register \p RegIdx before \p InsertPos in \p UseRegion,
|
|
/// adding the new rematerializable register to the backing vector \ref Regs
|
|
/// and returning its index inside the vector. Sets the new register's
|
|
/// rematerializable dependencies to \p Dependencies (these are assumed to
|
|
/// already exist in the MIR) and its unrematerializable dependencies to the
|
|
/// same as \p RegIdx. The new register initially has no user. Since the
|
|
/// method appends to \ref Regs, references to elements within it should be
|
|
/// considered invalidated across calls to this method unless the vector can
|
|
/// be guaranteed to have enough space for an extra element.
|
|
LLVM_ABI RegisterIdx
|
|
rematerializeReg(RegisterIdx RegIdx, unsigned UseRegion,
|
|
MachineBasicBlock::iterator InsertPos,
|
|
SmallVectorImpl<Reg::Dependency> &&Dependencies);
|
|
|
|
/// Re-creates a previously deleted register \p RegIdx before \p InsertPos in
|
|
/// \p DefRegion. \p DefReg must be the original virtual register that \p
|
|
/// RegIdx used to define. Sets the new register's rematerializable
|
|
/// dependencies to \p Dependencies (these are assumed to already exist in the
|
|
/// MIR).
|
|
LLVM_ABI void recreateReg(RegisterIdx RegIdx, unsigned DefRegion,
|
|
MachineBasicBlock::iterator InsertPos,
|
|
Register DefReg,
|
|
SmallVectorImpl<Reg::Dependency> &&Dependencies);
|
|
|
|
/// Transfers all users of register \p FromRegIdx in region \p UseRegion to \p
|
|
/// ToRegIdx, the latter of which must be a rematerialization of the former or
|
|
/// have the same origin register. Users in \p UseRegion must be reachable
|
|
/// from \p ToRegIdx.
|
|
LLVM_ABI void transferRegionUsers(RegisterIdx FromRegIdx,
|
|
RegisterIdx ToRegIdx, unsigned UseRegion);
|
|
|
|
/// Transfers user \p UserMI in region \p UserRegion from register \p
|
|
/// FromRegIdx to \p ToRegIdx, the latter of which must be a rematerialization
|
|
/// of the former or have the same origin register. \p UserMI must be a direct
|
|
/// user of \p FromRegIdx. \p UserMI must be reachable from \p ToRegIdx.
|
|
LLVM_ABI void transferUser(RegisterIdx FromRegIdx, RegisterIdx ToRegIdx,
|
|
unsigned UserRegion, MachineInstr &UserMI);
|
|
|
|
/// Transfers all users of register \p FromRegIdx to register \p ToRegIdx, the
|
|
/// latter of which must be a rematerialization of the former or have the same
|
|
/// origin register. Users of \p FromRegIdx must be reachable from \p
|
|
/// ToRegIdx.
|
|
LLVM_ABI void transferAllUsers(RegisterIdx FromRegIdx, RegisterIdx ToRegIdx);
|
|
|
|
/// Recomputes all live intervals that have changed as a result of previous
|
|
/// rematerializations.
|
|
LLVM_ABI void updateLiveIntervals();
|
|
|
|
/// Determines whether (sub-)register operand \p MO has the same value at
|
|
/// all \p Uses as at \p MO. This implies that it is also available at all \p
|
|
/// Uses according to its current live interval.
|
|
LLVM_ABI bool isMOIdenticalAtUses(MachineOperand &MO,
|
|
ArrayRef<SlotIndex> Uses) const;
|
|
|
|
/// Finds the closest rematerialization of register \p RegIdx in region \p
|
|
/// Region that exists before slot \p Before. If no such rematerialization
|
|
/// exists, returns \ref Rematerializer::NoReg.
|
|
LLVM_ABI RegisterIdx findRematInRegion(RegisterIdx RegIdx, unsigned Region,
|
|
SlotIndex Before) const;
|
|
|
|
LLVM_ABI Printable printDependencyDAG(RegisterIdx RootIdx) const;
|
|
LLVM_ABI Printable printID(RegisterIdx RegIdx) const;
|
|
LLVM_ABI Printable printRematReg(RegisterIdx RegIdx,
|
|
bool SkipRegions = false) const;
|
|
LLVM_ABI Printable printRegUsers(RegisterIdx RegIdx) const;
|
|
LLVM_ABI Printable
|
|
printUser(const MachineInstr *MI,
|
|
std::optional<unsigned> UseRegion = std::nullopt) const;
|
|
|
|
private:
|
|
SmallVectorImpl<RegionBoundaries> &Regions;
|
|
MachineRegisterInfo &MRI;
|
|
LiveIntervals &LIS;
|
|
const TargetInstrInfo &TII;
|
|
const TargetRegisterInfo &TRI;
|
|
SmallPtrSet<Listener *, 1> Listeners;
|
|
|
|
void noteRegCreated(RegisterIdx RegIdx) const {
|
|
for (Listener *Listen : Listeners)
|
|
Listen->rematerializerNoteRegCreated(*this, RegIdx);
|
|
}
|
|
|
|
void noteRegDeleted(RegisterIdx RegIdx) const {
|
|
for (Listener *Listen : Listeners)
|
|
Listen->rematerializerNoteRegDeleted(*this, RegIdx);
|
|
}
|
|
|
|
/// Rematerializable registers identified since the rematerializer's creation,
|
|
/// both dead and alive, originals and rematerializations. No register is ever
|
|
/// deleted. Indices inside this vector serve as handles for rematerializable
|
|
/// registers.
|
|
SmallVector<Reg> Regs;
|
|
/// For each original register, stores indices of its read register operands
|
|
/// which are unrematerializable. This doesn't change after the initial
|
|
/// collection period, so the size of the vector indicates the number of
|
|
/// original registers.
|
|
SmallVector<SmallVector<unsigned, 2>> UnrematableOprds;
|
|
/// Indicates the original register index of each rematerialization, in the
|
|
/// order in which they are created. The size of the vector indicates the
|
|
/// total number of rematerializations ever created, including those that were
|
|
/// deleted.
|
|
SmallVector<RegisterIdx> Origins;
|
|
/// Maps original register indices to their currently alive
|
|
/// rematerializations. In practice most registers don't have
|
|
/// rematerializations so this is represented as a map to lower memory cost.
|
|
DenseMap<RegisterIdx, RematsOf> Rematerializations;
|
|
|
|
/// Registers mapped to the index of their corresponding rematerialization
|
|
/// data in the \ref Regs vector. This includes registers that no longer exist
|
|
/// in the MIR.
|
|
DenseMap<Register, RegisterIdx> RegToIdx;
|
|
/// Parent block of each region, in order.
|
|
SmallVector<MachineBasicBlock *> RegionMBB;
|
|
/// Set of registers whose live-range may have changed during past
|
|
/// rematerializations.
|
|
DenseSet<RegisterIdx> LISUpdates;
|
|
|
|
/// Common post-processing step after creating a new register \p RematRegIdx
|
|
/// at \p InsertPos based on register \p ModelRegIdx.
|
|
void postRematerialization(RegisterIdx ModelRegIdx, RegisterIdx RematRegIdx,
|
|
MachineBasicBlock::iterator InsertPos);
|
|
|
|
/// During the analysis phase, creates a \ref Rematerializer::Reg object for
|
|
/// virtual register \p VirtRegIdx if it is rematerializable. \p MIRegion maps
|
|
/// all MIs to their parent region. Set bits in \p SeenRegs indicate virtual
|
|
/// register indices that have already been visited.
|
|
void
|
|
addRegIfRematerializable(unsigned VirtRegIdx,
|
|
const DenseMap<MachineInstr *, unsigned> &MIRegion,
|
|
BitVector &SeenRegs);
|
|
|
|
/// Determines whether \p MI is considered rematerializable. This further
|
|
/// restricts constraints imposed by the TII on rematerializable instructions,
|
|
/// requiring for example that the defined register is virtual and only
|
|
/// defined once.
|
|
bool isMIRematerializable(const MachineInstr &MI) const;
|
|
|
|
/// Implementation of \ref Rematerializer::transferUser that doesn't update
|
|
/// register users.
|
|
void transferUserImpl(RegisterIdx FromRegIdx, RegisterIdx ToRegIdx,
|
|
MachineInstr &UserMI);
|
|
|
|
/// Deletes register \p RootIdx if it no longer has any user. If the register
|
|
/// is deleted, recursively deletes any of its transitive rematerializable
|
|
/// dependencies that no longer have users as a result.
|
|
void deleteRegIfUnused(RegisterIdx RootIdx);
|
|
|
|
/// Deletes rematerializable register \p RegIdx from the DAG and relevant
|
|
/// internal state.
|
|
void deleteReg(RegisterIdx RegIdx);
|
|
};
|
|
|
|
/// Rematerializer listener with the ability to re-create deleted registers and
|
|
/// rollback rematerializations. Starts recording register deletions and
|
|
/// rematerializations as soon as it is attached to the rematerializer.
|
|
class LLVM_ABI Rollbacker : public Rematerializer::Listener {
|
|
public:
|
|
Rollbacker() = default;
|
|
|
|
/// Re-creates all deleted registers and rolls back all rematerializations
|
|
/// that were recorded.
|
|
void rollback(Rematerializer &Remater);
|
|
|
|
void rematerializerNoteRegCreated(const Rematerializer &Remater,
|
|
RegisterIdx RegIdx) override;
|
|
|
|
void rematerializerNoteRegDeleted(const Rematerializer &Remater,
|
|
RegisterIdx RegIdx) override;
|
|
|
|
private:
|
|
struct RollbackInfo {
|
|
/// Original register.
|
|
Register DefReg;
|
|
/// Original defining region.
|
|
unsigned DefRegion;
|
|
/// Original dependencies.
|
|
SmallVector<Rematerializer::Reg::Dependency, 2> Dependencies;
|
|
/// Position to re-create the register before in case of rollback. This
|
|
/// becomes invalid if it originally points to an MI that is deleted later
|
|
/// as a consequence of other rematerializations. In such cases \ref
|
|
/// NextRegIdx is guaranteed to be an actual register index from which the
|
|
/// rollback logic will determine a valid insert position before which to
|
|
/// re-create this register.
|
|
MachineBasicBlock::iterator InsertPos;
|
|
/// If \ref InsertPos points to an MI defining a rematerializable register,
|
|
/// stores its index. Otherwise equals \ref Rematerializer::NoReg.
|
|
RegisterIdx NextRegIdx;
|
|
|
|
LLVM_ABI RollbackInfo(const Rematerializer &Remater, RegisterIdx RegIdx);
|
|
};
|
|
|
|
/// Original registers that have been deleted, in order of deletion.
|
|
MapVector<RegisterIdx, RollbackInfo> DeadRegs;
|
|
/// Registers which have been rematerialized (from original index to
|
|
/// rematerialized index).
|
|
DenseMap<RegisterIdx, Rematerializer::RematsOf> Rematerializations;
|
|
/// Used to block further recording of events whenver we are actively rolling
|
|
/// back.
|
|
bool RollingBack = false;
|
|
};
|
|
|
|
} // namespace llvm
|
|
|
|
#endif // LLVM_CODEGEN_REMATERIALIZER_H
|