162c7b5bfSEvan Cheng //===-- ARMHazardRecognizer.cpp - ARM postra hazard recognizer ------------===//
262c7b5bfSEvan Cheng //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
662c7b5bfSEvan Cheng //
762c7b5bfSEvan Cheng //===----------------------------------------------------------------------===//
862c7b5bfSEvan Cheng
962c7b5bfSEvan Cheng #include "ARMHazardRecognizer.h"
1062c7b5bfSEvan Cheng #include "ARMBaseInstrInfo.h"
11e45d6858SEvan Cheng #include "ARMBaseRegisterInfo.h"
1262c7b5bfSEvan Cheng #include "ARMSubtarget.h"
13a9f14cdcSDavid Penry #include "llvm/Analysis/ValueTracking.h"
14*989f1c72Sserge-sans-paille #include "llvm/CodeGen/MachineFrameInfo.h"
15*989f1c72Sserge-sans-paille #include "llvm/CodeGen/MachineFunctionPass.h"
1662c7b5bfSEvan Cheng #include "llvm/CodeGen/MachineInstr.h"
1762c7b5bfSEvan Cheng #include "llvm/CodeGen/ScheduleDAG.h"
18b3bde2eaSDavid Blaikie #include "llvm/CodeGen/TargetRegisterInfo.h"
19a9f14cdcSDavid Penry #include "llvm/Support/CommandLine.h"
20a9f14cdcSDavid Penry
2162c7b5bfSEvan Cheng using namespace llvm;
2262c7b5bfSEvan Cheng
23a9f14cdcSDavid Penry static cl::opt<int> DataBankMask("arm-data-bank-mask", cl::init(-1),
24a9f14cdcSDavid Penry cl::Hidden);
25a9f14cdcSDavid Penry static cl::opt<bool> AssumeITCMConflict("arm-assume-itcm-bankconflict",
26a9f14cdcSDavid Penry cl::init(false), cl::Hidden);
27a9f14cdcSDavid Penry
hasRAWHazard(MachineInstr * DefMI,MachineInstr * MI,const TargetRegisterInfo & TRI)2862c7b5bfSEvan Cheng static bool hasRAWHazard(MachineInstr *DefMI, MachineInstr *MI,
2962c7b5bfSEvan Cheng const TargetRegisterInfo &TRI) {
3062c7b5bfSEvan Cheng // FIXME: Detect integer instructions properly.
316cc775f9SEvan Cheng const MCInstrDesc &MCID = MI->getDesc();
326cc775f9SEvan Cheng unsigned Domain = MCID.TSFlags & ARMII::DomainMask;
337f8e563aSEvan Cheng if (MI->mayStore())
3462c7b5bfSEvan Cheng return false;
356cc775f9SEvan Cheng unsigned Opcode = MCID.getOpcode();
3604ad35b5SEvan Cheng if (Opcode == ARM::VMOVRS || Opcode == ARM::VMOVRRD)
3762c7b5bfSEvan Cheng return false;
3804ad35b5SEvan Cheng if ((Domain & ARMII::DomainVFP) || (Domain & ARMII::DomainNEON))
3962c7b5bfSEvan Cheng return MI->readsRegister(DefMI->getOperand(0).getReg(), &TRI);
4004ad35b5SEvan Cheng return false;
4162c7b5bfSEvan Cheng }
4262c7b5bfSEvan Cheng
4362c7b5bfSEvan Cheng ScheduleHazardRecognizer::HazardType
getHazardType(SUnit * SU,int Stalls)4461bc18deSDavid Green ARMHazardRecognizerFPMLx::getHazardType(SUnit *SU, int Stalls) {
4510ffc2b6SAndrew Trick assert(Stalls == 0 && "ARM hazards don't support scoreboard lookahead");
4610ffc2b6SAndrew Trick
4762c7b5bfSEvan Cheng MachineInstr *MI = SU->getInstr();
4862c7b5bfSEvan Cheng
49801bf7ebSShiva Chen if (!MI->isDebugInstr()) {
5062c7b5bfSEvan Cheng // Look for special VMLA / VMLS hazards. A VMUL / VADD / VSUB following
5162c7b5bfSEvan Cheng // a VMLA / VMLS will cause 4 cycle stall.
526cc775f9SEvan Cheng const MCInstrDesc &MCID = MI->getDesc();
536cc775f9SEvan Cheng if (LastMI && (MCID.TSFlags & ARMII::DomainMask) != ARMII::DomainGeneral) {
5462c7b5bfSEvan Cheng MachineInstr *DefMI = LastMI;
556cc775f9SEvan Cheng const MCInstrDesc &LastMCID = LastMI->getDesc();
561b21f009SEric Christopher const MachineFunction *MF = MI->getParent()->getParent();
57d913448bSEric Christopher const ARMBaseInstrInfo &TII = *static_cast<const ARMBaseInstrInfo *>(
581b21f009SEric Christopher MF->getSubtarget().getInstrInfo());
59f95178e6SBill Wendling
6062c7b5bfSEvan Cheng // Skip over one non-VFP / NEON instruction.
617f8e563aSEvan Cheng if (!LastMI->isBarrier() &&
624879b050SDiana Picus !(TII.getSubtarget().hasMuxedUnits() && LastMI->mayLoadOrStore()) &&
636cc775f9SEvan Cheng (LastMCID.TSFlags & ARMII::DomainMask) == ARMII::DomainGeneral) {
6462c7b5bfSEvan Cheng MachineBasicBlock::iterator I = LastMI;
6562c7b5bfSEvan Cheng if (I != LastMI->getParent()->begin()) {
66b6d0bd48SBenjamin Kramer I = std::prev(I);
6762c7b5bfSEvan Cheng DefMI = &*I;
6862c7b5bfSEvan Cheng }
6962c7b5bfSEvan Cheng }
7062c7b5bfSEvan Cheng
7162c7b5bfSEvan Cheng if (TII.isFpMLxInstruction(DefMI->getOpcode()) &&
7262c7b5bfSEvan Cheng (TII.canCauseFpMLxStall(MI->getOpcode()) ||
73f95178e6SBill Wendling hasRAWHazard(DefMI, MI, TII.getRegisterInfo()))) {
7462c7b5bfSEvan Cheng // Try to schedule another instruction for the next 4 cycles.
7510ffc2b6SAndrew Trick if (FpMLxStalls == 0)
7610ffc2b6SAndrew Trick FpMLxStalls = 4;
7762c7b5bfSEvan Cheng return Hazard;
7862c7b5bfSEvan Cheng }
7962c7b5bfSEvan Cheng }
8062c7b5bfSEvan Cheng }
8161bc18deSDavid Green return NoHazard;
8262c7b5bfSEvan Cheng }
8362c7b5bfSEvan Cheng
Reset()8461bc18deSDavid Green void ARMHazardRecognizerFPMLx::Reset() {
85062a2baeSCraig Topper LastMI = nullptr;
8610ffc2b6SAndrew Trick FpMLxStalls = 0;
8762c7b5bfSEvan Cheng }
8862c7b5bfSEvan Cheng
EmitInstruction(SUnit * SU)8961bc18deSDavid Green void ARMHazardRecognizerFPMLx::EmitInstruction(SUnit *SU) {
9062c7b5bfSEvan Cheng MachineInstr *MI = SU->getInstr();
91801bf7ebSShiva Chen if (!MI->isDebugInstr()) {
9262c7b5bfSEvan Cheng LastMI = MI;
9310ffc2b6SAndrew Trick FpMLxStalls = 0;
9462c7b5bfSEvan Cheng }
9562c7b5bfSEvan Cheng }
9662c7b5bfSEvan Cheng
AdvanceCycle()9761bc18deSDavid Green void ARMHazardRecognizerFPMLx::AdvanceCycle() {
9810ffc2b6SAndrew Trick if (FpMLxStalls && --FpMLxStalls == 0)
9962c7b5bfSEvan Cheng // Stalled for 4 cycles but still can't schedule any other instructions.
100062a2baeSCraig Topper LastMI = nullptr;
10100067fb1SAndrew Trick }
10200067fb1SAndrew Trick
RecedeCycle()10361bc18deSDavid Green void ARMHazardRecognizerFPMLx::RecedeCycle() {
10400067fb1SAndrew Trick llvm_unreachable("reverse ARM hazard checking unsupported");
10562c7b5bfSEvan Cheng }
106a9f14cdcSDavid Penry
107a9f14cdcSDavid Penry ///////// Bank conflicts handled as hazards //////////////
108a9f14cdcSDavid Penry
getBaseOffset(const MachineInstr & MI,const MachineOperand * & BaseOp,int64_t & Offset)109a9f14cdcSDavid Penry static bool getBaseOffset(const MachineInstr &MI, const MachineOperand *&BaseOp,
110a9f14cdcSDavid Penry int64_t &Offset) {
111a9f14cdcSDavid Penry
112a9f14cdcSDavid Penry uint64_t TSFlags = MI.getDesc().TSFlags;
113a9f14cdcSDavid Penry unsigned AddrMode = (TSFlags & ARMII::AddrModeMask);
114a9f14cdcSDavid Penry unsigned IndexMode =
115a9f14cdcSDavid Penry (TSFlags & ARMII::IndexModeMask) >> ARMII::IndexModeShift;
116a9f14cdcSDavid Penry
117a9f14cdcSDavid Penry // Address mode tells us what we want to know about operands for T2
118a9f14cdcSDavid Penry // instructions (but not size). It tells us size (but not about operands)
119a9f14cdcSDavid Penry // for T1 instructions.
120a9f14cdcSDavid Penry switch (AddrMode) {
121a9f14cdcSDavid Penry default:
122a9f14cdcSDavid Penry return false;
123a9f14cdcSDavid Penry case ARMII::AddrModeT2_i8:
124a9f14cdcSDavid Penry // t2LDRBT, t2LDRB_POST, t2LDRB_PRE, t2LDRBi8,
125a9f14cdcSDavid Penry // t2LDRHT, t2LDRH_POST, t2LDRH_PRE, t2LDRHi8,
126a9f14cdcSDavid Penry // t2LDRSBT, t2LDRSB_POST, t2LDRSB_PRE, t2LDRSBi8,
127a9f14cdcSDavid Penry // t2LDRSHT, t2LDRSH_POST, t2LDRSH_PRE, t2LDRSHi8,
128a9f14cdcSDavid Penry // t2LDRT, t2LDR_POST, t2LDR_PRE, t2LDRi8
129a9f14cdcSDavid Penry BaseOp = &MI.getOperand(1);
130a9f14cdcSDavid Penry Offset = (IndexMode == ARMII::IndexModePost)
131a9f14cdcSDavid Penry ? 0
132a9f14cdcSDavid Penry : (IndexMode == ARMII::IndexModePre ||
133a9f14cdcSDavid Penry IndexMode == ARMII::IndexModeUpd)
134a9f14cdcSDavid Penry ? MI.getOperand(3).getImm()
135a9f14cdcSDavid Penry : MI.getOperand(2).getImm();
136a9f14cdcSDavid Penry return true;
137a9f14cdcSDavid Penry case ARMII::AddrModeT2_i12:
138a9f14cdcSDavid Penry // t2LDRBi12, t2LDRHi12
139a9f14cdcSDavid Penry // t2LDRSBi12, t2LDRSHi12
140a9f14cdcSDavid Penry // t2LDRi12
141a9f14cdcSDavid Penry BaseOp = &MI.getOperand(1);
142a9f14cdcSDavid Penry Offset = MI.getOperand(2).getImm();
143a9f14cdcSDavid Penry return true;
144a9f14cdcSDavid Penry case ARMII::AddrModeT2_i8s4:
145a9f14cdcSDavid Penry // t2LDRD_POST, t2LDRD_PRE, t2LDRDi8
146a9f14cdcSDavid Penry BaseOp = &MI.getOperand(2);
147a9f14cdcSDavid Penry Offset = (IndexMode == ARMII::IndexModePost)
148a9f14cdcSDavid Penry ? 0
149a9f14cdcSDavid Penry : (IndexMode == ARMII::IndexModePre ||
150a9f14cdcSDavid Penry IndexMode == ARMII::IndexModeUpd)
151a9f14cdcSDavid Penry ? MI.getOperand(4).getImm()
152a9f14cdcSDavid Penry : MI.getOperand(3).getImm();
153a9f14cdcSDavid Penry return true;
154a9f14cdcSDavid Penry case ARMII::AddrModeT1_1:
155a9f14cdcSDavid Penry // tLDRBi, tLDRBr (watch out!), TLDRSB
156a9f14cdcSDavid Penry case ARMII::AddrModeT1_2:
157a9f14cdcSDavid Penry // tLDRHi, tLDRHr (watch out!), TLDRSH
158a9f14cdcSDavid Penry case ARMII::AddrModeT1_4:
159a9f14cdcSDavid Penry // tLDRi, tLDRr (watch out!)
160a9f14cdcSDavid Penry BaseOp = &MI.getOperand(1);
161a9f14cdcSDavid Penry Offset = MI.getOperand(2).isImm() ? MI.getOperand(2).getImm() : 0;
162a9f14cdcSDavid Penry return MI.getOperand(2).isImm();
163a9f14cdcSDavid Penry }
164a9f14cdcSDavid Penry return false;
165a9f14cdcSDavid Penry }
166a9f14cdcSDavid Penry
ARMBankConflictHazardRecognizer(const ScheduleDAG * DAG,int64_t CPUBankMask,bool CPUAssumeITCMConflict)167a9f14cdcSDavid Penry ARMBankConflictHazardRecognizer::ARMBankConflictHazardRecognizer(
168a9f14cdcSDavid Penry const ScheduleDAG *DAG, int64_t CPUBankMask, bool CPUAssumeITCMConflict)
169f3a344d2SKazu Hirata : MF(DAG->MF), DL(DAG->MF.getDataLayout()),
170a9f14cdcSDavid Penry DataMask(DataBankMask.getNumOccurrences() ? int64_t(DataBankMask)
171a9f14cdcSDavid Penry : CPUBankMask),
172a9f14cdcSDavid Penry AssumeITCMBankConflict(AssumeITCMConflict.getNumOccurrences()
173a9f14cdcSDavid Penry ? AssumeITCMConflict
174a9f14cdcSDavid Penry : CPUAssumeITCMConflict) {
175a9f14cdcSDavid Penry MaxLookAhead = 1;
176a9f14cdcSDavid Penry }
177a9f14cdcSDavid Penry
178a9f14cdcSDavid Penry ScheduleHazardRecognizer::HazardType
CheckOffsets(unsigned O0,unsigned O1)179a9f14cdcSDavid Penry ARMBankConflictHazardRecognizer::CheckOffsets(unsigned O0, unsigned O1) {
180a9f14cdcSDavid Penry return (((O0 ^ O1) & DataMask) != 0) ? NoHazard : Hazard;
181a9f14cdcSDavid Penry }
182a9f14cdcSDavid Penry
183a9f14cdcSDavid Penry ScheduleHazardRecognizer::HazardType
getHazardType(SUnit * SU,int Stalls)184a9f14cdcSDavid Penry ARMBankConflictHazardRecognizer::getHazardType(SUnit *SU, int Stalls) {
185a9f14cdcSDavid Penry MachineInstr &L0 = *SU->getInstr();
186a9f14cdcSDavid Penry if (!L0.mayLoad() || L0.mayStore() || L0.getNumMemOperands() != 1)
187a9f14cdcSDavid Penry return NoHazard;
188a9f14cdcSDavid Penry
189a9f14cdcSDavid Penry auto MO0 = *L0.memoperands().begin();
190a9f14cdcSDavid Penry auto BaseVal0 = MO0->getValue();
191a9f14cdcSDavid Penry auto BasePseudoVal0 = MO0->getPseudoValue();
192a9f14cdcSDavid Penry int64_t Offset0 = 0;
193a9f14cdcSDavid Penry
194a9f14cdcSDavid Penry if (MO0->getSize() > 4)
195a9f14cdcSDavid Penry return NoHazard;
196a9f14cdcSDavid Penry
197a9f14cdcSDavid Penry bool SPvalid = false;
198a9f14cdcSDavid Penry const MachineOperand *SP = nullptr;
199a9f14cdcSDavid Penry int64_t SPOffset0 = 0;
200a9f14cdcSDavid Penry
201a9f14cdcSDavid Penry for (auto L1 : Accesses) {
202a9f14cdcSDavid Penry auto MO1 = *L1->memoperands().begin();
203a9f14cdcSDavid Penry auto BaseVal1 = MO1->getValue();
204a9f14cdcSDavid Penry auto BasePseudoVal1 = MO1->getPseudoValue();
205a9f14cdcSDavid Penry int64_t Offset1 = 0;
206a9f14cdcSDavid Penry
207a9f14cdcSDavid Penry // Pointers to the same object
208a9f14cdcSDavid Penry if (BaseVal0 && BaseVal1) {
209a9f14cdcSDavid Penry const Value *Ptr0, *Ptr1;
210a9f14cdcSDavid Penry Ptr0 = GetPointerBaseWithConstantOffset(BaseVal0, Offset0, DL, true);
211a9f14cdcSDavid Penry Ptr1 = GetPointerBaseWithConstantOffset(BaseVal1, Offset1, DL, true);
212a9f14cdcSDavid Penry if (Ptr0 == Ptr1 && Ptr0)
213a9f14cdcSDavid Penry return CheckOffsets(Offset0, Offset1);
214a9f14cdcSDavid Penry }
215a9f14cdcSDavid Penry
216a9f14cdcSDavid Penry if (BasePseudoVal0 && BasePseudoVal1 &&
217a9f14cdcSDavid Penry BasePseudoVal0->kind() == BasePseudoVal1->kind() &&
218a9f14cdcSDavid Penry BasePseudoVal0->kind() == PseudoSourceValue::FixedStack) {
219a9f14cdcSDavid Penry // Spills/fills
220a9f14cdcSDavid Penry auto FS0 = cast<FixedStackPseudoSourceValue>(BasePseudoVal0);
221a9f14cdcSDavid Penry auto FS1 = cast<FixedStackPseudoSourceValue>(BasePseudoVal1);
222a9f14cdcSDavid Penry Offset0 = MF.getFrameInfo().getObjectOffset(FS0->getFrameIndex());
223a9f14cdcSDavid Penry Offset1 = MF.getFrameInfo().getObjectOffset(FS1->getFrameIndex());
224a9f14cdcSDavid Penry return CheckOffsets(Offset0, Offset1);
225a9f14cdcSDavid Penry }
226a9f14cdcSDavid Penry
227a9f14cdcSDavid Penry // Constant pools (likely in ITCM)
228a9f14cdcSDavid Penry if (BasePseudoVal0 && BasePseudoVal1 &&
229a9f14cdcSDavid Penry BasePseudoVal0->kind() == BasePseudoVal1->kind() &&
230a9f14cdcSDavid Penry BasePseudoVal0->isConstantPool() && AssumeITCMBankConflict)
231a9f14cdcSDavid Penry return Hazard;
232a9f14cdcSDavid Penry
233a9f14cdcSDavid Penry // Is this a stack pointer-relative access? We could in general try to
234a9f14cdcSDavid Penry // use "is this the same register and is it unchanged?", but the
235a9f14cdcSDavid Penry // memory operand tracking is highly likely to have already found that.
236a9f14cdcSDavid Penry // What we're after here is bank conflicts between different objects in
237a9f14cdcSDavid Penry // the stack frame.
238a9f14cdcSDavid Penry if (!SPvalid) { // set up SP
239a9f14cdcSDavid Penry if (!getBaseOffset(L0, SP, SPOffset0) || SP->getReg().id() != ARM::SP)
240a9f14cdcSDavid Penry SP = nullptr;
241a9f14cdcSDavid Penry SPvalid = true;
242a9f14cdcSDavid Penry }
243a9f14cdcSDavid Penry if (SP) {
244a9f14cdcSDavid Penry int64_t SPOffset1;
245a9f14cdcSDavid Penry const MachineOperand *SP1;
246a9f14cdcSDavid Penry if (getBaseOffset(*L1, SP1, SPOffset1) && SP1->getReg().id() == ARM::SP)
247a9f14cdcSDavid Penry return CheckOffsets(SPOffset0, SPOffset1);
248a9f14cdcSDavid Penry }
249a9f14cdcSDavid Penry }
250a9f14cdcSDavid Penry
251a9f14cdcSDavid Penry return NoHazard;
252a9f14cdcSDavid Penry }
253a9f14cdcSDavid Penry
Reset()254a9f14cdcSDavid Penry void ARMBankConflictHazardRecognizer::Reset() { Accesses.clear(); }
255a9f14cdcSDavid Penry
EmitInstruction(SUnit * SU)256a9f14cdcSDavid Penry void ARMBankConflictHazardRecognizer::EmitInstruction(SUnit *SU) {
257a9f14cdcSDavid Penry MachineInstr &MI = *SU->getInstr();
258a9f14cdcSDavid Penry if (!MI.mayLoad() || MI.mayStore() || MI.getNumMemOperands() != 1)
259a9f14cdcSDavid Penry return;
260a9f14cdcSDavid Penry
261a9f14cdcSDavid Penry auto MO = *MI.memoperands().begin();
262a9f14cdcSDavid Penry uint64_t Size1 = MO->getSize();
263a9f14cdcSDavid Penry if (Size1 > 4)
264a9f14cdcSDavid Penry return;
265a9f14cdcSDavid Penry Accesses.push_back(&MI);
266a9f14cdcSDavid Penry }
267a9f14cdcSDavid Penry
AdvanceCycle()268a9f14cdcSDavid Penry void ARMBankConflictHazardRecognizer::AdvanceCycle() { Accesses.clear(); }
269a9f14cdcSDavid Penry
RecedeCycle()270a9f14cdcSDavid Penry void ARMBankConflictHazardRecognizer::RecedeCycle() { Accesses.clear(); }
271