Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b1a67b7
Added interface to customize individual instructions and possibility …
r-belenov Aug 26, 2025
4abee0f
Fix formatting
r-belenov Aug 26, 2025
1298dd3
Fix formatting
r-belenov Aug 26, 2025
b5c3b95
Fix formatting
r-belenov Aug 26, 2025
302b994
Fix formatting
r-belenov Aug 26, 2025
2338978
Fix formatting
r-belenov Aug 26, 2025
0be9fbf
Fix formatting
r-belenov Aug 26, 2025
f1ad060
Fix formatting
r-belenov Aug 26, 2025
757fec3
Fix formatting
r-belenov Aug 26, 2025
452bb40
Fix formatting
r-belenov Aug 26, 2025
4d655f8
Fix formatting
r-belenov Aug 26, 2025
ef2a0f0
Fix formatting
r-belenov Aug 26, 2025
77eefdb
Fix formatting
r-belenov Aug 26, 2025
d44bfbe
Merge branch 'main' into mca-instruction-customizer
r-belenov Aug 26, 2025
bd8cedf
Merge branch 'main' into mca-instruction-customizer
r-belenov Aug 27, 2025
6e0d12b
Using function_ref to pass function arguments
r-belenov Aug 27, 2025
08cc266
Use InstrumentManager to customize instructions
r-belenov Aug 27, 2025
1282e70
Fix formatting
r-belenov Aug 27, 2025
71403fb
Move latency customization logic to base Instrument
r-belenov Aug 28, 2025
b805a3c
Simplify IM creation
r-belenov Aug 28, 2025
801b0da
Fix formatting
r-belenov Aug 28, 2025
ed4e0bd
Remove target IM from base class
r-belenov Aug 29, 2025
042b3ba
Fix formatting
r-belenov Aug 29, 2025
5fb0db7
Use explicit latency instrument
r-belenov Sep 12, 2025
e124416
Fix formatting (#12)
r-belenov Sep 12, 2025
74482dd
Removing redundant include
r-belenov Sep 12, 2025
37d0734
Merge branch 'main' into mca-instruction-customizer
r-belenov Sep 12, 2025
5824130
Make latency test more consistent
r-belenov Sep 12, 2025
5029c24
Merge branch 'main' into mca-instruction-customizer
r-belenov Sep 15, 2025
7c40f7b
Move customization logic to InstrumentManager
r-belenov Sep 17, 2025
9e276b6
Fix formatting (#14)
r-belenov Sep 17, 2025
129b7e4
Merge branch 'main' into mca-instruction-customizer
r-belenov Sep 17, 2025
e28c48c
FIxed incorrect Enlish
r-belenov Sep 18, 2025
7e90de7
Addressed minor comments
r-belenov Sep 18, 2025
4065142
Fixed formatting
r-belenov Sep 18, 2025
f8113c4
Merge branch 'main' into mca-instruction-customizer
r-belenov Sep 18, 2025
6043f89
Ensure test instruction can not be eliminated on dispatch stage
r-belenov Sep 18, 2025
0d00426
Merge branch 'main' into mca-instruction-customizer
r-belenov Sep 18, 2025
7bf43cf
Revert to explicit StringRef conversion to check the tests
r-belenov Sep 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions llvm/include/llvm/MCA/InstrBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "llvm/MCA/Support.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Error.h"
#include <functional>

namespace llvm {
namespace mca {
Expand Down Expand Up @@ -78,6 +79,10 @@ class InstrBuilder {
DenseMap<std::pair<hash_code, unsigned>, std::unique_ptr<const InstrDesc>>
VariantDescriptors;

// These descriptors are customized for particular instructions and cannot
// be reused
SmallVector<std::unique_ptr<const InstrDesc>> CustomDescriptors;

bool FirstCallInst;
bool FirstReturnInst;
unsigned CallLatency;
Expand All @@ -87,7 +92,8 @@ class InstrBuilder {

Expected<unsigned> getVariantSchedClassID(const MCInst &MCI, unsigned SchedClassID);
Expected<const InstrDesc &>
createInstrDescImpl(const MCInst &MCI, const SmallVector<Instrument *> &IVec);
createInstrDescImpl(const MCInst &MCI, const SmallVector<Instrument *> &IVec,
std::function<void(InstrDesc &)> Customizer = {});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Expected<const InstrDesc &>
getOrCreateInstrDesc(const MCInst &MCI,
const SmallVector<Instrument *> &IVec);
Expand Down Expand Up @@ -116,7 +122,8 @@ class InstrBuilder {
void setInstRecycleCallback(InstRecycleCallback CB) { InstRecycleCB = CB; }

LLVM_ABI Expected<std::unique_ptr<Instruction>>
createInstruction(const MCInst &MCI, const SmallVector<Instrument *> &IVec);
createInstruction(const MCInst &MCI, const SmallVector<Instrument *> &IVec,
std::function<void(InstrDesc &)> Customizer = {});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

};
} // namespace mca
} // namespace llvm
Expand Down
16 changes: 13 additions & 3 deletions llvm/lib/MCA/InstrBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,8 @@ Expected<unsigned> InstrBuilder::getVariantSchedClassID(const MCInst &MCI,

Expected<const InstrDesc &>
InstrBuilder::createInstrDescImpl(const MCInst &MCI,
const SmallVector<Instrument *> &IVec) {
const SmallVector<Instrument *> &IVec,
std::function<void(InstrDesc &)> Customizer) {
assert(STI.getSchedModel().hasInstrSchedModel() &&
"Itineraries are not yet supported!");

Expand Down Expand Up @@ -632,6 +633,12 @@ InstrBuilder::createInstrDescImpl(const MCInst &MCI,
return std::move(Err);

// Now add the new descriptor.

if (Customizer) {
Customizer(*ID);
return *CustomDescriptors.emplace_back(std::move(ID));
}

bool IsVariadic = MCDesc.isVariadic();
if ((ID->IsRecyclable = !IsVariadic && !IsVariant)) {
auto DKey = std::make_pair(MCI.getOpcode(), SchedClassID);
Expand Down Expand Up @@ -675,8 +682,11 @@ STATISTIC(NumVariantInst, "Number of MCInsts that doesn't have static Desc");

Expected<std::unique_ptr<Instruction>>
InstrBuilder::createInstruction(const MCInst &MCI,
const SmallVector<Instrument *> &IVec) {
Expected<const InstrDesc &> DescOrErr = getOrCreateInstrDesc(MCI, IVec);
const SmallVector<Instrument *> &IVec,
std::function<void(InstrDesc &)> Customizer) {
Expected<const InstrDesc &> DescOrErr =
Customizer ? createInstrDescImpl(MCI, IVec, Customizer)
: getOrCreateInstrDesc(MCI, IVec);
if (!DescOrErr)
return DescOrErr.takeError();
const InstrDesc &D = *DescOrErr;
Expand Down
10 changes: 10 additions & 0 deletions llvm/test/tools/llvm-mca/X86/llvm-mca-markers-13.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# NOTE: Assertions have been autogenerated by utils/update_mca_test_checks.py
# RUN: llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=btver2 -iterations=10 %s 2>&1 | FileCheck %s

add (%eax), %eax // LLVM-MCA-LATENCY : 100
mov %eax, (%ebx)

# CHECK: Iterations: 10
# CHECK-NEXT: Instructions: 20
# CHECK-NEXT: Total Cycles: 1004
# CHECK-NEXT: Total uOps: 20
10 changes: 10 additions & 0 deletions llvm/test/tools/llvm-mca/X86/llvm-mca-markers-14.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# NOTE: Assertions have been autogenerated by utils/update_mca_test_checks.py
# RUN: llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=btver2 -iterations=10 %s 2>&1 | FileCheck %s

add (%eax), %eax // LLVM-MCA-LATENCY : 100
mov %eax, (%ebx) // LLVM-MCA-LATENCY : 100

# CHECK: Iterations: 10
# CHECK-NEXT: Instructions: 20
# CHECK-NEXT: Total Cycles: 1103
# CHECK-NEXT: Total uOps: 20
20 changes: 20 additions & 0 deletions llvm/tools/llvm-mca/CodeRegion.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,16 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
#include <utility>
#include <vector>

namespace llvm {
namespace mca {

struct InstAnnotation {
std::optional<unsigned> Latency;
};

/// A region of assembly code.
///
/// It identifies a sequence of machine instructions.
Expand Down Expand Up @@ -155,6 +160,9 @@ class CodeRegions {
llvm::StringMap<unsigned> ActiveRegions;
bool FoundErrors;

// Annotations specified in comments, indexed by SMLoc value
llvm::DenseMap<const char *, InstAnnotation> Annotations;

public:
CodeRegions(llvm::SourceMgr &S) : SM(S), FoundErrors(false) {}
virtual ~CodeRegions() = default;
Expand All @@ -170,6 +178,18 @@ class CodeRegions {
void addInstruction(const llvm::MCInst &Instruction);
llvm::SourceMgr &getSourceMgr() const { return SM; }

void Annotate(llvm::SMLoc Loc, const InstAnnotation &A) {
Annotations[Loc.getPointer()] = A;
}
std::optional<unsigned> getExplicitLatency(llvm::SMLoc Loc) const {
const auto It = Annotations.find(Loc.getPointer());
if (It != Annotations.end()) {
return It->second.Latency;
} else {
return {};
}
}

llvm::ArrayRef<llvm::MCInst> getInstructionSequence(unsigned Idx) const {
return Regions[Idx]->getInstructions();
}
Expand Down
12 changes: 12 additions & 0 deletions llvm/tools/llvm-mca/CodeRegionGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc,
return;

Comment = Comment.drop_front(Position);
if (Comment.starts_with("LLVM-MCA-LATENCY")) {
auto Parts = Comment.split(':');
Position = Parts.second.find_first_not_of(" \t");
if (Position >= Parts.second.size())
return;
auto LatStr = Parts.second.drop_front(Position);
unsigned Latency = 0;
if (!LatStr.getAsInteger(10, Latency))
Streamer.AddLatencyAnnotation(Latency);
return;
}

if (Comment.consume_front("LLVM-MCA-END")) {
// Skip spaces and tabs.
Position = Comment.find_first_not_of(" \t");
Expand Down
87 changes: 50 additions & 37 deletions llvm/tools/llvm-mca/CodeRegionGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,51 @@
namespace llvm {
namespace mca {

// This class provides the callbacks that occur when parsing input assembly.
class MCStreamerWrapper : public MCStreamer {
protected:
CodeRegions &Regions;
std::optional<InstAnnotation> CurrentAnnotation;

public:
MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R)
: MCStreamer(Context), Regions(R) {}

// We only want to intercept the emission of new instructions.
void emitInstruction(const MCInst &Inst,
const MCSubtargetInfo & /* unused */) override {
Regions.addInstruction(Inst);
if (CurrentAnnotation) {
Regions.Annotate(Inst.getLoc(), *CurrentAnnotation);
CurrentAnnotation = {};
}
}

void AddLatencyAnnotation(unsigned Lat) {
if (!CurrentAnnotation)
CurrentAnnotation = InstAnnotation();
CurrentAnnotation->Latency = Lat;
}

bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
return true;
}

void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
Align ByteAlignment) override {}
void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
uint64_t Size = 0, Align ByteAlignment = Align(1),
SMLoc Loc = SMLoc()) override {}
void beginCOFFSymbolDef(const MCSymbol *Symbol) override {}
void emitCOFFSymbolStorageClass(int StorageClass) override {}
void emitCOFFSymbolType(int Type) override {}
void endCOFFSymbolDef() override {}

ArrayRef<MCInst> GetInstructionSequence(unsigned Index) const {
return Regions.getInstructionSequence(Index);
}
};

class MCACommentConsumer : public AsmCommentConsumer {
protected:
bool FoundError = false;
Expand All @@ -44,9 +89,11 @@ class MCACommentConsumer : public AsmCommentConsumer {
/// A comment consumer that parses strings. The only valid tokens are strings.
class AnalysisRegionCommentConsumer : public MCACommentConsumer {
AnalysisRegions &Regions;
MCStreamerWrapper &Streamer;

public:
AnalysisRegionCommentConsumer(AnalysisRegions &R) : Regions(R) {}
AnalysisRegionCommentConsumer(AnalysisRegions &R, MCStreamerWrapper &S)
: Regions(R), Streamer(S) {}

/// Parses a comment. It begins a new region if it is of the form
/// LLVM-MCA-BEGIN. It ends a region if it is of the form LLVM-MCA-END.
Expand Down Expand Up @@ -82,40 +129,6 @@ class InstrumentRegionCommentConsumer : public MCACommentConsumer {
InstrumentManager &getInstrumentManager() { return IM; }
};

// This class provides the callbacks that occur when parsing input assembly.
class MCStreamerWrapper : public MCStreamer {
protected:
CodeRegions &Regions;

public:
MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R)
: MCStreamer(Context), Regions(R) {}

// We only want to intercept the emission of new instructions.
void emitInstruction(const MCInst &Inst,
const MCSubtargetInfo & /* unused */) override {
Regions.addInstruction(Inst);
}

bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
return true;
}

void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
Align ByteAlignment) override {}
void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
uint64_t Size = 0, Align ByteAlignment = Align(1),
SMLoc Loc = SMLoc()) override {}
void beginCOFFSymbolDef(const MCSymbol *Symbol) override {}
void emitCOFFSymbolStorageClass(int StorageClass) override {}
void emitCOFFSymbolType(int Type) override {}
void endCOFFSymbolDef() override {}

ArrayRef<MCInst> GetInstructionSequence(unsigned Index) const {
return Regions.getInstructionSequence(Index);
}
};

class InstrumentMCStreamer : public MCStreamerWrapper {
InstrumentManager &IM;

Expand Down Expand Up @@ -210,15 +223,15 @@ class AsmCodeRegionGenerator : public virtual CodeRegionGenerator {

class AsmAnalysisRegionGenerator final : public AnalysisRegionGenerator,
public AsmCodeRegionGenerator {
AnalysisRegionCommentConsumer CC;
MCStreamerWrapper Streamer;
AnalysisRegionCommentConsumer CC;

public:
AsmAnalysisRegionGenerator(const Target &T, llvm::SourceMgr &SM, MCContext &C,
const MCAsmInfo &A, const MCSubtargetInfo &S,
const MCInstrInfo &I)
: AnalysisRegionGenerator(SM), AsmCodeRegionGenerator(T, C, A, S, I),
CC(Regions), Streamer(Ctx, Regions) {}
Streamer(Ctx, Regions), CC(Regions, Streamer) {}

MCACommentConsumer *getCommentConsumer() override { return &CC; };
CodeRegions &getRegions() override { return Regions; };
Expand Down
9 changes: 8 additions & 1 deletion llvm/tools/llvm-mca/llvm-mca.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,15 @@ int main(int argc, char **argv) {
const SmallVector<mca::Instrument *> Instruments =
InstrumentRegions.getActiveInstruments(Loc);

auto Latency = Regions.getExplicitLatency(Loc);
Expected<std::unique_ptr<mca::Instruction>> Inst =
IB.createInstruction(MCI, Instruments);
Latency ? IB.createInstruction(MCI, Instruments,
[=](llvm::mca::InstrDesc &ID) {
for (auto &W : ID.Writes)
W.Latency = *Latency;
ID.MaxLatency = *Latency;
})
: IB.createInstruction(MCI, Instruments);
if (!Inst) {
if (auto NewE = handleErrors(
Inst.takeError(),
Expand Down
4 changes: 2 additions & 2 deletions llvm/unittests/tools/llvm-mca/MCATestBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void MCATestBase::SetUp() {

Error MCATestBase::runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
ArrayRef<mca::View *> Views,
const mca::PipelineOptions *PO) {
const mca::PipelineOptions *PO, Builder B) {
mca::Context MCA(*MRI, *STI);

// Default InstrumentManager
Expand All @@ -72,7 +72,7 @@ Error MCATestBase::runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
SmallVector<std::unique_ptr<mca::Instruction>> LoweredInsts;
for (const auto &MCI : Insts) {
Expected<std::unique_ptr<mca::Instruction>> Inst =
IB.createInstruction(MCI, Instruments);
B ? B(IB, MCI, Instruments) : IB.createInstruction(MCI, Instruments);
if (!Inst) {
if (auto NewE =
handleErrors(Inst.takeError(),
Expand Down
8 changes: 7 additions & 1 deletion llvm/unittests/tools/llvm-mca/MCATestBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/MCA/Context.h"
#include "llvm/MCA/InstrBuilder.h"
#include "llvm/TargetParser/SubtargetFeature.h"
#include "llvm/TargetParser/Triple.h"

Expand Down Expand Up @@ -70,12 +71,17 @@ class MCATestBase : public ::testing::Test {

void SetUp() override;

using Builder = std::function<Expected<std::unique_ptr<mca::Instruction>>(
mca::InstrBuilder &, const MCInst &,
const SmallVector<mca::Instrument *> &)>;

/// Utility function to run MCA with (nearly) the same configuration as the
/// `llvm-mca` tool to verify result correctness.
/// This function only displays on SummaryView by default.
virtual Error runBaselineMCA(json::Object &Result, ArrayRef<MCInst> Insts,
ArrayRef<mca::View *> Views = {},
const mca::PipelineOptions *PO = nullptr);
const mca::PipelineOptions *PO = nullptr,
Builder B = {});
};

} // end namespace mca
Expand Down
28 changes: 28 additions & 0 deletions llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,31 @@ TEST_F(X86TestBase, TestVariantInstructionsSameAddress) {
Expected<unsigned> Cycles = P->run();
ASSERT_TRUE(static_cast<bool>(Cycles));
}

TEST_F(X86TestBase, TestInstructionCustomization) {
const unsigned ExplicitLatency = 100;
SmallVector<MCInst> MCIs;
MCInst InstructionToAdd = MCInstBuilder(X86::XOR64rr)
.addReg(X86::RAX)
.addReg(X86::RAX)
.addReg(X86::RAX);
MCIs.push_back(InstructionToAdd);

// Run the baseline.
json::Object BaselineResult;
auto E = runBaselineMCA(BaselineResult, MCIs, {}, nullptr,
[=](InstrBuilder &IB, const MCInst &MCI,
const SmallVector<Instrument *> &Instruments) {
return IB.createInstruction(
MCI, Instruments, [=](InstrDesc &ID) {
for (auto &W : ID.Writes)
W.Latency = ExplicitLatency;
ID.MaxLatency = ExplicitLatency;
});
});
auto *BaselineObj = BaselineResult.getObject("SummaryView");
auto V = BaselineObj->getInteger("TotalCycles");
ASSERT_TRUE(V);
// Additional 3 cycles for Dispatch, Executed and Retired states
ASSERT_EQ(unsigned(*V), ExplicitLatency + 3) << "Total cycles do not match";
}
Loading