LLVM 24.0.0git
HexagonSubtarget.cpp
Go to the documentation of this file.
1//===- HexagonSubtarget.cpp - Hexagon Subtarget Information ---------------===//
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// This file implements the Hexagon specific subclass of TargetSubtarget.
10//
11//===----------------------------------------------------------------------===//
12
13#include "HexagonSubtarget.h"
14#include "HexagonInstrInfo.h"
15#include "HexagonRegisterInfo.h"
17#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/StringRef.h"
25#include "llvm/IR/IntrinsicsHexagon.h"
29#include <algorithm>
30#include <cassert>
31#include <optional>
32
33using namespace llvm;
34
35#define DEBUG_TYPE "hexagon-subtarget"
36
37#define GET_SUBTARGETINFO_CTOR
38#define GET_SUBTARGETINFO_TARGET_DESC
39#include "HexagonGenSubtargetInfo.inc"
40
41static cl::opt<bool> EnableBSBSched("enable-bsb-sched", cl::Hidden,
42 cl::init(true));
43
44static cl::opt<bool> EnableTCLatencySched("enable-tc-latency-sched", cl::Hidden,
45 cl::init(false));
46
47static cl::opt<bool>
48 EnableDotCurSched("enable-cur-sched", cl::Hidden, cl::init(true),
49 cl::desc("Enable the scheduler to generate .cur"));
50
51static cl::opt<bool>
52 DisableHexagonMISched("disable-hexagon-misched", cl::Hidden,
53 cl::desc("Disable Hexagon MI Scheduling"));
54
56 "hexagon-long-calls", cl::Hidden,
57 cl::desc("If present, forces/disables the use of long calls"));
58
59static cl::opt<bool>
60 EnablePredicatedCalls("hexagon-pred-calls", cl::Hidden,
61 cl::desc("Consider calls to be predicable"));
62
63static cl::opt<bool> SchedPredsCloser("sched-preds-closer", cl::Hidden,
64 cl::init(true));
65
66static cl::opt<bool> SchedRetvalOptimization("sched-retval-optimization",
67 cl::Hidden, cl::init(true));
68
70 "hexagon-check-bank-conflict", cl::Hidden, cl::init(true),
71 cl::desc("Enable checking for cache bank conflicts"));
72
74 StringRef FS, const TargetMachine &TM)
75 : HexagonGenSubtargetInfo(TT, CPU, /*TuneCPU*/ CPU, FS),
76 OptLevel(TM.getOptLevel()),
77 CPUString(std::string(Hexagon_MC::selectHexagonCPU(CPU))),
78 TargetTriple(TT), InstrInfo(initializeSubtargetDependencies(CPU, FS)),
79 TLInfo(TM, *this), InstrItins(getInstrItineraryForCPU(CPUString)) {
81 // Beware of the default constructor of InstrItineraryData: it will
82 // reset all members to 0.
83 assert(InstrItins.Itineraries != nullptr && "InstrItins not initialized");
84}
85
88 std::optional<Hexagon::ArchEnum> ArchVer = Hexagon::getCpu(CPUString);
89 if (ArchVer)
90 HexagonArchVersion = *ArchVer;
91 else
92 llvm_unreachable("Unrecognized Hexagon processor version");
93
94 UseHVX128BOps = false;
95 UseHVX64BOps = false;
96 UseAudioOps = false;
97 UseLongCalls = false;
98
99 SubtargetFeatures Features(FS);
100
101 // Turn on QFloat if the HVX version is v68+.
102 // The function ParseSubtargetFeatures will set feature bits and initialize
103 // subtarget's variables all in one, so there isn't a good way to preprocess
104 // the feature string, other than by tinkering with it directly.
105 auto IsQFloatFS = [](StringRef F) {
106 return F == "+hvx-qfloat" || F == "-hvx-qfloat";
107 };
108 if (!llvm::count_if(Features.getFeatures(), IsQFloatFS)) {
109 auto getHvxVersion = [&Features](StringRef FS) -> StringRef {
110 for (StringRef F : llvm::reverse(Features.getFeatures())) {
111 if (F.starts_with("+hvxv"))
112 return F;
113 }
114 for (StringRef F : llvm::reverse(Features.getFeatures())) {
115 if (F == "-hvx")
116 return StringRef();
117 if (F.starts_with("+hvx") || F == "-hvx")
118 return F.take_front(4); // Return "+hvx" or "-hvx".
119 }
120 return StringRef();
121 };
122
123 bool AddQFloat = false;
124 StringRef HvxVer = getHvxVersion(FS);
125 if (HvxVer.starts_with("+hvxv")) {
126 int Ver = 0;
127 if (!HvxVer.drop_front(5).consumeInteger(10, Ver) && Ver >= 68)
128 AddQFloat = true;
129 } else if (HvxVer == "+hvx") {
130 if (hasV68Ops())
131 AddQFloat = true;
132 }
133
134 if (AddQFloat)
135 Features.AddFeature("+hvx-qfloat");
136 }
137
138 std::string FeatureString = Features.getString();
139 ParseSubtargetFeatures(CPUString, /*TuneCPU*/ CPUString, FeatureString);
140
141 // Resolve the shadow call stack pointer register. At most one "scs-reg-rN"
142 // feature may be given; R18 is the default. R18 is chosen because it is the
143 // lowest callee-saved register that neither the Hexagon Linux kernel (which
144 // reserves R19 for the thread-info pointer) nor code that reserves the upper
145 // callee-saved range already claims.
146 static_assert(Hexagon::R27 - Hexagon::R16 == 11,
147 "Callee-saved R16-R27 are assumed to be consecutive");
148 SCSPReg = Hexagon::R18;
149 bool SCSRegSelected = false;
150 for (unsigned Reg = Hexagon::R16; Reg <= Hexagon::R27; ++Reg) {
151 if (!SCSPointerRegister[Reg])
152 continue;
153 if (SCSRegSelected)
155 "Only one shadow call stack pointer register may be selected");
156 SCSPReg = Reg;
157 SCSRegSelected = true;
158 }
159
160 if (useHVXV68Ops())
161 UseHVXFloatingPoint = UseHVXIEEEFPOps || UseHVXQFloatOps;
162
163 if (UseHVXQFloatOps && UseHVXIEEEFPOps && UseHVXFloatingPoint)
165 dbgs() << "Behavior is undefined for simultaneous qfloat and ieee hvx codegen...");
166
167 if (OverrideLongCalls.getPosition())
168 UseLongCalls = OverrideLongCalls;
169
171
172 if (isTinyCore()) {
173 // Tiny core has a single thread, so back-to-back scheduling is enabled by
174 // default.
175 if (!EnableBSBSched.getPosition())
176 UseBSBScheduling = false;
177 }
178
179 FeatureBitset FeatureBits = getFeatureBits();
181 setFeatureBits(FeatureBits.reset(Hexagon::FeatureDuplex));
182 setFeatureBits(Hexagon_MC::completeHVXFeatures(FeatureBits));
183
184 return *this;
185}
186
187bool HexagonSubtarget::isHVXElementType(MVT Ty, bool IncludeBool) const {
188 if (!useHVXOps())
189 return false;
190 if (Ty.isVector())
191 Ty = Ty.getVectorElementType();
192 if (IncludeBool && Ty == MVT::i1)
193 return true;
194 ArrayRef<MVT> ElemTypes = getHVXElementTypes();
195 return llvm::is_contained(ElemTypes, Ty);
196}
197
198bool HexagonSubtarget::isHVXVectorType(EVT VecTy, bool IncludeBool) const {
199 if (!VecTy.isSimple())
200 return false;
201 if (!VecTy.isVector() || !useHVXOps() || VecTy.isScalableVector())
202 return false;
203 MVT ElemTy = VecTy.getSimpleVT().getVectorElementType();
204 if (!IncludeBool && ElemTy == MVT::i1)
205 return false;
206
207 unsigned HwLen = getVectorLength();
208 unsigned NumElems = VecTy.getVectorNumElements();
209 ArrayRef<MVT> ElemTypes = getHVXElementTypes();
210
211 if (IncludeBool && ElemTy == MVT::i1) {
212 // Boolean HVX vector types are formed from regular HVX vector types
213 // by replacing the element type with i1.
214 for (MVT T : ElemTypes)
215 if (NumElems * T.getSizeInBits() == 8 * HwLen)
216 return true;
217 return false;
218 }
219
220 unsigned VecWidth = VecTy.getSizeInBits();
221 if (VecWidth != 8 * HwLen && VecWidth != 16 * HwLen)
222 return false;
223 return llvm::is_contained(ElemTypes, ElemTy);
224}
225
226bool HexagonSubtarget::isTypeForHVX(Type *VecTy, bool IncludeBool) const {
227 if (!VecTy->isVectorTy() || isa<ScalableVectorType>(VecTy))
228 return false;
229 // Avoid types like <2 x i32*>.
230 Type *ScalTy = VecTy->getScalarType();
231 if (!ScalTy->isIntegerTy() &&
232 !(ScalTy->isFloatingPointTy() && useHVXFloatingPoint()))
233 return false;
234 // The given type may be something like <17 x i32>, which is not MVT,
235 // but can be represented as (non-simple) EVT.
236 EVT Ty = EVT::getEVT(VecTy, /*HandleUnknown*/false);
237 if (!Ty.getVectorElementType().isSimple())
238 return false;
239
240 auto isHvxTy = [this, IncludeBool](MVT SimpleTy) {
241 if (isHVXVectorType(SimpleTy, IncludeBool))
242 return true;
243 auto Action = getTargetLowering()->getPreferredVectorAction(SimpleTy);
245 };
246
247 // Round up EVT to have power-of-2 elements, and keep checking if it
248 // qualifies for HVX, dividing it in half after each step.
249 MVT ElemTy = Ty.getVectorElementType().getSimpleVT();
250 unsigned VecLen = PowerOf2Ceil(Ty.getVectorNumElements());
251 while (VecLen > 1) {
252 MVT SimpleTy = MVT::getVectorVT(ElemTy, VecLen);
253 if (SimpleTy.isValid() && isHvxTy(SimpleTy))
254 return true;
255 VecLen /= 2;
256 }
257
258 return false;
259}
260
262 for (SUnit &SU : DAG->SUnits) {
263 if (!SU.isInstr())
264 continue;
266 for (auto &D : SU.Preds)
267 if (D.getKind() == SDep::Output && D.getReg() == Hexagon::USR_OVF)
268 Erase.push_back(D);
269 for (auto &E : Erase)
270 SU.removePred(E);
271 }
272}
273
275 for (SUnit &SU : DAG->SUnits) {
276 // Update the latency of chain edges between v60 vector load or store
277 // instructions to be 1. These instruction cannot be scheduled in the
278 // same packet.
279 MachineInstr &MI1 = *SU.getInstr();
280 auto *QII = static_cast<const HexagonInstrInfo*>(DAG->TII);
281 bool IsStoreMI1 = MI1.mayStore();
282 bool IsLoadMI1 = MI1.mayLoad();
283 if (!QII->isHVXVec(MI1) || !(IsStoreMI1 || IsLoadMI1))
284 continue;
285 for (SDep &SI : SU.Succs) {
286 if (SI.getKind() != SDep::Order || SI.getLatency() != 0)
287 continue;
288 MachineInstr &MI2 = *SI.getSUnit()->getInstr();
289 if (!QII->isHVXVec(MI2))
290 continue;
291 if ((IsStoreMI1 && MI2.mayStore()) || (IsLoadMI1 && MI2.mayLoad())) {
292 SI.setLatency(1);
293 SU.setHeightDirty();
294 // Change the dependence in the opposite direction too.
295 for (SDep &PI : SI.getSUnit()->Preds) {
296 if (PI.getSUnit() != &SU || PI.getKind() != SDep::Order)
297 continue;
298 PI.setLatency(1);
299 SI.getSUnit()->setDepthDirty();
300 }
301 }
302 }
303 }
304}
305
306// Check if a call and subsequent A2_tfrpi instructions should maintain
307// scheduling affinity. We are looking for the TFRI to be consumed in
308// the next instruction. This should help reduce the instances of
309// double register pairs being allocated and scheduled before a call
310// when not used until after the call. This situation is exacerbated
311// by the fact that we allocate the pair from the callee saves list,
312// leading to excess spills and restores.
313bool HexagonSubtarget::CallMutation::shouldTFRICallBind(
314 const HexagonInstrInfo &HII, const SUnit &Inst1,
315 const SUnit &Inst2) const {
316 if (Inst1.getInstr()->getOpcode() != Hexagon::A2_tfrpi)
317 return false;
318
319 // TypeXTYPE are 64 bit operations.
320 unsigned Type = HII.getType(*Inst2.getInstr());
323}
324
326 ScheduleDAGMI *DAG = static_cast<ScheduleDAGMI*>(DAGInstrs);
327 SUnit* LastSequentialCall = nullptr;
328 // Map from virtual register to physical register from the copy.
329 DenseMap<unsigned, unsigned> VRegHoldingReg;
330 // Map from the physical register to the instruction that uses virtual
331 // register. This is used to create the barrier edge.
332 DenseMap<unsigned, SUnit *> LastVRegUse;
333 auto &TRI = *DAG->MF.getSubtarget().getRegisterInfo();
334 auto &HII = *DAG->MF.getSubtarget<HexagonSubtarget>().getInstrInfo();
335
336 // Currently we only catch the situation when compare gets scheduled
337 // before preceding call.
338 for (unsigned su = 0, e = DAG->SUnits.size(); su != e; ++su) {
339 // Remember the call.
340 if (DAG->SUnits[su].getInstr()->isCall())
341 LastSequentialCall = &DAG->SUnits[su];
342 // Look for a compare that defines a predicate.
343 else if (DAG->SUnits[su].getInstr()->isCompare() && LastSequentialCall)
344 DAG->addEdge(&DAG->SUnits[su], SDep(LastSequentialCall, SDep::Barrier));
345 // Look for call and tfri* instructions.
346 else if (SchedPredsCloser && LastSequentialCall && su > 1 && su < e-1 &&
347 shouldTFRICallBind(HII, DAG->SUnits[su], DAG->SUnits[su+1]))
348 DAG->addEdge(&DAG->SUnits[su], SDep(&DAG->SUnits[su-1], SDep::Barrier));
349 // Prevent redundant register copies due to reads and writes of physical
350 // registers. The original motivation for this was the code generated
351 // between two calls, which are caused both the return value and the
352 // argument for the next call being in %r0.
353 // Example:
354 // 1: <call1>
355 // 2: %vreg = COPY %r0
356 // 3: <use of %vreg>
357 // 4: %r0 = ...
358 // 5: <call2>
359 // The scheduler would often swap 3 and 4, so an additional register is
360 // needed. This code inserts a Barrier dependence between 3 & 4 to prevent
361 // this.
362 // The code below checks for all the physical registers, not just R0/D0/V0.
363 else if (SchedRetvalOptimization) {
364 const MachineInstr *MI = DAG->SUnits[su].getInstr();
365 if (MI->isCopy() && MI->getOperand(1).getReg().isPhysical()) {
366 // %vregX = COPY %r0
367 VRegHoldingReg[MI->getOperand(0).getReg()] = MI->getOperand(1).getReg();
368 LastVRegUse.erase(MI->getOperand(1).getReg());
369 } else {
370 for (const MachineOperand &MO : MI->operands()) {
371 if (!MO.isReg())
372 continue;
373 if (MO.isUse() && !MI->isCopy() &&
374 VRegHoldingReg.count(MO.getReg())) {
375 // <use of %vregX>
376 LastVRegUse[VRegHoldingReg[MO.getReg()]] = &DAG->SUnits[su];
377 } else if (MO.isDef() && MO.getReg().isPhysical()) {
378 for (MCRegAliasIterator AI(MO.getReg(), &TRI, true); AI.isValid();
379 ++AI) {
380 if (auto It = LastVRegUse.find(*AI); It != LastVRegUse.end()) {
381 if (It->second != &DAG->SUnits[su])
382 // %r0 = ...
383 DAG->addEdge(&DAG->SUnits[su],
384 SDep(It->second, SDep::Barrier));
385 LastVRegUse.erase(It);
386 }
387 }
388 }
389 }
390 }
391 }
392 }
393}
394
397 return;
398
399 const auto &HII = static_cast<const HexagonInstrInfo&>(*DAG->TII);
400
401 // Create artificial edges between loads that could likely cause a bank
402 // conflict. Since such loads would normally not have any dependency
403 // between them, we cannot rely on existing edges.
404 for (unsigned i = 0, e = DAG->SUnits.size(); i != e; ++i) {
405 SUnit &S0 = DAG->SUnits[i];
406 MachineInstr &L0 = *S0.getInstr();
407 if (!L0.mayLoad() || L0.mayStore() ||
409 continue;
410 int64_t Offset0;
412 MachineOperand *BaseOp0 = HII.getBaseAndOffset(L0, Offset0, Size0);
413 // Is the access size is longer than the L1 cache line, skip the check.
414 if (BaseOp0 == nullptr || !BaseOp0->isReg() || !Size0.hasValue() ||
415 Size0.getValue() >= 32)
416 continue;
417 // Scan only up to 32 instructions ahead (to avoid n^2 complexity).
418 for (unsigned j = i+1, m = std::min(i+32, e); j != m; ++j) {
419 SUnit &S1 = DAG->SUnits[j];
420 MachineInstr &L1 = *S1.getInstr();
421 if (!L1.mayLoad() || L1.mayStore() ||
423 continue;
424 int64_t Offset1;
426 MachineOperand *BaseOp1 = HII.getBaseAndOffset(L1, Offset1, Size1);
427 if (BaseOp1 == nullptr || !BaseOp1->isReg() || !Size0.hasValue() ||
428 Size1.getValue() >= 32 || BaseOp0->getReg() != BaseOp1->getReg())
429 continue;
430 // Check bits 3 and 4 of the offset: if they differ, a bank conflict
431 // is unlikely.
432 if (((Offset0 ^ Offset1) & 0x18) != 0)
433 continue;
434 // Bits 3 and 4 are the same, add an artificial edge and set extra
435 // latency.
436 SDep A(&S0, SDep::Artificial);
437 A.setLatency(1);
438 S1.addPred(A, true);
439 }
440 }
441}
442
443/// Enable use of alias analysis during code generation (during MI
444/// scheduling, DAGCombine, etc.).
447 return true;
448 return false;
449}
450
451/// Perform target specific adjustments to the latency of a schedule
452/// dependency.
454 SUnit *Src, int SrcOpIdx, SUnit *Dst, int DstOpIdx, SDep &Dep,
455 const TargetSchedModel *SchedModel) const {
456 if (!Src->isInstr() || !Dst->isInstr())
457 return;
458
459 MachineInstr *SrcInst = Src->getInstr();
460 MachineInstr *DstInst = Dst->getInstr();
461 const HexagonInstrInfo *QII = getInstrInfo();
462
463 // Instructions with .new operands have zero latency.
466 if (QII->canExecuteInBundle(*SrcInst, *DstInst) &&
467 isBestZeroLatency(Src, Dst, QII, ExclSrc, ExclDst)) {
468 Dep.setLatency(0);
469 return;
470 }
471
472 // Set the latency for a copy to zero since we hope that is will get
473 // removed.
474 if (DstInst->isCopy())
475 Dep.setLatency(0);
476
477 // If it's a REG_SEQUENCE/COPY, use its destination instruction to determine
478 // the correct latency.
479 // If there are multiple uses of the def of COPY/REG_SEQUENCE, set the latency
480 // only if the latencies on all the uses are equal, otherwise set it to
481 // default.
482 if ((DstInst->isRegSequence() || DstInst->isCopy())) {
483 Register DReg = DstInst->getOperand(0).getReg();
484 std::optional<unsigned> DLatency;
485 for (const auto &DDep : Dst->Succs) {
486 MachineInstr *DDst = DDep.getSUnit()->getInstr();
487 int UseIdx = -1;
488 for (unsigned OpNum = 0; OpNum < DDst->getNumOperands(); OpNum++) {
489 const MachineOperand &MO = DDst->getOperand(OpNum);
490 if (MO.isReg() && MO.getReg() && MO.isUse() && MO.getReg() == DReg) {
491 UseIdx = OpNum;
492 break;
493 }
494 }
495
496 if (UseIdx == -1)
497 continue;
498
499 std::optional<unsigned> Latency =
500 InstrInfo.getOperandLatency(&InstrItins, *SrcInst, 0, *DDst, UseIdx);
501
502 // Set DLatency for the first time.
503 if (!DLatency)
504 DLatency = Latency;
505
506 // For multiple uses, if the Latency is different across uses, reset
507 // DLatency.
508 if (DLatency != Latency) {
509 DLatency = std::nullopt;
510 break;
511 }
512 }
513 Dep.setLatency(DLatency.value_or(0));
514 }
515
516 // Try to schedule uses near definitions to generate .cur.
517 ExclSrc.clear();
518 ExclDst.clear();
519 if (EnableDotCurSched && QII->isToBeScheduledASAP(*SrcInst, *DstInst) &&
520 isBestZeroLatency(Src, Dst, QII, ExclSrc, ExclDst)) {
521 Dep.setLatency(0);
522 return;
523 }
524 int Latency = Dep.getLatency();
525 bool IsArtificial = Dep.isArtificial();
526 Latency = updateLatency(*SrcInst, *DstInst, IsArtificial, Latency);
527 Dep.setLatency(Latency);
528}
529
531 std::vector<std::unique_ptr<ScheduleDAGMutation>> &Mutations) const {
532 Mutations.push_back(std::make_unique<UsrOverflowMutation>());
533 Mutations.push_back(std::make_unique<HVXMemLatencyMutation>());
534 Mutations.push_back(std::make_unique<BankConflictMutation>());
535}
536
538 std::vector<std::unique_ptr<ScheduleDAGMutation>> &Mutations) const {
539 Mutations.push_back(std::make_unique<UsrOverflowMutation>());
540 Mutations.push_back(std::make_unique<HVXMemLatencyMutation>());
541}
542
543// Pin the vtable to this file.
544void HexagonSubtarget::anchor() {}
545
547 if (DisableHexagonMISched.getNumOccurrences())
548 return !DisableHexagonMISched;
549 return true;
550}
551
555
556int HexagonSubtarget::updateLatency(MachineInstr &SrcInst,
557 MachineInstr &DstInst, bool IsArtificial,
558 int Latency) const {
559 if (IsArtificial)
560 return 1;
561 if (!hasV60Ops())
562 return Latency;
563
564 const HexagonInstrInfo &QII = *getInstrInfo();
565 // BSB scheduling.
566 if (QII.isHVXVec(SrcInst) || useBSBScheduling())
567 Latency = (Latency + 1) >> 1;
568 return Latency;
569}
570
571void HexagonSubtarget::restoreLatency(SUnit *Src, SUnit *Dst) const {
572 MachineInstr *SrcI = Src->getInstr();
573 for (auto &I : Src->Succs) {
574 if (!I.isAssignedRegDep() || I.getSUnit() != Dst)
575 continue;
576 Register DepR = I.getReg();
577 int DefIdx = -1;
578 for (unsigned OpNum = 0; OpNum < SrcI->getNumOperands(); OpNum++) {
579 const MachineOperand &MO = SrcI->getOperand(OpNum);
580 bool IsSameOrSubReg = false;
581 if (MO.isReg()) {
582 Register MOReg = MO.getReg();
583 if (DepR.isVirtual()) {
584 IsSameOrSubReg = (MOReg == DepR);
585 } else {
586 IsSameOrSubReg = getRegisterInfo()->isSubRegisterEq(DepR, MOReg);
587 }
588 if (MO.isDef() && IsSameOrSubReg)
589 DefIdx = OpNum;
590 }
591 }
592 assert(DefIdx >= 0 && "Def Reg not found in Src MI");
593 MachineInstr *DstI = Dst->getInstr();
594 SDep T = I;
595 for (unsigned OpNum = 0; OpNum < DstI->getNumOperands(); OpNum++) {
596 const MachineOperand &MO = DstI->getOperand(OpNum);
597 if (MO.isReg() && MO.isUse() && MO.getReg() == DepR) {
598 std::optional<unsigned> Latency = InstrInfo.getOperandLatency(
599 &InstrItins, *SrcI, DefIdx, *DstI, OpNum);
600
601 // For some instructions (ex: COPY), we might end up with < 0 latency
602 // as they don't have any Itinerary class associated with them.
603 if (!Latency)
604 Latency = 0;
605 bool IsArtificial = I.isArtificial();
606 Latency = updateLatency(*SrcI, *DstI, IsArtificial, *Latency);
607 I.setLatency(*Latency);
608 }
609 }
610
611 // Update the latency of opposite edge too.
612 T.setSUnit(Src);
613 auto F = find(Dst->Preds, T);
614 assert(F != Dst->Preds.end());
615 F->setLatency(I.getLatency());
616 }
617}
618
619/// Change the latency between the two SUnits.
620void HexagonSubtarget::changeLatency(SUnit *Src, SUnit *Dst, unsigned Lat)
621 const {
622 for (auto &I : Src->Succs) {
623 if (!I.isAssignedRegDep() || I.getSUnit() != Dst)
624 continue;
625 SDep T = I;
626 I.setLatency(Lat);
627
628 // Update the latency of opposite edge too.
629 T.setSUnit(Src);
630 auto F = find(Dst->Preds, T);
631 assert(F != Dst->Preds.end());
632 F->setLatency(Lat);
633 }
634}
635
636/// If the SUnit has a zero latency edge, return the other SUnit.
638 for (auto &I : Deps)
639 if (I.isAssignedRegDep() && I.getLatency() == 0 &&
640 !I.getSUnit()->getInstr()->isPseudo())
641 return I.getSUnit();
642 return nullptr;
643}
644
645// Return true if these are the best two instructions to schedule
646// together with a zero latency. Only one dependence should have a zero
647// latency. If there are multiple choices, choose the best, and change
648// the others, if needed.
649bool HexagonSubtarget::isBestZeroLatency(
650 SUnit *Src, SUnit *Dst, const HexagonInstrInfo *TII,
651 SmallPtrSet<SUnit *, 4> &ExclSrc, SmallPtrSet<SUnit *, 4> &ExclDst) const {
652 MachineInstr &SrcInst = *Src->getInstr();
653 MachineInstr &DstInst = *Dst->getInstr();
654
655 // Ignore Boundary SU nodes as these have null instructions.
656 if (Dst->isBoundaryNode())
657 return false;
658
659 if (SrcInst.isPHI() || DstInst.isPHI())
660 return false;
661
662 if (!TII->isToBeScheduledASAP(SrcInst, DstInst) &&
663 !TII->canExecuteInBundle(SrcInst, DstInst))
664 return false;
665
666 // The architecture doesn't allow three dependent instructions in the same
667 // packet. So, if the destination has a zero latency successor, then it's
668 // not a candidate for a zero latency predecessor.
669 if (getZeroLatency(Dst, Dst->Succs) != nullptr)
670 return false;
671
672 // Check if the Dst instruction is the best candidate first.
673 SUnit *Best = nullptr;
674 SUnit *DstBest = nullptr;
675 SUnit *SrcBest = getZeroLatency(Dst, Dst->Preds);
676 if (SrcBest == nullptr || Src->NodeNum >= SrcBest->NodeNum) {
677 // Check that Src doesn't have a better candidate.
678 DstBest = getZeroLatency(Src, Src->Succs);
679 if (DstBest == nullptr || Dst->NodeNum <= DstBest->NodeNum)
680 Best = Dst;
681 }
682 if (Best != Dst)
683 return false;
684
685 // The caller frequently adds the same dependence twice. If so, then
686 // return true for this case too.
687 if ((Src == SrcBest && Dst == DstBest ) ||
688 (SrcBest == nullptr && Dst == DstBest) ||
689 (Src == SrcBest && Dst == nullptr))
690 return true;
691
692 // Reassign the latency for the previous bests, which requires setting
693 // the dependence edge in both directions.
694 if (SrcBest != nullptr) {
695 if (!hasV60Ops())
696 changeLatency(SrcBest, Dst, 1);
697 else
698 restoreLatency(SrcBest, Dst);
699 }
700 if (DstBest != nullptr) {
701 if (!hasV60Ops())
702 changeLatency(Src, DstBest, 1);
703 else
704 restoreLatency(Src, DstBest);
705 }
706
707 // Attempt to find another opportunity for zero latency in a different
708 // dependence.
709 if (SrcBest && DstBest)
710 // If there is an edge from SrcBest to DstBst, then try to change that
711 // to 0 now.
712 changeLatency(SrcBest, DstBest, 0);
713 else if (DstBest) {
714 // Check if the previous best destination instruction has a new zero
715 // latency dependence opportunity.
716 ExclSrc.insert(Src);
717 for (auto &I : DstBest->Preds)
718 if (ExclSrc.count(I.getSUnit()) == 0 &&
719 isBestZeroLatency(I.getSUnit(), DstBest, TII, ExclSrc, ExclDst))
720 changeLatency(I.getSUnit(), DstBest, 0);
721 } else if (SrcBest) {
722 // Check if previous best source instruction has a new zero latency
723 // dependence opportunity.
724 ExclDst.insert(Dst);
725 for (auto &I : SrcBest->Succs)
726 if (ExclDst.count(I.getSUnit()) == 0 &&
727 isBestZeroLatency(SrcBest, I.getSUnit(), TII, ExclSrc, ExclDst))
728 changeLatency(SrcBest, I.getSUnit(), 0);
729 }
730
731 return true;
732}
733
735 return 32;
736}
737
739 return 32;
740}
741
742bool HexagonSubtarget::enableSubRegLiveness() const { return true; }
743
745 struct Scalar {
746 unsigned Opcode;
747 Intrinsic::ID IntId;
748 };
749 struct Hvx {
750 unsigned Opcode;
751 Intrinsic::ID Int64Id, Int128Id;
752 };
753
754 static Scalar ScalarInts[] = {
755#define GET_SCALAR_INTRINSICS
757#undef GET_SCALAR_INTRINSICS
758 };
759
760 static Hvx HvxInts[] = {
761#define GET_HVX_INTRINSICS
763#undef GET_HVX_INTRINSICS
764 };
765
766 const auto CmpOpcode = [](auto A, auto B) { return A.Opcode < B.Opcode; };
767 [[maybe_unused]] static bool SortedScalar =
768 (llvm::sort(ScalarInts, CmpOpcode), true);
769 [[maybe_unused]] static bool SortedHvx =
770 (llvm::sort(HvxInts, CmpOpcode), true);
771
772 auto [BS, ES] = std::make_pair(std::begin(ScalarInts), std::end(ScalarInts));
773 auto [BH, EH] = std::make_pair(std::begin(HvxInts), std::end(HvxInts));
774
775 auto FoundScalar = std::lower_bound(BS, ES, Scalar{Opc, 0}, CmpOpcode);
776 if (FoundScalar != ES && FoundScalar->Opcode == Opc)
777 return FoundScalar->IntId;
778
779 auto FoundHvx = std::lower_bound(BH, EH, Hvx{Opc, 0, 0}, CmpOpcode);
780 if (FoundHvx != EH && FoundHvx->Opcode == Opc) {
781 unsigned HwLen = getVectorLength();
782 if (HwLen == 64)
783 return FoundHvx->Int64Id;
784 if (HwLen == 128)
785 return FoundHvx->Int128Id;
786 }
787
788 std::string error = "Invalid opcode (" + std::to_string(Opc) + ")";
789 llvm_unreachable(error.c_str());
790 return 0;
791}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
constexpr LLT S1
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
const HexagonInstrInfo * TII
static cl::opt< bool > DisableHexagonMISched("disable-hexagon-misched", cl::Hidden, cl::desc("Disable Hexagon MI Scheduling"))
static cl::opt< bool > EnableDotCurSched("enable-cur-sched", cl::Hidden, cl::init(true), cl::desc("Enable the scheduler to generate .cur"))
static cl::opt< bool > EnableCheckBankConflict("hexagon-check-bank-conflict", cl::Hidden, cl::init(true), cl::desc("Enable checking for cache bank conflicts"))
static cl::opt< bool > OverrideLongCalls("hexagon-long-calls", cl::Hidden, cl::desc("If present, forces/disables the use of long calls"))
static cl::opt< bool > SchedPredsCloser("sched-preds-closer", cl::Hidden, cl::init(true))
static cl::opt< bool > SchedRetvalOptimization("sched-retval-optimization", cl::Hidden, cl::init(true))
static cl::opt< bool > EnableTCLatencySched("enable-tc-latency-sched", cl::Hidden, cl::init(false))
static cl::opt< bool > EnableBSBSched("enable-bsb-sched", cl::Hidden, cl::init(true))
static SUnit * getZeroLatency(SUnit *N, SmallVector< SDep, 4 > &Deps)
If the SUnit has a zero latency edge, return the other SUnit.
static cl::opt< bool > EnablePredicatedCalls("hexagon-pred-calls", cl::Hidden, cl::desc("Consider calls to be predicable"))
IRTranslator LLVM IR MI
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Register const TargetRegisterInfo * TRI
#define T
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
#define LLVM_DEBUG(...)
Definition Debug.h:119
#define error(X)
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:223
bool erase(const KeyT &Val)
Definition DenseMap.h:377
size_type count(const_arg_type_t< KeyT > Val) const
Return 1 if the specified key is in the map, 0 otherwise.
Definition DenseMap.h:219
iterator end()
Definition DenseMap.h:141
Container class for subtarget features.
constexpr FeatureBitset & reset(unsigned I)
unsigned getAddrMode(const MachineInstr &MI) const
bool canExecuteInBundle(const MachineInstr &First, const MachineInstr &Second) const
Can these instructions execute at the same time in a bundle.
bool isHVXVec(const MachineInstr &MI) const
bool isToBeScheduledASAP(const MachineInstr &MI1, const MachineInstr &MI2) const
MachineOperand * getBaseAndOffset(const MachineInstr &MI, int64_t &Offset, LocationSize &AccessSize) const
uint64_t getType(const MachineInstr &MI) const
Hexagon::ArchEnum HexagonArchVersion
void adjustSchedDependency(SUnit *Def, int DefOpIdx, SUnit *Use, int UseOpIdx, SDep &Dep, const TargetSchedModel *SchedModel) const override
Perform target specific adjustments to the latency of a schedule dependency.
const HexagonInstrInfo * getInstrInfo() const override
const HexagonRegisterInfo * getRegisterInfo() const override
void getSMSMutations(std::vector< std::unique_ptr< ScheduleDAGMutation > > &Mutations) const override
HexagonSubtarget(const Triple &TT, StringRef CPU, StringRef FS, const TargetMachine &TM)
bool isHVXVectorType(EVT VecTy, bool IncludeBool=false) const
void getPostRAMutations(std::vector< std::unique_ptr< ScheduleDAGMutation > > &Mutations) const override
const HexagonTargetLowering * getTargetLowering() const override
bool UseBSBScheduling
True if the target should use Back-Skip-Back scheduling.
unsigned getL1PrefetchDistance() const
ArrayRef< MVT > getHVXElementTypes() const
bool enableSubRegLiveness() const override
unsigned getVectorLength() const
void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS)
ParseSubtargetFeatures - Parses features string setting specified subtarget options.
unsigned getL1CacheLineSize() const
bool isTypeForHVX(Type *VecTy, bool IncludeBool=false) const
Intrinsic::ID getIntrinsicId(unsigned Opc) const
HexagonSubtarget & initializeSubtargetDependencies(StringRef CPU, StringRef FS)
bool enableMachineScheduler() const override
bool isHVXElementType(MVT Ty, bool IncludeBool=false) const
bool useAA() const override
Enable use of alias analysis during code generation (during MI scheduling, DAGCombine,...
LegalizeTypeAction getPreferredVectorAction(MVT VT) const override
Return the preferred vector type legalization action.
bool hasValue() const
static LocationSize precise(uint64_t Value)
TypeSize getValue() const
MCRegAliasIterator enumerates all registers aliasing Reg.
Machine Value Type.
static MVT getVectorVT(MVT VT, unsigned NumElements)
MVT getVectorElementType() const
bool isValid() const
Return true if this is a valid simple valuetype.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
bool isCopy() const
unsigned getNumOperands() const
Retuns the total number of operands.
bool mayLoad(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly read memory.
bool isRegSequence() const
bool mayStore(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly modify memory.
const MachineOperand & getOperand(unsigned i) const
MachineOperand class - Representation of each machine instruction operand.
bool isReg() const
isReg - Tests if this is a MO_Register operand.
Register getReg() const
getReg - Returns the register number.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
constexpr bool isVirtual() const
Return true if the specified register number is in the virtual register namespace.
Definition Register.h:79
Scheduling dependency.
Definition ScheduleDAG.h:52
@ Output
A register output-dependence (aka WAW).
Definition ScheduleDAG.h:58
@ Order
Any other ordering dependency.
Definition ScheduleDAG.h:59
void setLatency(unsigned Lat)
Sets the latency for this edge.
@ Barrier
An unknown scheduling barrier.
Definition ScheduleDAG.h:72
@ Artificial
Arbitrary strong DAG edge (no real dependence).
Definition ScheduleDAG.h:75
unsigned getLatency() const
Returns the latency value for this edge, which roughly means the minimum number of cycles that must e...
bool isArtificial() const
Tests if this is an Order dependence that is marked as "artificial", meaning it isn't necessary for c...
Scheduling unit. This is a node in the scheduling DAG.
bool isInstr() const
Returns true if this SUnit refers to a machine instruction as opposed to an SDNode.
unsigned NodeNum
Entry # of node in the node vector.
LLVM_ABI void setHeightDirty()
Sets a flag in this node to indicate that its stored Height value will require recomputation the next...
LLVM_ABI void removePred(const SDep &D)
Removes the specified edge as a pred of the current node if it exists.
SmallVector< SDep, 4 > Succs
All sunit successors.
SmallVector< SDep, 4 > Preds
All sunit predecessors.
MachineInstr * getInstr() const
Returns the representative MachineInstr for this SUnit.
A ScheduleDAG for scheduling lists of MachineInstr.
bool addEdge(SUnit *SuccSU, const SDep &PredDep)
Add a DAG edge to the given SU with the given predecessor dependence data.
ScheduleDAGMI is an implementation of ScheduleDAGInstrs that simply schedules machine instructions ac...
const TargetInstrInfo * TII
Target instruction information.
std::vector< SUnit > SUnits
The scheduling units.
MachineFunction & MF
Machine function.
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
bool consumeInteger(unsigned Radix, T &Result)
Parse the current string as an integer of the specified radix.
Definition StringRef.h:519
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition StringRef.h:258
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
Definition StringRef.h:635
Manages the enabling and disabling of subtarget specific features.
const std::vector< std::string > & getFeatures() const
Returns the vector of individual subtarget features.
LLVM_ABI std::string getString() const
Returns features as a string.
LLVM_ABI void AddFeature(StringRef String, bool Enable=true)
Adds Features.
Primary interface to the complete machine description for the target machine.
Provide an instruction scheduling machine model to CodeGen passes.
virtual const TargetRegisterInfo * getRegisterInfo() const =0
Return the target's register information.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
bool isVectorTy() const
True if this is an instance of VectorType.
Definition Type.h:288
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
Definition Type.h:368
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:257
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
void addArchSubtarget(MCSubtargetInfo const *STI, StringRef FS)
FeatureBitset completeHVXFeatures(const FeatureBitset &FB)
std::optional< Hexagon::ArchEnum > getCpu(StringRef CPU)
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
auto find(R &&Range, const T &Val)
Provide wrappers to std::find which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1765
uint64_t PowerOf2Ceil(uint64_t A)
Returns the power of two which is greater than or equal to the given value.
Definition MathExtras.h:386
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1636
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
auto count_if(R &&Range, UnaryPredicate P)
Wrapper function around std::count_if to count the number of times an element satisfying a given pred...
Definition STLExtras.h:2019
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1947
cl::opt< bool > HexagonDisableDuplex
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878
#define N
Extended Value Type.
Definition ValueTypes.h:35
bool isSimple() const
Test if the given EVT is simple (as opposed to being extended).
Definition ValueTypes.h:145
TypeSize getSizeInBits() const
Return the size of the specified value type in bits.
Definition ValueTypes.h:396
static LLVM_ABI EVT getEVT(Type *Ty, bool HandleUnknown=false)
Return the value type corresponding to the specified type.
MVT getSimpleVT() const
Return the SimpleValueType held in the specified simple EVT.
Definition ValueTypes.h:339
bool isVector() const
Return true if this is a vector value type.
Definition ValueTypes.h:176
bool isScalableVector() const
Return true if this is a vector type where the runtime length is machine dependent.
Definition ValueTypes.h:187
unsigned getVectorNumElements() const
Given a vector type, return the number of elements it contains.
Definition ValueTypes.h:359
void apply(ScheduleDAGInstrs *DAG) override
void apply(ScheduleDAGInstrs *DAG) override
void apply(ScheduleDAGInstrs *DAG) override
void apply(ScheduleDAGInstrs *DAG) override