2016-04-30 08:23:06 +08:00
|
|
|
//===-- GCNHazardRecognizers.cpp - GCN Hazard Recognizer Impls ------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file implements hazard recognizers for scheduling on GCN processors.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2017-01-21 01:52:16 +08:00
|
|
|
#include "GCNHazardRecognizer.h"
|
2017-06-06 19:49:48 +08:00
|
|
|
#include "AMDGPUSubtarget.h"
|
2017-01-21 01:52:16 +08:00
|
|
|
#include "SIDefines.h"
|
2016-04-30 08:23:06 +08:00
|
|
|
#include "SIInstrInfo.h"
|
2017-01-21 01:52:16 +08:00
|
|
|
#include "SIRegisterInfo.h"
|
AMDGPU: Remove #include "MCTargetDesc/AMDGPUMCTargetDesc.h" from common headers
Summary:
MCTargetDesc/AMDGPUMCTargetDesc.h contains enums for all the instuction
and register defintions, which are huge so we only want to include
them where needed.
This will also make it easier if we want to split the R600 and GCN
definitions into separate tablegenerated files.
I was unable to remove AMDGPUMCTargetDesc.h from SIMachineFunctionInfo.h
because it uses some enums from the header to initialize default values
for the SIMachineFunction class, so I ended up having to remove includes of
SIMachineFunctionInfo.h from headers too.
Reviewers: arsenm, nhaehnle
Reviewed By: nhaehnle
Subscribers: MatzeB, kzhuravl, wdng, yaxunl, dstuttard, tpr, t-tye, javed.absar, llvm-commits
Differential Revision: https://reviews.llvm.org/D46272
llvm-svn: 332930
2018-05-22 10:03:23 +08:00
|
|
|
#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
|
2017-01-21 01:52:16 +08:00
|
|
|
#include "Utils/AMDGPUBaseInfo.h"
|
|
|
|
#include "llvm/ADT/iterator_range.h"
|
|
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
|
|
|
#include "llvm/CodeGen/MachineOperand.h"
|
2016-04-30 08:23:06 +08:00
|
|
|
#include "llvm/CodeGen/ScheduleDAG.h"
|
2017-01-21 01:52:16 +08:00
|
|
|
#include "llvm/MC/MCInstrDesc.h"
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cassert>
|
|
|
|
#include <limits>
|
|
|
|
#include <set>
|
|
|
|
#include <vector>
|
2016-04-30 08:23:06 +08:00
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Hazard Recoginizer Implementation
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
GCNHazardRecognizer::GCNHazardRecognizer(const MachineFunction &MF) :
|
|
|
|
CurrCycleInstr(nullptr),
|
2016-06-24 14:30:11 +08:00
|
|
|
MF(MF),
|
2017-03-18 05:36:28 +08:00
|
|
|
ST(MF.getSubtarget<SISubtarget>()),
|
2017-11-17 12:18:24 +08:00
|
|
|
TII(*ST.getInstrInfo()),
|
|
|
|
TRI(TII.getRegisterInfo()),
|
|
|
|
ClauseUses(TRI.getNumRegUnits()),
|
|
|
|
ClauseDefs(TRI.getNumRegUnits()) {
|
2016-04-30 08:23:06 +08:00
|
|
|
MaxLookAhead = 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GCNHazardRecognizer::EmitInstruction(SUnit *SU) {
|
|
|
|
EmitInstruction(SU->getInstr());
|
|
|
|
}
|
|
|
|
|
|
|
|
void GCNHazardRecognizer::EmitInstruction(MachineInstr *MI) {
|
|
|
|
CurrCycleInstr = MI;
|
|
|
|
}
|
|
|
|
|
2016-10-08 07:42:48 +08:00
|
|
|
static bool isDivFMas(unsigned Opcode) {
|
|
|
|
return Opcode == AMDGPU::V_DIV_FMAS_F32 || Opcode == AMDGPU::V_DIV_FMAS_F64;
|
|
|
|
}
|
|
|
|
|
2016-10-15 08:58:14 +08:00
|
|
|
static bool isSGetReg(unsigned Opcode) {
|
|
|
|
return Opcode == AMDGPU::S_GETREG_B32;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isSSetReg(unsigned Opcode) {
|
|
|
|
return Opcode == AMDGPU::S_SETREG_B32 || Opcode == AMDGPU::S_SETREG_IMM32_B32;
|
|
|
|
}
|
|
|
|
|
2016-10-28 07:42:29 +08:00
|
|
|
static bool isRWLane(unsigned Opcode) {
|
|
|
|
return Opcode == AMDGPU::V_READLANE_B32 || Opcode == AMDGPU::V_WRITELANE_B32;
|
|
|
|
}
|
|
|
|
|
2016-10-28 07:50:21 +08:00
|
|
|
static bool isRFE(unsigned Opcode) {
|
|
|
|
return Opcode == AMDGPU::S_RFE_B64;
|
|
|
|
}
|
|
|
|
|
2017-02-19 02:29:53 +08:00
|
|
|
static bool isSMovRel(unsigned Opcode) {
|
2017-03-18 05:36:28 +08:00
|
|
|
switch (Opcode) {
|
|
|
|
case AMDGPU::S_MOVRELS_B32:
|
|
|
|
case AMDGPU::S_MOVRELS_B64:
|
|
|
|
case AMDGPU::S_MOVRELD_B32:
|
|
|
|
case AMDGPU::S_MOVRELD_B64:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
2017-02-19 02:29:53 +08:00
|
|
|
}
|
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
static bool isSendMsgTraceDataOrGDS(const MachineInstr &MI) {
|
|
|
|
switch (MI.getOpcode()) {
|
|
|
|
case AMDGPU::S_SENDMSG:
|
|
|
|
case AMDGPU::S_SENDMSGHALT:
|
|
|
|
case AMDGPU::S_TTRACEDATA:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
// TODO: GDS
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 07:50:21 +08:00
|
|
|
static unsigned getHWReg(const SIInstrInfo *TII, const MachineInstr &RegInstr) {
|
2016-10-15 08:58:14 +08:00
|
|
|
const MachineOperand *RegOp = TII->getNamedOperand(RegInstr,
|
|
|
|
AMDGPU::OpName::simm16);
|
|
|
|
return RegOp->getImm() & AMDGPU::Hwreg::ID_MASK_;
|
|
|
|
}
|
|
|
|
|
2016-04-30 08:23:06 +08:00
|
|
|
ScheduleHazardRecognizer::HazardType
|
|
|
|
GCNHazardRecognizer::getHazardType(SUnit *SU, int Stalls) {
|
|
|
|
MachineInstr *MI = SU->getInstr();
|
|
|
|
|
2016-05-02 22:48:03 +08:00
|
|
|
if (SIInstrInfo::isSMRD(*MI) && checkSMRDHazards(MI) > 0)
|
2016-04-30 08:23:06 +08:00
|
|
|
return NoopHazard;
|
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
// FIXME: Should flat be considered vmem?
|
|
|
|
if ((SIInstrInfo::isVMEM(*MI) ||
|
|
|
|
SIInstrInfo::isFLAT(*MI))
|
|
|
|
&& checkVMEMHazards(MI) > 0)
|
2016-04-30 08:23:06 +08:00
|
|
|
return NoopHazard;
|
|
|
|
|
2016-10-28 07:05:31 +08:00
|
|
|
if (SIInstrInfo::isVALU(*MI) && checkVALUHazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2016-05-03 00:23:09 +08:00
|
|
|
if (SIInstrInfo::isDPP(*MI) && checkDPPHazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2016-10-08 07:42:48 +08:00
|
|
|
if (isDivFMas(MI->getOpcode()) && checkDivFMasHazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2016-10-28 07:42:29 +08:00
|
|
|
if (isRWLane(MI->getOpcode()) && checkRWLaneHazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2016-10-15 08:58:14 +08:00
|
|
|
if (isSGetReg(MI->getOpcode()) && checkGetRegHazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2016-10-28 04:39:09 +08:00
|
|
|
if (isSSetReg(MI->getOpcode()) && checkSetRegHazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2016-10-28 07:50:21 +08:00
|
|
|
if (isRFE(MI->getOpcode()) && checkRFEHazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
if (ST.hasReadM0MovRelInterpHazard() &&
|
|
|
|
(TII.isVINTRP(*MI) || isSMovRel(MI->getOpcode())) &&
|
|
|
|
checkReadM0Hazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
|
|
|
if (ST.hasReadM0SendMsgHazard() && isSendMsgTraceDataOrGDS(*MI) &&
|
2017-02-19 02:29:53 +08:00
|
|
|
checkReadM0Hazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2017-12-08 04:34:25 +08:00
|
|
|
if (MI->isInlineAsm() && checkInlineAsmHazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2017-02-19 02:29:53 +08:00
|
|
|
if (checkAnyInstHazards(MI) > 0)
|
|
|
|
return NoopHazard;
|
|
|
|
|
2016-04-30 08:23:06 +08:00
|
|
|
return NoHazard;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned GCNHazardRecognizer::PreEmitNoops(SUnit *SU) {
|
|
|
|
return PreEmitNoops(SU->getInstr());
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned GCNHazardRecognizer::PreEmitNoops(MachineInstr *MI) {
|
2017-02-19 02:29:53 +08:00
|
|
|
int WaitStates = std::max(0, checkAnyInstHazards(MI));
|
|
|
|
|
2016-05-02 22:48:03 +08:00
|
|
|
if (SIInstrInfo::isSMRD(*MI))
|
2017-02-19 02:29:53 +08:00
|
|
|
return std::max(WaitStates, checkSMRDHazards(MI));
|
2016-04-30 08:23:06 +08:00
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
if (SIInstrInfo::isVALU(*MI))
|
|
|
|
WaitStates = std::max(WaitStates, checkVALUHazards(MI));
|
2016-04-30 08:23:06 +08:00
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
if (SIInstrInfo::isVMEM(*MI) || SIInstrInfo::isFLAT(*MI))
|
|
|
|
WaitStates = std::max(WaitStates, checkVMEMHazards(MI));
|
2016-05-03 00:23:09 +08:00
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
if (SIInstrInfo::isDPP(*MI))
|
|
|
|
WaitStates = std::max(WaitStates, checkDPPHazards(MI));
|
2016-10-28 07:05:31 +08:00
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
if (isDivFMas(MI->getOpcode()))
|
|
|
|
WaitStates = std::max(WaitStates, checkDivFMasHazards(MI));
|
2016-10-28 07:05:31 +08:00
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
if (isRWLane(MI->getOpcode()))
|
|
|
|
WaitStates = std::max(WaitStates, checkRWLaneHazards(MI));
|
2016-10-08 07:42:48 +08:00
|
|
|
|
2017-12-08 04:34:25 +08:00
|
|
|
if (MI->isInlineAsm())
|
|
|
|
return std::max(WaitStates, checkInlineAsmHazards(MI));
|
|
|
|
|
2016-10-15 08:58:14 +08:00
|
|
|
if (isSGetReg(MI->getOpcode()))
|
2017-02-19 02:29:53 +08:00
|
|
|
return std::max(WaitStates, checkGetRegHazards(MI));
|
2016-10-15 08:58:14 +08:00
|
|
|
|
2016-10-28 04:39:09 +08:00
|
|
|
if (isSSetReg(MI->getOpcode()))
|
2017-02-19 02:29:53 +08:00
|
|
|
return std::max(WaitStates, checkSetRegHazards(MI));
|
2016-10-28 04:39:09 +08:00
|
|
|
|
2016-10-28 07:50:21 +08:00
|
|
|
if (isRFE(MI->getOpcode()))
|
2017-02-19 02:29:53 +08:00
|
|
|
return std::max(WaitStates, checkRFEHazards(MI));
|
2016-10-28 07:50:21 +08:00
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
if (ST.hasReadM0MovRelInterpHazard() && (TII.isVINTRP(*MI) ||
|
|
|
|
isSMovRel(MI->getOpcode())))
|
|
|
|
return std::max(WaitStates, checkReadM0Hazards(MI));
|
|
|
|
|
|
|
|
if (ST.hasReadM0SendMsgHazard() && isSendMsgTraceDataOrGDS(*MI))
|
2017-02-19 02:29:53 +08:00
|
|
|
return std::max(WaitStates, checkReadM0Hazards(MI));
|
|
|
|
|
|
|
|
return WaitStates;
|
2016-04-30 08:23:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void GCNHazardRecognizer::EmitNoop() {
|
|
|
|
EmittedInstrs.push_front(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GCNHazardRecognizer::AdvanceCycle() {
|
|
|
|
// When the scheduler detects a stall, it will call AdvanceCycle() without
|
|
|
|
// emitting any instructions.
|
|
|
|
if (!CurrCycleInstr)
|
|
|
|
return;
|
|
|
|
|
2017-03-18 05:36:28 +08:00
|
|
|
unsigned NumWaitStates = TII.getNumWaitStates(*CurrCycleInstr);
|
2016-04-30 08:23:06 +08:00
|
|
|
|
|
|
|
// Keep track of emitted instructions
|
|
|
|
EmittedInstrs.push_front(CurrCycleInstr);
|
|
|
|
|
|
|
|
// Add a nullptr for each additional wait state after the first. Make sure
|
|
|
|
// not to add more than getMaxLookAhead() items to the list, since we
|
|
|
|
// truncate the list to that size right after this loop.
|
|
|
|
for (unsigned i = 1, e = std::min(NumWaitStates, getMaxLookAhead());
|
|
|
|
i < e; ++i) {
|
|
|
|
EmittedInstrs.push_front(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// getMaxLookahead() is the largest number of wait states we will ever need
|
|
|
|
// to insert, so there is no point in keeping track of more than that many
|
|
|
|
// wait states.
|
|
|
|
EmittedInstrs.resize(getMaxLookAhead());
|
|
|
|
|
|
|
|
CurrCycleInstr = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GCNHazardRecognizer::RecedeCycle() {
|
|
|
|
llvm_unreachable("hazard recognizer does not support bottom-up scheduling.");
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Helper Functions
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2016-10-28 07:05:31 +08:00
|
|
|
int GCNHazardRecognizer::getWaitStatesSince(
|
|
|
|
function_ref<bool(MachineInstr *)> IsHazard) {
|
2017-09-02 00:56:32 +08:00
|
|
|
int WaitStates = 0;
|
2016-04-30 08:23:06 +08:00
|
|
|
for (MachineInstr *MI : EmittedInstrs) {
|
2017-09-02 00:56:32 +08:00
|
|
|
if (MI) {
|
|
|
|
if (IsHazard(MI))
|
|
|
|
return WaitStates;
|
|
|
|
|
|
|
|
unsigned Opcode = MI->getOpcode();
|
2017-09-06 21:50:13 +08:00
|
|
|
if (Opcode == AMDGPU::DBG_VALUE || Opcode == AMDGPU::IMPLICIT_DEF ||
|
|
|
|
Opcode == AMDGPU::INLINEASM)
|
2017-09-02 00:56:32 +08:00
|
|
|
continue;
|
|
|
|
}
|
2016-04-30 08:23:06 +08:00
|
|
|
++WaitStates;
|
|
|
|
}
|
|
|
|
return std::numeric_limits<int>::max();
|
|
|
|
}
|
|
|
|
|
2016-10-28 07:05:31 +08:00
|
|
|
int GCNHazardRecognizer::getWaitStatesSinceDef(
|
|
|
|
unsigned Reg, function_ref<bool(MachineInstr *)> IsHazardDef) {
|
|
|
|
const SIRegisterInfo *TRI = ST.getRegisterInfo();
|
|
|
|
|
|
|
|
auto IsHazardFn = [IsHazardDef, TRI, Reg] (MachineInstr *MI) {
|
|
|
|
return IsHazardDef(MI) && MI->modifiesRegister(Reg, TRI);
|
|
|
|
};
|
|
|
|
|
|
|
|
return getWaitStatesSince(IsHazardFn);
|
|
|
|
}
|
|
|
|
|
2016-10-15 08:58:14 +08:00
|
|
|
int GCNHazardRecognizer::getWaitStatesSinceSetReg(
|
|
|
|
function_ref<bool(MachineInstr *)> IsHazard) {
|
2016-10-28 07:05:31 +08:00
|
|
|
auto IsHazardFn = [IsHazard] (MachineInstr *MI) {
|
|
|
|
return isSSetReg(MI->getOpcode()) && IsHazard(MI);
|
|
|
|
};
|
|
|
|
|
|
|
|
return getWaitStatesSince(IsHazardFn);
|
2016-10-15 08:58:14 +08:00
|
|
|
}
|
|
|
|
|
2016-04-30 08:23:06 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// No-op Hazard Detection
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2017-11-17 12:18:24 +08:00
|
|
|
static void addRegUnits(const SIRegisterInfo &TRI,
|
|
|
|
BitVector &BV, unsigned Reg) {
|
|
|
|
for (MCRegUnitIterator RUI(Reg, &TRI); RUI.isValid(); ++RUI)
|
|
|
|
BV.set(*RUI);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addRegsToSet(const SIRegisterInfo &TRI,
|
|
|
|
iterator_range<MachineInstr::const_mop_iterator> Ops,
|
|
|
|
BitVector &Set) {
|
2016-05-03 01:39:06 +08:00
|
|
|
for (const MachineOperand &Op : Ops) {
|
|
|
|
if (Op.isReg())
|
2017-11-17 12:18:24 +08:00
|
|
|
addRegUnits(TRI, Set, Op.getReg());
|
2016-05-03 01:39:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-17 12:18:24 +08:00
|
|
|
void GCNHazardRecognizer::addClauseInst(const MachineInstr &MI) {
|
|
|
|
// XXX: Do we need to worry about implicit operands
|
|
|
|
addRegsToSet(TRI, MI.defs(), ClauseDefs);
|
|
|
|
addRegsToSet(TRI, MI.uses(), ClauseUses);
|
|
|
|
}
|
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
int GCNHazardRecognizer::checkSoftClauseHazards(MachineInstr *MEM) {
|
2017-11-17 12:18:24 +08:00
|
|
|
// SMEM soft clause are only present on VI+, and only matter if xnack is
|
|
|
|
// enabled.
|
|
|
|
if (!ST.isXNACKEnabled())
|
2016-05-03 01:39:06 +08:00
|
|
|
return 0;
|
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
bool IsSMRD = TII.isSMRD(*MEM);
|
|
|
|
|
2017-11-17 12:18:24 +08:00
|
|
|
resetClause();
|
|
|
|
|
2016-05-03 01:39:06 +08:00
|
|
|
// A soft-clause is any group of consecutive SMEM instructions. The
|
|
|
|
// instructions in this group may return out of order and/or may be
|
|
|
|
// replayed (i.e. the same instruction issued more than once).
|
|
|
|
//
|
|
|
|
// In order to handle these situations correctly we need to make sure
|
|
|
|
// that when a clause has more than one instruction, no instruction in the
|
|
|
|
// clause writes to a register that is read another instruction in the clause
|
|
|
|
// (including itself). If we encounter this situaion, we need to break the
|
|
|
|
// clause by inserting a non SMEM instruction.
|
|
|
|
|
|
|
|
for (MachineInstr *MI : EmittedInstrs) {
|
|
|
|
// When we hit a non-SMEM instruction then we have passed the start of the
|
|
|
|
// clause and we can stop.
|
2017-11-18 05:35:32 +08:00
|
|
|
if (!MI)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (IsSMRD != SIInstrInfo::isSMRD(*MI))
|
2016-05-03 01:39:06 +08:00
|
|
|
break;
|
|
|
|
|
2017-11-17 12:18:24 +08:00
|
|
|
addClauseInst(*MI);
|
2016-05-03 01:39:06 +08:00
|
|
|
}
|
|
|
|
|
2017-11-17 12:18:24 +08:00
|
|
|
if (ClauseDefs.none())
|
2016-05-03 01:39:06 +08:00
|
|
|
return 0;
|
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
// We need to make sure not to put loads and stores in the same clause if they
|
|
|
|
// use the same address. For now, just start a new clause whenever we see a
|
|
|
|
// store.
|
|
|
|
if (MEM->mayStore())
|
2016-05-03 01:39:06 +08:00
|
|
|
return 1;
|
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
addClauseInst(*MEM);
|
2016-05-03 01:39:06 +08:00
|
|
|
|
|
|
|
// If the set of defs and uses intersect then we cannot add this instruction
|
|
|
|
// to the clause, so we have a hazard.
|
2017-11-17 12:18:24 +08:00
|
|
|
return ClauseDefs.anyCommon(ClauseUses) ? 1 : 0;
|
2016-05-03 01:39:06 +08:00
|
|
|
}
|
|
|
|
|
2016-04-30 08:23:06 +08:00
|
|
|
int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) {
|
2016-06-24 14:30:11 +08:00
|
|
|
const SISubtarget &ST = MF.getSubtarget<SISubtarget>();
|
2016-05-03 01:39:06 +08:00
|
|
|
int WaitStatesNeeded = 0;
|
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
WaitStatesNeeded = checkSoftClauseHazards(SMRD);
|
2016-04-30 08:23:06 +08:00
|
|
|
|
|
|
|
// This SMRD hazard only affects SI.
|
2016-06-24 14:30:11 +08:00
|
|
|
if (ST.getGeneration() != SISubtarget::SOUTHERN_ISLANDS)
|
2016-05-03 01:39:06 +08:00
|
|
|
return WaitStatesNeeded;
|
2016-04-30 08:23:06 +08:00
|
|
|
|
|
|
|
// A read of an SGPR by SMRD instruction requires 4 wait states when the
|
|
|
|
// SGPR was written by a VALU instruction.
|
|
|
|
int SmrdSgprWaitStates = 4;
|
2017-03-18 05:36:28 +08:00
|
|
|
auto IsHazardDefFn = [this] (MachineInstr *MI) { return TII.isVALU(*MI); };
|
2017-10-26 22:43:02 +08:00
|
|
|
auto IsBufferHazardDefFn = [this] (MachineInstr *MI) { return TII.isSALU(*MI); };
|
|
|
|
|
2017-11-17 12:18:26 +08:00
|
|
|
bool IsBufferSMRD = TII.isBufferSMRD(*SMRD);
|
2016-04-30 08:23:06 +08:00
|
|
|
|
|
|
|
for (const MachineOperand &Use : SMRD->uses()) {
|
|
|
|
if (!Use.isReg())
|
|
|
|
continue;
|
|
|
|
int WaitStatesNeededForUse =
|
|
|
|
SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn);
|
|
|
|
WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
|
2017-10-26 22:43:02 +08:00
|
|
|
|
|
|
|
// This fixes what appears to be undocumented hardware behavior in SI where
|
|
|
|
// s_mov writing a descriptor and s_buffer_load_dword reading the descriptor
|
|
|
|
// needs some number of nops in between. We don't know how many we need, but
|
|
|
|
// let's use 4. This wasn't discovered before probably because the only
|
|
|
|
// case when this happens is when we expand a 64-bit pointer into a full
|
|
|
|
// descriptor and use s_buffer_load_dword instead of s_load_dword, which was
|
|
|
|
// probably never encountered in the closed-source land.
|
|
|
|
if (IsBufferSMRD) {
|
|
|
|
int WaitStatesNeededForUse =
|
|
|
|
SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(),
|
|
|
|
IsBufferHazardDefFn);
|
|
|
|
WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
|
|
|
|
}
|
2016-04-30 08:23:06 +08:00
|
|
|
}
|
2017-10-26 22:43:02 +08:00
|
|
|
|
2016-04-30 08:23:06 +08:00
|
|
|
return WaitStatesNeeded;
|
|
|
|
}
|
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkVMEMHazards(MachineInstr* VMEM) {
|
2016-06-24 14:30:11 +08:00
|
|
|
if (ST.getGeneration() < SISubtarget::VOLCANIC_ISLANDS)
|
2016-04-30 08:23:06 +08:00
|
|
|
return 0;
|
|
|
|
|
2017-11-18 05:35:32 +08:00
|
|
|
int WaitStatesNeeded = checkSoftClauseHazards(VMEM);
|
2016-04-30 08:23:06 +08:00
|
|
|
|
|
|
|
// A read of an SGPR by a VMEM instruction requires 5 wait states when the
|
|
|
|
// SGPR was written by a VALU Instruction.
|
2017-11-18 05:35:32 +08:00
|
|
|
const int VmemSgprWaitStates = 5;
|
|
|
|
auto IsHazardDefFn = [this] (MachineInstr *MI) { return TII.isVALU(*MI); };
|
2016-04-30 08:23:06 +08:00
|
|
|
|
|
|
|
for (const MachineOperand &Use : VMEM->uses()) {
|
|
|
|
if (!Use.isReg() || TRI.isVGPR(MF.getRegInfo(), Use.getReg()))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int WaitStatesNeededForUse =
|
|
|
|
VmemSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn);
|
|
|
|
WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
|
|
|
|
}
|
|
|
|
return WaitStatesNeeded;
|
|
|
|
}
|
2016-05-03 00:23:09 +08:00
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkDPPHazards(MachineInstr *DPP) {
|
2016-06-24 14:30:11 +08:00
|
|
|
const SIRegisterInfo *TRI = ST.getRegisterInfo();
|
2017-08-04 09:09:43 +08:00
|
|
|
const SIInstrInfo *TII = ST.getInstrInfo();
|
2016-05-03 00:23:09 +08:00
|
|
|
|
2017-08-04 09:09:43 +08:00
|
|
|
// Check for DPP VGPR read after VALU VGPR write and EXEC write.
|
2016-05-03 00:23:09 +08:00
|
|
|
int DppVgprWaitStates = 2;
|
2017-08-04 09:09:43 +08:00
|
|
|
int DppExecWaitStates = 5;
|
2016-05-03 00:23:09 +08:00
|
|
|
int WaitStatesNeeded = 0;
|
2017-08-04 09:09:43 +08:00
|
|
|
auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); };
|
2016-05-03 00:23:09 +08:00
|
|
|
|
|
|
|
for (const MachineOperand &Use : DPP->uses()) {
|
|
|
|
if (!Use.isReg() || !TRI->isVGPR(MF.getRegInfo(), Use.getReg()))
|
|
|
|
continue;
|
|
|
|
int WaitStatesNeededForUse =
|
|
|
|
DppVgprWaitStates - getWaitStatesSinceDef(Use.getReg());
|
|
|
|
WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
|
|
|
|
}
|
|
|
|
|
2017-08-04 09:09:43 +08:00
|
|
|
WaitStatesNeeded = std::max(
|
|
|
|
WaitStatesNeeded,
|
|
|
|
DppExecWaitStates - getWaitStatesSinceDef(AMDGPU::EXEC, IsHazardDefFn));
|
|
|
|
|
2016-05-03 00:23:09 +08:00
|
|
|
return WaitStatesNeeded;
|
|
|
|
}
|
2016-10-08 07:42:48 +08:00
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkDivFMasHazards(MachineInstr *DivFMas) {
|
|
|
|
const SIInstrInfo *TII = ST.getInstrInfo();
|
|
|
|
|
|
|
|
// v_div_fmas requires 4 wait states after a write to vcc from a VALU
|
|
|
|
// instruction.
|
|
|
|
const int DivFMasWaitStates = 4;
|
|
|
|
auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); };
|
|
|
|
int WaitStatesNeeded = getWaitStatesSinceDef(AMDGPU::VCC, IsHazardDefFn);
|
|
|
|
|
|
|
|
return DivFMasWaitStates - WaitStatesNeeded;
|
|
|
|
}
|
2016-10-15 08:58:14 +08:00
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkGetRegHazards(MachineInstr *GetRegInstr) {
|
|
|
|
const SIInstrInfo *TII = ST.getInstrInfo();
|
|
|
|
unsigned GetRegHWReg = getHWReg(TII, *GetRegInstr);
|
|
|
|
|
|
|
|
const int GetRegWaitStates = 2;
|
|
|
|
auto IsHazardFn = [TII, GetRegHWReg] (MachineInstr *MI) {
|
|
|
|
return GetRegHWReg == getHWReg(TII, *MI);
|
|
|
|
};
|
|
|
|
int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn);
|
|
|
|
|
|
|
|
return GetRegWaitStates - WaitStatesNeeded;
|
|
|
|
}
|
2016-10-28 04:39:09 +08:00
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkSetRegHazards(MachineInstr *SetRegInstr) {
|
|
|
|
const SIInstrInfo *TII = ST.getInstrInfo();
|
|
|
|
unsigned HWReg = getHWReg(TII, *SetRegInstr);
|
|
|
|
|
|
|
|
const int SetRegWaitStates =
|
|
|
|
ST.getGeneration() <= AMDGPUSubtarget::SEA_ISLANDS ? 1 : 2;
|
|
|
|
auto IsHazardFn = [TII, HWReg] (MachineInstr *MI) {
|
|
|
|
return HWReg == getHWReg(TII, *MI);
|
|
|
|
};
|
|
|
|
int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn);
|
|
|
|
return SetRegWaitStates - WaitStatesNeeded;
|
|
|
|
}
|
2016-10-28 07:05:31 +08:00
|
|
|
|
|
|
|
int GCNHazardRecognizer::createsVALUHazard(const MachineInstr &MI) {
|
|
|
|
if (!MI.mayStore())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
const SIInstrInfo *TII = ST.getInstrInfo();
|
|
|
|
unsigned Opcode = MI.getOpcode();
|
|
|
|
const MCInstrDesc &Desc = MI.getDesc();
|
|
|
|
|
|
|
|
int VDataIdx = AMDGPU::getNamedOperandIdx(Opcode, AMDGPU::OpName::vdata);
|
|
|
|
int VDataRCID = -1;
|
|
|
|
if (VDataIdx != -1)
|
|
|
|
VDataRCID = Desc.OpInfo[VDataIdx].RegClass;
|
|
|
|
|
|
|
|
if (TII->isMUBUF(MI) || TII->isMTBUF(MI)) {
|
2016-11-16 07:55:15 +08:00
|
|
|
// There is no hazard if the instruction does not use vector regs
|
|
|
|
// (like wbinvl1)
|
|
|
|
if (VDataIdx == -1)
|
|
|
|
return -1;
|
2016-10-28 07:05:31 +08:00
|
|
|
// For MUBUF/MTBUF instructions this hazard only exists if the
|
|
|
|
// instruction is not using a register in the soffset field.
|
|
|
|
const MachineOperand *SOffset =
|
|
|
|
TII->getNamedOperand(MI, AMDGPU::OpName::soffset);
|
|
|
|
// If we have no soffset operand, then assume this field has been
|
|
|
|
// hardcoded to zero.
|
|
|
|
if (AMDGPU::getRegBitWidth(VDataRCID) > 64 &&
|
|
|
|
(!SOffset || !SOffset->isReg()))
|
|
|
|
return VDataIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
// MIMG instructions create a hazard if they don't use a 256-bit T# and
|
|
|
|
// the store size is greater than 8 bytes and they have more than two bits
|
|
|
|
// of their dmask set.
|
|
|
|
// All our MIMG definitions use a 256-bit T#, so we can skip checking for them.
|
|
|
|
if (TII->isMIMG(MI)) {
|
|
|
|
int SRsrcIdx = AMDGPU::getNamedOperandIdx(Opcode, AMDGPU::OpName::srsrc);
|
|
|
|
assert(SRsrcIdx != -1 &&
|
|
|
|
AMDGPU::getRegBitWidth(Desc.OpInfo[SRsrcIdx].RegClass) == 256);
|
2016-10-28 07:28:03 +08:00
|
|
|
(void)SRsrcIdx;
|
2016-10-28 07:05:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (TII->isFLAT(MI)) {
|
2016-11-30 03:30:44 +08:00
|
|
|
int DataIdx = AMDGPU::getNamedOperandIdx(Opcode, AMDGPU::OpName::vdata);
|
2016-10-28 07:05:31 +08:00
|
|
|
if (AMDGPU::getRegBitWidth(Desc.OpInfo[DataIdx].RegClass) > 64)
|
|
|
|
return DataIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-12-08 04:34:25 +08:00
|
|
|
int GCNHazardRecognizer::checkVALUHazardsHelper(const MachineOperand &Def,
|
|
|
|
const MachineRegisterInfo &MRI) {
|
|
|
|
// Helper to check for the hazard where VMEM instructions that store more than
|
|
|
|
// 8 bytes can have there store data over written by the next instruction.
|
|
|
|
const SIRegisterInfo *TRI = ST.getRegisterInfo();
|
|
|
|
|
|
|
|
const int VALUWaitStates = 1;
|
|
|
|
int WaitStatesNeeded = 0;
|
|
|
|
|
|
|
|
if (!TRI->isVGPR(MRI, Def.getReg()))
|
|
|
|
return WaitStatesNeeded;
|
|
|
|
unsigned Reg = Def.getReg();
|
|
|
|
auto IsHazardFn = [this, Reg, TRI] (MachineInstr *MI) {
|
|
|
|
int DataIdx = createsVALUHazard(*MI);
|
|
|
|
return DataIdx >= 0 &&
|
|
|
|
TRI->regsOverlap(MI->getOperand(DataIdx).getReg(), Reg);
|
|
|
|
};
|
|
|
|
int WaitStatesNeededForDef =
|
|
|
|
VALUWaitStates - getWaitStatesSince(IsHazardFn);
|
|
|
|
WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForDef);
|
|
|
|
|
|
|
|
return WaitStatesNeeded;
|
|
|
|
}
|
|
|
|
|
2016-10-28 07:05:31 +08:00
|
|
|
int GCNHazardRecognizer::checkVALUHazards(MachineInstr *VALU) {
|
|
|
|
// This checks for the hazard where VMEM instructions that store more than
|
|
|
|
// 8 bytes can have there store data over written by the next instruction.
|
|
|
|
if (!ST.has12DWordStoreHazard())
|
|
|
|
return 0;
|
|
|
|
|
2017-12-08 04:34:25 +08:00
|
|
|
const MachineRegisterInfo &MRI = MF.getRegInfo();
|
2016-10-28 07:05:31 +08:00
|
|
|
int WaitStatesNeeded = 0;
|
|
|
|
|
|
|
|
for (const MachineOperand &Def : VALU->defs()) {
|
2017-12-08 04:34:25 +08:00
|
|
|
WaitStatesNeeded = std::max(WaitStatesNeeded, checkVALUHazardsHelper(Def, MRI));
|
|
|
|
}
|
|
|
|
|
|
|
|
return WaitStatesNeeded;
|
|
|
|
}
|
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkInlineAsmHazards(MachineInstr *IA) {
|
|
|
|
// This checks for hazards associated with inline asm statements.
|
|
|
|
// Since inline asms can contain just about anything, we use this
|
|
|
|
// to call/leverage other check*Hazard routines. Note that
|
|
|
|
// this function doesn't attempt to address all possible inline asm
|
|
|
|
// hazards (good luck), but is a collection of what has been
|
|
|
|
// problematic thus far.
|
|
|
|
|
|
|
|
// see checkVALUHazards()
|
|
|
|
if (!ST.has12DWordStoreHazard())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
const MachineRegisterInfo &MRI = MF.getRegInfo();
|
|
|
|
int WaitStatesNeeded = 0;
|
|
|
|
|
|
|
|
for (unsigned I = InlineAsm::MIOp_FirstOperand, E = IA->getNumOperands();
|
|
|
|
I != E; ++I) {
|
|
|
|
const MachineOperand &Op = IA->getOperand(I);
|
|
|
|
if (Op.isReg() && Op.isDef()) {
|
|
|
|
WaitStatesNeeded = std::max(WaitStatesNeeded, checkVALUHazardsHelper(Op, MRI));
|
|
|
|
}
|
2016-10-28 07:05:31 +08:00
|
|
|
}
|
2017-12-08 04:34:25 +08:00
|
|
|
|
2016-10-28 07:05:31 +08:00
|
|
|
return WaitStatesNeeded;
|
|
|
|
}
|
2016-10-28 07:42:29 +08:00
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkRWLaneHazards(MachineInstr *RWLane) {
|
|
|
|
const SIInstrInfo *TII = ST.getInstrInfo();
|
|
|
|
const SIRegisterInfo *TRI = ST.getRegisterInfo();
|
2017-12-08 04:34:25 +08:00
|
|
|
const MachineRegisterInfo &MRI = MF.getRegInfo();
|
2016-10-28 07:42:29 +08:00
|
|
|
|
|
|
|
const MachineOperand *LaneSelectOp =
|
|
|
|
TII->getNamedOperand(*RWLane, AMDGPU::OpName::src1);
|
|
|
|
|
|
|
|
if (!LaneSelectOp->isReg() || !TRI->isSGPRReg(MRI, LaneSelectOp->getReg()))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
unsigned LaneSelectReg = LaneSelectOp->getReg();
|
|
|
|
auto IsHazardFn = [TII] (MachineInstr *MI) {
|
|
|
|
return TII->isVALU(*MI);
|
|
|
|
};
|
|
|
|
|
|
|
|
const int RWLaneWaitStates = 4;
|
|
|
|
int WaitStatesSince = getWaitStatesSinceDef(LaneSelectReg, IsHazardFn);
|
|
|
|
return RWLaneWaitStates - WaitStatesSince;
|
|
|
|
}
|
2016-10-28 07:50:21 +08:00
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkRFEHazards(MachineInstr *RFE) {
|
|
|
|
if (ST.getGeneration() < AMDGPUSubtarget::VOLCANIC_ISLANDS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
const SIInstrInfo *TII = ST.getInstrInfo();
|
|
|
|
|
|
|
|
const int RFEWaitStates = 1;
|
|
|
|
|
|
|
|
auto IsHazardFn = [TII] (MachineInstr *MI) {
|
|
|
|
return getHWReg(TII, *MI) == AMDGPU::Hwreg::ID_TRAPSTS;
|
|
|
|
};
|
|
|
|
int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn);
|
|
|
|
return RFEWaitStates - WaitStatesNeeded;
|
|
|
|
}
|
2017-02-19 02:29:53 +08:00
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkAnyInstHazards(MachineInstr *MI) {
|
2018-05-09 10:42:00 +08:00
|
|
|
if (MI->isDebugInstr())
|
2017-02-19 02:29:53 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
const SIRegisterInfo *TRI = ST.getRegisterInfo();
|
|
|
|
if (!ST.hasSMovFedHazard())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Check for any instruction reading an SGPR after a write from
|
|
|
|
// s_mov_fed_b32.
|
|
|
|
int MovFedWaitStates = 1;
|
|
|
|
int WaitStatesNeeded = 0;
|
|
|
|
|
|
|
|
for (const MachineOperand &Use : MI->uses()) {
|
|
|
|
if (!Use.isReg() || TRI->isVGPR(MF.getRegInfo(), Use.getReg()))
|
|
|
|
continue;
|
|
|
|
auto IsHazardFn = [] (MachineInstr *MI) {
|
|
|
|
return MI->getOpcode() == AMDGPU::S_MOV_FED_B32;
|
|
|
|
};
|
|
|
|
int WaitStatesNeededForUse =
|
|
|
|
MovFedWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardFn);
|
|
|
|
WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
|
|
|
|
}
|
|
|
|
|
|
|
|
return WaitStatesNeeded;
|
|
|
|
}
|
|
|
|
|
|
|
|
int GCNHazardRecognizer::checkReadM0Hazards(MachineInstr *MI) {
|
|
|
|
const SIInstrInfo *TII = ST.getInstrInfo();
|
2017-11-18 05:35:32 +08:00
|
|
|
const int SMovRelWaitStates = 1;
|
2017-02-19 02:29:53 +08:00
|
|
|
auto IsHazardFn = [TII] (MachineInstr *MI) {
|
|
|
|
return TII->isSALU(*MI);
|
|
|
|
};
|
|
|
|
return SMovRelWaitStates - getWaitStatesSinceDef(AMDGPU::M0, IsHazardFn);
|
|
|
|
}
|