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