|  | //===-- 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 "ARMSubtarget.h" | 
|  | #include "llvm/Analysis/ValueTracking.h" | 
|  | #include "llvm/CodeGen/MachineFrameInfo.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(); } |