Skip to content

Commit

Permalink
[MachineSink] replace MachineLoop with MachineCycle
Browse files Browse the repository at this point in the history
reapply 62a9b36 and fix module build
failue:
1: remove MachineCycleInfoWrapperPass in MachinePassRegistry.def
   MachineCycleInfoWrapperPass is a anylysis pass, should not be there.
2: move the definition for MachineCycleInfoPrinterPass to cpp file.

Otherwise, there are module conflicit for MachineCycleInfoWrapperPass
in MachinePassRegistry.def and MachineCycleAnalysis.h after
62a9b36.

MachineCycle can handle irreducible loop. Natural loop
analysis (MachineLoop) can not return correct loop depth if
the loop is irreducible loop. And MachineSink is sensitive
to the loop depth, see MachineSinking::isProfitableToSinkTo().

This patch tries to use MachineCycle so that we can handle
irreducible loop better.

Reviewed By: sameerds, MatzeB

Differential Revision: https://reviews.llvm.org/D123995
  • Loading branch information
chenzheng1030 committed May 26, 2022
1 parent ad1d60c commit d792752
Show file tree
Hide file tree
Showing 17 changed files with 340 additions and 199 deletions.
50 changes: 50 additions & 0 deletions llvm/include/llvm/ADT/GenericCycleImpl.h
Expand Up @@ -66,6 +66,44 @@ void GenericCycle<ContextT>::getExitBlocks(
}
}

template <typename ContextT>
auto GenericCycle<ContextT>::getCyclePreheader() const -> BlockT * {
BlockT *Predecessor = getCyclePredecessor();
if (!Predecessor)
return nullptr;

assert(isReducible() && "Cycle Predecessor must be in a reducible cycle!");

if (succ_size(Predecessor) != 1)
return nullptr;

// Make sure we are allowed to hoist instructions into the predecessor.
if (!Predecessor->isLegalToHoistInto())
return nullptr;

return Predecessor;
}

template <typename ContextT>
auto GenericCycle<ContextT>::getCyclePredecessor() const -> BlockT * {
if (!isReducible())
return nullptr;

BlockT *Out = nullptr;

// Loop over the predecessors of the header node...
BlockT *Header = getHeader();
for (const auto Pred : predecessors(Header)) {
if (!contains(Pred)) {
if (Out && Out != Pred)
return nullptr;
Out = Pred;
}
}

return Out;
}

/// \brief Helper class for computing cycle information.
template <typename ContextT> class GenericCycleInfoCompute {
using BlockT = typename ContextT::BlockT;
Expand Down Expand Up @@ -326,6 +364,18 @@ auto GenericCycleInfo<ContextT>::getCycle(const BlockT *Block) const
return nullptr;
}

/// \brief get the depth for the cycle which containing a given block.
///
/// \returns the depth for the innermost cycle containing \p Block or 0 if it is
/// not contained in any cycle.
template <typename ContextT>
unsigned GenericCycleInfo<ContextT>::getCycleDepth(const BlockT *Block) const {
CycleT *Cycle = getCycle(Block);
if (!Cycle)
return 0;
return Cycle->getDepth();
}

