[llvm] [llvm-exegesis] Process memory operands with unsuitable register class (PR #190342)
Timur Baidusenov via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 13 05:58:51 PDT 2026
https://github.com/bai-tim updated https://github.com/llvm/llvm-project/pull/190342
>From 71f3c3435b2a40e6a23a8c90231ea5d4477f5b0c Mon Sep 17 00:00:00 2001
From: bai-tim <timurbaidusenov at gmail.com>
Date: Fri, 6 Mar 2026 13:46:05 +0000
Subject: [PATCH] [llvm-exegesis] Process memory operands with unsuitable
register class
---
.../llvm-exegesis/RISCV/mem-register-sp.s | 41 +++++++++++
llvm/tools/llvm-exegesis/lib/Assembler.cpp | 2 +
.../tools/llvm-exegesis/lib/BenchmarkResult.h | 2 +
llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp | 12 ++++
llvm/tools/llvm-exegesis/lib/CodeTemplate.h | 3 +
.../lib/ParallelSnippetGenerator.cpp | 34 ++++++---
.../lib/ParallelSnippetGenerator.h | 5 +-
llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp | 50 +++++++------
.../lib/SerialSnippetGenerator.cpp | 48 +++++++------
.../llvm-exegesis/lib/SnippetGenerator.cpp | 72 ++++++++++++++++++-
.../llvm-exegesis/lib/SnippetGenerator.h | 6 ++
llvm/tools/llvm-exegesis/lib/Target.h | 8 +++
12 files changed, 229 insertions(+), 54 deletions(-)
create mode 100644 llvm/test/tools/llvm-exegesis/RISCV/mem-register-sp.s
diff --git a/llvm/test/tools/llvm-exegesis/RISCV/mem-register-sp.s b/llvm/test/tools/llvm-exegesis/RISCV/mem-register-sp.s
new file mode 100644
index 0000000000000..0bb6b08750194
--- /dev/null
+++ b/llvm/test/tools/llvm-exegesis/RISCV/mem-register-sp.s
@@ -0,0 +1,41 @@
+# RUN: llvm-exegesis -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=C_LWSP --min-instructions=100000 \
+# RUN: --dump-object-to-disk=%d -mtriple=riscv32-unknown-linux-gnu --mcpu=generic --repetition-mode=loop --loop-body-size=100 \
+# RUN: -mattr=+c
+# RUN: llvm-objdump -M numeric -d %d > %t.s
+# RUN: FileCheck %s < %t.s --check-prefix=CHECK-LOAD-ASM
+
+# RUN: llvm-exegesis -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=C_LWSP --min-instructions=100000 \
+# RUN: --dump-object-to-disk=%d -mtriple=riscv32-unknown-linux-gnu --mcpu=generic --repetition-mode=loop --loop-body-size=100 \
+# RUN: -mattr=+c --mode=inverse_throughput
+# RUN: llvm-objdump -M numeric -d %d > %t.s
+# RUN: FileCheck %s < %t.s --check-prefix=CHECK-LOAD-ASM
+
+# RUN: llvm-exegesis -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=C_LWSP --min-instructions=100000 \
+# RUN: --dump-object-to-disk=%d -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --repetition-mode=loop --loop-body-size=100 \
+# RUN: -mattr=+c
+# RUN: llvm-objdump -M numeric -d %d > %t.s
+# RUN: FileCheck %s < %t.s --check-prefix=CHECK-LOAD-ASM
+
+# RUN: llvm-exegesis -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=C_LDSP --min-instructions=100000 \
+# RUN: --dump-object-to-disk=%d -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --repetition-mode=loop --loop-body-size=100 \
+# RUN: -mattr=+c
+# RUN: llvm-objdump -M numeric -d %d > %t.s
+# RUN: FileCheck %s < %t.s --check-prefix=CHECK-LOAD-ASM
+
+# RUN: llvm-exegesis -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=C_SWSP --min-instructions=100000 \
+# RUN: --dump-object-to-disk=%d -mtriple=riscv32-unknown-linux-gnu --mcpu=generic --repetition-mode=loop --loop-body-size=100 \
+# RUN: -mattr=+c --mode=inverse_throughput
+# RUN: llvm-objdump -M numeric -d %d > %t.s
+# RUN: FileCheck %s < %t.s --check-prefix=CHECK-STORE-ASM
+
+# RUN: llvm-exegesis -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=C_SDSP --min-instructions=100000 \
+# RUN: --dump-object-to-disk=%d -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --repetition-mode=loop --loop-body-size=100 \
+# RUN: -mattr=+c --mode=inverse_throughput
+# RUN: llvm-objdump -M numeric -d %d > %t.s
+# RUN: FileCheck %s < %t.s --check-prefix=CHECK-STORE-ASM
+
+CHECK-LOAD-ASM: mv x2, x10
+CHECK-LOAD-ASM-COUNT-100: l{{[wd]}} x{{[0-9]+}}, 0x0(x2)
+
+CHECK-STORE-ASM: mv x2, x10
+CHECK-STORE-ASM-COUNT-100: s{{[wd]}} x{{[0-9]+}}, 0x0(x2)
diff --git a/llvm/tools/llvm-exegesis/lib/Assembler.cpp b/llvm/tools/llvm-exegesis/lib/Assembler.cpp
index 7191e49c6dccd..61bdac8ba3605 100644
--- a/llvm/tools/llvm-exegesis/lib/Assembler.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Assembler.cpp
@@ -291,6 +291,8 @@ Error assembleToStream(const ExegesisTarget &ET,
Entry.MBB->addLiveIn(Reg);
}
+ Entry.addInstructions(Key.PrologueInstructions);
+
const bool IsSnippetSetupComplete = generateSnippetSetupCode(
ET, TM->getMCSubtargetInfo(), Entry, Key, GenerateMemoryInstructions);
diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h
index 7984c8805cadc..c737ccd2f5b7d 100644
--- a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h
+++ b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h
@@ -76,6 +76,8 @@ struct BenchmarkKey {
uintptr_t SnippetAddress = 0;
// The register that should be used to hold the loop counter.
MCRegister LoopRegister;
+ // Instructions for inserting into entry.
+ std::vector<MCInst> PrologueInstructions;
};
struct BenchmarkMeasure {
diff --git a/llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp b/llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp
index fd156ee01e7ce..72080f117b336 100644
--- a/llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp
+++ b/llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp
@@ -63,6 +63,18 @@ bool InstructionTemplate::hasImmediateVariables() const {
});
}
+Operand InstructionTemplate::getMemOpReg() const {
+ auto &I = getInstr();
+ auto MemOpIt =
+ find_if(I.Operands, [](const Operand &Op) { return Op.isMemory(); });
+ assert(MemOpIt != I.Operands.end() &&
+ "Instruction must have memory operands");
+
+ const Operand &MemOp = *MemOpIt;
+ assert(MemOp.isReg() && "Memory operand expected to be register");
+ return MemOp;
+}
+
MCInst InstructionTemplate::build() const {
MCInst Result;
Result.setOpcode(Instr->Description.Opcode);
diff --git a/llvm/tools/llvm-exegesis/lib/CodeTemplate.h b/llvm/tools/llvm-exegesis/lib/CodeTemplate.h
index a65015b45b786..d1a3d2faa3bca 100644
--- a/llvm/tools/llvm-exegesis/lib/CodeTemplate.h
+++ b/llvm/tools/llvm-exegesis/lib/CodeTemplate.h
@@ -38,6 +38,7 @@ struct InstructionTemplate {
bool hasImmediateVariables() const;
const Instruction &getInstr() const { return *Instr; }
ArrayRef<MCOperand> getVariableValues() const { return VariableValues; }
+ Operand getMemOpReg() const;
void setVariableValues(ArrayRef<MCOperand> NewVariableValues) {
assert(VariableValues.size() == NewVariableValues.size() &&
"Value count mismatch");
@@ -133,6 +134,8 @@ struct CodeTemplate {
// the pointer to this memory is passed in to the function.
MCRegister ScratchSpacePointerInReg;
+ std::vector<MCInst> Prologue;
+
#if defined(__GNUC__) && (defined(__clang__) || LLVM_GNUC_PREREQ(8, 0, 0))
// FIXME: GCC7 bug workaround. Drop #if after GCC7 no longer supported.
private:
diff --git a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp
index aa409138ae71b..2333fb2d2131d 100644
--- a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp
+++ b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp
@@ -88,28 +88,36 @@ static bool hasVariablesWithTiedOperands(const Instruction &Instr) {
ParallelSnippetGenerator::~ParallelSnippetGenerator() = default;
-void ParallelSnippetGenerator::instantiateMemoryOperands(
+Error ParallelSnippetGenerator::callInstantiateMemoryOperands(
const MCRegister ScratchSpacePointerInReg,
- std::vector<InstructionTemplate> &Instructions) const {
+ std::vector<InstructionTemplate> &Instructions,
+ std::vector<MCInst> &Prologue, const BitVector &ForbiddenRegisters) const {
if (!ScratchSpacePointerInReg)
- return; // no memory operands.
+ return Error::success(); // no memory operands.
const auto &ET = State.getExegesisTarget();
const unsigned MemStep = ET.getMaxMemoryAccessSize();
const size_t OriginalInstructionsSize = Instructions.size();
size_t I = 0;
for (InstructionTemplate &IT : Instructions) {
- ET.fillMemoryOperands(IT, ScratchSpacePointerInReg, I * MemStep);
+ if (auto Err = instantiateMemoryOperands(
+ State, IT, ScratchSpacePointerInReg, I * MemStep, Prologue,
+ ForbiddenRegisters))
+ return Err;
++I;
}
while (Instructions.size() < kMinNumDifferentAddresses) {
InstructionTemplate IT = Instructions[I % OriginalInstructionsSize];
- ET.fillMemoryOperands(IT, ScratchSpacePointerInReg, I * MemStep);
+ if (auto Err = instantiateMemoryOperands(
+ State, IT, ScratchSpacePointerInReg, I * MemStep, Prologue,
+ ForbiddenRegisters))
+ return Err;
++I;
Instructions.push_back(std::move(IT));
}
assert(I * MemStep < BenchmarkRunner::ScratchSpace::kSize &&
"not enough scratch space");
+ return Error::success();
}
enum class RegRandomizationStrategy : uint8_t {
@@ -304,13 +312,19 @@ ParallelSnippetGenerator::generateCodeTemplates(
if (SelfAliasing.empty()) {
CT.Info = "instruction is parallel, repeating a random one.";
CT.Instructions.push_back(std::move(Variant));
- instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ if (auto Err = callInstantiateMemoryOperands(CT.ScratchSpacePointerInReg,
+ CT.Instructions, CT.Prologue,
+ ForbiddenRegisters))
+ return std::move(Err);
return getSingleton(std::move(CT));
}
if (SelfAliasing.hasImplicitAliasing()) {
CT.Info = "instruction is serial, repeating a random one.";
CT.Instructions.push_back(std::move(Variant));
- instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ if (auto Err = callInstantiateMemoryOperands(CT.ScratchSpacePointerInReg,
+ CT.Instructions, CT.Prologue,
+ ForbiddenRegisters))
+ return std::move(Err);
return getSingleton(std::move(CT));
}
std::vector<CodeTemplate> Result;
@@ -343,8 +357,10 @@ ParallelSnippetGenerator::generateCodeTemplates(
return make_error<StringError>(
Twine("Failed to produce any snippet via: ").concat(CurrCT.Info),
inconvertibleErrorCode());
- instantiateMemoryOperands(CurrCT.ScratchSpacePointerInReg,
- CurrCT.Instructions);
+ if (auto Err = callInstantiateMemoryOperands(
+ CurrCT.ScratchSpacePointerInReg, CurrCT.Instructions,
+ CurrCT.Prologue, ForbiddenRegisters))
+ return std::move(Err);
Result.push_back(std::move(CurrCT));
}
return Result;
diff --git a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h
index d3c85c0c303a2..4609c2397d47e 100644
--- a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h
+++ b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h
@@ -54,9 +54,10 @@ class ParallelSnippetGenerator : public SnippetGenerator {
// mov eax, [rdi + 128]
// add eax, [rdi + 192]
// mov eax, [rdi + 256]
- void instantiateMemoryOperands(
+ Error callInstantiateMemoryOperands(
MCRegister ScratchSpaceReg,
- std::vector<InstructionTemplate> &SnippetTemplate) const;
+ std::vector<InstructionTemplate> &SnippetTemplate,
+ std::vector<MCInst> &Prologue, const BitVector &ForbiddenRegisters) const;
};
} // namespace exegesis
diff --git a/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp b/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp
index 887df7ac7b393..14c720a5d8c7a 100644
--- a/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp
+++ b/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp
@@ -736,6 +736,10 @@ class ExegesisRISCVTarget : public ExegesisTarget {
generateInstructionVariants(const Instruction &Instr,
unsigned MaxConfigsPerOpcode) const override;
+ virtual std::optional<MCInst>
+ createCopyInstruction(MCRegister Src, MCRegister Dst) const override;
+ void processInstructionReservedRegs(InstructionTemplate &IT) const override;
+
void addTargetSpecificPasses(PassManagerBase &PM) const override {
// Turn AVL operand of physical registers into virtual registers.
PM.add(exegesis::createRISCVPreprocessingPass());
@@ -808,29 +812,18 @@ void ExegesisRISCVTarget::fillMemoryOperands(InstructionTemplate &IT,
unsigned Offset) const {
// TODO: for now we ignore Offset because have no way
// to detect it in instruction.
- auto &I = IT.getInstr();
-
- auto MemOpIt =
- find_if(I.Operands, [](const Operand &Op) { return Op.isMemory(); });
- assert(MemOpIt != I.Operands.end() &&
- "Instruction must have memory operands");
-
- const Operand &MemOp = *MemOpIt;
-
- assert(MemOp.isReg() && "Memory operand expected to be register");
-
- unsigned Opcode = I.getOpcode();
- if (Opcode == RISCV::C_LDSP || Opcode == RISCV::C_LWSP ||
- Opcode == RISCV::C_SDSP || Opcode == RISCV::C_SWSP) {
- IT.getValueFor(I.Operands[0]) = MCOperand::createReg(RISCV::X2);
- // Force base register to SP (X2)
- IT.getValueFor(MemOp) = MCOperand::createReg(RISCV::X2);
- return;
- }
-
+ const Operand &MemOp = IT.getMemOpReg();
IT.getValueFor(MemOp) = MCOperand::createReg(Reg);
}
+std::optional<MCInst>
+ExegesisRISCVTarget::createCopyInstruction(MCRegister Src,
+ MCRegister Dst) const {
+ if (!RISCV::GPRRegClass.contains(Src) || !RISCV::GPRRegClass.contains(Dst))
+ return std::nullopt;
+ return MCInstBuilder(RISCV::ADDI).addReg(Dst).addReg(Src).addImm(0);
+}
+
const MCPhysReg UnavailableRegisters[4] = {RISCV::X0, DefaultLoopCounterReg,
ScratchIntReg, ScratchMemoryReg};
@@ -890,6 +883,23 @@ ExegesisRISCVTarget::generateInstructionVariants(
return {IT};
}
+// Process instructions that used reserved registers.
+void ExegesisRISCVTarget::processInstructionReservedRegs(
+ InstructionTemplate &IT) const {
+ MCOperand &AssignedValue = IT.getValueFor(IT.getMemOpReg());
+
+ switch (IT.getOpcode()) {
+ case RISCV::C_LWSP:
+ case RISCV::C_LDSP:
+ case RISCV::C_SWSP:
+ case RISCV::C_SDSP:
+ AssignedValue = MCOperand::createReg(RISCV::X2);
+ break;
+ default:
+ break;
+ }
+}
+
} // anonymous namespace
static ExegesisTarget *getTheRISCVExegesisTarget() {
diff --git a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
index 71b2d71e084e1..911331a97ec28 100644
--- a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
+++ b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
@@ -89,12 +89,13 @@ static ExecutionMode getExecutionModes(const Instruction &Instr,
return EM;
}
-static void appendCodeTemplates(const LLVMState &State,
- InstructionTemplate Variant,
- const BitVector &ForbiddenRegisters,
- ExecutionMode ExecutionModeBit,
- StringRef ExecutionClassDescription,
- std::vector<CodeTemplate> &CodeTemplates) {
+static Error appendCodeTemplates(const LLVMState &State,
+ const SerialSnippetGenerator &Generator,
+ InstructionTemplate Variant,
+ const BitVector &ForbiddenRegisters,
+ ExecutionMode ExecutionModeBit,
+ StringRef ExecutionClassDescription,
+ std::vector<CodeTemplate> &CodeTemplates) {
assert(isEnumValue(ExecutionModeBit) && "Bit must be a power of two");
switch (ExecutionModeBit) {
case ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS:
@@ -108,7 +109,7 @@ static void appendCodeTemplates(const LLVMState &State,
CT.Info = std::string(ExecutionClassDescription);
CT.Instructions.push_back(std::move(Variant));
CodeTemplates.push_back(std::move(CT));
- return;
+ return Error::success();
}
case ExecutionMode::SERIAL_VIA_MEMORY_INSTR: {
// Select back-to-back memory instruction.
@@ -126,7 +127,7 @@ static void appendCodeTemplates(const LLVMState &State,
});
if (DefOpIt == I.Operands.end())
- return;
+ return Error::success();
const Operand &DefOp = *DefOpIt;
const ExegesisTarget &ET = State.getExegesisTarget();
@@ -139,17 +140,17 @@ static void appendCodeTemplates(const LLVMState &State,
// Register classes of def operand and memory operand must be the same
// to perform aliasing.
if (!RegClass.contains(ScratchMemoryRegister))
- return;
-
- ET.fillMemoryOperands(Variant, ScratchMemoryRegister, 0);
-
- // Only force the def register to ScratchMemoryRegister if the target
- // hasn't assigned a value yet.
- MCOperand &DefVal = Variant.getValueFor(DefOp);
- if (!DefVal.isValid())
- DefVal = MCOperand::createReg(ScratchMemoryRegister);
+ return Error::success();
CodeTemplate CT;
+ const Operand &MemOp = Variant.getMemOpReg();
+ if (auto Err = Generator.instantiateMemoryOperands(
+ State, Variant, ScratchMemoryRegister, 0, CT.Prologue,
+ ForbiddenRegisters))
+ return Err;
+ MCOperand MemOpVal = Variant.getValueFor(MemOp);
+ Variant.getValueFor(DefOp) = MCOperand::createReg(MemOpVal.getReg());
+
CT.Execution = ExecutionModeBit;
CT.ScratchSpacePointerInReg = ScratchMemoryRegister;
@@ -159,7 +160,7 @@ static void appendCodeTemplates(const LLVMState &State,
}
// TODO: implement more cases
- return;
+ return Error::success();
}
case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS: {
// Making the execution of this instruction serial by selecting one def
@@ -176,7 +177,7 @@ static void appendCodeTemplates(const LLVMState &State,
CT.Info = std::string(ExecutionClassDescription);
CT.Instructions.push_back(std::move(Variant));
CodeTemplates.push_back(std::move(CT));
- return;
+ return Error::success();
}
case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR: {
const Instruction &Instr = Variant.getInstr();
@@ -199,11 +200,12 @@ static void appendCodeTemplates(const LLVMState &State,
CT.Instructions.push_back(std::move(OtherIT));
CodeTemplates.push_back(std::move(CT));
}
- return;
+ return Error::success();
}
default:
llvm_unreachable("Unhandled enum value");
}
+ return Error::success();
}
SerialSnippetGenerator::~SerialSnippetGenerator() = default;
@@ -216,8 +218,10 @@ SerialSnippetGenerator::generateCodeTemplates(
getExecutionModes(Variant.getInstr(), ForbiddenRegisters);
for (const auto EC : kExecutionClasses) {
for (const auto ExecutionModeBit : getExecutionModeBits(EM & EC.Mask))
- appendCodeTemplates(State, Variant, ForbiddenRegisters, ExecutionModeBit,
- EC.Description, Results);
+ if (auto Err =
+ appendCodeTemplates(State, *this, Variant, ForbiddenRegisters,
+ ExecutionModeBit, EC.Description, Results))
+ return std::move(Err);
if (!Results.empty())
break;
}
diff --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
index 86d4e197b6063..5badcc63a01e4 100644
--- a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
+++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
@@ -63,7 +63,6 @@ Error SnippetGenerator::generateConfigurations(
const auto &ScratchRegAliases =
State.getRATC().getRegister(ScratchSpacePointerInReg).aliasedBits();
// If the instruction implicitly writes to ScratchSpacePointerInReg , abort.
- // FIXME: We could make a copy of the scratch register.
for (const auto &Op : Variant.getInstr().Operands) {
if (Op.isDef() && Op.isImplicitReg() &&
ScratchRegAliases.test(Op.getImplicitReg().id()))
@@ -95,6 +94,9 @@ Error SnippetGenerator::generateConfigurations(
return Error;
BC.Key.Instructions.push_back(Inst);
}
+
+ BC.Key.PrologueInstructions = CT.Prologue;
+
if (CT.ScratchSpacePointerInReg)
BC.LiveIns.push_back(CT.ScratchSpacePointerInReg);
BC.Key.RegisterInitialValues =
@@ -215,6 +217,74 @@ template <typename C> static decltype(auto) randomElement(const C &Container) {
return Container[randomIndex(Container.size() - 1)];
}
+// Instantiates memory operands for the given instruction template, ensuring
+// that the register used in the memory operand belongs to the required
+// register class.
+//
+// This function handles cases where the provided register (e.g., from a
+// forced scratch register insertion) may not be compatible with the memory
+// operand's register class. It attempts to:
+// 1. Use the provided register if it is already in the required class.
+// 2. Otherwise, reuse an existing register from the instruction template's
+// memory operand if it is valid.
+// 3. As a fallback, allocate a free register from the required class and
+// insert a copy prologue to move the value.
+Error SnippetGenerator::instantiateMemoryOperands(
+ const LLVMState &State, InstructionTemplate &IT, MCRegister Reg,
+ unsigned Offset, std::vector<MCInst> &Prologue,
+ const BitVector &ForbiddenRegisters) const {
+ const ExegesisTarget &ET = State.getExegesisTarget();
+ const Operand &MemOp = IT.getMemOpReg();
+ unsigned MemOpIndex = MemOp.getIndex();
+
+ const MCRegisterInfo &MRI = State.getRATC().regInfo();
+ const MCInstrDesc &Desc = IT.getInstr().Description;
+ const MCOperandInfo &OpInfo = Desc.operands()[MemOpIndex];
+ const MCRegisterClass &OpRC = MRI.getRegClass(OpInfo.RegClass);
+ unsigned OpBitSize = OpRC.getSizeInBits();
+
+ unsigned RegBitSize = 0;
+ for (unsigned RCId = 0, NumRC = MRI.getNumRegClasses(); RCId < NumRC;
+ ++RCId) {
+ const MCRegisterClass &RC = MRI.getRegClass(RCId);
+ if (RC.contains(Reg)) {
+ RegBitSize = RC.getSizeInBits();
+ break;
+ }
+ }
+
+ if (OpRC.contains(Reg) ||
+ (OpBitSize && RegBitSize && (OpBitSize != RegBitSize))) {
+ ET.fillMemoryOperands(IT, Reg, Offset);
+ return Error::success();
+ }
+
+ MCRegister NewReg;
+
+ ET.processInstructionReservedRegs(IT);
+ auto &MemOpValue = IT.getValueFor(MemOp);
+ if (MemOpValue.isValid() && MemOpValue.isReg()) {
+ NewReg = MemOpValue.getReg();
+ } else {
+ std::vector<MCPhysReg> Candidates;
+ for (MCPhysReg R : OpRC) {
+ if (!ForbiddenRegisters.test(R))
+ Candidates.push_back(R);
+ }
+
+ if (Candidates.empty())
+ return make_error<Failure>("No free register in required class");
+ NewReg = MCRegister::from(randomElement(Candidates));
+ }
+
+ auto Copy = ET.createCopyInstruction(Reg, NewReg);
+ if (!Copy)
+ return make_error<Failure>("Cannot copy into required register class");
+ Prologue.push_back(*Copy);
+ ET.fillMemoryOperands(IT, NewReg, Offset);
+ return Error::success();
+}
+
static void setRegisterOperandValue(const RegisterOperandAssignment &ROV,
InstructionTemplate &IB) {
assert(ROV.Op);
diff --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
index 1ef0beb6d7c5a..7fd86f3e1bfb0 100644
--- a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
+++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
@@ -69,6 +69,12 @@ class SnippetGenerator {
std::vector<RegisterValue> computeRegisterInitialValues(
const std::vector<InstructionTemplate> &Snippet) const;
+ Error instantiateMemoryOperands(const LLVMState &State,
+ InstructionTemplate &IT, MCRegister Reg,
+ unsigned Offset,
+ std::vector<MCInst> &Prologue,
+ const BitVector &ForbiddenRegisters) const;
+
protected:
const LLVMState &State;
const Options Opts;
diff --git a/llvm/tools/llvm-exegesis/lib/Target.h b/llvm/tools/llvm-exegesis/lib/Target.h
index 77fbaa6e95412..1f32de63bd3a3 100644
--- a/llvm/tools/llvm-exegesis/lib/Target.h
+++ b/llvm/tools/llvm-exegesis/lib/Target.h
@@ -207,6 +207,11 @@ class ExegesisTarget {
"fillMemoryOperands() requires getScratchMemoryRegister() > 0");
}
+ virtual std::optional<MCInst> createCopyInstruction(MCRegister Src,
+ MCRegister Dst) const {
+ llvm_unreachable("");
+ }
+
// Returns a counter usable as a loop counter.
virtual MCRegister getDefaultLoopCounterRegister(const Triple &) const {
return MCRegister();
@@ -244,6 +249,9 @@ class ExegesisTarget {
"targets with target-specific operands should implement this");
}
+ // Process instructions that used reserved registers.
+ virtual void processInstructionReservedRegs(InstructionTemplate &IT) const {}
+
// Returns true if this instruction is supported as a back-to-back
// instructions.
// FIXME: Eventually we should discover this dynamically.
More information about the llvm-commits
mailing list