
This is part of #70452 that changes the type used for the external interface of MMO to LocationSize as opposed to uint64_t. This means the constructors take LocationSize, and convert ~UINT64_C(0) to LocationSize::beforeOrAfter(). The getSize methods return a LocationSize. This allows us to be more precise with unknown sizes, not accidentally treating them as unsigned values, and in the future should allow us to add proper scalable vector support but none of that is included in this patch. It should mostly be an NFC. Global ISel is still expected to use the underlying LLT as it needs, and are not expected to see unknown sizes for generic operations. Most of the changes are hopefully fairly mechanical, adding a lot of getValue() calls and protecting them with hasValue() where needed.
271 lines
9.6 KiB
C++
271 lines
9.6 KiB
C++
//===-- ARMHazardRecognizer.cpp - ARM postra hazard recognizer ------------===//
|
|
//
|
|
// 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 "ARMHazardRecognizer.h"
|
|
#include "ARMBaseInstrInfo.h"
|
|
#include "ARMBaseRegisterInfo.h"
|
|
#include "ARMSubtarget.h"
|
|
#include "llvm/Analysis/ValueTracking.h"
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
#include "llvm/CodeGen/MachineFunctionPass.h"
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
|
#include "llvm/CodeGen/ScheduleDAG.h"
|
|
#include "llvm/CodeGen/TargetRegisterInfo.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static cl::opt<int> DataBankMask("arm-data-bank-mask", cl::init(-1),
|
|
cl::Hidden);
|
|
static cl::opt<bool> AssumeITCMConflict("arm-assume-itcm-bankconflict",
|
|
cl::init(false), cl::Hidden);
|
|
|
|
static bool hasRAWHazard(MachineInstr *DefMI, MachineInstr *MI,
|
|
const TargetRegisterInfo &TRI) {
|
|
// FIXME: Detect integer instructions properly.
|
|
const MCInstrDesc &MCID = MI->getDesc();
|
|
unsigned Domain = MCID.TSFlags & ARMII::DomainMask;
|
|
if (MI->mayStore())
|
|
return false;
|
|
unsigned Opcode = MCID.getOpcode();
|
|
if (Opcode == ARM::VMOVRS || Opcode == ARM::VMOVRRD)
|
|
return false;
|
|
if ((Domain & ARMII::DomainVFP) || (Domain & ARMII::DomainNEON))
|
|
return MI->readsRegister(DefMI->getOperand(0).getReg(), &TRI);
|
|
return false;
|
|
}
|
|
|
|
ScheduleHazardRecognizer::HazardType
|
|
ARMHazardRecognizerFPMLx::getHazardType(SUnit *SU, int Stalls) {
|
|
assert(Stalls == 0 && "ARM hazards don't support scoreboard lookahead");
|
|
|
|
MachineInstr *MI = SU->getInstr();
|
|
|
|
if (!MI->isDebugInstr()) {
|
|
// Look for special VMLA / VMLS hazards. A VMUL / VADD / VSUB following
|
|
// a VMLA / VMLS will cause 4 cycle stall.
|
|
const MCInstrDesc &MCID = MI->getDesc();
|
|
if (LastMI && (MCID.TSFlags & ARMII::DomainMask) != ARMII::DomainGeneral) {
|
|
MachineInstr *DefMI = LastMI;
|
|
const MCInstrDesc &LastMCID = LastMI->getDesc();
|
|
const MachineFunction *MF = MI->getParent()->getParent();
|
|
const ARMBaseInstrInfo &TII = *static_cast<const ARMBaseInstrInfo *>(
|
|
MF->getSubtarget().getInstrInfo());
|
|
|
|
// Skip over one non-VFP / NEON instruction.
|
|
if (!LastMI->isBarrier() &&
|
|
!(TII.getSubtarget().hasMuxedUnits() && LastMI->mayLoadOrStore()) &&
|
|
(LastMCID.TSFlags & ARMII::DomainMask) == ARMII::DomainGeneral) {
|
|
MachineBasicBlock::iterator I = LastMI;
|
|
if (I != LastMI->getParent()->begin()) {
|
|
I = std::prev(I);
|
|
DefMI = &*I;
|
|
}
|
|
}
|
|
|
|
if (TII.isFpMLxInstruction(DefMI->getOpcode()) &&
|
|
(TII.canCauseFpMLxStall(MI->getOpcode()) ||
|
|
hasRAWHazard(DefMI, MI, TII.getRegisterInfo()))) {
|
|
// Try to schedule another instruction for the next 4 cycles.
|
|
if (FpMLxStalls == 0)
|
|
FpMLxStalls = 4;
|
|
return Hazard;
|
|
}
|
|
}
|
|
}
|
|
return NoHazard;
|
|
}
|
|
|
|
void ARMHazardRecognizerFPMLx::Reset() {
|
|
LastMI = nullptr;
|
|
FpMLxStalls = 0;
|
|
}
|
|
|
|
void ARMHazardRecognizerFPMLx::EmitInstruction(SUnit *SU) {
|
|
MachineInstr *MI = SU->getInstr();
|
|
if (!MI->isDebugInstr()) {
|
|
LastMI = MI;
|
|
FpMLxStalls = 0;
|
|
}
|
|
}
|
|
|
|
void ARMHazardRecognizerFPMLx::AdvanceCycle() {
|
|
if (FpMLxStalls && --FpMLxStalls == 0)
|
|
// Stalled for 4 cycles but still can't schedule any other instructions.
|
|
LastMI = nullptr;
|
|
}
|
|
|
|
void ARMHazardRecognizerFPMLx::RecedeCycle() {
|
|
llvm_unreachable("reverse ARM hazard checking unsupported");
|
|
}
|
|
|
|
///////// Bank conflicts handled as hazards //////////////
|
|
|
|
static bool getBaseOffset(const MachineInstr &MI, const MachineOperand *&BaseOp,
|
|
int64_t &Offset) {
|
|
|
|
uint64_t TSFlags = MI.getDesc().TSFlags;
|
|
unsigned AddrMode = (TSFlags & ARMII::AddrModeMask);
|
|
unsigned IndexMode =
|
|
(TSFlags & ARMII::IndexModeMask) >> ARMII::IndexModeShift;
|
|
|
|
// Address mode tells us what we want to know about operands for T2
|
|
// instructions (but not size). It tells us size (but not about operands)
|
|
// for T1 instructions.
|
|
switch (AddrMode) {
|
|
default:
|
|
return false;
|
|
case ARMII::AddrModeT2_i8:
|
|
// t2LDRBT, t2LDRB_POST, t2LDRB_PRE, t2LDRBi8,
|
|
// t2LDRHT, t2LDRH_POST, t2LDRH_PRE, t2LDRHi8,
|
|
// t2LDRSBT, t2LDRSB_POST, t2LDRSB_PRE, t2LDRSBi8,
|
|
// t2LDRSHT, t2LDRSH_POST, t2LDRSH_PRE, t2LDRSHi8,
|
|
// t2LDRT, t2LDR_POST, t2LDR_PRE, t2LDRi8
|
|
BaseOp = &MI.getOperand(1);
|
|
Offset = (IndexMode == ARMII::IndexModePost)
|
|
? 0
|
|
: (IndexMode == ARMII::IndexModePre ||
|
|
IndexMode == ARMII::IndexModeUpd)
|
|
? MI.getOperand(3).getImm()
|
|
: MI.getOperand(2).getImm();
|
|
return true;
|
|
case ARMII::AddrModeT2_i12:
|
|
// t2LDRBi12, t2LDRHi12
|
|
// t2LDRSBi12, t2LDRSHi12
|
|
// t2LDRi12
|
|
BaseOp = &MI.getOperand(1);
|
|
Offset = MI.getOperand(2).getImm();
|
|
return true;
|
|
case ARMII::AddrModeT2_i8s4:
|
|
// t2LDRD_POST, t2LDRD_PRE, t2LDRDi8
|
|
BaseOp = &MI.getOperand(2);
|
|
Offset = (IndexMode == ARMII::IndexModePost)
|
|
? 0
|
|
: (IndexMode == ARMII::IndexModePre ||
|
|
IndexMode == ARMII::IndexModeUpd)
|
|
? MI.getOperand(4).getImm()
|
|
: MI.getOperand(3).getImm();
|
|
return true;
|
|
case ARMII::AddrModeT1_1:
|
|
// tLDRBi, tLDRBr (watch out!), TLDRSB
|
|
case ARMII::AddrModeT1_2:
|
|
// tLDRHi, tLDRHr (watch out!), TLDRSH
|
|
case ARMII::AddrModeT1_4:
|
|
// tLDRi, tLDRr (watch out!)
|
|
BaseOp = &MI.getOperand(1);
|
|
Offset = MI.getOperand(2).isImm() ? MI.getOperand(2).getImm() : 0;
|
|
return MI.getOperand(2).isImm();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ARMBankConflictHazardRecognizer::ARMBankConflictHazardRecognizer(
|
|
const ScheduleDAG *DAG, int64_t CPUBankMask, bool CPUAssumeITCMConflict)
|
|
: MF(DAG->MF), DL(DAG->MF.getDataLayout()),
|
|
DataMask(DataBankMask.getNumOccurrences() ? int64_t(DataBankMask)
|
|
: CPUBankMask),
|
|
AssumeITCMBankConflict(AssumeITCMConflict.getNumOccurrences()
|
|
? AssumeITCMConflict
|
|
: CPUAssumeITCMConflict) {
|
|
MaxLookAhead = 1;
|
|
}
|
|
|
|
ScheduleHazardRecognizer::HazardType
|
|
ARMBankConflictHazardRecognizer::CheckOffsets(unsigned O0, unsigned O1) {
|
|
return (((O0 ^ O1) & DataMask) != 0) ? NoHazard : Hazard;
|
|
}
|
|
|
|
ScheduleHazardRecognizer::HazardType
|
|
ARMBankConflictHazardRecognizer::getHazardType(SUnit *SU, int Stalls) {
|
|
MachineInstr &L0 = *SU->getInstr();
|
|
if (!L0.mayLoad() || L0.mayStore() || L0.getNumMemOperands() != 1)
|
|
return NoHazard;
|
|
|
|
auto MO0 = *L0.memoperands().begin();
|
|
auto BaseVal0 = MO0->getValue();
|
|
auto BasePseudoVal0 = MO0->getPseudoValue();
|
|
int64_t Offset0 = 0;
|
|
|
|
if (!MO0->getSize().hasValue() || MO0->getSize().getValue() > 4)
|
|
return NoHazard;
|
|
|
|
bool SPvalid = false;
|
|
const MachineOperand *SP = nullptr;
|
|
int64_t SPOffset0 = 0;
|
|
|
|
for (auto L1 : Accesses) {
|
|
auto MO1 = *L1->memoperands().begin();
|
|
auto BaseVal1 = MO1->getValue();
|
|
auto BasePseudoVal1 = MO1->getPseudoValue();
|
|
int64_t Offset1 = 0;
|
|
|
|
// Pointers to the same object
|
|
if (BaseVal0 && BaseVal1) {
|
|
const Value *Ptr0, *Ptr1;
|
|
Ptr0 = GetPointerBaseWithConstantOffset(BaseVal0, Offset0, DL, true);
|
|
Ptr1 = GetPointerBaseWithConstantOffset(BaseVal1, Offset1, DL, true);
|
|
if (Ptr0 == Ptr1 && Ptr0)
|
|
return CheckOffsets(Offset0, Offset1);
|
|
}
|
|
|
|
if (BasePseudoVal0 && BasePseudoVal1 &&
|
|
BasePseudoVal0->kind() == BasePseudoVal1->kind() &&
|
|
BasePseudoVal0->kind() == PseudoSourceValue::FixedStack) {
|
|
// Spills/fills
|
|
auto FS0 = cast<FixedStackPseudoSourceValue>(BasePseudoVal0);
|
|
auto FS1 = cast<FixedStackPseudoSourceValue>(BasePseudoVal1);
|
|
Offset0 = MF.getFrameInfo().getObjectOffset(FS0->getFrameIndex());
|
|
Offset1 = MF.getFrameInfo().getObjectOffset(FS1->getFrameIndex());
|
|
return CheckOffsets(Offset0, Offset1);
|
|
}
|
|
|
|
// Constant pools (likely in ITCM)
|
|
if (BasePseudoVal0 && BasePseudoVal1 &&
|
|
BasePseudoVal0->kind() == BasePseudoVal1->kind() &&
|
|
BasePseudoVal0->isConstantPool() && AssumeITCMBankConflict)
|
|
return Hazard;
|
|
|
|
// Is this a stack pointer-relative access? We could in general try to
|
|
// use "is this the same register and is it unchanged?", but the
|
|
// memory operand tracking is highly likely to have already found that.
|
|
// What we're after here is bank conflicts between different objects in
|
|
// the stack frame.
|
|
if (!SPvalid) { // set up SP
|
|
if (!getBaseOffset(L0, SP, SPOffset0) || SP->getReg().id() != ARM::SP)
|
|
SP = nullptr;
|
|
SPvalid = true;
|
|
}
|
|
if (SP) {
|
|
int64_t SPOffset1;
|
|
const MachineOperand *SP1;
|
|
if (getBaseOffset(*L1, SP1, SPOffset1) && SP1->getReg().id() == ARM::SP)
|
|
return CheckOffsets(SPOffset0, SPOffset1);
|
|
}
|
|
}
|
|
|
|
return NoHazard;
|
|
}
|
|
|
|
void ARMBankConflictHazardRecognizer::Reset() { Accesses.clear(); }
|
|
|
|
void ARMBankConflictHazardRecognizer::EmitInstruction(SUnit *SU) {
|
|
MachineInstr &MI = *SU->getInstr();
|
|
if (!MI.mayLoad() || MI.mayStore() || MI.getNumMemOperands() != 1)
|
|
return;
|
|
|
|
auto MO = *MI.memoperands().begin();
|
|
LocationSize Size1 = MO->getSize();
|
|
if (Size1.hasValue() && Size1.getValue() > 4)
|
|
return;
|
|
Accesses.push_back(&MI);
|
|
}
|
|
|
|
void ARMBankConflictHazardRecognizer::AdvanceCycle() { Accesses.clear(); }
|
|
|
|
void ARMBankConflictHazardRecognizer::RecedeCycle() { Accesses.clear(); }
|