blob: 0bab8e4255579092556a9940accba10be740e607 [file] [edit]
//===- AArch64PTrueCoalescing.cpp - Coalesce SVE PTRUEs ---------*- 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
//
//===----------------------------------------------------------------------===//
//
// This pass coalesces compatible all-active SVE PTRUE instructions.
//
// Consider two all-active PTRUE instructions X and Y with element sizes XSize
// and YSize. If X dominates Y and XSize <= YSize, then every predicate bit that
// Y sets is also set by X. In that case, uses of Y can be redirected to X as
// long as each user of Y only reads predicate bits at YSize granularity or
// larger.
//
// If the dominating PTRUE has a larger element size, we can coalesce the pair
// by changing the dominating PTRUE to the smaller element size, provided that
// all of its existing users are also safe with that granularity.
//
//===----------------------------------------------------------------------===//
#include "AArch64.h"
#include "AArch64InstrInfo.h"
#include "AArch64Subtarget.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
#define DEBUG_TYPE "aarch64-ptrue-coalesce"
static cl::opt<bool> EnablePTrueCoalescing(
"aarch64-enable-ptrue-coalescing", cl::init(false), cl::Hidden,
cl::desc("Enable coalescing of compatible AArch64 SVE PTRUE instructions"));
namespace {
class AArch64PTrueCoalescingImpl {
const AArch64InstrInfo *TII = nullptr;
MachineRegisterInfo *MRI = nullptr;
MachineDominatorTree *MDT = nullptr;
public:
explicit AArch64PTrueCoalescingImpl(MachineDominatorTree &MDT) : MDT(&MDT) {}
bool run(MachineFunction &MF);
private:
struct PredicateInfo {
// Instruction that created the predicate.
MachineInstr *MI = nullptr;
// Element size of the MI.
unsigned ElementSize = AArch64::ElementSizeNone;
// Smallest element size of all instructions that use the predicate.
unsigned SmallestUsedElementSize = AArch64::ElementSizeNone;
bool isValid() const {
assert(ElementSize != AArch64::ElementSizeNone &&
"PTRUE missing element size!");
return MI && SmallestUsedElementSize != AArch64::ElementSizeNone;
}
void invalidate() {
assert(isValid());
MI = nullptr;
}
};
std::optional<PredicateInfo> createPredicateInfo(MachineInstr &MI) const {
// TODO: Extend support beyond "PTRUE all"?
if (!isPTrueOpcode(MI.getOpcode()) || MI.getOperand(1).getImm() != 31)
return std::nullopt;
Register Pred = MI.getOperand(0).getReg();
unsigned SmallestUsedElementSize = getSmallestElementSizeInUse(Pred);
unsigned ElementSize = TII->getElementSizeForOpcode(MI.getOpcode());
assert(ElementSize != AArch64::ElementSizeNone &&
"PTRUE missing element size!");
if (SmallestUsedElementSize == AArch64::ElementSizeNone)
return std::nullopt;
return PredicateInfo{&MI, ElementSize, SmallestUsedElementSize};
}
// Return the smallest element size of all instructions that use Reg, or
// AArch64::ElementSizeNone when unknown.
unsigned getSmallestElementSizeInUse(Register Reg) const;
// Try to replace uses of CanPred with DomPred. In some cases that means
// modifying DomPred to support smaller element types.
bool tryCoalesce(PredicateInfo &DomPred, PredicateInfo &CanPred) const;
};
class AArch64PTrueCoalescingLegacy : public MachineFunctionPass {
public:
static char ID;
AArch64PTrueCoalescingLegacy() : MachineFunctionPass(ID) {}
bool runOnMachineFunction(MachineFunction &MF) override;
StringRef getPassName() const override { return "AArch64 PTRUE Coalescing"; }
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
AU.addRequired<MachineDominatorTreeWrapperPass>();
MachineFunctionPass::getAnalysisUsage(AU);
}
};
char AArch64PTrueCoalescingLegacy::ID = 0;
} // end anonymous namespace
INITIALIZE_PASS_BEGIN(AArch64PTrueCoalescingLegacy, DEBUG_TYPE,
"AArch64 PTRUE Coalescing", false, false)
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass)
INITIALIZE_PASS_END(AArch64PTrueCoalescingLegacy, DEBUG_TYPE,
"AArch64 PTRUE Coalescing", false, false)
unsigned
AArch64PTrueCoalescingImpl::getSmallestElementSizeInUse(Register Reg) const {
// SSA form only applies to virtual registers.
if (!Reg.isVirtual())
return AArch64::ElementSizeNone;
unsigned SmallestElementSize = AArch64::ElementSizeNone;
for (MachineOperand &UseMO : MRI->use_nodbg_operands(Reg)) {
assert(UseMO.getSubReg() == 0 && "Unexpected SubReg!");
MachineInstr *UseMI = UseMO.getParent();
unsigned ElementSize = TII->getElementSizeForOpcode(UseMI->getOpcode());
if (ElementSize == AArch64::ElementSizeNone)
return AArch64::ElementSizeNone;
if (SmallestElementSize == AArch64::ElementSizeNone ||
SmallestElementSize > ElementSize)
SmallestElementSize = ElementSize;
}
return SmallestElementSize;
}
bool AArch64PTrueCoalescingImpl::tryCoalesce(PredicateInfo &DomPI,
PredicateInfo &CanPI) const {
assert(DomPI.isValid() && CanPI.isValid());
MachineInstr *DomMI = DomPI.MI;
MachineInstr *CanMI = CanPI.MI;
if (DomMI == CanMI || !MDT->dominates(DomMI, CanMI))
return false;
// A predicate's observable shape is the larger of the element size of the
// instruction writing the predicate and the one reading it. First check if
// DomPI can replace CanPI as-is for CanPI's users. If not, try changing DomPI
// to CanPI's element size, but only if DomPI's existing users would observe
// the same shape after that change.
bool MutateDomPTrue = false;
if (std::max(CanPI.ElementSize, CanPI.SmallestUsedElementSize) !=
std::max(DomPI.ElementSize, CanPI.SmallestUsedElementSize)) {
if (std::max(CanPI.ElementSize, DomPI.SmallestUsedElementSize) !=
std::max(DomPI.ElementSize, DomPI.SmallestUsedElementSize))
return false;
MutateDomPTrue = true;
}
Register DomReg = DomMI->getOperand(0).getReg();
Register CanReg = CanMI->getOperand(0).getReg();
if (!MRI->constrainRegClass(DomReg, MRI->getRegClass(CanReg)))
return false;
LLVM_DEBUG(dbgs() << "Coalescing PTRUE: " << CanMI);
LLVM_DEBUG(dbgs() << " with: " << DomMI);
if (MutateDomPTrue) {
LLVM_DEBUG(dbgs() << " updated: " << DomMI);
DomMI->setDesc(TII->get(CanMI->getOpcode()));
DomPI.ElementSize = CanPI.ElementSize;
LLVM_DEBUG(dbgs() << " to: " << DomMI);
}
MRI->replaceRegWith(CanReg, DomReg);
MRI->clearKillFlags(DomReg);
CanMI->eraseFromParent();
// Update DomPI based on uses inherited from CanPI.
if (CanPI.SmallestUsedElementSize < DomPI.SmallestUsedElementSize)
DomPI.SmallestUsedElementSize = CanPI.SmallestUsedElementSize;
CanPI.invalidate();
return true;
}
bool AArch64PTrueCoalescingImpl::run(MachineFunction &MF) {
if (!EnablePTrueCoalescing ||
!MF.getSubtarget<AArch64Subtarget>().isSVEorStreamingSVEAvailable())
return false;
TII = static_cast<const AArch64InstrInfo *>(MF.getSubtarget().getInstrInfo());
MRI = &MF.getRegInfo();
assert(MRI->isSSA() && "Expected to be run on SSA form!");
// TODO: Until we prove candidates share the same VG definition, do not
// coalesce in functions that define VG.
if (!MRI->def_empty(AArch64::VG))
return false;
// A list of predicate setting instructions with some usage information.
SmallVector<PredicateInfo, 8> PIs;
// Build a list of predicates whose uses all have a known size.
for (MachineBasicBlock &MBB : MF)
for (MachineInstr &MI : MBB)
if (auto PI = createPredicateInfo(MI))
PIs.push_back(*PI);
LLVM_DEBUG(dbgs() << "Coalescable PTRUE candidates: " << PIs.size() << "\n");
bool Changed = false;
for (PredicateInfo &DominantPI : PIs) {
if (!DominantPI.isValid())
continue;
for (PredicateInfo &CandidatePI : PIs) {
if (!CandidatePI.isValid())
continue;
Changed |= tryCoalesce(DominantPI, CandidatePI);
}
}
return Changed;
}
bool AArch64PTrueCoalescingLegacy::runOnMachineFunction(MachineFunction &MF) {
MachineDominatorTree &MDT =
getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
return AArch64PTrueCoalescingImpl(MDT).run(MF);
}
FunctionPass *llvm::createAArch64PTrueCoalescingLegacyPass() {
return new AArch64PTrueCoalescingLegacy();
}
PreservedAnalyses
AArch64PTrueCoalescingPass::run(MachineFunction &MF,
MachineFunctionAnalysisManager &MFAM) {
MachineDominatorTree &MDT = MFAM.getResult<MachineDominatorTreeAnalysis>(MF);
const bool Changed = AArch64PTrueCoalescingImpl(MDT).run(MF);
if (!Changed)
return PreservedAnalyses::all();
auto PA = getMachineFunctionPassPreservedAnalyses();
PA.preserveSet<CFGAnalyses>();
return PA;
}