mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-30 13:16:07 +00:00

Uniformity analysis is a generalization of divergence analysis to include irreducible control flow: 1. The proposed spec presents a notion of "maximal convergence" that captures the existing convention of converging threads at the headers of natual loops. 2. Maximal convergence is then extended to irreducible cycles. The identity of irreducible cycles is determined by the choices made in a depth-first traversal of the control flow graph. Uniformity analysis uses criteria that depend only on closed paths and not cycles, to determine maximal convergence. This makes it a conservative analysis that is independent of the effect of DFS on CycleInfo. 3. The analysis is implemented as a template that can be instantiated for both LLVM IR and Machine IR. Validation: - passes existing tests for divergence analysis - passes new tests with irreducible control flow - passes equivalent tests in MIR and GMIR Based on concepts originally outlined by Nicolai Haehnle <nicolai.haehnle@amd.com> With contributions from Ruiling Song <ruiling.song@amd.com> and Jay Foad <jay.foad@amd.com>. Support for GMIR and lit tests for GMIR/MIR added by Yashwant Singh <yashwant.singh@amd.com>. Differential Revision: https://reviews.llvm.org/D130746
150 lines
5.1 KiB
C++
150 lines
5.1 KiB
C++
//===- MachineCycleAnalysis.cpp - Compute CycleInfo for Machine IR --------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/CodeGen/MachineCycleAnalysis.h"
|
|
#include "llvm/ADT/GenericCycleImpl.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/MachineSSAContext.h"
|
|
#include "llvm/CodeGen/TargetInstrInfo.h"
|
|
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
|
#include "llvm/InitializePasses.h"
|
|
|
|
using namespace llvm;
|
|
|
|
template class llvm::GenericCycleInfo<llvm::MachineSSAContext>;
|
|
template class llvm::GenericCycle<llvm::MachineSSAContext>;
|
|
|
|
char MachineCycleInfoWrapperPass::ID = 0;
|
|
|
|
MachineCycleInfoWrapperPass::MachineCycleInfoWrapperPass()
|
|
: MachineFunctionPass(ID) {
|
|
initializeMachineCycleInfoWrapperPassPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
INITIALIZE_PASS_BEGIN(MachineCycleInfoWrapperPass, "machine-cycles",
|
|
"Machine Cycle Info Analysis", true, true)
|
|
INITIALIZE_PASS_END(MachineCycleInfoWrapperPass, "machine-cycles",
|
|
"Machine Cycle Info Analysis", true, true)
|
|
|
|
void MachineCycleInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
AU.setPreservesAll();
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
}
|
|
|
|
bool MachineCycleInfoWrapperPass::runOnMachineFunction(MachineFunction &Func) {
|
|
CI.clear();
|
|
|
|
F = &Func;
|
|
CI.compute(Func);
|
|
return false;
|
|
}
|
|
|
|
void MachineCycleInfoWrapperPass::print(raw_ostream &OS, const Module *) const {
|
|
OS << "MachineCycleInfo for function: " << F->getName() << "\n";
|
|
CI.print(OS);
|
|
}
|
|
|
|
void MachineCycleInfoWrapperPass::releaseMemory() {
|
|
CI.clear();
|
|
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()
|
|
: MachineFunctionPass(ID) {
|
|
initializeMachineCycleInfoPrinterPassPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
INITIALIZE_PASS_BEGIN(MachineCycleInfoPrinterPass, "print-machine-cycles",
|
|
"Print Machine Cycle Info Analysis", true, true)
|
|
INITIALIZE_PASS_DEPENDENCY(MachineCycleInfoWrapperPass)
|
|
INITIALIZE_PASS_END(MachineCycleInfoPrinterPass, "print-machine-cycles",
|
|
"Print Machine Cycle Info Analysis", true, true)
|
|
|
|
void MachineCycleInfoPrinterPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
AU.setPreservesAll();
|
|
AU.addRequired<MachineCycleInfoWrapperPass>();
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
}
|
|
|
|
bool MachineCycleInfoPrinterPass::runOnMachineFunction(MachineFunction &F) {
|
|
auto &CI = getAnalysis<MachineCycleInfoWrapperPass>();
|
|
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;
|
|
}
|