1 | //===-- ARMHazardRecognizer.cpp - ARM postra hazard recognizer ------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "ARMHazardRecognizer.h" |
10 | #include "ARMBaseInstrInfo.h" |
11 | #include "ARMSubtarget.h" |
12 | #include "llvm/Analysis/ValueTracking.h" |
13 | #include "llvm/CodeGen/MachineFrameInfo.h" |
14 | #include "llvm/CodeGen/MachineInstr.h" |
15 | #include "llvm/CodeGen/ScheduleDAG.h" |
16 | #include "llvm/CodeGen/TargetRegisterInfo.h" |
17 | #include "llvm/Support/CommandLine.h" |
18 | |
19 | using namespace llvm; |
20 | |
21 | static cl::opt<int> DataBankMask("arm-data-bank-mask" , cl::init(Val: -1), |
22 | cl::Hidden); |
23 | static cl::opt<bool> AssumeITCMConflict("arm-assume-itcm-bankconflict" , |
24 | cl::init(Val: false), cl::Hidden); |
25 | |
26 | static bool hasRAWHazard(MachineInstr *DefMI, MachineInstr *MI, |
27 | const TargetRegisterInfo &TRI) { |
28 | // FIXME: Detect integer instructions properly. |
29 | const MCInstrDesc &MCID = MI->getDesc(); |
30 | unsigned Domain = MCID.TSFlags & ARMII::DomainMask; |
31 | if (MI->mayStore()) |
32 | return false; |
33 | unsigned Opcode = MCID.getOpcode(); |
34 | if (Opcode == ARM::VMOVRS || Opcode == ARM::VMOVRRD) |
35 | return false; |
36 | if ((Domain & ARMII::DomainVFP) || (Domain & ARMII::DomainNEON)) |
37 | return MI->readsRegister(Reg: DefMI->getOperand(i: 0).getReg(), TRI: &TRI); |
38 | return false; |
39 | } |
40 | |
41 | ScheduleHazardRecognizer::HazardType |
42 | ARMHazardRecognizerFPMLx::getHazardType(SUnit *SU, int Stalls) { |
43 | assert(Stalls == 0 && "ARM hazards don't support scoreboard lookahead" ); |
44 | |
45 | MachineInstr *MI = SU->getInstr(); |
46 | |
47 | if (!MI->isDebugInstr()) { |
48 | // Look for special VMLA / VMLS hazards. A VMUL / VADD / VSUB following |
49 | // a VMLA / VMLS will cause 4 cycle stall. |
50 | const MCInstrDesc &MCID = MI->getDesc(); |
51 | if (LastMI && (MCID.TSFlags & ARMII::DomainMask) != ARMII::DomainGeneral) { |
52 | MachineInstr *DefMI = LastMI; |
53 | const MCInstrDesc &LastMCID = LastMI->getDesc(); |
54 | const MachineFunction *MF = MI->getParent()->getParent(); |
55 | const ARMBaseInstrInfo &TII = *static_cast<const ARMBaseInstrInfo *>( |
56 | MF->getSubtarget().getInstrInfo()); |
57 | |
58 | // Skip over one non-VFP / NEON instruction. |
59 | if (!LastMI->isBarrier() && |
60 | !(TII.getSubtarget().hasMuxedUnits() && LastMI->mayLoadOrStore()) && |
61 | (LastMCID.TSFlags & ARMII::DomainMask) == ARMII::DomainGeneral) { |
62 | MachineBasicBlock::iterator I = LastMI; |
63 | if (I != LastMI->getParent()->begin()) { |
64 | I = std::prev(x: I); |
65 | DefMI = &*I; |
66 | } |
67 | } |
68 | |
69 | if (TII.isFpMLxInstruction(Opcode: DefMI->getOpcode()) && |
70 | (TII.canCauseFpMLxStall(Opcode: MI->getOpcode()) || |
71 | hasRAWHazard(DefMI, MI, TRI: TII.getRegisterInfo()))) { |
72 | // Try to schedule another instruction for the next 4 cycles. |
73 | if (FpMLxStalls == 0) |
74 | FpMLxStalls = 4; |
75 | return Hazard; |
76 | } |
77 | } |
78 | } |
79 | return NoHazard; |
80 | } |
81 | |
82 | void ARMHazardRecognizerFPMLx::Reset() { |
83 | LastMI = nullptr; |
84 | FpMLxStalls = 0; |
85 | } |
86 | |
87 | void ARMHazardRecognizerFPMLx::EmitInstruction(SUnit *SU) { |
88 | MachineInstr *MI = SU->getInstr(); |
89 | if (!MI->isDebugInstr()) { |
90 | LastMI = MI; |
91 | FpMLxStalls = 0; |
92 | } |
93 | } |
94 | |
95 | void ARMHazardRecognizerFPMLx::AdvanceCycle() { |
96 | if (FpMLxStalls && --FpMLxStalls == 0) |
97 | // Stalled for 4 cycles but still can't schedule any other instructions. |
98 | LastMI = nullptr; |
99 | } |
100 | |
101 | void ARMHazardRecognizerFPMLx::RecedeCycle() { |
102 | llvm_unreachable("reverse ARM hazard checking unsupported" ); |
103 | } |
104 | |
105 | ///////// Bank conflicts handled as hazards ////////////// |
106 | |
107 | static bool getBaseOffset(const MachineInstr &MI, const MachineOperand *&BaseOp, |
108 | int64_t &Offset) { |
109 | |
110 | uint64_t TSFlags = MI.getDesc().TSFlags; |
111 | unsigned AddrMode = (TSFlags & ARMII::AddrModeMask); |
112 | unsigned IndexMode = |
113 | (TSFlags & ARMII::IndexModeMask) >> ARMII::IndexModeShift; |
114 | |
115 | // Address mode tells us what we want to know about operands for T2 |
116 | // instructions (but not size). It tells us size (but not about operands) |
117 | // for T1 instructions. |
118 | switch (AddrMode) { |
119 | default: |
120 | return false; |
121 | case ARMII::AddrModeT2_i8: |
122 | // t2LDRBT, t2LDRB_POST, t2LDRB_PRE, t2LDRBi8, |
123 | // t2LDRHT, t2LDRH_POST, t2LDRH_PRE, t2LDRHi8, |
124 | // t2LDRSBT, t2LDRSB_POST, t2LDRSB_PRE, t2LDRSBi8, |
125 | // t2LDRSHT, t2LDRSH_POST, t2LDRSH_PRE, t2LDRSHi8, |
126 | // t2LDRT, t2LDR_POST, t2LDR_PRE, t2LDRi8 |
127 | BaseOp = &MI.getOperand(i: 1); |
128 | Offset = (IndexMode == ARMII::IndexModePost) |
129 | ? 0 |
130 | : (IndexMode == ARMII::IndexModePre || |
131 | IndexMode == ARMII::IndexModeUpd) |
132 | ? MI.getOperand(i: 3).getImm() |
133 | : MI.getOperand(i: 2).getImm(); |
134 | return true; |
135 | case ARMII::AddrModeT2_i12: |
136 | // t2LDRBi12, t2LDRHi12 |
137 | // t2LDRSBi12, t2LDRSHi12 |
138 | // t2LDRi12 |
139 | BaseOp = &MI.getOperand(i: 1); |
140 | Offset = MI.getOperand(i: 2).getImm(); |
141 | return true; |
142 | case ARMII::AddrModeT2_i8s4: |
143 | // t2LDRD_POST, t2LDRD_PRE, t2LDRDi8 |
144 | BaseOp = &MI.getOperand(i: 2); |
145 | Offset = (IndexMode == ARMII::IndexModePost) |
146 | ? 0 |
147 | : (IndexMode == ARMII::IndexModePre || |
148 | IndexMode == ARMII::IndexModeUpd) |
149 | ? MI.getOperand(i: 4).getImm() |
150 | : MI.getOperand(i: 3).getImm(); |
151 | return true; |
152 | case ARMII::AddrModeT1_1: |
153 | // tLDRBi, tLDRBr (watch out!), TLDRSB |
154 | case ARMII::AddrModeT1_2: |
155 | // tLDRHi, tLDRHr (watch out!), TLDRSH |
156 | case ARMII::AddrModeT1_4: |
157 | // tLDRi, tLDRr (watch out!) |
158 | BaseOp = &MI.getOperand(i: 1); |
159 | Offset = MI.getOperand(i: 2).isImm() ? MI.getOperand(i: 2).getImm() : 0; |
160 | return MI.getOperand(i: 2).isImm(); |
161 | } |
162 | return false; |
163 | } |
164 | |
165 | ARMBankConflictHazardRecognizer::ARMBankConflictHazardRecognizer( |
166 | const ScheduleDAG *DAG, int64_t CPUBankMask, bool CPUAssumeITCMConflict) |
167 | : MF(DAG->MF), DL(DAG->MF.getDataLayout()), |
168 | DataMask(DataBankMask.getNumOccurrences() ? int64_t(DataBankMask) |
169 | : CPUBankMask), |
170 | AssumeITCMBankConflict(AssumeITCMConflict.getNumOccurrences() |
171 | ? AssumeITCMConflict |
172 | : CPUAssumeITCMConflict) { |
173 | MaxLookAhead = 1; |
174 | } |
175 | |
176 | ScheduleHazardRecognizer::HazardType |
177 | ARMBankConflictHazardRecognizer::CheckOffsets(unsigned O0, unsigned O1) { |
178 | return (((O0 ^ O1) & DataMask) != 0) ? NoHazard : Hazard; |
179 | } |
180 | |
181 | ScheduleHazardRecognizer::HazardType |
182 | ARMBankConflictHazardRecognizer::getHazardType(SUnit *SU, int Stalls) { |
183 | MachineInstr &L0 = *SU->getInstr(); |
184 | if (!L0.mayLoad() || L0.mayStore() || L0.getNumMemOperands() != 1) |
185 | return NoHazard; |
186 | |
187 | auto MO0 = *L0.memoperands().begin(); |
188 | auto BaseVal0 = MO0->getValue(); |
189 | auto BasePseudoVal0 = MO0->getPseudoValue(); |
190 | int64_t Offset0 = 0; |
191 | |
192 | if (!MO0->getSize().hasValue() || MO0->getSize().getValue() > 4) |
193 | return NoHazard; |
194 | |
195 | bool SPvalid = false; |
196 | const MachineOperand *SP = nullptr; |
197 | int64_t SPOffset0 = 0; |
198 | |
199 | for (auto L1 : Accesses) { |
200 | auto MO1 = *L1->memoperands().begin(); |
201 | auto BaseVal1 = MO1->getValue(); |
202 | auto BasePseudoVal1 = MO1->getPseudoValue(); |
203 | int64_t Offset1 = 0; |
204 | |
205 | // Pointers to the same object |
206 | if (BaseVal0 && BaseVal1) { |
207 | const Value *Ptr0, *Ptr1; |
208 | Ptr0 = GetPointerBaseWithConstantOffset(Ptr: BaseVal0, Offset&: Offset0, DL, AllowNonInbounds: true); |
209 | Ptr1 = GetPointerBaseWithConstantOffset(Ptr: BaseVal1, Offset&: Offset1, DL, AllowNonInbounds: true); |
210 | if (Ptr0 == Ptr1 && Ptr0) |
211 | return CheckOffsets(O0: Offset0, O1: Offset1); |
212 | } |
213 | |
214 | if (BasePseudoVal0 && BasePseudoVal1 && |
215 | BasePseudoVal0->kind() == BasePseudoVal1->kind() && |
216 | BasePseudoVal0->kind() == PseudoSourceValue::FixedStack) { |
217 | // Spills/fills |
218 | auto FS0 = cast<FixedStackPseudoSourceValue>(Val: BasePseudoVal0); |
219 | auto FS1 = cast<FixedStackPseudoSourceValue>(Val: BasePseudoVal1); |
220 | Offset0 = MF.getFrameInfo().getObjectOffset(ObjectIdx: FS0->getFrameIndex()); |
221 | Offset1 = MF.getFrameInfo().getObjectOffset(ObjectIdx: FS1->getFrameIndex()); |
222 | return CheckOffsets(O0: Offset0, O1: Offset1); |
223 | } |
224 | |
225 | // Constant pools (likely in ITCM) |
226 | if (BasePseudoVal0 && BasePseudoVal1 && |
227 | BasePseudoVal0->kind() == BasePseudoVal1->kind() && |
228 | BasePseudoVal0->isConstantPool() && AssumeITCMBankConflict) |
229 | return Hazard; |
230 | |
231 | // Is this a stack pointer-relative access? We could in general try to |
232 | // use "is this the same register and is it unchanged?", but the |
233 | // memory operand tracking is highly likely to have already found that. |
234 | // What we're after here is bank conflicts between different objects in |
235 | // the stack frame. |
236 | if (!SPvalid) { // set up SP |
237 | if (!getBaseOffset(MI: L0, BaseOp&: SP, Offset&: SPOffset0) || SP->getReg().id() != ARM::SP) |
238 | SP = nullptr; |
239 | SPvalid = true; |
240 | } |
241 | if (SP) { |
242 | int64_t SPOffset1; |
243 | const MachineOperand *SP1; |
244 | if (getBaseOffset(MI: *L1, BaseOp&: SP1, Offset&: SPOffset1) && SP1->getReg().id() == ARM::SP) |
245 | return CheckOffsets(O0: SPOffset0, O1: SPOffset1); |
246 | } |
247 | } |
248 | |
249 | return NoHazard; |
250 | } |
251 | |
252 | void ARMBankConflictHazardRecognizer::Reset() { Accesses.clear(); } |
253 | |
254 | void ARMBankConflictHazardRecognizer::EmitInstruction(SUnit *SU) { |
255 | MachineInstr &MI = *SU->getInstr(); |
256 | if (!MI.mayLoad() || MI.mayStore() || MI.getNumMemOperands() != 1) |
257 | return; |
258 | |
259 | auto MO = *MI.memoperands().begin(); |
260 | LocationSize Size1 = MO->getSize(); |
261 | if (Size1.hasValue() && Size1.getValue() > 4) |
262 | return; |
263 | Accesses.push_back(Elt: &MI); |
264 | } |
265 | |
266 | void ARMBankConflictHazardRecognizer::AdvanceCycle() { Accesses.clear(); } |
267 | |
268 | void ARMBankConflictHazardRecognizer::RecedeCycle() { Accesses.clear(); } |
269 | |