Skip to content

Commit 4a9fdda

Browse files
[MCA] Enable customization of individual instructions (#155420)
Currently MCA takes instruction properties from scheduling model. However, some instructions may execute differently depending on external factors - for example, latency of memory instructions may vary differently depending on whether the load comes from L1 cache, L2 or DRAM. While MCA as a static analysis tool cannot model such differences (and currently takes some static decision, e.g. all memory ops are treated as L1 accesses), it makes sense to allow manual modification of instruction properties to model different behavior (e.g. sensitivity of code performance to cache misses in particular load instruction). This patch addresses this need. The library modification is intentionally generic - arbitrary modifications to InstrDesc are allowed. The tool support is currently limited to changing instruction latencies (single number applies to all output arguments and MaxLatency) via coments in the input assembler code; the format is the like this: add (%eax), eax // LLVM-MCA-LATENCY:100 Users of MCA library can already make additional customizations; command line tool can be extended in the future. Note that InstructionView currently shows per-instruction information according to scheduling model and is not affected by this change. See #133429 for additional clarifications (including explanation why existing customization mechanisms do not provide required functionality) --------- Co-authored-by: Min-Yih Hsu <[email protected]>
1 parent c91fa95 commit 4a9fdda

File tree

11 files changed

+175
-21
lines changed

11 files changed

+175
-21
lines changed

llvm/docs/CommandGuide/llvm-mca.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,20 @@ that do not start with `LLVM-MCA-` are ignored by :program:`llvm-mca`.
383383
An instruction (a MCInst) is added to an InstrumentRegion R only
384384
if its location is in range [R.RangeStart, R.RangeEnd].
385385

386+
There is one instrument that can be used on all targets to explicitly
387+
set instruction latencies. It can be used, for example, to model the
388+
cache misses that impact load latencies. The syntax is like
389+
390+
.. code-block:: none
391+
392+
# LLVM-MCA-LATENCY 100
393+
mov (%edi), %eax
394+
# LLVM-MCA-LATENCY
395+
396+
It sets the latency of mov instruction to 100. LLVM-MCA-LATENCY without
397+
argument ends the region with explicit latency, after it default target
398+
latencies are used.
399+
386400
On RISCV targets, vector instructions have different behaviour depending
387401
on the LMUL. Code can be instrumented with a comment that takes the
388402
following form:

llvm/include/llvm/MCA/CustomBehaviour.h

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,25 @@ class Instrument {
134134
StringRef getData() const { return Data; }
135135
};
136136

137+
class LatencyInstrument : public Instrument {
138+
std::optional<unsigned> Latency;
139+
140+
public:
141+
static const StringRef DESC_NAME;
142+
LatencyInstrument(StringRef Data) : Instrument(DESC_NAME, Data) {
143+
// Skip spaces and tabs.
144+
Data = Data.trim();
145+
if (Data.empty()) // Empty description. Bail out.
146+
return;
147+
unsigned L = 0;
148+
if (!Data.getAsInteger(10, L))
149+
Latency = L;
150+
}
151+
152+
bool hasValue() const { return bool(Latency); }
153+
unsigned getLatency() const { return *Latency; }
154+
};
155+
137156
using UniqueInstrument = std::unique_ptr<Instrument>;
138157

139158
/// This class allows targets to optionally customize the logic that resolves
@@ -143,19 +162,21 @@ class LLVM_ABI InstrumentManager {
143162
protected:
144163
const MCSubtargetInfo &STI;
145164
const MCInstrInfo &MCII;
165+
bool EnableInstruments;
146166

147167
public:
148-
InstrumentManager(const MCSubtargetInfo &STI, const MCInstrInfo &MCII)
149-
: STI(STI), MCII(MCII) {}
168+
InstrumentManager(const MCSubtargetInfo &STI, const MCInstrInfo &MCII,
169+
bool EnableInstruments = true)
170+
: STI(STI), MCII(MCII), EnableInstruments(EnableInstruments) {};
150171

151172
virtual ~InstrumentManager() = default;
152173

153174
/// Returns true if llvm-mca should ignore instruments.
154-
virtual bool shouldIgnoreInstruments() const { return true; }
175+
virtual bool shouldIgnoreInstruments() const { return !EnableInstruments; }
155176

156177
// Returns true if this supports processing Instrument with
157178
// Instrument.Desc equal to Type
158-
virtual bool supportsInstrumentType(StringRef Type) const { return false; }
179+
virtual bool supportsInstrumentType(StringRef Type) const;
159180

160181
/// Allocate an Instrument, and return a unique pointer to it. This function
161182
/// may be useful to create instruments coming from comments in the assembly.
@@ -175,6 +196,13 @@ class LLVM_ABI InstrumentManager {
175196
/// it returns the SchedClassID that belongs to MCI.
176197
virtual unsigned getSchedClassID(const MCInstrInfo &MCII, const MCInst &MCI,
177198
const SmallVector<Instrument *> &IVec) const;
199+
200+
// Return true if instruments can modify instruction description
201+
virtual bool canCustomize(const ArrayRef<Instrument *> IVec) const;
202+
203+
// Customize instruction description
204+
virtual void customize(const ArrayRef<Instrument *> IVec,
205+
llvm::mca::InstrDesc &Desc) const;
178206
};
179207

180208
} // namespace mca

llvm/include/llvm/MCA/InstrBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class InstrBuilder {
7878
DenseMap<std::pair<hash_code, unsigned>, std::unique_ptr<const InstrDesc>>
7979
VariantDescriptors;
8080

81+
// These descriptors are customized for particular instructions and cannot
82+
// be reused
83+
SmallVector<std::unique_ptr<const InstrDesc>> CustomDescriptors;
84+
8185
bool FirstCallInst;
8286
bool FirstReturnInst;
8387
unsigned CallLatency;

llvm/lib/MCA/CustomBehaviour.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "llvm/MCA/CustomBehaviour.h"
15+
#include "llvm/MCA/Instruction.h"
1516

1617
namespace llvm {
1718
namespace mca {
@@ -42,8 +43,44 @@ CustomBehaviour::getEndViews(llvm::MCInstPrinter &IP,
4243
return std::vector<std::unique_ptr<View>>();
4344
}
4445

45-
UniqueInstrument InstrumentManager::createInstrument(llvm::StringRef Desc,
46-
llvm::StringRef Data) {
46+
const llvm::StringRef LatencyInstrument::DESC_NAME = "LATENCY";
47+
48+
bool InstrumentManager::supportsInstrumentType(StringRef Type) const {
49+
return EnableInstruments && Type == LatencyInstrument::DESC_NAME;
50+
}
51+
52+
bool InstrumentManager::canCustomize(const ArrayRef<Instrument *> IVec) const {
53+
for (const auto I : IVec) {
54+
if (I->getDesc() == LatencyInstrument::DESC_NAME) {
55+
auto LatInst = static_cast<LatencyInstrument *>(I);
56+
return LatInst->hasValue();
57+
}
58+
}
59+
return false;
60+
}
61+
62+
void InstrumentManager::customize(const ArrayRef<Instrument *> IVec,
63+
InstrDesc &ID) const {
64+
for (const auto I : IVec) {
65+
if (I->getDesc() == LatencyInstrument::DESC_NAME) {
66+
auto LatInst = static_cast<LatencyInstrument *>(I);
67+
if (LatInst->hasValue()) {
68+
unsigned Latency = LatInst->getLatency();
69+
// TODO Allow to customize a subset of ID.Writes
70+
for (auto &W : ID.Writes)
71+
W.Latency = Latency;
72+
ID.MaxLatency = Latency;
73+
}
74+
}
75+
}
76+
}
77+
78+
UniqueInstrument InstrumentManager::createInstrument(StringRef Desc,
79+
StringRef Data) {
80+
if (EnableInstruments) {
81+
if (Desc == LatencyInstrument::DESC_NAME)
82+
return std::make_unique<LatencyInstrument>(Data);
83+
}
4784
return std::make_unique<Instrument>(Desc, Data);
4885
}
4986

llvm/lib/MCA/InstrBuilder.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,12 @@ InstrBuilder::createInstrDescImpl(const MCInst &MCI,
632632
return std::move(Err);
633633

634634
// Now add the new descriptor.
635+
636+
if (IM.canCustomize(IVec)) {
637+
IM.customize(IVec, *ID);
638+
return *CustomDescriptors.emplace_back(std::move(ID));
639+
}
640+
635641
bool IsVariadic = MCDesc.isVariadic();
636642
if ((ID->IsRecyclable = !IsVariadic && !IsVariant)) {
637643
auto DKey = std::make_pair(MCI.getOpcode(), SchedClassID);
@@ -676,7 +682,9 @@ STATISTIC(NumVariantInst, "Number of MCInsts that doesn't have static Desc");
676682
Expected<std::unique_ptr<Instruction>>
677683
InstrBuilder::createInstruction(const MCInst &MCI,
678684
const SmallVector<Instrument *> &IVec) {
679-
Expected<const InstrDesc &> DescOrErr = getOrCreateInstrDesc(MCI, IVec);
685+
Expected<const InstrDesc &> DescOrErr = IM.canCustomize(IVec)
686+
? createInstrDescImpl(MCI, IVec)
687+
: getOrCreateInstrDesc(MCI, IVec);
680688
if (!DescOrErr)
681689
return DescOrErr.takeError();
682690
const InstrDesc &D = *DescOrErr;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mca_test_checks.py
2+
# RUN: llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=btver2 -iterations=10 %s 2>&1 | FileCheck %s
3+
4+
# LLVM-MCA-LATENCY 100
5+
add (%eax), %eax
6+
# LLVM-MCA-LATENCY
7+
mov %eax, (%ebx)
8+
9+
# CHECK: Iterations: 10
10+
# CHECK-NEXT: Instructions: 20
11+
# CHECK-NEXT: Total Cycles: 1004
12+
# CHECK-NEXT: Total uOps: 20
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mca_test_checks.py
2+
# RUN: llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=btver2 -iterations=10 %s 2>&1 | FileCheck %s
3+
4+
# LLVM-MCA-LATENCY 100
5+
add (%eax), %eax
6+
mov %eax, (%ebx)
7+
8+
# CHECK: Iterations: 10
9+
# CHECK-NEXT: Instructions: 20
10+
# CHECK-NEXT: Total Cycles: 1103
11+
# CHECK-NEXT: Total uOps: 20

llvm/tools/llvm-mca/llvm-mca.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -510,11 +510,16 @@ int main(int argc, char **argv) {
510510
if (!DisableInstrumentManager) {
511511
IM = std::unique_ptr<mca::InstrumentManager>(
512512
TheTarget->createInstrumentManager(*STI, *MCII));
513-
}
514-
if (!IM) {
515-
// If the target doesn't have its own IM implemented (or the -disable-cb
516-
// flag is set) then we use the base class (which does nothing).
517-
IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
513+
if (!IM) {
514+
// If the target doesn't have its own IM implemented we use base class
515+
// with instruments enabled.
516+
IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
517+
}
518+
} else {
519+
// If the -disable-im flag is set then we use the default base class
520+
// implementation and disable the instruments.
521+
IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII,
522+
/*EnableInstruments=*/false);
518523
}
519524

520525
// Parse the input and create InstrumentRegion that llvm-mca

llvm/unittests/tools/llvm-mca/MCATestBase.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,24 @@ void MCATestBase::SetUp() {
5757
ASSERT_TRUE(IP);
5858
}
5959

60-
Error MCATestBase::runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
61-
ArrayRef<mca::View *> Views,
62-
const mca::PipelineOptions *PO) {
60+
Error MCATestBase::runBaselineMCA(
61+
json::Object &Result, ArrayRef<MCInst> Insts, ArrayRef<mca::View *> Views,
62+
const mca::PipelineOptions *PO,
63+
ArrayRef<std::pair<StringRef, StringRef>> Descs) {
6364
mca::Context MCA(*MRI, *STI);
6465

65-
// Default InstrumentManager
66-
auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
66+
// Enable instruments when descriptions are provided
67+
auto IM =
68+
std::make_unique<mca::InstrumentManager>(*STI, *MCII, !Descs.empty());
6769
mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, /*CallLatency=*/100);
6870

69-
const SmallVector<mca::Instrument *> Instruments;
71+
SmallVector<mca::Instrument *> Instruments;
72+
SmallVector<mca::UniqueInstrument> InstrumentsOwner;
73+
for (const auto &Desc : Descs) {
74+
auto I = IM->createInstrument(Desc.first, Desc.second);
75+
Instruments.push_back(I.get());
76+
InstrumentsOwner.push_back(std::move(I));
77+
}
7078
SmallVector<std::unique_ptr<mca::Instruction>> LoweredInsts;
7179
for (const auto &MCI : Insts) {
7280
Expected<std::unique_ptr<mca::Instruction>> Inst =

llvm/unittests/tools/llvm-mca/MCATestBase.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/MC/MCTargetOptions.h"
2525
#include "llvm/MC/TargetRegistry.h"
2626
#include "llvm/MCA/Context.h"
27+
#include "llvm/MCA/InstrBuilder.h"
2728
#include "llvm/TargetParser/SubtargetFeature.h"
2829
#include "llvm/TargetParser/Triple.h"
2930

@@ -73,9 +74,11 @@ class MCATestBase : public ::testing::Test {
7374
/// Utility function to run MCA with (nearly) the same configuration as the
7475
/// `llvm-mca` tool to verify result correctness.
7576
/// This function only displays on SummaryView by default.
76-
virtual Error runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
77-
ArrayRef<mca::View *> Views = {},
78-
const mca::PipelineOptions *PO = nullptr);
77+
virtual Error
78+
runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
79+
ArrayRef<mca::View *> Views = {},
80+
const mca::PipelineOptions *PO = nullptr,
81+
ArrayRef<std::pair<StringRef, StringRef>> Descs = {});
7982
};
8083

8184
} // end namespace mca

0 commit comments

Comments
 (0)