#ifndef NDEBUG
/// \brief Validate the internal consistency of the cycle tree.
///
Expand Down
15 changes: 15 additions & 0 deletions llvm/include/llvm/ADT/GenericCycleInfo.h
Expand Up @@ -100,6 +100,10 @@ template <typename ContextT> class GenericCycle {

BlockT *getHeader() const { return Entries[0]; }

const SmallVectorImpl<BlockT *> & getEntries() const {
return Entries;
}

/// \brief Return whether \p Block is an entry block of the cycle.
bool isEntry(BlockT *Block) const { return is_contained(Entries, Block); }

Expand All @@ -124,6 +128,16 @@ template <typename ContextT> class GenericCycle {
/// branched to.
void getExitBlocks(SmallVectorImpl<BlockT *> &TmpStorage) const;

/// Return the preheader block for this cycle. Pre-header is well-defined for
/// reducible cycle in docs/LoopTerminology.rst as: the only one entering
/// block and its only edge is to the entry block. Return null for irreducible
/// cycles.
BlockT *getCyclePreheader() const;

/// If the cycle has exactly one entry with exactly one predecessor, return
/// it, otherwise return nullptr.
BlockT *getCyclePredecessor() const;

/// Iteration over child cycles.
//@{
using const_child_iterator_base =
Expand Down Expand Up @@ -239,6 +253,7 @@ template <typename ContextT> class GenericCycleInfo {
const ContextT &getSSAContext() const { return Context; }

CycleT *getCycle(const BlockT *Block) const;
unsigned getCycleDepth(const BlockT *Block) const;
CycleT *getTopLevelParentCycle(const BlockT *Block) const;

/// Move \p Child to \p NewParent by manipulating Children vectors.
Expand Down
25 changes: 25 additions & 0 deletions llvm/include/llvm/CodeGen/MachineCycleAnalysis.h
Expand Up @@ -16,6 +16,8 @@

#include "llvm/ADT/GenericCycleInfo.h"
#include "llvm/CodeGen/MachineSSAContext.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/InitializePasses.h"

namespace llvm {

Expand All @@ -25,6 +27,29 @@ extern template class GenericCycle<MachineSSAContext>;
using MachineCycleInfo = GenericCycleInfo<MachineSSAContext>;
using MachineCycle = MachineCycleInfo::CycleT;

/// Legacy analysis pass which computes a \ref MachineCycleInfo.
class MachineCycleInfoWrapperPass : public MachineFunctionPass {
MachineFunction *F = nullptr;
MachineCycleInfo CI;

public:
static char ID;

MachineCycleInfoWrapperPass();

MachineCycleInfo &getCycleInfo() { return CI; }
const MachineCycleInfo &getCycleInfo() const { return CI; }

bool runOnMachineFunction(MachineFunction &F) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
void releaseMemory() override;
void print(raw_ostream &OS, const Module *M = nullptr) const override;
};

// TODO: add this function to GenericCycle template after implementing IR
// version.
bool isCycleInvariant(const MachineCycle *Cycle, MachineInstr &I);

} // end namespace llvm

#endif // LLVM_CODEGEN_MACHINECYCLEANALYSIS_H
1 change: 0 additions & 1 deletion llvm/include/llvm/CodeGen/MachinePassRegistry.def
Expand Up @@ -199,6 +199,5 @@ DUMMY_MACHINE_FUNCTION_PASS("regbankselect", RegBankSelectPass, ())
DUMMY_MACHINE_FUNCTION_PASS("instruction-select", InstructionSelectPass, ())
DUMMY_MACHINE_FUNCTION_PASS("reset-machine-function", ResetMachineFunctionPass, ())
DUMMY_MACHINE_FUNCTION_PASS("machineverifier", MachineVerifierPass, ())
DUMMY_MACHINE_FUNCTION_PASS("machine-cycles", MachineCycleInfoWrapperPass, ())
DUMMY_MACHINE_FUNCTION_PASS("print-machine-cycles", MachineCycleInfoPrinterPass, ())
#undef DUMMY_MACHINE_FUNCTION_PASS
2 changes: 2 additions & 0 deletions llvm/include/llvm/CodeGen/MachineSSAContext.h
Expand Up @@ -28,6 +28,8 @@ template <typename, bool> class DominatorTreeBase;

inline auto successors(MachineBasicBlock *BB) { return BB->successors(); }
inline auto predecessors(MachineBasicBlock *BB) { return BB->predecessors(); }
inline unsigned succ_size(MachineBasicBlock *BB) { return BB->succ_size(); }
inline unsigned pred_size(MachineBasicBlock *BB) { return BB->pred_size(); }

template <> class GenericSSAContext<MachineFunction> {
const MachineRegisterInfo *RegInfo = nullptr;
Expand Down
110 changes: 72 additions & 38 deletions llvm/lib/CodeGen/MachineCycleAnalysis.cpp
Expand Up @@ -8,50 +8,15 @@

#include "llvm/CodeGen/MachineCycleAnalysis.h"
#include "llvm/ADT/GenericCycleImpl.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineSSAContext.h"
#include "llvm/InitializePasses.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"

using namespace llvm;

template class llvm::GenericCycleInfo<llvm::MachineSSAContext>;
template class llvm::GenericCycle<llvm::MachineSSAContext>;

namespace {

/// Legacy analysis pass which computes a \ref MachineCycleInfo.
class MachineCycleInfoWrapperPass : public MachineFunctionPass {
MachineFunction *F = nullptr;
MachineCycleInfo CI;

public:
static char ID;

MachineCycleInfoWrapperPass();

MachineCycleInfo &getCycleInfo() { return CI; }
const MachineCycleInfo &getCycleInfo() const { return CI; }

bool runOnMachineFunction(MachineFunction &F) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
void releaseMemory() override;
void print(raw_ostream &OS, const Module *M = nullptr) const override;

// TODO: verify analysis
};

class MachineCycleInfoPrinterPass : public MachineFunctionPass {
public:
static char ID;

MachineCycleInfoPrinterPass();

bool runOnMachineFunction(MachineFunction &F) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
};

} // namespace

char MachineCycleInfoWrapperPass::ID = 0;

MachineCycleInfoWrapperPass::MachineCycleInfoWrapperPass()
Expand Down Expand Up @@ -87,6 +52,16 @@ void MachineCycleInfoWrapperPass::releaseMemory() {
F = nullptr;
}

class MachineCycleInfoPrinterPass : public MachineFunctionPass {
public:
static char ID;

MachineCycleInfoPrinterPass();

bool runOnMachineFunction(MachineFunction &F) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
};

char MachineCycleInfoPrinterPass::ID = 0;

MachineCycleInfoPrinterPass::MachineCycleInfoPrinterPass()
Expand All @@ -111,3 +86,62 @@ bool MachineCycleInfoPrinterPass::runOnMachineFunction(MachineFunction &F) {
CI.print(errs());
return false;
}

bool llvm::isCycleInvariant(const MachineCycle *Cycle, MachineInstr &I) {
MachineFunction *MF = I.getParent()->getParent();
MachineRegisterInfo *MRI = &MF->getRegInfo();
const TargetSubtargetInfo &ST = MF->getSubtarget();
const TargetRegisterInfo *TRI = ST.getRegisterInfo();
const TargetInstrInfo *TII = ST.getInstrInfo();

// The instruction is cycle invariant if all of its operands are.
for (const MachineOperand &MO : I.operands()) {
if (!MO.isReg())
continue;

Register Reg = MO.getReg();
if (Reg == 0)
continue;

// An instruction that uses or defines a physical register can't e.g. be
// hoisted, so mark this as not invariant.
if (Register::isPhysicalRegister(Reg)) {
if (MO.isUse()) {
// If the physreg has no defs anywhere, it's just an ambient register
// and we can freely move its uses. Alternatively, if it's allocatable,
// it could get allocated to something with a def during allocation.
// However, if the physreg is known to always be caller saved/restored
// then this use is safe to hoist.
if (!MRI->isConstantPhysReg(Reg) &&
!(TRI->isCallerPreservedPhysReg(Reg.asMCReg(), *I.getMF())) &&
!TII->isIgnorableUse(MO))
return false;
// Otherwise it's safe to move.
continue;
} else if (!MO.isDead()) {
// A def that isn't dead can't be moved.
return false;
} else if (any_of(Cycle->getEntries(),
[&](const MachineBasicBlock *Block) {
return Block->isLiveIn(Reg);
})) {
// If the reg is live into any header of the cycle we can't hoist an
// instruction which would clobber it.
return false;
}
}

if (!MO.isUse())
continue;

assert(MRI->getVRegDef(Reg) && "Machine instr not mapped for this vreg?!");

// If the cycle contains the definition of an operand, then the instruction
// isn't cycle invariant.
if (Cycle->contains(MRI->getVRegDef(Reg)->getParent()))
return false;
}

// If we got this far, the instruction is cycle invariant!
return true;
}

0 comments on commit d792752

Please sign in to comment.