Skip to content

Commit 9468de4

Browse files
authored
TargetInstrInfo: make getOperandLatency return optional (NFC) (#73769)
getOperandLatency has the following behavior: it returns -1 as a special value, negative numbers other than -1 on some target-specific overrides, or a valid non-negative latency. This behavior can be surprising, as some callers do arithmetic on these negative values. Change the interface of getOperandLatency to return a std::optional<unsigned> to prevent surprises in callers. While at it, change the interface of getInstrLatency to return unsigned instead of int. This change was inspired by a refactoring in TargetSchedModel::computeOperandLatency.
1 parent 460faa0 commit 9468de4

File tree

13 files changed

+209
-206
lines changed

13 files changed

+209
-206
lines changed

llvm/include/llvm/CodeGen/TargetInstrInfo.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,9 +1706,9 @@ class TargetInstrInfo : public MCInstrInfo {
17061706
return Opcode <= TargetOpcode::COPY;
17071707
}
17081708

1709-
virtual int getOperandLatency(const InstrItineraryData *ItinData,
1710-
SDNode *DefNode, unsigned DefIdx,
1711-
SDNode *UseNode, unsigned UseIdx) const;
1709+
virtual std::optional<unsigned>
1710+
getOperandLatency(const InstrItineraryData *ItinData, SDNode *DefNode,
1711+
unsigned DefIdx, SDNode *UseNode, unsigned UseIdx) const;
17121712

17131713
/// Compute and return the use operand latency of a given pair of def and use.
17141714
/// In most cases, the static scheduling itinerary was enough to determine the
@@ -1718,10 +1718,10 @@ class TargetInstrInfo : public MCInstrInfo {
17181718
/// This is a raw interface to the itinerary that may be directly overridden
17191719
/// by a target. Use computeOperandLatency to get the best estimate of
17201720
/// latency.
1721-
virtual int getOperandLatency(const InstrItineraryData *ItinData,
1722-
const MachineInstr &DefMI, unsigned DefIdx,
1723-
const MachineInstr &UseMI,
1724-
unsigned UseIdx) const;
1721+
virtual std::optional<unsigned>
1722+
getOperandLatency(const InstrItineraryData *ItinData,
1723+
const MachineInstr &DefMI, unsigned DefIdx,
1724+
const MachineInstr &UseMI, unsigned UseIdx) const;
17251725

17261726
/// Compute the instruction latency of a given instruction.
17271727
/// If the instruction has higher cost when predicated, it's returned via
@@ -1732,8 +1732,8 @@ class TargetInstrInfo : public MCInstrInfo {
17321732

17331733
virtual unsigned getPredicationCost(const MachineInstr &MI) const;
17341734

1735-
virtual int getInstrLatency(const InstrItineraryData *ItinData,
1736-
SDNode *Node) const;
1735+
virtual unsigned getInstrLatency(const InstrItineraryData *ItinData,
1736+
SDNode *Node) const;
17371737

17381738
/// Return the default expected latency for a def based on its opcode.
17391739
unsigned defaultDefLatency(const MCSchedModel &SchedModel,

llvm/include/llvm/MC/MCInstrItineraries.h

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "llvm/MC/MCSchedule.h"
1919
#include <algorithm>
20+
#include <optional>
2021

2122
namespace llvm {
2223

@@ -162,18 +163,19 @@ class InstrItineraryData {
162163
return Latency;
163164
}
164165

165-
/// Return the cycle for the given class and operand. Return -1 if no
166-
/// cycle is specified for the operand.
167-
int getOperandCycle(unsigned ItinClassIndx, unsigned OperandIdx) const {
166+
/// Return the cycle for the given class and operand. Return std::nullopt if
167+
/// the information is not available for the operand.
168+
std::optional<unsigned> getOperandCycle(unsigned ItinClassIndx,
169+
unsigned OperandIdx) const {
168170
if (isEmpty())
169-
return -1;
171+
return std::nullopt;
170172

171173
unsigned FirstIdx = Itineraries[ItinClassIndx].FirstOperandCycle;
172174
unsigned LastIdx = Itineraries[ItinClassIndx].LastOperandCycle;
173175
if ((FirstIdx + OperandIdx) >= LastIdx)
174-
return -1;
176+
return std::nullopt;
175177

176-
return (int)OperandCycles[FirstIdx + OperandIdx];
178+
return OperandCycles[FirstIdx + OperandIdx];
177179
}
178180

179181
/// Return true if there is a pipeline forwarding between instructions
@@ -201,25 +203,27 @@ class InstrItineraryData {
201203

202204
/// Compute and return the use operand latency of a given itinerary
203205
/// class and operand index if the value is produced by an instruction of the
204-
/// specified itinerary class and def operand index.
205-
int getOperandLatency(unsigned DefClass, unsigned DefIdx,
206-
unsigned UseClass, unsigned UseIdx) const {
206+
/// specified itinerary class and def operand index. Return std::nullopt if
207+
/// the information is not available for the operand.
208+
std::optional<unsigned> getOperandLatency(unsigned DefClass, unsigned DefIdx,
209+
unsigned UseClass,
210+
unsigned UseIdx) const {
207211
if (isEmpty())
208-
return -1;
212+
return std::nullopt;
209213

210-
int DefCycle = getOperandCycle(DefClass, DefIdx);
211-
if (DefCycle == -1)
212-
return -1;
214+
std::optional<unsigned> DefCycle = getOperandCycle(DefClass, DefIdx);
215+
std::optional<unsigned> UseCycle = getOperandCycle(UseClass, UseIdx);
216+
if (!DefCycle || !UseCycle)
217+
return std::nullopt;
213218

214-
int UseCycle = getOperandCycle(UseClass, UseIdx);
215-
if (UseCycle == -1)
216-
return -1;
219+
if (UseCycle > *DefCycle + 1)
220+
return std::nullopt;
217221

218-
UseCycle = DefCycle - UseCycle + 1;
222+
UseCycle = *DefCycle - *UseCycle + 1;
219223
if (UseCycle > 0 &&
220224
hasPipelineForwarding(DefClass, DefIdx, UseClass, UseIdx))
221225
// FIXME: This assumes one cycle benefit for every pipeline forwarding.
222-
--UseCycle;
226+
UseCycle = *UseCycle - 1;
223227
return UseCycle;
224228
}
225229

llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -659,18 +659,19 @@ void ScheduleDAGSDNodes::computeOperandLatency(SDNode *Def, SDNode *Use,
659659
if (Use->isMachineOpcode())
660660
// Adjust the use operand index by num of defs.
661661
OpIdx += TII->get(Use->getMachineOpcode()).getNumDefs();
662-
int Latency = TII->getOperandLatency(InstrItins, Def, DefIdx, Use, OpIdx);
662+
std::optional<unsigned> Latency =
663+
TII->getOperandLatency(InstrItins, Def, DefIdx, Use, OpIdx);
663664
if (Latency > 1 && Use->getOpcode() == ISD::CopyToReg &&
664665
!BB->succ_empty()) {
665666
unsigned Reg = cast<RegisterSDNode>(Use->getOperand(1))->getReg();
666667
if (Register::isVirtualRegister(Reg))
667668
// This copy is a liveout value. It is likely coalesced, so reduce the
668669
// latency so not to penalize the def.
669670
// FIXME: need target specific adjustment here?
670-
Latency = Latency - 1;
671+
Latency = *Latency - 1;
671672
}
672-
if (Latency >= 0)
673-
dep.setLatency(Latency);
673+
if (Latency)
674+
dep.setLatency(*Latency);
674675
}
675676

676677
void ScheduleDAGSDNodes::dumpNode(const SUnit &SU) const {

llvm/lib/CodeGen/TargetInstrInfo.cpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,15 +1379,15 @@ bool TargetInstrInfo::getMemOperandWithOffset(
13791379
// SelectionDAG latency interface.
13801380
//===----------------------------------------------------------------------===//
13811381

1382-
int
1382+
std::optional<unsigned>
13831383
TargetInstrInfo::getOperandLatency(const InstrItineraryData *ItinData,
13841384
SDNode *DefNode, unsigned DefIdx,
13851385
SDNode *UseNode, unsigned UseIdx) const {
13861386
if (!ItinData || ItinData->isEmpty())
1387-
return -1;
1387+
return std::nullopt;
13881388

13891389
if (!DefNode->isMachineOpcode())
1390-
return -1;
1390+
return std::nullopt;
13911391

13921392
unsigned DefClass = get(DefNode->getMachineOpcode()).getSchedClass();
13931393
if (!UseNode->isMachineOpcode())
@@ -1396,8 +1396,8 @@ TargetInstrInfo::getOperandLatency(const InstrItineraryData *ItinData,
13961396
return ItinData->getOperandLatency(DefClass, DefIdx, UseClass, UseIdx);
13971397
}
13981398

1399-
int TargetInstrInfo::getInstrLatency(const InstrItineraryData *ItinData,
1400-
SDNode *N) const {
1399+
unsigned TargetInstrInfo::getInstrLatency(const InstrItineraryData *ItinData,
1400+
SDNode *N) const {
14011401
if (!ItinData || ItinData->isEmpty())
14021402
return 1;
14031403

@@ -1461,8 +1461,9 @@ bool TargetInstrInfo::hasLowDefLatency(const TargetSchedModel &SchedModel,
14611461
return false;
14621462

14631463
unsigned DefClass = DefMI.getDesc().getSchedClass();
1464-
int DefCycle = ItinData->getOperandCycle(DefClass, DefIdx);
1465-
return (DefCycle != -1 && DefCycle <= 1);
1464+
std::optional<unsigned> DefCycle =
1465+
ItinData->getOperandCycle(DefClass, DefIdx);
1466+
return DefCycle <= 1;
14661467
}
14671468

14681469
bool TargetInstrInfo::isFunctionSafeToSplit(const MachineFunction &MF) const {
@@ -1580,11 +1581,9 @@ unsigned TargetInstrInfo::getCallFrameSizeAt(MachineInstr &MI) const {
15801581

15811582
/// Both DefMI and UseMI must be valid. By default, call directly to the
15821583
/// itinerary. This may be overriden by the target.
1583-
int TargetInstrInfo::getOperandLatency(const InstrItineraryData *ItinData,
1584-
const MachineInstr &DefMI,
1585-
unsigned DefIdx,
1586-
const MachineInstr &UseMI,
1587-
unsigned UseIdx) const {
1584+
std::optional<unsigned> TargetInstrInfo::getOperandLatency(
1585+
const InstrItineraryData *ItinData, const MachineInstr &DefMI,
1586+
unsigned DefIdx, const MachineInstr &UseMI, unsigned UseIdx) const {
15881587
unsigned DefClass = DefMI.getDesc().getSchedClass();
15891588
unsigned UseClass = UseMI.getDesc().getSchedClass();
15901589
return ItinData->getOperandLatency(DefClass, DefIdx, UseClass, UseIdx);

llvm/lib/CodeGen/TargetSchedule.cpp

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -168,16 +168,20 @@ static unsigned findUseIdx(const MachineInstr *MI, unsigned UseOperIdx) {
168168
return UseIdx;
169169
}
170170

171-
// Top-level API for clients that know the operand indices.
171+
// Top-level API for clients that know the operand indices. This doesn't need to
172+
// return std::optional<unsigned>, as it always returns a valid latency.
172173
unsigned TargetSchedModel::computeOperandLatency(
173174
const MachineInstr *DefMI, unsigned DefOperIdx,
174175
const MachineInstr *UseMI, unsigned UseOperIdx) const {
175176

177+
const unsigned InstrLatency = computeInstrLatency(DefMI);
178+
const unsigned DefaultDefLatency = TII->defaultDefLatency(SchedModel, *DefMI);
179+
176180
if (!hasInstrSchedModel() && !hasInstrItineraries())
177-
return TII->defaultDefLatency(SchedModel, *DefMI);
181+
return InstrLatency;
178182

179183
if (hasInstrItineraries()) {
180-
int OperLatency = 0;
184+
std::optional<unsigned> OperLatency;
181185
if (UseMI) {
182186
OperLatency = TII->getOperandLatency(&InstrItins, *DefMI, DefOperIdx,
183187
*UseMI, UseOperIdx);
@@ -186,21 +190,13 @@ unsigned TargetSchedModel::computeOperandLatency(
186190
unsigned DefClass = DefMI->getDesc().getSchedClass();
187191
OperLatency = InstrItins.getOperandCycle(DefClass, DefOperIdx);
188192
}
189-
if (OperLatency >= 0)
190-
return OperLatency;
191-
192-
// No operand latency was found.
193-
unsigned InstrLatency = TII->getInstrLatency(&InstrItins, *DefMI);
194-
195-
// Expected latency is the max of the stage latency and itinerary props.
196-
// Rather than directly querying InstrItins stage latency, we call a TII
197-
// hook to allow subtargets to specialize latency. This hook is only
198-
// applicable to the InstrItins model. InstrSchedModel should model all
199-
// special cases without TII hooks.
200-
InstrLatency =
201-
std::max(InstrLatency, TII->defaultDefLatency(SchedModel, *DefMI));
202-
return InstrLatency;
193+
194+
// Expected latency is the max of InstrLatency and DefaultDefLatency, if we
195+
// didn't find an operand latency.
196+
return OperLatency ? *OperLatency
197+
: std::max(InstrLatency, DefaultDefLatency);
203198
}
199+
204200
// hasInstrSchedModel()
205201
const MCSchedClassDesc *SCDesc = resolveSchedClass(DefMI);
206202
unsigned DefIdx = findDefIdx(DefMI, DefOperIdx);
@@ -237,7 +233,7 @@ unsigned TargetSchedModel::computeOperandLatency(
237233
// FIXME: Automatically giving all implicit defs defaultDefLatency is
238234
// undesirable. We should only do it for defs that are known to the MC
239235
// desc like flags. Truly implicit defs should get 1 cycle latency.
240-
return DefMI->isTransient() ? 0 : TII->defaultDefLatency(SchedModel, *DefMI);
236+
return DefMI->isTransient() ? 0 : DefaultDefLatency;
241237
}
242238

243239
unsigned

llvm/lib/MC/MCDisassembler/Disassembler.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,13 @@ static int getItineraryLatency(LLVMDisasmContext *DC, const MCInst &Inst) {
180180
const MCInstrDesc& Desc = DC->getInstrInfo()->get(Inst.getOpcode());
181181
unsigned SCClass = Desc.getSchedClass();
182182

183-
int Latency = 0;
184-
for (unsigned OpIdx = 0, OpIdxEnd = Inst.getNumOperands(); OpIdx != OpIdxEnd;
185-
++OpIdx)
186-
Latency = std::max(Latency, IID.getOperandCycle(SCClass, OpIdx));
183+
unsigned Latency = 0;
187184

188-
return Latency;
185+
for (unsigned Idx = 0, IdxEnd = Inst.getNumOperands(); Idx != IdxEnd; ++Idx)
186+
if (std::optional<unsigned> OperCycle = IID.getOperandCycle(SCClass, Idx))
187+
Latency = std::max(Latency, *OperCycle);
188+
189+
return (int)Latency;
189190
}
190191

191192
/// Gets latency information for \p Inst, based on \p DC information.

0 commit comments

Comments
 (0)