summaryrefslogtreecommitdiffstats
path: root/llvm/tools/llvm-exegesis/lib/Uops.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-exegesis/lib/Uops.cpp')
-rw-r--r--llvm/tools/llvm-exegesis/lib/Uops.cpp94
1 files changed, 75 insertions, 19 deletions
diff --git a/llvm/tools/llvm-exegesis/lib/Uops.cpp b/llvm/tools/llvm-exegesis/lib/Uops.cpp
index 897f1080888..729b4847dbc 100644
--- a/llvm/tools/llvm-exegesis/lib/Uops.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Uops.cpp
@@ -13,6 +13,7 @@
#include "BenchmarkRunner.h"
#include "MCInstrDescView.h"
#include "PerfHelper.h"
+#include "Target.h"
// FIXME: Load constants into registers (e.g. with fld1) to not break
// instructions like x87.
@@ -84,19 +85,11 @@ static bool hasUnknownOperand(const llvm::MCOperandInfo &OpInfo) {
return OpInfo.OperandType == llvm::MCOI::OPERAND_UNKNOWN;
}
-// FIXME: Handle memory, see PR36905.
-static bool hasMemoryOperand(const llvm::MCOperandInfo &OpInfo) {
- return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY;
-}
-
llvm::Error
UopsBenchmarkRunner::isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const {
if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand))
return llvm::make_error<BenchmarkFailure>(
"Infeasible : has unknown operands");
- if (llvm::any_of(MCInstrDesc.operands(), hasMemoryOperand))
- return llvm::make_error<BenchmarkFailure>(
- "Infeasible : has memory operands");
return llvm::Error::success();
}
@@ -131,18 +124,70 @@ static void remove(llvm::BitVector &a, const llvm::BitVector &b) {
UopsBenchmarkRunner::~UopsBenchmarkRunner() = default;
+void UopsBenchmarkRunner::instantiateMemoryOperands(
+ const unsigned ScratchSpaceReg,
+ std::vector<InstructionInstance> &Snippet) const {
+ if (ScratchSpaceReg == 0)
+ return; // no memory operands.
+ const auto &ET = State.getExegesisTarget();
+ const unsigned MemStep = ET.getMaxMemoryAccessSize();
+ const size_t OriginalSnippetSize = Snippet.size();
+ size_t I = 0;
+ for (InstructionInstance &II : Snippet) {
+ ET.fillMemoryOperands(II, ScratchSpaceReg, I * MemStep);
+ ++I;
+ }
+
+ while (Snippet.size() < kMinNumDifferentAddresses) {
+ InstructionInstance II = Snippet[I % OriginalSnippetSize];
+ ET.fillMemoryOperands(II, ScratchSpaceReg, I * MemStep);
+ ++I;
+ Snippet.push_back(std::move(II));
+ }
+ assert(I * MemStep < ScratchSpace::kSize && "not enough scratch space");
+}
+
llvm::Expected<SnippetPrototype>
UopsBenchmarkRunner::generatePrototype(unsigned Opcode) const {
const auto &InstrDesc = State.getInstrInfo().get(Opcode);
if (auto E = isInfeasible(InstrDesc))
return std::move(E);
const Instruction Instr(InstrDesc, RATC);
+ const auto &ET = State.getExegesisTarget();
+ SnippetPrototype Prototype;
+
+ const llvm::BitVector *ScratchSpaceAliasedRegs = nullptr;
+ if (Instr.hasMemoryOperands()) {
+ Prototype.ScratchSpaceReg =
+ ET.getScratchMemoryRegister(State.getTargetMachine().getTargetTriple());
+ if (Prototype.ScratchSpaceReg == 0)
+ return llvm::make_error<BenchmarkFailure>(
+ "Infeasible : target does not support memory instructions");
+ ScratchSpaceAliasedRegs =
+ &RATC.getRegister(Prototype.ScratchSpaceReg).aliasedBits();
+ // If the instruction implicitly writes to ScratchSpaceReg , abort.
+ // FIXME: We could make a copy of the scratch register.
+ for (const auto &Op : Instr.Operands) {
+ if (Op.IsDef && Op.ImplicitReg &&
+ ScratchSpaceAliasedRegs->test(*Op.ImplicitReg))
+ return llvm::make_error<BenchmarkFailure>(
+ "Infeasible : memory instruction uses scratch memory register");
+ }
+ }
+
const AliasingConfigurations SelfAliasing(Instr, Instr);
+ InstructionInstance II(Instr);
if (SelfAliasing.empty()) {
- return generateUnconstrainedPrototype(Instr, "instruction is parallel");
+ Prototype.Explanation = "instruction is parallel, repeating a random one.";
+ Prototype.Snippet.push_back(std::move(II));
+ instantiateMemoryOperands(Prototype.ScratchSpaceReg, Prototype.Snippet);
+ return std::move(Prototype);
}
if (SelfAliasing.hasImplicitAliasing()) {
- return generateUnconstrainedPrototype(Instr, "instruction is serial");
+ Prototype.Explanation = "instruction is serial, repeating a random one.";
+ Prototype.Snippet.push_back(std::move(II));
+ instantiateMemoryOperands(Prototype.ScratchSpaceReg, Prototype.Snippet);
+ return std::move(Prototype);
}
const auto TiedVariables = getTiedVariables(Instr);
if (!TiedVariables.empty()) {
@@ -155,23 +200,27 @@ UopsBenchmarkRunner::generatePrototype(unsigned Opcode) const {
assert(!Var->TiedOperands.empty());
const Operand &Op = Instr.Operands[Var->TiedOperands.front()];
assert(Op.Tracker);
- SnippetPrototype Prototype;
Prototype.Explanation =
"instruction has tied variables using static renaming.";
for (const llvm::MCPhysReg Reg : Op.Tracker->sourceBits().set_bits()) {
- Prototype.Snippet.emplace_back(Instr);
- Prototype.Snippet.back().getValueFor(*Var) =
- llvm::MCOperand::createReg(Reg);
+ if (ScratchSpaceAliasedRegs && ScratchSpaceAliasedRegs->test(Reg))
+ continue; // Do not use the scratch memory address register.
+ InstructionInstance TmpII = II;
+ TmpII.getValueFor(*Var) = llvm::MCOperand::createReg(Reg);
+ Prototype.Snippet.push_back(std::move(TmpII));
}
+ instantiateMemoryOperands(Prototype.ScratchSpaceReg, Prototype.Snippet);
return std::move(Prototype);
}
- InstructionInstance II(Instr);
// No tied variables, we pick random values for defs.
llvm::BitVector Defs(State.getRegInfo().getNumRegs());
for (const auto &Op : Instr.Operands) {
- if (Op.Tracker && Op.IsExplicit && Op.IsDef) {
+ if (Op.Tracker && Op.IsExplicit && Op.IsDef && !Op.IsMem) {
auto PossibleRegisters = Op.Tracker->sourceBits();
remove(PossibleRegisters, RATC.reservedRegisters());
+ // Do not use the scratch memory address register.
+ if (ScratchSpaceAliasedRegs)
+ remove(PossibleRegisters, *ScratchSpaceAliasedRegs);
assert(PossibleRegisters.any() && "No register left to choose from");
const auto RandomReg = randomBit(PossibleRegisters);
Defs.set(RandomReg);
@@ -181,24 +230,28 @@ UopsBenchmarkRunner::generatePrototype(unsigned Opcode) const {
// And pick random use values that are not reserved and don't alias with defs.
const auto DefAliases = getAliasedBits(State.getRegInfo(), Defs);
for (const auto &Op : Instr.Operands) {
- if (Op.Tracker && Op.IsExplicit && !Op.IsDef) {
+ if (Op.Tracker && Op.IsExplicit && !Op.IsDef && !Op.IsMem) {
auto PossibleRegisters = Op.Tracker->sourceBits();
remove(PossibleRegisters, RATC.reservedRegisters());
+ // Do not use the scratch memory address register.
+ if (ScratchSpaceAliasedRegs)
+ remove(PossibleRegisters, *ScratchSpaceAliasedRegs);
remove(PossibleRegisters, DefAliases);
assert(PossibleRegisters.any() && "No register left to choose from");
const auto RandomReg = randomBit(PossibleRegisters);
II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg);
}
}
- SnippetPrototype Prototype;
Prototype.Explanation =
"instruction has no tied variables picking Uses different from defs";
Prototype.Snippet.push_back(std::move(II));
+ instantiateMemoryOperands(Prototype.ScratchSpaceReg, Prototype.Snippet);
return std::move(Prototype);
}
std::vector<BenchmarkMeasure>
UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
+ ScratchSpace &Scratch,
const unsigned NumRepetitions) const {
const auto &SchedModel = State.getSubtargetInfo().getSchedModel();
@@ -220,8 +273,9 @@ UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
llvm::report_fatal_error(
llvm::Twine("invalid perf event ").concat(PfmCounters));
pfm::Counter Counter(UopPerfEvent);
+ Scratch.clear();
Counter.start();
- Function();
+ Function(Scratch.ptr());
Counter.stop();
CounterValue += Counter.read();
}
@@ -232,4 +286,6 @@ UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
return Result;
}
+constexpr const size_t UopsBenchmarkRunner::kMinNumDifferentAddresses;
+
} // namespace exegesis
OpenPOWER on IntegriCloud