10b57cec5SDimitry Andric //===-- ARMHazardRecognizer.cpp - ARM postra hazard recognizer ------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "ARMHazardRecognizer.h" 100b57cec5SDimitry Andric #include "ARMBaseInstrInfo.h" 110b57cec5SDimitry Andric #include "ARMBaseRegisterInfo.h" 120b57cec5SDimitry Andric #include "ARMSubtarget.h" 13*e8d8bef9SDimitry Andric #include "llvm/Analysis/ValueTracking.h" 140b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstr.h" 150b57cec5SDimitry Andric #include "llvm/CodeGen/ScheduleDAG.h" 160b57cec5SDimitry Andric #include "llvm/CodeGen/TargetRegisterInfo.h" 17*e8d8bef9SDimitry Andric #include "llvm/Support/CommandLine.h" 18*e8d8bef9SDimitry Andric 190b57cec5SDimitry Andric using namespace llvm; 200b57cec5SDimitry Andric 21*e8d8bef9SDimitry Andric static cl::opt<int> DataBankMask("arm-data-bank-mask", cl::init(-1), 22*e8d8bef9SDimitry Andric cl::Hidden); 23*e8d8bef9SDimitry Andric static cl::opt<bool> AssumeITCMConflict("arm-assume-itcm-bankconflict", 24*e8d8bef9SDimitry Andric cl::init(false), cl::Hidden); 25*e8d8bef9SDimitry Andric 260b57cec5SDimitry Andric static bool hasRAWHazard(MachineInstr *DefMI, MachineInstr *MI, 270b57cec5SDimitry Andric const TargetRegisterInfo &TRI) { 280b57cec5SDimitry Andric // FIXME: Detect integer instructions properly. 290b57cec5SDimitry Andric const MCInstrDesc &MCID = MI->getDesc(); 300b57cec5SDimitry Andric unsigned Domain = MCID.TSFlags & ARMII::DomainMask; 310b57cec5SDimitry Andric if (MI->mayStore()) 320b57cec5SDimitry Andric return false; 330b57cec5SDimitry Andric unsigned Opcode = MCID.getOpcode(); 340b57cec5SDimitry Andric if (Opcode == ARM::VMOVRS || Opcode == ARM::VMOVRRD) 350b57cec5SDimitry Andric return false; 360b57cec5SDimitry Andric if ((Domain & ARMII::DomainVFP) || (Domain & ARMII::DomainNEON)) 370b57cec5SDimitry Andric return MI->readsRegister(DefMI->getOperand(0).getReg(), &TRI); 380b57cec5SDimitry Andric return false; 390b57cec5SDimitry Andric } 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric ScheduleHazardRecognizer::HazardType 42*e8d8bef9SDimitry Andric ARMHazardRecognizerFPMLx::getHazardType(SUnit *SU, int Stalls) { 430b57cec5SDimitry Andric assert(Stalls == 0 && "ARM hazards don't support scoreboard lookahead"); 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric MachineInstr *MI = SU->getInstr(); 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric if (!MI->isDebugInstr()) { 480b57cec5SDimitry Andric // Look for special VMLA / VMLS hazards. A VMUL / VADD / VSUB following 490b57cec5SDimitry Andric // a VMLA / VMLS will cause 4 cycle stall. 500b57cec5SDimitry Andric const MCInstrDesc &MCID = MI->getDesc(); 510b57cec5SDimitry Andric if (LastMI && (MCID.TSFlags & ARMII::DomainMask) != ARMII::DomainGeneral) { 520b57cec5SDimitry Andric MachineInstr *DefMI = LastMI; 530b57cec5SDimitry Andric const MCInstrDesc &LastMCID = LastMI->getDesc(); 540b57cec5SDimitry Andric const MachineFunction *MF = MI->getParent()->getParent(); 550b57cec5SDimitry Andric const ARMBaseInstrInfo &TII = *static_cast<const ARMBaseInstrInfo *>( 560b57cec5SDimitry Andric MF->getSubtarget().getInstrInfo()); 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric // Skip over one non-VFP / NEON instruction. 590b57cec5SDimitry Andric if (!LastMI->isBarrier() && 600b57cec5SDimitry Andric !(TII.getSubtarget().hasMuxedUnits() && LastMI->mayLoadOrStore()) && 610b57cec5SDimitry Andric (LastMCID.TSFlags & ARMII::DomainMask) == ARMII::DomainGeneral) { 620b57cec5SDimitry Andric MachineBasicBlock::iterator I = LastMI; 630b57cec5SDimitry Andric if (I != LastMI->getParent()->begin()) { 640b57cec5SDimitry Andric I = std::prev(I); 650b57cec5SDimitry Andric DefMI = &*I; 660b57cec5SDimitry Andric } 670b57cec5SDimitry Andric } 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric if (TII.isFpMLxInstruction(DefMI->getOpcode()) && 700b57cec5SDimitry Andric (TII.canCauseFpMLxStall(MI->getOpcode()) || 710b57cec5SDimitry Andric hasRAWHazard(DefMI, MI, TII.getRegisterInfo()))) { 720b57cec5SDimitry Andric // Try to schedule another instruction for the next 4 cycles. 730b57cec5SDimitry Andric if (FpMLxStalls == 0) 740b57cec5SDimitry Andric FpMLxStalls = 4; 750b57cec5SDimitry Andric return Hazard; 760b57cec5SDimitry Andric } 770b57cec5SDimitry Andric } 780b57cec5SDimitry Andric } 79*e8d8bef9SDimitry Andric return NoHazard; 800b57cec5SDimitry Andric } 810b57cec5SDimitry Andric 82*e8d8bef9SDimitry Andric void ARMHazardRecognizerFPMLx::Reset() { 830b57cec5SDimitry Andric LastMI = nullptr; 840b57cec5SDimitry Andric FpMLxStalls = 0; 850b57cec5SDimitry Andric } 860b57cec5SDimitry Andric 87*e8d8bef9SDimitry Andric void ARMHazardRecognizerFPMLx::EmitInstruction(SUnit *SU) { 880b57cec5SDimitry Andric MachineInstr *MI = SU->getInstr(); 890b57cec5SDimitry Andric if (!MI->isDebugInstr()) { 900b57cec5SDimitry Andric LastMI = MI; 910b57cec5SDimitry Andric FpMLxStalls = 0; 920b57cec5SDimitry Andric } 930b57cec5SDimitry Andric } 940b57cec5SDimitry Andric 95*e8d8bef9SDimitry Andric void ARMHazardRecognizerFPMLx::AdvanceCycle() { 960b57cec5SDimitry Andric if (FpMLxStalls && --FpMLxStalls == 0) 970b57cec5SDimitry Andric // Stalled for 4 cycles but still can't schedule any other instructions. 980b57cec5SDimitry Andric LastMI = nullptr; 990b57cec5SDimitry Andric } 1000b57cec5SDimitry Andric 101*e8d8bef9SDimitry Andric void ARMHazardRecognizerFPMLx::RecedeCycle() { 1020b57cec5SDimitry Andric llvm_unreachable("reverse ARM hazard checking unsupported"); 1030b57cec5SDimitry Andric } 104*e8d8bef9SDimitry Andric 105*e8d8bef9SDimitry Andric ///////// Bank conflicts handled as hazards ////////////// 106*e8d8bef9SDimitry Andric 107*e8d8bef9SDimitry Andric static bool getBaseOffset(const MachineInstr &MI, const MachineOperand *&BaseOp, 108*e8d8bef9SDimitry Andric int64_t &Offset) { 109*e8d8bef9SDimitry Andric 110*e8d8bef9SDimitry Andric uint64_t TSFlags = MI.getDesc().TSFlags; 111*e8d8bef9SDimitry Andric unsigned AddrMode = (TSFlags & ARMII::AddrModeMask); 112*e8d8bef9SDimitry Andric unsigned IndexMode = 113*e8d8bef9SDimitry Andric (TSFlags & ARMII::IndexModeMask) >> ARMII::IndexModeShift; 114*e8d8bef9SDimitry Andric 115*e8d8bef9SDimitry Andric // Address mode tells us what we want to know about operands for T2 116*e8d8bef9SDimitry Andric // instructions (but not size). It tells us size (but not about operands) 117*e8d8bef9SDimitry Andric // for T1 instructions. 118*e8d8bef9SDimitry Andric switch (AddrMode) { 119*e8d8bef9SDimitry Andric default: 120*e8d8bef9SDimitry Andric return false; 121*e8d8bef9SDimitry Andric case ARMII::AddrModeT2_i8: 122*e8d8bef9SDimitry Andric // t2LDRBT, t2LDRB_POST, t2LDRB_PRE, t2LDRBi8, 123*e8d8bef9SDimitry Andric // t2LDRHT, t2LDRH_POST, t2LDRH_PRE, t2LDRHi8, 124*e8d8bef9SDimitry Andric // t2LDRSBT, t2LDRSB_POST, t2LDRSB_PRE, t2LDRSBi8, 125*e8d8bef9SDimitry Andric // t2LDRSHT, t2LDRSH_POST, t2LDRSH_PRE, t2LDRSHi8, 126*e8d8bef9SDimitry Andric // t2LDRT, t2LDR_POST, t2LDR_PRE, t2LDRi8 127*e8d8bef9SDimitry Andric BaseOp = &MI.getOperand(1); 128*e8d8bef9SDimitry Andric Offset = (IndexMode == ARMII::IndexModePost) 129*e8d8bef9SDimitry Andric ? 0 130*e8d8bef9SDimitry Andric : (IndexMode == ARMII::IndexModePre || 131*e8d8bef9SDimitry Andric IndexMode == ARMII::IndexModeUpd) 132*e8d8bef9SDimitry Andric ? MI.getOperand(3).getImm() 133*e8d8bef9SDimitry Andric : MI.getOperand(2).getImm(); 134*e8d8bef9SDimitry Andric return true; 135*e8d8bef9SDimitry Andric case ARMII::AddrModeT2_i12: 136*e8d8bef9SDimitry Andric // t2LDRBi12, t2LDRHi12 137*e8d8bef9SDimitry Andric // t2LDRSBi12, t2LDRSHi12 138*e8d8bef9SDimitry Andric // t2LDRi12 139*e8d8bef9SDimitry Andric BaseOp = &MI.getOperand(1); 140*e8d8bef9SDimitry Andric Offset = MI.getOperand(2).getImm(); 141*e8d8bef9SDimitry Andric return true; 142*e8d8bef9SDimitry Andric case ARMII::AddrModeT2_i8s4: 143*e8d8bef9SDimitry Andric // t2LDRD_POST, t2LDRD_PRE, t2LDRDi8 144*e8d8bef9SDimitry Andric BaseOp = &MI.getOperand(2); 145*e8d8bef9SDimitry Andric Offset = (IndexMode == ARMII::IndexModePost) 146*e8d8bef9SDimitry Andric ? 0 147*e8d8bef9SDimitry Andric : (IndexMode == ARMII::IndexModePre || 148*e8d8bef9SDimitry Andric IndexMode == ARMII::IndexModeUpd) 149*e8d8bef9SDimitry Andric ? MI.getOperand(4).getImm() 150*e8d8bef9SDimitry Andric : MI.getOperand(3).getImm(); 151*e8d8bef9SDimitry Andric return true; 152*e8d8bef9SDimitry Andric case ARMII::AddrModeT1_1: 153*e8d8bef9SDimitry Andric // tLDRBi, tLDRBr (watch out!), TLDRSB 154*e8d8bef9SDimitry Andric case ARMII::AddrModeT1_2: 155*e8d8bef9SDimitry Andric // tLDRHi, tLDRHr (watch out!), TLDRSH 156*e8d8bef9SDimitry Andric case ARMII::AddrModeT1_4: 157*e8d8bef9SDimitry Andric // tLDRi, tLDRr (watch out!) 158*e8d8bef9SDimitry Andric BaseOp = &MI.getOperand(1); 159*e8d8bef9SDimitry Andric Offset = MI.getOperand(2).isImm() ? MI.getOperand(2).getImm() : 0; 160*e8d8bef9SDimitry Andric return MI.getOperand(2).isImm(); 161*e8d8bef9SDimitry Andric } 162*e8d8bef9SDimitry Andric return false; 163*e8d8bef9SDimitry Andric } 164*e8d8bef9SDimitry Andric 165*e8d8bef9SDimitry Andric ARMBankConflictHazardRecognizer::ARMBankConflictHazardRecognizer( 166*e8d8bef9SDimitry Andric const ScheduleDAG *DAG, int64_t CPUBankMask, bool CPUAssumeITCMConflict) 167*e8d8bef9SDimitry Andric : ScheduleHazardRecognizer(), MF(DAG->MF), DL(DAG->MF.getDataLayout()), 168*e8d8bef9SDimitry Andric DataMask(DataBankMask.getNumOccurrences() ? int64_t(DataBankMask) 169*e8d8bef9SDimitry Andric : CPUBankMask), 170*e8d8bef9SDimitry Andric AssumeITCMBankConflict(AssumeITCMConflict.getNumOccurrences() 171*e8d8bef9SDimitry Andric ? AssumeITCMConflict 172*e8d8bef9SDimitry Andric : CPUAssumeITCMConflict) { 173*e8d8bef9SDimitry Andric MaxLookAhead = 1; 174*e8d8bef9SDimitry Andric } 175*e8d8bef9SDimitry Andric 176*e8d8bef9SDimitry Andric ScheduleHazardRecognizer::HazardType 177*e8d8bef9SDimitry Andric ARMBankConflictHazardRecognizer::CheckOffsets(unsigned O0, unsigned O1) { 178*e8d8bef9SDimitry Andric return (((O0 ^ O1) & DataMask) != 0) ? NoHazard : Hazard; 179*e8d8bef9SDimitry Andric } 180*e8d8bef9SDimitry Andric 181*e8d8bef9SDimitry Andric ScheduleHazardRecognizer::HazardType 182*e8d8bef9SDimitry Andric ARMBankConflictHazardRecognizer::getHazardType(SUnit *SU, int Stalls) { 183*e8d8bef9SDimitry Andric MachineInstr &L0 = *SU->getInstr(); 184*e8d8bef9SDimitry Andric if (!L0.mayLoad() || L0.mayStore() || L0.getNumMemOperands() != 1) 185*e8d8bef9SDimitry Andric return NoHazard; 186*e8d8bef9SDimitry Andric 187*e8d8bef9SDimitry Andric auto MO0 = *L0.memoperands().begin(); 188*e8d8bef9SDimitry Andric auto BaseVal0 = MO0->getValue(); 189*e8d8bef9SDimitry Andric auto BasePseudoVal0 = MO0->getPseudoValue(); 190*e8d8bef9SDimitry Andric int64_t Offset0 = 0; 191*e8d8bef9SDimitry Andric 192*e8d8bef9SDimitry Andric if (MO0->getSize() > 4) 193*e8d8bef9SDimitry Andric return NoHazard; 194*e8d8bef9SDimitry Andric 195*e8d8bef9SDimitry Andric bool SPvalid = false; 196*e8d8bef9SDimitry Andric const MachineOperand *SP = nullptr; 197*e8d8bef9SDimitry Andric int64_t SPOffset0 = 0; 198*e8d8bef9SDimitry Andric 199*e8d8bef9SDimitry Andric for (auto L1 : Accesses) { 200*e8d8bef9SDimitry Andric auto MO1 = *L1->memoperands().begin(); 201*e8d8bef9SDimitry Andric auto BaseVal1 = MO1->getValue(); 202*e8d8bef9SDimitry Andric auto BasePseudoVal1 = MO1->getPseudoValue(); 203*e8d8bef9SDimitry Andric int64_t Offset1 = 0; 204*e8d8bef9SDimitry Andric 205*e8d8bef9SDimitry Andric // Pointers to the same object 206*e8d8bef9SDimitry Andric if (BaseVal0 && BaseVal1) { 207*e8d8bef9SDimitry Andric const Value *Ptr0, *Ptr1; 208*e8d8bef9SDimitry Andric Ptr0 = GetPointerBaseWithConstantOffset(BaseVal0, Offset0, DL, true); 209*e8d8bef9SDimitry Andric Ptr1 = GetPointerBaseWithConstantOffset(BaseVal1, Offset1, DL, true); 210*e8d8bef9SDimitry Andric if (Ptr0 == Ptr1 && Ptr0) 211*e8d8bef9SDimitry Andric return CheckOffsets(Offset0, Offset1); 212*e8d8bef9SDimitry Andric } 213*e8d8bef9SDimitry Andric 214*e8d8bef9SDimitry Andric if (BasePseudoVal0 && BasePseudoVal1 && 215*e8d8bef9SDimitry Andric BasePseudoVal0->kind() == BasePseudoVal1->kind() && 216*e8d8bef9SDimitry Andric BasePseudoVal0->kind() == PseudoSourceValue::FixedStack) { 217*e8d8bef9SDimitry Andric // Spills/fills 218*e8d8bef9SDimitry Andric auto FS0 = cast<FixedStackPseudoSourceValue>(BasePseudoVal0); 219*e8d8bef9SDimitry Andric auto FS1 = cast<FixedStackPseudoSourceValue>(BasePseudoVal1); 220*e8d8bef9SDimitry Andric Offset0 = MF.getFrameInfo().getObjectOffset(FS0->getFrameIndex()); 221*e8d8bef9SDimitry Andric Offset1 = MF.getFrameInfo().getObjectOffset(FS1->getFrameIndex()); 222*e8d8bef9SDimitry Andric return CheckOffsets(Offset0, Offset1); 223*e8d8bef9SDimitry Andric } 224*e8d8bef9SDimitry Andric 225*e8d8bef9SDimitry Andric // Constant pools (likely in ITCM) 226*e8d8bef9SDimitry Andric if (BasePseudoVal0 && BasePseudoVal1 && 227*e8d8bef9SDimitry Andric BasePseudoVal0->kind() == BasePseudoVal1->kind() && 228*e8d8bef9SDimitry Andric BasePseudoVal0->isConstantPool() && AssumeITCMBankConflict) 229*e8d8bef9SDimitry Andric return Hazard; 230*e8d8bef9SDimitry Andric 231*e8d8bef9SDimitry Andric // Is this a stack pointer-relative access? We could in general try to 232*e8d8bef9SDimitry Andric // use "is this the same register and is it unchanged?", but the 233*e8d8bef9SDimitry Andric // memory operand tracking is highly likely to have already found that. 234*e8d8bef9SDimitry Andric // What we're after here is bank conflicts between different objects in 235*e8d8bef9SDimitry Andric // the stack frame. 236*e8d8bef9SDimitry Andric if (!SPvalid) { // set up SP 237*e8d8bef9SDimitry Andric if (!getBaseOffset(L0, SP, SPOffset0) || SP->getReg().id() != ARM::SP) 238*e8d8bef9SDimitry Andric SP = nullptr; 239*e8d8bef9SDimitry Andric SPvalid = true; 240*e8d8bef9SDimitry Andric } 241*e8d8bef9SDimitry Andric if (SP) { 242*e8d8bef9SDimitry Andric int64_t SPOffset1; 243*e8d8bef9SDimitry Andric const MachineOperand *SP1; 244*e8d8bef9SDimitry Andric if (getBaseOffset(*L1, SP1, SPOffset1) && SP1->getReg().id() == ARM::SP) 245*e8d8bef9SDimitry Andric return CheckOffsets(SPOffset0, SPOffset1); 246*e8d8bef9SDimitry Andric } 247*e8d8bef9SDimitry Andric } 248*e8d8bef9SDimitry Andric 249*e8d8bef9SDimitry Andric return NoHazard; 250*e8d8bef9SDimitry Andric } 251*e8d8bef9SDimitry Andric 252*e8d8bef9SDimitry Andric void ARMBankConflictHazardRecognizer::Reset() { Accesses.clear(); } 253*e8d8bef9SDimitry Andric 254*e8d8bef9SDimitry Andric void ARMBankConflictHazardRecognizer::EmitInstruction(SUnit *SU) { 255*e8d8bef9SDimitry Andric MachineInstr &MI = *SU->getInstr(); 256*e8d8bef9SDimitry Andric if (!MI.mayLoad() || MI.mayStore() || MI.getNumMemOperands() != 1) 257*e8d8bef9SDimitry Andric return; 258*e8d8bef9SDimitry Andric 259*e8d8bef9SDimitry Andric auto MO = *MI.memoperands().begin(); 260*e8d8bef9SDimitry Andric uint64_t Size1 = MO->getSize(); 261*e8d8bef9SDimitry Andric if (Size1 > 4) 262*e8d8bef9SDimitry Andric return; 263*e8d8bef9SDimitry Andric Accesses.push_back(&MI); 264*e8d8bef9SDimitry Andric } 265*e8d8bef9SDimitry Andric 266*e8d8bef9SDimitry Andric void ARMBankConflictHazardRecognizer::AdvanceCycle() { Accesses.clear(); } 267*e8d8bef9SDimitry Andric 268*e8d8bef9SDimitry Andric void ARMBankConflictHazardRecognizer::RecedeCycle() { Accesses.clear(); } 269