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" 13e8d8bef9SDimitry 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" 17e8d8bef9SDimitry Andric #include "llvm/Support/CommandLine.h" 18e8d8bef9SDimitry Andric 190b57cec5SDimitry Andric using namespace llvm; 200b57cec5SDimitry Andric 21e8d8bef9SDimitry Andric static cl::opt<int> DataBankMask("arm-data-bank-mask", cl::init(-1), 22e8d8bef9SDimitry Andric cl::Hidden); 23e8d8bef9SDimitry Andric static cl::opt<bool> AssumeITCMConflict("arm-assume-itcm-bankconflict", 24e8d8bef9SDimitry Andric cl::init(false), cl::Hidden); 25e8d8bef9SDimitry 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 42e8d8bef9SDimitry 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 } 79e8d8bef9SDimitry Andric return NoHazard; 800b57cec5SDimitry Andric } 810b57cec5SDimitry Andric 82e8d8bef9SDimitry Andric void ARMHazardRecognizerFPMLx::Reset() { 830b57cec5SDimitry Andric LastMI = nullptr; 840b57cec5SDimitry Andric FpMLxStalls = 0; 850b57cec5SDimitry Andric } 860b57cec5SDimitry Andric 87e8d8bef9SDimitry 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 95e8d8bef9SDimitry 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 101e8d8bef9SDimitry Andric void ARMHazardRecognizerFPMLx::RecedeCycle() { 1020b57cec5SDimitry Andric llvm_unreachable("reverse ARM hazard checking unsupported"); 1030b57cec5SDimitry Andric } 104e8d8bef9SDimitry Andric 105e8d8bef9SDimitry Andric ///////// Bank conflicts handled as hazards ////////////// 106e8d8bef9SDimitry Andric 107e8d8bef9SDimitry Andric static bool getBaseOffset(const MachineInstr &MI, const MachineOperand *&BaseOp, 108e8d8bef9SDimitry Andric int64_t &Offset) { 109e8d8bef9SDimitry Andric 110e8d8bef9SDimitry Andric uint64_t TSFlags = MI.getDesc().TSFlags; 111e8d8bef9SDimitry Andric unsigned AddrMode = (TSFlags & ARMII::AddrModeMask); 112e8d8bef9SDimitry Andric unsigned IndexMode = 113e8d8bef9SDimitry Andric (TSFlags & ARMII::IndexModeMask) >> ARMII::IndexModeShift; 114e8d8bef9SDimitry Andric 115e8d8bef9SDimitry Andric // Address mode tells us what we want to know about operands for T2 116e8d8bef9SDimitry Andric // instructions (but not size). It tells us size (but not about operands) 117e8d8bef9SDimitry Andric // for T1 instructions. 118e8d8bef9SDimitry Andric switch (AddrMode) { 119e8d8bef9SDimitry Andric default: 120e8d8bef9SDimitry Andric return false; 121e8d8bef9SDimitry Andric case ARMII::AddrModeT2_i8: 122e8d8bef9SDimitry Andric // t2LDRBT, t2LDRB_POST, t2LDRB_PRE, t2LDRBi8, 123e8d8bef9SDimitry Andric // t2LDRHT, t2LDRH_POST, t2LDRH_PRE, t2LDRHi8, 124e8d8bef9SDimitry Andric // t2LDRSBT, t2LDRSB_POST, t2LDRSB_PRE, t2LDRSBi8, 125e8d8bef9SDimitry Andric // t2LDRSHT, t2LDRSH_POST, t2LDRSH_PRE, t2LDRSHi8, 126e8d8bef9SDimitry Andric // t2LDRT, t2LDR_POST, t2LDR_PRE, t2LDRi8 127e8d8bef9SDimitry Andric BaseOp = &MI.getOperand(1); 128e8d8bef9SDimitry Andric Offset = (IndexMode == ARMII::IndexModePost) 129e8d8bef9SDimitry Andric ? 0 130e8d8bef9SDimitry Andric : (IndexMode == ARMII::IndexModePre || 131e8d8bef9SDimitry Andric IndexMode == ARMII::IndexModeUpd) 132e8d8bef9SDimitry Andric ? MI.getOperand(3).getImm() 133e8d8bef9SDimitry Andric : MI.getOperand(2).getImm(); 134e8d8bef9SDimitry Andric return true; 135e8d8bef9SDimitry Andric case ARMII::AddrModeT2_i12: 136e8d8bef9SDimitry Andric // t2LDRBi12, t2LDRHi12 137e8d8bef9SDimitry Andric // t2LDRSBi12, t2LDRSHi12 138e8d8bef9SDimitry Andric // t2LDRi12 139e8d8bef9SDimitry Andric BaseOp = &MI.getOperand(1); 140e8d8bef9SDimitry Andric Offset = MI.getOperand(2).getImm(); 141e8d8bef9SDimitry Andric return true; 142e8d8bef9SDimitry Andric case ARMII::AddrModeT2_i8s4: 143e8d8bef9SDimitry Andric // t2LDRD_POST, t2LDRD_PRE, t2LDRDi8 144e8d8bef9SDimitry Andric BaseOp = &MI.getOperand(2); 145e8d8bef9SDimitry Andric Offset = (IndexMode == ARMII::IndexModePost) 146e8d8bef9SDimitry Andric ? 0 147e8d8bef9SDimitry Andric : (IndexMode == ARMII::IndexModePre || 148e8d8bef9SDimitry Andric IndexMode == ARMII::IndexModeUpd) 149e8d8bef9SDimitry Andric ? MI.getOperand(4).getImm() 150e8d8bef9SDimitry Andric : MI.getOperand(3).getImm(); 151e8d8bef9SDimitry Andric return true; 152e8d8bef9SDimitry Andric case ARMII::AddrModeT1_1: 153e8d8bef9SDimitry Andric // tLDRBi, tLDRBr (watch out!), TLDRSB 154e8d8bef9SDimitry Andric case ARMII::AddrModeT1_2: 155e8d8bef9SDimitry Andric // tLDRHi, tLDRHr (watch out!), TLDRSH 156e8d8bef9SDimitry Andric case ARMII::AddrModeT1_4: 157e8d8bef9SDimitry Andric // tLDRi, tLDRr (watch out!) 158e8d8bef9SDimitry Andric BaseOp = &MI.getOperand(1); 159e8d8bef9SDimitry Andric Offset = MI.getOperand(2).isImm() ? MI.getOperand(2).getImm() : 0; 160e8d8bef9SDimitry Andric return MI.getOperand(2).isImm(); 161e8d8bef9SDimitry Andric } 162e8d8bef9SDimitry Andric return false; 163e8d8bef9SDimitry Andric } 164e8d8bef9SDimitry Andric 165e8d8bef9SDimitry Andric ARMBankConflictHazardRecognizer::ARMBankConflictHazardRecognizer( 166e8d8bef9SDimitry Andric const ScheduleDAG *DAG, int64_t CPUBankMask, bool CPUAssumeITCMConflict) 167*04eeddc0SDimitry Andric : MF(DAG->MF), DL(DAG->MF.getDataLayout()), 168e8d8bef9SDimitry Andric DataMask(DataBankMask.getNumOccurrences() ? int64_t(DataBankMask) 169e8d8bef9SDimitry Andric : CPUBankMask), 170e8d8bef9SDimitry Andric AssumeITCMBankConflict(AssumeITCMConflict.getNumOccurrences() 171e8d8bef9SDimitry Andric ? AssumeITCMConflict 172e8d8bef9SDimitry Andric : CPUAssumeITCMConflict) { 173e8d8bef9SDimitry Andric MaxLookAhead = 1; 174e8d8bef9SDimitry Andric } 175e8d8bef9SDimitry Andric 176e8d8bef9SDimitry Andric ScheduleHazardRecognizer::HazardType 177e8d8bef9SDimitry Andric ARMBankConflictHazardRecognizer::CheckOffsets(unsigned O0, unsigned O1) { 178e8d8bef9SDimitry Andric return (((O0 ^ O1) & DataMask) != 0) ? NoHazard : Hazard; 179e8d8bef9SDimitry Andric } 180e8d8bef9SDimitry Andric 181e8d8bef9SDimitry Andric ScheduleHazardRecognizer::HazardType 182e8d8bef9SDimitry Andric ARMBankConflictHazardRecognizer::getHazardType(SUnit *SU, int Stalls) { 183e8d8bef9SDimitry Andric MachineInstr &L0 = *SU->getInstr(); 184e8d8bef9SDimitry Andric if (!L0.mayLoad() || L0.mayStore() || L0.getNumMemOperands() != 1) 185e8d8bef9SDimitry Andric return NoHazard; 186e8d8bef9SDimitry Andric 187e8d8bef9SDimitry Andric auto MO0 = *L0.memoperands().begin(); 188e8d8bef9SDimitry Andric auto BaseVal0 = MO0->getValue(); 189e8d8bef9SDimitry Andric auto BasePseudoVal0 = MO0->getPseudoValue(); 190e8d8bef9SDimitry Andric int64_t Offset0 = 0; 191e8d8bef9SDimitry Andric 192e8d8bef9SDimitry Andric if (MO0->getSize() > 4) 193e8d8bef9SDimitry Andric return NoHazard; 194e8d8bef9SDimitry Andric 195e8d8bef9SDimitry Andric bool SPvalid = false; 196e8d8bef9SDimitry Andric const MachineOperand *SP = nullptr; 197e8d8bef9SDimitry Andric int64_t SPOffset0 = 0; 198e8d8bef9SDimitry Andric 199e8d8bef9SDimitry Andric for (auto L1 : Accesses) { 200e8d8bef9SDimitry Andric auto MO1 = *L1->memoperands().begin(); 201e8d8bef9SDimitry Andric auto BaseVal1 = MO1->getValue(); 202e8d8bef9SDimitry Andric auto BasePseudoVal1 = MO1->getPseudoValue(); 203e8d8bef9SDimitry Andric int64_t Offset1 = 0; 204e8d8bef9SDimitry Andric 205e8d8bef9SDimitry Andric // Pointers to the same object 206e8d8bef9SDimitry Andric if (BaseVal0 && BaseVal1) { 207e8d8bef9SDimitry Andric const Value *Ptr0, *Ptr1; 208e8d8bef9SDimitry Andric Ptr0 = GetPointerBaseWithConstantOffset(BaseVal0, Offset0, DL, true); 209e8d8bef9SDimitry Andric Ptr1 = GetPointerBaseWithConstantOffset(BaseVal1, Offset1, DL, true); 210e8d8bef9SDimitry Andric if (Ptr0 == Ptr1 && Ptr0) 211e8d8bef9SDimitry Andric return CheckOffsets(Offset0, Offset1); 212e8d8bef9SDimitry Andric } 213e8d8bef9SDimitry Andric 214e8d8bef9SDimitry Andric if (BasePseudoVal0 && BasePseudoVal1 && 215e8d8bef9SDimitry Andric BasePseudoVal0->kind() == BasePseudoVal1->kind() && 216e8d8bef9SDimitry Andric BasePseudoVal0->kind() == PseudoSourceValue::FixedStack) { 217e8d8bef9SDimitry Andric // Spills/fills 218e8d8bef9SDimitry Andric auto FS0 = cast<FixedStackPseudoSourceValue>(BasePseudoVal0); 219e8d8bef9SDimitry Andric auto FS1 = cast<FixedStackPseudoSourceValue>(BasePseudoVal1); 220e8d8bef9SDimitry Andric Offset0 = MF.getFrameInfo().getObjectOffset(FS0->getFrameIndex()); 221e8d8bef9SDimitry Andric Offset1 = MF.getFrameInfo().getObjectOffset(FS1->getFrameIndex()); 222e8d8bef9SDimitry Andric return CheckOffsets(Offset0, Offset1); 223e8d8bef9SDimitry Andric } 224e8d8bef9SDimitry Andric 225e8d8bef9SDimitry Andric // Constant pools (likely in ITCM) 226e8d8bef9SDimitry Andric if (BasePseudoVal0 && BasePseudoVal1 && 227e8d8bef9SDimitry Andric BasePseudoVal0->kind() == BasePseudoVal1->kind() && 228e8d8bef9SDimitry Andric BasePseudoVal0->isConstantPool() && AssumeITCMBankConflict) 229e8d8bef9SDimitry Andric return Hazard; 230e8d8bef9SDimitry Andric 231e8d8bef9SDimitry Andric // Is this a stack pointer-relative access? We could in general try to 232e8d8bef9SDimitry Andric // use "is this the same register and is it unchanged?", but the 233e8d8bef9SDimitry Andric // memory operand tracking is highly likely to have already found that. 234e8d8bef9SDimitry Andric // What we're after here is bank conflicts between different objects in 235e8d8bef9SDimitry Andric // the stack frame. 236e8d8bef9SDimitry Andric if (!SPvalid) { // set up SP 237e8d8bef9SDimitry Andric if (!getBaseOffset(L0, SP, SPOffset0) || SP->getReg().id() != ARM::SP) 238e8d8bef9SDimitry Andric SP = nullptr; 239e8d8bef9SDimitry Andric SPvalid = true; 240e8d8bef9SDimitry Andric } 241e8d8bef9SDimitry Andric if (SP) { 242e8d8bef9SDimitry Andric int64_t SPOffset1; 243e8d8bef9SDimitry Andric const MachineOperand *SP1; 244e8d8bef9SDimitry Andric if (getBaseOffset(*L1, SP1, SPOffset1) && SP1->getReg().id() == ARM::SP) 245e8d8bef9SDimitry Andric return CheckOffsets(SPOffset0, SPOffset1); 246e8d8bef9SDimitry Andric } 247e8d8bef9SDimitry Andric } 248e8d8bef9SDimitry Andric 249e8d8bef9SDimitry Andric return NoHazard; 250e8d8bef9SDimitry Andric } 251e8d8bef9SDimitry Andric 252e8d8bef9SDimitry Andric void ARMBankConflictHazardRecognizer::Reset() { Accesses.clear(); } 253e8d8bef9SDimitry Andric 254e8d8bef9SDimitry Andric void ARMBankConflictHazardRecognizer::EmitInstruction(SUnit *SU) { 255e8d8bef9SDimitry Andric MachineInstr &MI = *SU->getInstr(); 256e8d8bef9SDimitry Andric if (!MI.mayLoad() || MI.mayStore() || MI.getNumMemOperands() != 1) 257e8d8bef9SDimitry Andric return; 258e8d8bef9SDimitry Andric 259e8d8bef9SDimitry Andric auto MO = *MI.memoperands().begin(); 260e8d8bef9SDimitry Andric uint64_t Size1 = MO->getSize(); 261e8d8bef9SDimitry Andric if (Size1 > 4) 262e8d8bef9SDimitry Andric return; 263e8d8bef9SDimitry Andric Accesses.push_back(&MI); 264e8d8bef9SDimitry Andric } 265e8d8bef9SDimitry Andric 266e8d8bef9SDimitry Andric void ARMBankConflictHazardRecognizer::AdvanceCycle() { Accesses.clear(); } 267e8d8bef9SDimitry Andric 268e8d8bef9SDimitry Andric void ARMBankConflictHazardRecognizer::RecedeCycle() { Accesses.clear(); } 269