Skip to content

Commit 370555c

Browse files
[MCA] Parameterize variant scheduling classes by hash (#92849)
This patch looks up variant scheduling classes using a hash of the instruction. Keying by the pointer breaks certain use cases that might occur out of tree, like decoding an execution trace instruction by instruction and creating MCA instructions as one goes along, like in the MCAD case. In this case, the MCInst will always have the same address and thus all instructions with the same variant scheduling class will end up with the same instruction description, leading to undesired behavior (assertions, uses after free, invalid results, etc.).
1 parent 3d11b3d commit 370555c

File tree

3 files changed

+108
-15
lines changed

3 files changed

+108
-15
lines changed

llvm/include/llvm/MCA/InstrBuilder.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_MCA_INSTRBUILDER_H
1515
#define LLVM_MCA_INSTRBUILDER_H
1616

17+
#include "llvm/ADT/Hashing.h"
1718
#include "llvm/ADT/STLExtras.h"
1819
#include "llvm/MC/MCInstrAnalysis.h"
1920
#include "llvm/MC/MCInstrInfo.h"
@@ -71,9 +72,9 @@ class InstrBuilder {
7172
std::unique_ptr<const InstrDesc>>
7273
Descriptors;
7374

74-
// Key is the MCIInst and SchedClassID the describe the value InstrDesc
75-
DenseMap<std::pair<const MCInst *, unsigned>,
76-
std::unique_ptr<const InstrDesc>>
75+
// Key is a hash of the MCInstruction and a SchedClassID that describe the
76+
// value InstrDesc
77+
DenseMap<std::pair<hash_code, unsigned>, std::unique_ptr<const InstrDesc>>
7778
VariantDescriptors;
7879

7980
bool FirstCallInst;
@@ -83,6 +84,7 @@ class InstrBuilder {
8384
using InstRecycleCallback = std::function<Instruction *(const InstrDesc &)>;
8485
InstRecycleCallback InstRecycleCB;
8586

87+
Expected<unsigned> getVariantSchedClassID(const MCInst &MCI, unsigned SchedClassID);
8688
Expected<const InstrDesc &>
8789
createInstrDescImpl(const MCInst &MCI, const SmallVector<Instrument *> &IVec);
8890
Expected<const InstrDesc &>

llvm/lib/MCA/InstrBuilder.cpp

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm/MCA/InstrBuilder.h"
1515
#include "llvm/ADT/APInt.h"
1616
#include "llvm/ADT/DenseMap.h"
17+
#include "llvm/ADT/Hashing.h"
1718
#include "llvm/ADT/Statistic.h"
1819
#include "llvm/MC/MCInst.h"
1920
#include "llvm/Support/Debug.h"
@@ -504,6 +505,24 @@ void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
504505
ID.Reads.resize(CurrentUse);
505506
}
506507

508+
hash_code hashMCOperand(const MCOperand &MCO) {
509+
hash_code TypeHash = hash_combine(MCO.isReg(), MCO.isImm(), MCO.isSFPImm(),
510+
MCO.isDFPImm(), MCO.isExpr(), MCO.isInst());
511+
if (MCO.isReg())
512+
return hash_combine(TypeHash, MCO.getReg());
513+
514+
return TypeHash;
515+
}
516+
517+
hash_code hashMCInst(const MCInst &MCI) {
518+
hash_code InstructionHash = hash_combine(MCI.getOpcode(), MCI.getFlags());
519+
for (unsigned I = 0; I < MCI.getNumOperands(); ++I) {
520+
InstructionHash =
521+
hash_combine(InstructionHash, hashMCOperand(MCI.getOperand(I)));
522+
}
523+
return InstructionHash;
524+
}
525+
507526
Error InstrBuilder::verifyInstrDesc(const InstrDesc &ID,
508527
const MCInst &MCI) const {
509528
if (ID.NumMicroOps != 0)
@@ -521,6 +540,22 @@ Error InstrBuilder::verifyInstrDesc(const InstrDesc &ID,
521540
return make_error<InstructionError<MCInst>>(std::string(Message), MCI);
522541
}
523542

543+
Expected<unsigned> InstrBuilder::getVariantSchedClassID(const MCInst &MCI,
544+
unsigned SchedClassID) {
545+
const MCSchedModel &SM = STI.getSchedModel();
546+
unsigned CPUID = SM.getProcessorID();
547+
while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
548+
SchedClassID =
549+
STI.resolveVariantSchedClass(SchedClassID, &MCI, &MCII, CPUID);
550+
551+
if (!SchedClassID) {
552+
return make_error<InstructionError<MCInst>>(
553+
"unable to resolve scheduling class for write variant.", MCI);
554+
}
555+
556+
return SchedClassID;
557+
}
558+
524559
Expected<const InstrDesc &>
525560
InstrBuilder::createInstrDescImpl(const MCInst &MCI,
526561
const SmallVector<Instrument *> &IVec) {
@@ -539,15 +574,13 @@ InstrBuilder::createInstrDescImpl(const MCInst &MCI,
539574

540575
// Try to solve variant scheduling classes.
541576
if (IsVariant) {
542-
unsigned CPUID = SM.getProcessorID();
543-
while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
544-
SchedClassID =
545-
STI.resolveVariantSchedClass(SchedClassID, &MCI, &MCII, CPUID);
546-
547-
if (!SchedClassID) {
548-
return make_error<InstructionError<MCInst>>(
549-
"unable to resolve scheduling class for write variant.", MCI);
577+
Expected<unsigned> VariantSchedClassIDOrErr =
578+
getVariantSchedClassID(MCI, SchedClassID);
579+
if (!VariantSchedClassIDOrErr) {
580+
return VariantSchedClassIDOrErr.takeError();
550581
}
582+
583+
SchedClassID = *VariantSchedClassIDOrErr;
551584
}
552585

553586
// Check if this instruction is supported. Otherwise, report an error.
@@ -605,7 +638,10 @@ InstrBuilder::createInstrDescImpl(const MCInst &MCI,
605638
return *Descriptors[DKey];
606639
}
607640

608-
auto VDKey = std::make_pair(&MCI, SchedClassID);
641+
auto VDKey = std::make_pair(hashMCInst(MCI), SchedClassID);
642+
assert(
643+
!VariantDescriptors.contains(VDKey) &&
644+
"Expected VariantDescriptors to not already have a value for this key.");
609645
VariantDescriptors[VDKey] = std::move(ID);
610646
return *VariantDescriptors[VDKey];
611647
}
@@ -620,9 +656,15 @@ InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI,
620656
if (Descriptors.find_as(DKey) != Descriptors.end())
621657
return *Descriptors[DKey];
622658

623-
unsigned CPUID = STI.getSchedModel().getProcessorID();
624-
SchedClassID = STI.resolveVariantSchedClass(SchedClassID, &MCI, &MCII, CPUID);
625-
auto VDKey = std::make_pair(&MCI, SchedClassID);
659+
Expected<unsigned> VariantSchedClassIDOrErr =
660+
getVariantSchedClassID(MCI, SchedClassID);
661+
if (!VariantSchedClassIDOrErr) {
662+
return VariantSchedClassIDOrErr.takeError();
663+
}
664+
665+
SchedClassID = *VariantSchedClassIDOrErr;
666+
667+
auto VDKey = std::make_pair(hashMCInst(MCI), SchedClassID);
626668
if (VariantDescriptors.contains(VDKey))
627669
return *VariantDescriptors[VDKey];
628670

llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
#include "MCTargetDesc/X86MCTargetDesc.h"
12
#include "Views/SummaryView.h"
23
#include "X86TestBase.h"
34
#include "llvm/ADT/SmallPtrSet.h"
5+
#include "llvm/MC/MCInstBuilder.h"
46
#include "llvm/MCA/CustomBehaviour.h"
57
#include "llvm/MCA/IncrementalSourceMgr.h"
68
#include "llvm/MCA/InstrBuilder.h"
79
#include "llvm/MCA/Pipeline.h"
810
#include "llvm/Support/Format.h"
911
#include "llvm/Support/JSON.h"
1012
#include "llvm/Support/raw_ostream.h"
13+
#include <memory>
1114
#include <unordered_map>
1215

1316
using namespace llvm;
@@ -185,3 +188,49 @@ TEST_F(X86TestBase, TestInstructionRecycling) {
185188
ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";
186189
}
187190
}
191+
192+
// Test that we do not depend upon the MCInst address for variant description
193+
// construction. This test creates two instructions that will use variant
194+
// description as they are both zeroing idioms, but write to different
195+
// registers. If the key used to access the variant instruction description is
196+
// the same between the descriptions (like the MCInst pointer), we will run into
197+
// an assertion failure due to the different writes.
198+
TEST_F(X86TestBase, TestVariantInstructionsSameAddress) {
199+
mca::Context MCA(*MRI, *STI);
200+
201+
mca::IncrementalSourceMgr ISM;
202+
// Empty CustomBehaviour.
203+
auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);
204+
205+
auto PO = getDefaultPipelineOptions();
206+
auto P = MCA.createDefaultPipeline(PO, ISM, *CB);
207+
ASSERT_TRUE(P);
208+
209+
auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
210+
mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, 100);
211+
212+
const SmallVector<mca::Instrument *> Instruments;
213+
214+
MCInst InstructionToAdd;
215+
InstructionToAdd = MCInstBuilder(X86::XOR64rr)
216+
.addReg(X86::RAX)
217+
.addReg(X86::RAX)
218+
.addReg(X86::RAX);
219+
Expected<std::unique_ptr<mca::Instruction>> Instruction1OrErr =
220+
IB.createInstruction(InstructionToAdd, Instruments);
221+
ASSERT_TRUE(static_cast<bool>(Instruction1OrErr));
222+
ISM.addInst(std::move(Instruction1OrErr.get()));
223+
224+
InstructionToAdd = MCInstBuilder(X86::XORPSrr)
225+
.addReg(X86::XMM0)
226+
.addReg(X86::XMM0)
227+
.addReg(X86::XMM0);
228+
Expected<std::unique_ptr<mca::Instruction>> Instruction2OrErr =
229+
IB.createInstruction(InstructionToAdd, Instruments);
230+
ASSERT_TRUE(static_cast<bool>(Instruction2OrErr));
231+
ISM.addInst(std::move(Instruction2OrErr.get()));
232+
233+
ISM.endOfStream();
234+
Expected<unsigned> Cycles = P->run();
235+
ASSERT_TRUE(static_cast<bool>(Cycles));
236+
}

0 commit comments

Comments
 (0)