blob: 31025ca1a7857d4106f4fcbe5e1d0f23c8af8724 [file]
//===-- SIPostRA16BitMovFolding.cpp ------------------------------------===//
//
// 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
/// This pass performs the post RA 16bit Mov folding
///
//===----------------------------------------------------------------------===//
#include "AMDGPU.h"
#include "GCNSubtarget.h"
#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/MachinePostDominators.h"
#include "llvm/CodeGen/TargetSchedule.h"
#include "llvm/Support/BranchProbability.h"
using namespace llvm;
#define DEBUG_TYPE "si-post-ra-16bit-mov-folding"
namespace {
class SIPostRA16BitMovFolding {
private:
const SIInstrInfo *TII = nullptr;
const SIRegisterInfo *TRI = nullptr;
void getMovB16Info(const MachineInstr &MI, const SIRegisterInfo *TRI,
MCRegister &SrcReg16, bool &SrcIsVGPR,
MCRegister &SrcReg32, bool &SrcIsHi, bool &SrcIsImm,
int64_t &ImmVal) const;
bool mergeSingleMovB16Pair(MachineInstr &Lo, MachineInstr &Hi,
bool IsHiFirst) const;
bool mergeMovB16Pairs(MachineFunction &MF) const;
public:
bool run(MachineFunction &MF);
};
class SIPostRA16BitMovFoldingLegacy : public MachineFunctionPass {
public:
static char ID;
SIPostRA16BitMovFoldingLegacy() : MachineFunctionPass(ID) {}
StringRef getPassName() const override {
return "SI post-RA 16bit Mov Folding";
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
MachineFunctionPass::getAnalysisUsage(AU);
}
bool runOnMachineFunction(MachineFunction &MF) override {
return SIPostRA16BitMovFolding().run(MF);
}
};
} // End anonymous namespace.
INITIALIZE_PASS(SIPostRA16BitMovFoldingLegacy, DEBUG_TYPE,
"SI Post RA 16bit Mov Folding", false, false)
char SIPostRA16BitMovFoldingLegacy::ID = 0;
char &llvm::SIPostRA16BitMovFoldingLegacyID = SIPostRA16BitMovFoldingLegacy::ID;
// Helper: extract the src operand and whether it is from the hi16 half.
// Post-RA, both V_MOV_B16_t16_e32 and V_MOV_B16_t16_e64 use VGPR_16 dst
// physical registers whose encoding already encodes hi/lo (IS_HI16 bit).
void SIPostRA16BitMovFolding::getMovB16Info(
const MachineInstr &MI, const SIRegisterInfo *TRI, MCRegister &SrcReg16,
bool &SrcIsVGPR, MCRegister &SrcReg32, bool &SrcIsHi, bool &SrcIsImm,
int64_t &ImmVal) const {
SrcIsImm = false;
SrcIsHi = false;
SrcIsVGPR = false;
SrcReg16 = MCRegister();
SrcReg32 = MCRegister();
const MachineOperand *SrcOp = TII->getNamedOperand(MI, AMDGPU::OpName::src0);
if (SrcOp->isImm()) {
SrcIsImm = true;
ImmVal = SrcOp->getImm();
return;
}
SrcReg16 = SrcOp->getReg().asMCReg();
SrcIsVGPR = AMDGPU::VGPR_16RegClass.contains(SrcReg16);
if (SrcIsVGPR) {
SrcIsHi = AMDGPU::isHi16Reg(SrcReg16, *TRI);
SrcReg32 = TRI->get32BitRegister(SrcReg16);
} else {
SrcIsHi = false;
SrcReg32 = SrcReg16;
}
}
// clang-format off
// Try to merge a pair of v_mov_b16 instructions targeting the lo16 and hi16
// halves of the same VGPR into a single 32-bit instruction.
//
// Caller guarantee the pair to be two v_mov_b16 and targets the same dst32
//
// Patterns:
// v_mov_b16 v0.h, 0 v_mov_b16 v0.l, v2.l/s2 => v_and_b32 v0,0xffff,v2/s2
// v_mov_b16 v0.h, 0 v_mov_b16 v0.l, v2.h => v_lshrrev_b32 v0,16,v2
// v_mov_b16 v0.l, 0 v_mov_b16 v0.h, v2.l/s2 => v_lshlrev_b32 v0,16,v2/s2
// v_mov_b16 v0.l, 0 v_mov_b16 v0.h, v2.h => v_and_b32 v0,0xffff0000,v2
// v_mov_b16 v0.l, v.x/s v_mov_b16 v0.h, v.y/s => v_perm_b32_e64 v0, v.x/s, v.y/s, mask
// clang-format on
bool SIPostRA16BitMovFolding::mergeSingleMovB16Pair(MachineInstr &Lo,
MachineInstr &Hi,
bool IsHiFirst) const {
// Lo and Hi share the same Dst32
MCRegister LoDst = Lo.getOperand(0).getReg().asMCReg();
MCRegister HiDst = Hi.getOperand(0).getReg().asMCReg();
MCRegister Dst32 = TRI->get32BitRegister(LoDst);
// Extract source info for Lo and Hi.
MCRegister LoSrc16, LoSrc32, HiSrc16, HiSrc32;
bool LoSrcIsHi, HiSrcIsHi, LoSrcIsImm, HiSrcIsImm, LoSrcIsVGPR, HiSrcIsVGPR;
int64_t LoImm = 0, HiImm = 0;
getMovB16Info(Lo, TRI, LoSrc16, LoSrcIsVGPR, LoSrc32, LoSrcIsHi, LoSrcIsImm,
LoImm);
getMovB16Info(Hi, TRI, HiSrc16, HiSrcIsVGPR, HiSrc32, HiSrcIsHi, HiSrcIsImm,
HiImm);
MachineInstr &FirstMI = IsHiFirst ? Hi : Lo;
MachineInstr &SecondMI = IsHiFirst ? Lo : Hi;
// Data Conflict counter
MachineBasicBlock::iterator UpperBound = SecondMI.getIterator();
MachineBasicBlock::iterator LowerBound = FirstMI.getIterator();
unsigned LoopCnt = 0, UpperBoundCnt = UINT_MAX, LowerBoundCnt = 0;
MachineBasicBlock &MBB = *Lo.getParent();
// Check that between Lo and Hi, there are no instructions that:
// - modify Dst32
// - modify LoSrc16 or HiSrc16 depending on order (data dependency)
// We scan from the instruction after the first mov up to (but not including)
// the second mov.
MCRegister FirstSrc16 = IsHiFirst ? HiSrc16 : LoSrc16;
MCRegister FirstDst16 = IsHiFirst ? HiDst : LoDst;
MCRegister SecondSrc16 = IsHiFirst ? LoSrc16 : HiSrc16;
MCRegister SecondDst16 = IsHiFirst ? LoDst : HiDst;
for (MachineInstr &Scan :
drop_begin(make_range(FirstMI.getIterator(), SecondMI.getIterator()))) {
if (Scan.isDebugInstr())
continue;
if (Scan.modifiesRegister(Dst32, TRI))
return false;
assert(!Scan.modifiesRegister(AMDGPU::EXEC, TRI) &&
"Expect no write on EXEC!");
LoopCnt++;
if (LoopCnt < UpperBoundCnt &&
((FirstSrc16 && Scan.modifiesRegister(FirstSrc16, TRI)) ||
Scan.readsRegister(FirstDst16, TRI))) {
UpperBound = Scan.getIterator();
UpperBoundCnt = LoopCnt;
}
if (LoopCnt > LowerBoundCnt &&
((SecondSrc16 && Scan.modifiesRegister(SecondSrc16, TRI)) ||
Scan.readsRegister(SecondDst16, TRI))) {
LowerBound = Scan.getIterator();
LowerBoundCnt = LoopCnt;
}
}
// No spot maintains data dependency
if (LowerBoundCnt >= UpperBoundCnt)
return false;
// Insert MI before selected. Any spots between (LowerBound, UpperBound] would
// work
MachineInstr &Selected = *UpperBound;
const DebugLoc &DL = Selected.getDebugLoc();
// Now match patterns and emit the replacement instruction.
// Insert on Selected MI location, then remove both mov.
// Pattern: v_mov_b16 v0.l, v2.x/s2 + v_mov_b16 v0.h, v3.y/s3
// => v_perm_b32_e64 v0,v3.y/s3,v2.x/s2, mask
if (!HiSrcIsImm && !LoSrcIsImm) {
// Violate constant bus restriction
if (!LoSrcIsVGPR && !HiSrcIsVGPR && HiSrc32 != LoSrc32)
return false;
unsigned MaskHiSrc = HiSrcIsHi ? 0x0706 : 0x0504;
unsigned MaskLoSrc = LoSrcIsHi ? 0x0302 : 0x0100;
BuildMI(MBB, Selected, DL, TII->get(AMDGPU::V_PERM_B32_e64), Dst32)
.addReg(HiSrc32)
.addReg(LoSrc32)
.addImm((MaskHiSrc << 16) | MaskLoSrc);
Lo.eraseFromParent();
Hi.eraseFromParent();
return true;
}
bool Usevop2 =
AMDGPU::VGPR_32_Lo128RegClass.contains(Dst32) &&
(LoSrcIsImm ||
(LoSrcIsVGPR && AMDGPU::VGPR_32_Lo128RegClass.contains(LoSrc32))) &&
(HiSrcIsImm ||
(HiSrcIsVGPR && AMDGPU::VGPR_32_Lo128RegClass.contains(HiSrc32)));
// Pattern: v_mov_b16 v0.h, 0 + v_mov_b16 v0.l, v2.l/s2
// => v_and_b32 v0, 0x0000ffff, v2/s2
if (HiSrcIsImm && HiImm == 0 && !LoSrcIsImm && !LoSrcIsHi) {
BuildMI(MBB, Selected, DL,
TII->get(Usevop2 ? AMDGPU::V_AND_B32_e32 : AMDGPU::V_AND_B32_e64),
Dst32)
.addImm(0x0000ffff)
.addReg(LoSrc32);
Lo.eraseFromParent();
Hi.eraseFromParent();
return true;
}
// Pattern: v_mov_b16 v0.h, 0 + v_mov_b16 v0.l, v2.h
// => v_lshrrev_b32 v0, 16, v2
if (HiSrcIsImm && HiImm == 0 && !LoSrcIsImm && LoSrcIsHi) {
BuildMI(MBB, Selected, DL,
TII->get(Usevop2 ? AMDGPU::V_LSHRREV_B32_e32
: AMDGPU::V_LSHRREV_B32_e64),
Dst32)
.addImm(16)
.addReg(LoSrc32);
Lo.eraseFromParent();
Hi.eraseFromParent();
return true;
}
// Pattern: v_mov_b16 v0.l, 0 + v_mov_b16 v0.h, v2.l/s2
// => v_lshlrev_b32 v0, 16, v2/s2
if (LoSrcIsImm && LoImm == 0 && !HiSrcIsImm && !HiSrcIsHi) {
BuildMI(MBB, Selected, DL,
TII->get(Usevop2 ? AMDGPU::V_LSHLREV_B32_e32
: AMDGPU::V_LSHLREV_B32_e64),
Dst32)
.addImm(16)
.addReg(HiSrc32);
Lo.eraseFromParent();
Hi.eraseFromParent();
return true;
}
// Pattern: v_mov_b16 v0.l, 0 + v_mov_b16 v0.h, v2.h
// => v_and_b32 v0, 0xffff0000, v2
if (LoSrcIsImm && LoImm == 0 && !HiSrcIsImm && HiSrcIsHi) {
BuildMI(MBB, Selected, DL,
TII->get(Usevop2 ? AMDGPU::V_AND_B32_e32 : AMDGPU::V_AND_B32_e64),
Dst32)
.addImm(0xffff0000)
.addReg(HiSrc32);
Lo.eraseFromParent();
Hi.eraseFromParent();
return true;
}
return false;
}
// Merge pairs of v_mov_b16 targeting the lo16 and hi16 halves of the same
// VGPR into a single 32-bit instruction (true16 mode only).
bool SIPostRA16BitMovFolding::mergeMovB16Pairs(MachineFunction &MF) const {
bool Changed = false;
for (MachineBasicBlock &MBB : MF) {
// Map from 32-bit VGPR to the pending v_mov_b16 and its age.
// Tracks how many non-mov-b16 instructions have passed since the
// 16-bit write using a fixed size circular buffer
struct Pending {
MCRegister Dst32;
MachineInstr *MI;
unsigned IsHi;
};
// Search window size
const unsigned ScanLimit = 16;
std::array<Pending, ScanLimit> CirBuf = {};
SmallDenseMap<MCRegister, unsigned> PendingWrites;
unsigned Head = 0;
for (MachineInstr &MI : make_early_inc_range(MBB)) {
if (MI.isDebugInstr())
continue;
unsigned Opc = MI.getOpcode();
bool IsMovB16 = (Opc == AMDGPU::V_MOV_B16_t16_e32 ||
Opc == AMDGPU::V_MOV_B16_t16_e64);
if (++Head == ScanLimit)
Head = 0;
// Expire the last one
PendingWrites.erase(CirBuf[Head].Dst32);
if (!IsMovB16) {
CirBuf[Head] = {MCRegister(), nullptr, false};
continue;
}
LLVM_DEBUG(dbgs() << "Checking MI:" << MI << "\n");
MCRegister DstReg = MI.getOperand(0).getReg().asMCReg();
bool DstIsHi = AMDGPU::isHi16Reg(DstReg, *TRI);
MCRegister Dst32 = TRI->get32BitRegister(DstReg);
// Insert new one
CirBuf[Head] = {Dst32, &MI, DstIsHi};
auto [It, Inserted] = PendingWrites.insert({Dst32, Head});
if (!Inserted) {
if (CirBuf[It->second].IsHi == DstIsHi) {
It->second = Head;
continue;
}
// Look for a matching pending write.
MachineInstr &LoMI = !DstIsHi ? MI : *CirBuf[It->second].MI;
MachineInstr &HiMI = DstIsHi ? MI : *CirBuf[It->second].MI;
bool IsHiFirst = CirBuf[It->second].IsHi;
if (mergeSingleMovB16Pair(LoMI, HiMI, IsHiFirst)) {
Changed = true;
PendingWrites.erase(It);
} else {
It->second = Head;
}
}
}
}
return Changed;
}
PreservedAnalyses
llvm::SIPostRA16BitMovFoldingPass::run(MachineFunction &MF,
MachineFunctionAnalysisManager &MFAM) {
SIPostRA16BitMovFolding().run(MF);
return PreservedAnalyses::all();
}
bool SIPostRA16BitMovFolding::run(MachineFunction &MF) {
const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
TRI = MF.getSubtarget<GCNSubtarget>().getRegisterInfo();
TII = ST.getInstrInfo();
bool Changed = false;
// Try merge B16 Pair in true16 mode
if (ST.useRealTrue16Insts())
Changed |= mergeMovB16Pairs(MF);
return Changed;
}