aboutsummaryrefslogtreecommitdiff
path: root/tools/llvm-exegesis
diff options
context:
space:
mode:
authorClement Courbet <courbet@google.com>2018-06-25 13:12:02 +0000
committerClement Courbet <courbet@google.com>2018-06-25 13:12:02 +0000
commit15fac97085c3f1d11ae035207128c57401c2ec57 (patch)
tree34dbfc46d4a5193bd8d819140ce5663ef1a05b7e /tools/llvm-exegesis
parent647fa5f2e686fabf182f051badf8dcfc58f15ada (diff)
[llvm-exegesis] Generate snippet setup code.
Summary: This ensures that the snippet always sees the same values for registers, making measurements reproducible. This will also allow exploring different values. Reviewers: gchatelet Subscribers: tschuett, llvm-commits Differential Revision: https://reviews.llvm.org/D48542 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@335465 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/llvm-exegesis')
-rw-r--r--tools/llvm-exegesis/lib/Assembler.cpp38
-rw-r--r--tools/llvm-exegesis/lib/Assembler.h6
-rw-r--r--tools/llvm-exegesis/lib/BenchmarkRunner.cpp67
-rw-r--r--tools/llvm-exegesis/lib/BenchmarkRunner.h14
-rw-r--r--tools/llvm-exegesis/lib/LlvmState.cpp1
-rw-r--r--tools/llvm-exegesis/lib/LlvmState.h1
-rw-r--r--tools/llvm-exegesis/lib/MCInstrDescView.cpp16
-rw-r--r--tools/llvm-exegesis/lib/MCInstrDescView.h11
-rw-r--r--tools/llvm-exegesis/lib/Target.h7
-rw-r--r--tools/llvm-exegesis/lib/X86/Target.cpp82
10 files changed, 220 insertions, 23 deletions
diff --git a/tools/llvm-exegesis/lib/Assembler.cpp b/tools/llvm-exegesis/lib/Assembler.cpp
index 05a13dbe9a1..f5be120f281 100644
--- a/tools/llvm-exegesis/lib/Assembler.cpp
+++ b/tools/llvm-exegesis/lib/Assembler.cpp
@@ -28,6 +28,21 @@ namespace exegesis {
static constexpr const char ModuleID[] = "ExegesisInfoTest";
static constexpr const char FunctionID[] = "foo";
+static std::vector<llvm::MCInst>
+generateSnippetSetupCode(const llvm::ArrayRef<unsigned> RegsToDef,
+ const ExegesisTarget &ET, bool &IsComplete) {
+ IsComplete = true;
+ std::vector<llvm::MCInst> Result;
+ for (const unsigned Reg : RegsToDef) {
+ // Load a constant in the register.
+ const auto Code = ET.setRegToConstant(Reg);
+ if (Code.empty())
+ IsComplete = false;
+ Result.insert(Result.end(), Code.begin(), Code.end());
+ }
+ return Result;
+}
+
// Small utility function to add named passes.
static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName,
llvm::TargetPassConfig &TPC) {
@@ -123,7 +138,9 @@ llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM) {
return MF.getSubtarget().getRegisterInfo()->getReservedRegs(MF);
}
-void assembleToStream(std::unique_ptr<llvm::LLVMTargetMachine> TM,
+void assembleToStream(const ExegesisTarget *ET,
+ std::unique_ptr<llvm::LLVMTargetMachine> TM,
+ llvm::ArrayRef<unsigned> RegsToDef,
llvm::ArrayRef<llvm::MCInst> Instructions,
llvm::raw_pwrite_stream &AsmStream) {
std::unique_ptr<llvm::LLVMContext> Context =
@@ -140,9 +157,20 @@ void assembleToStream(std::unique_ptr<llvm::LLVMTargetMachine> TM,
auto &Properties = MF.getProperties();
Properties.set(llvm::MachineFunctionProperties::Property::NoVRegs);
Properties.reset(llvm::MachineFunctionProperties::Property::IsSSA);
- // FIXME: Remove this when we assign all used registers as config step. This
- // essentially disables checks that used registers are def'ed somewhere.
- Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness);
+ std::vector<llvm::MCInst> SnippetWithSetup;
+ bool IsSnippetSetupComplete;
+ if (ET) {
+ SnippetWithSetup =
+ generateSnippetSetupCode(RegsToDef, *ET, IsSnippetSetupComplete);
+ SnippetWithSetup.insert(SnippetWithSetup.end(), Instructions.begin(),
+ Instructions.end());
+ Instructions = SnippetWithSetup;
+ }
+ // If the snippet setup is not complete, we disable liveliness tracking. This
+ // means that we won't know what values are in the registers.
+ if (!IsSnippetSetupComplete)
+ Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness);
+
// prologue/epilogue pass needs the reserved registers to be frozen, this
// is usually done by the SelectionDAGISel pass.
MF.getRegInfo().freezeReservedRegs(MF);
@@ -162,7 +190,7 @@ void assembleToStream(std::unique_ptr<llvm::LLVMTargetMachine> TM,
PM.add(MMI.release());
TPC->printAndVerify("MachineFunctionGenerator::assemble");
// Add target-specific passes.
- if (const auto *ET = ExegesisTarget::lookup(TM->getTargetTriple())) {
+ if (ET) {
ET->addTargetSpecificPasses(PM);
TPC->printAndVerify("After ExegesisTarget::addTargetSpecificPasses");
}
diff --git a/tools/llvm-exegesis/lib/Assembler.h b/tools/llvm-exegesis/lib/Assembler.h
index 932542ca504..fb39053dbce 100644
--- a/tools/llvm-exegesis/lib/Assembler.h
+++ b/tools/llvm-exegesis/lib/Assembler.h
@@ -33,6 +33,8 @@
namespace exegesis {
+class ExegesisTarget;
+
// Gather the set of reserved registers (depends on function's calling
// convention and target machine).
llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM);
@@ -41,7 +43,9 @@ llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM);
// Instructions. Runs a set of llvm Passes to provide correct prologue and
// epilogue. Once the MachineFunction is ready, it is assembled for TM to
// AsmStream, the temporary function is eventually discarded.
-void assembleToStream(std::unique_ptr<llvm::LLVMTargetMachine> TM,
+void assembleToStream(const ExegesisTarget *ET,
+ std::unique_ptr<llvm::LLVMTargetMachine> TM,
+ llvm::ArrayRef<unsigned> RegsToDef,
llvm::ArrayRef<llvm::MCInst> Instructions,
llvm::raw_pwrite_stream &AsmStream);
diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
index 063d72e2911..94b24732f6f 100644
--- a/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
+++ b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
@@ -78,10 +78,11 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
// Repeat the snippet until there are at least NumInstructions in the
// resulting code. The snippet is always repeated at least once.
- const auto GenerateInstructions = [&Snippet](const int MinInstructions) {
- std::vector<llvm::MCInst> Code = Snippet;
+ const auto GenerateInstructions = [&Configuration](
+ const int MinInstructions) {
+ std::vector<llvm::MCInst> Code = Configuration.Snippet;
for (int I = 0; I < MinInstructions; ++I)
- Code.push_back(Snippet[I % Snippet.size()]);
+ Code.push_back(Configuration.Snippet[I % Configuration.Snippet.size()]);
return Code;
};
@@ -91,7 +92,8 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
constexpr const int kMinInstructionsForSnippet = 16;
{
auto ObjectFilePath =
- writeObjectFile(GenerateInstructions(kMinInstructionsForSnippet));
+ writeObjectFile(Configuration.SnippetSetup,
+ GenerateInstructions(kMinInstructionsForSnippet));
if (llvm::Error E = ObjectFilePath.takeError()) {
InstrBenchmark.Error = llvm::toString(std::move(E));
return InstrBenchmark;
@@ -105,7 +107,8 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
// Assemble NumRepetitions instructions repetitions of the snippet for
// measurements.
auto ObjectFilePath =
- writeObjectFile(GenerateInstructions(InstrBenchmark.NumRepetitions));
+ writeObjectFile(Configuration.SnippetSetup,
+ GenerateInstructions(InstrBenchmark.NumRepetitions));
if (llvm::Error E = ObjectFilePath.takeError()) {
InstrBenchmark.Error = llvm::toString(std::move(E));
return InstrBenchmark;
@@ -126,22 +129,68 @@ BenchmarkRunner::generateConfigurations(unsigned Opcode) const {
// TODO: Generate as many configurations as needed here.
BenchmarkConfiguration Configuration;
Configuration.Info = Prototype.Explanation;
- for (InstructionInstance &II : Prototype.Snippet)
- Configuration.Snippet.push_back(II.randomizeUnsetVariablesAndBuild());
+ for (InstructionInstance &II : Prototype.Snippet) {
+ II.randomizeUnsetVariables();
+ Configuration.Snippet.push_back(II.build());
+ }
+ Configuration.SnippetSetup.RegsToDef = computeRegsToDef(Prototype.Snippet);
return std::vector<BenchmarkConfiguration>{Configuration};
} else
return E.takeError();
}
+std::vector<unsigned> BenchmarkRunner::computeRegsToDef(
+ const std::vector<InstructionInstance> &Snippet) const {
+ // Collect all register uses and create an assignment for each of them.
+ // Loop invariant: DefinedRegs[i] is true iif it has been set at least once
+ // before the current instruction.
+ llvm::BitVector DefinedRegs = RATC.emptyRegisters();
+ std::vector<unsigned> RegsToDef;
+ for (const InstructionInstance &II : Snippet) {
+ // Returns the register that this Operand sets or uses, or 0 if this is not
+ // a register.
+ const auto GetOpReg = [&II](const Operand &Op) -> unsigned {
+ if (Op.ImplicitReg) {
+ return *Op.ImplicitReg;
+ } else if (Op.IsExplicit && II.getValueFor(Op).isReg()) {
+ return II.getValueFor(Op).getReg();
+ }
+ return 0;
+ };
+ // Collect used registers that have never been def'ed.
+ for (const Operand &Op : II.Instr.Operands) {
+ if (!Op.IsDef) {
+ const unsigned Reg = GetOpReg(Op);
+ if (Reg > 0 && !DefinedRegs.test(Reg)) {
+ RegsToDef.push_back(Reg);
+ DefinedRegs.set(Reg);
+ }
+ }
+ }
+ // Mark defs as having been def'ed.
+ for (const Operand &Op : II.Instr.Operands) {
+ if (Op.IsDef) {
+ const unsigned Reg = GetOpReg(Op);
+ if (Reg > 0) {
+ DefinedRegs.set(Reg);
+ }
+ }
+ }
+ }
+ return RegsToDef;
+}
+
llvm::Expected<std::string>
-BenchmarkRunner::writeObjectFile(llvm::ArrayRef<llvm::MCInst> Code) const {
+BenchmarkRunner::writeObjectFile(const BenchmarkConfiguration::Setup &Setup,
+ llvm::ArrayRef<llvm::MCInst> Code) const {
int ResultFD = 0;
llvm::SmallString<256> ResultPath;
if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile(
"snippet", "o", ResultFD, ResultPath)))
return std::move(E);
llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
- assembleToStream(State.createTargetMachine(), Code, OFS);
+ assembleToStream(State.getExegesisTarget(), State.createTargetMachine(),
+ Setup.RegsToDef, Code, OFS);
return ResultPath.str();
}
diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.h b/tools/llvm-exegesis/lib/BenchmarkRunner.h
index f23a8a458fd..4e8ba502b9b 100644
--- a/tools/llvm-exegesis/lib/BenchmarkRunner.h
+++ b/tools/llvm-exegesis/lib/BenchmarkRunner.h
@@ -39,7 +39,10 @@ struct BenchmarkConfiguration {
// This code is run before the Snippet is iterated. Since it is part of the
// measurement it should be as short as possible. It is usually used to setup
// the content of the Registers.
- std::vector<llvm::MCInst> SnippetSetup;
+ struct Setup {
+ std::vector<unsigned> RegsToDef;
+ };
+ Setup SnippetSetup;
// The sequence of instructions that are to be repeated.
std::vector<llvm::MCInst> Snippet;
@@ -71,6 +74,10 @@ public:
run(unsigned Opcode, const InstructionFilter &Filter,
unsigned NumRepetitions);
+ // Given a snippet, computes which registers the setup code needs to define.
+ std::vector<unsigned>
+ computeRegsToDef(const std::vector<InstructionInstance> &Snippet) const;
+
protected:
const LLVMState &State;
const llvm::MCInstrInfo &MCInstrInfo;
@@ -96,9 +103,8 @@ private:
const unsigned NumRepetitions) const = 0;
llvm::Expected<std::string>
- writeObjectFile(llvm::ArrayRef<llvm::MCInst> Code) const;
- llvm::Expected<ExecutableFunction>
- createExecutableFunction(llvm::ArrayRef<llvm::MCInst> Code) const;
+ writeObjectFile(const BenchmarkConfiguration::Setup &Setup,
+ llvm::ArrayRef<llvm::MCInst> Code) const;
};
} // namespace exegesis
diff --git a/tools/llvm-exegesis/lib/LlvmState.cpp b/tools/llvm-exegesis/lib/LlvmState.cpp
index efb5c831157..9b30d78b0c9 100644
--- a/tools/llvm-exegesis/lib/LlvmState.cpp
+++ b/tools/llvm-exegesis/lib/LlvmState.cpp
@@ -29,6 +29,7 @@ LLVMState::LLVMState(const std::string &Triple, const std::string &CpuName) {
TargetMachine.reset(static_cast<llvm::LLVMTargetMachine *>(
TheTarget->createTargetMachine(Triple, CpuName, /*Features*/ "", Options,
llvm::Reloc::Model::Static)));
+ TheExegesisTarget = ExegesisTarget::lookup(TargetMachine->getTargetTriple());
}
LLVMState::LLVMState()
diff --git a/tools/llvm-exegesis/lib/LlvmState.h b/tools/llvm-exegesis/lib/LlvmState.h
index 5c09014a814..18c92b835ca 100644
--- a/tools/llvm-exegesis/lib/LlvmState.h
+++ b/tools/llvm-exegesis/lib/LlvmState.h
@@ -15,6 +15,7 @@
#ifndef LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
#define LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
+#include "Target.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.cpp b/tools/llvm-exegesis/lib/MCInstrDescView.cpp
index 5678fcea94d..fc0d5b327b8 100644
--- a/tools/llvm-exegesis/lib/MCInstrDescView.cpp
+++ b/tools/llvm-exegesis/lib/MCInstrDescView.cpp
@@ -99,11 +99,22 @@ llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) {
return VariableValues[Var.Index];
}
+const llvm::MCOperand &
+InstructionInstance::getValueFor(const Variable &Var) const {
+ return VariableValues[Var.Index];
+}
+
llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) {
assert(Op.VariableIndex >= 0);
return getValueFor(Instr.Variables[Op.VariableIndex]);
}
+const llvm::MCOperand &
+InstructionInstance::getValueFor(const Operand &Op) const {
+ assert(Op.VariableIndex >= 0);
+ return getValueFor(Instr.Variables[Op.VariableIndex]);
+}
+
// forward declaration.
static void randomize(const Instruction &Instr, const Variable &Var,
llvm::MCOperand &AssignedValue);
@@ -118,12 +129,15 @@ bool InstructionInstance::hasImmediateVariables() const {
});
}
-llvm::MCInst InstructionInstance::randomizeUnsetVariablesAndBuild() {
+void InstructionInstance::randomizeUnsetVariables() {
for (const Variable &Var : Instr.Variables) {
llvm::MCOperand &AssignedValue = getValueFor(Var);
if (!AssignedValue.isValid())
randomize(Instr, Var, AssignedValue);
}
+}
+
+llvm::MCInst InstructionInstance::build() const {
llvm::MCInst Result;
Result.setOpcode(Instr.Description->Opcode);
for (const auto &Op : Instr.Operands)
diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.h b/tools/llvm-exegesis/lib/MCInstrDescView.h
index 893b786762f..c36582ac72d 100644
--- a/tools/llvm-exegesis/lib/MCInstrDescView.h
+++ b/tools/llvm-exegesis/lib/MCInstrDescView.h
@@ -93,12 +93,17 @@ struct InstructionInstance {
unsigned getOpcode() const;
llvm::MCOperand &getValueFor(const Variable &Var);
+ const llvm::MCOperand &getValueFor(const Variable &Var) const;
llvm::MCOperand &getValueFor(const Operand &Op);
+ const llvm::MCOperand &getValueFor(const Operand &Op) const;
bool hasImmediateVariables() const;
- // Assigns a Random Value to all Variables that are still Invalid and returns
- // the instance as an llvm::MCInst.
- llvm::MCInst randomizeUnsetVariablesAndBuild();
+ // Assigns a Random Value to all Variables that are still Invalid.
+ void randomizeUnsetVariables();
+
+ // Returns the instance as an llvm::MCInst. The InstructionInstance must be
+ // fully allocated (no invalid variables).
+ llvm::MCInst build() const;
Instruction Instr;
llvm::SmallVector<llvm::MCOperand, 4> VariableValues;
diff --git a/tools/llvm-exegesis/lib/Target.h b/tools/llvm-exegesis/lib/Target.h
index 73426797ab6..1d35824daed 100644
--- a/tools/llvm-exegesis/lib/Target.h
+++ b/tools/llvm-exegesis/lib/Target.h
@@ -20,6 +20,8 @@
#include "llvm/ADT/Triple.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCRegisterInfo.h"
namespace exegesis {
@@ -28,6 +30,11 @@ public:
// Targets can use this to add target-specific passes in assembleToStream();
virtual void addTargetSpecificPasses(llvm::PassManagerBase &PM) const {}
+ // Generates code to move a constant into a the given register.
+ virtual std::vector<llvm::MCInst> setRegToConstant(unsigned Reg) const {
+ return {};
+ }
+
// Returns the ExegesisTarget for the given triple or nullptr if the target
// does not exist.
static const ExegesisTarget *lookup(llvm::Triple TT);
diff --git a/tools/llvm-exegesis/lib/X86/Target.cpp b/tools/llvm-exegesis/lib/X86/Target.cpp
index 26a54591fb0..591edd2afae 100644
--- a/tools/llvm-exegesis/lib/X86/Target.cpp
+++ b/tools/llvm-exegesis/lib/X86/Target.cpp
@@ -8,7 +8,10 @@
//===----------------------------------------------------------------------===//
#include "../Target.h"
+#include "MCTargetDesc/X86MCTargetDesc.h"
#include "X86.h"
+#include "X86RegisterInfo.h"
+#include "llvm/MC/MCInstBuilder.h"
namespace exegesis {
@@ -22,9 +25,88 @@ class ExegesisX86Target : public ExegesisTarget {
// PM.add(llvm::createX86FloatingPointStackifierPass());
}
+ std::vector<llvm::MCInst>
+ setRegToConstant(const unsigned Reg) const override {
+ // FIXME: Handle FP stack:
+ // llvm::X86::RFP32RegClass
+ // llvm::X86::RFP64RegClass
+ // llvm::X86::RFP80RegClass
+ if (llvm::X86::GR8RegClass.contains(Reg)) {
+ return {llvm::MCInstBuilder(llvm::X86::MOV8ri).addReg(Reg).addImm(1)};
+ }
+ if (llvm::X86::GR16RegClass.contains(Reg)) {
+ return {llvm::MCInstBuilder(llvm::X86::MOV16ri).addReg(Reg).addImm(1)};
+ }
+ if (llvm::X86::GR32RegClass.contains(Reg)) {
+ return {llvm::MCInstBuilder(llvm::X86::MOV32ri).addReg(Reg).addImm(1)};
+ }
+ if (llvm::X86::GR64RegClass.contains(Reg)) {
+ return {llvm::MCInstBuilder(llvm::X86::MOV64ri32).addReg(Reg).addImm(1)};
+ }
+ if (llvm::X86::VR128XRegClass.contains(Reg)) {
+ return setVectorRegToConstant(Reg, 16, llvm::X86::VMOVDQUrm);
+ }
+ if (llvm::X86::VR256XRegClass.contains(Reg)) {
+ return setVectorRegToConstant(Reg, 32, llvm::X86::VMOVDQUYrm);
+ }
+ if (llvm::X86::VR512RegClass.contains(Reg)) {
+ return setVectorRegToConstant(Reg, 64, llvm::X86::VMOVDQU64Zrm);
+ }
+ return {};
+ }
+
bool matchesArch(llvm::Triple::ArchType Arch) const override {
return Arch == llvm::Triple::x86_64 || Arch == llvm::Triple::x86;
}
+
+private:
+ // setRegToConstant() specialized for a vector register of size
+ // `RegSizeBytes`. `RMOpcode` is the opcode used to do a memory -> vector
+ // register load.
+ static std::vector<llvm::MCInst>
+ setVectorRegToConstant(const unsigned Reg, const unsigned RegSizeBytes,
+ const unsigned RMOpcode) {
+ // There is no instruction to directly set XMM, go through memory.
+ // Since vector values can be interpreted as integers of various sizes (8
+ // to 64 bits) as well as floats and double, so we chose an immediate
+ // value that has set bits for all byte values and is a normal float/
+ // double. 0x40404040 is ~32.5 when interpreted as a double and ~3.0f when
+ // interpreted as a float.
+ constexpr const uint64_t kImmValue = 0x40404040ull;
+ std::vector<llvm::MCInst> Result;
+ // Allocate scratch memory on the stack.
+ Result.push_back(llvm::MCInstBuilder(llvm::X86::SUB64ri8)
+ .addReg(llvm::X86::RSP)
+ .addReg(llvm::X86::RSP)
+ .addImm(RegSizeBytes));
+ // Fill scratch memory.
+ for (unsigned Disp = 0; Disp < RegSizeBytes; Disp += 4) {
+ Result.push_back(llvm::MCInstBuilder(llvm::X86::MOV32mi)
+ // Address = ESP
+ .addReg(llvm::X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(Disp) // Disp
+ .addReg(0) // Segment
+ // Immediate.
+ .addImm(kImmValue));
+ }
+ // Load Reg from scratch memory.
+ Result.push_back(llvm::MCInstBuilder(RMOpcode)
+ .addReg(Reg)
+ // Address = ESP
+ .addReg(llvm::X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(0) // Disp
+ .addReg(0)); // Segment
+ // Release scratch memory.
+ Result.push_back(llvm::MCInstBuilder(llvm::X86::ADD64ri8)
+ .addReg(llvm::X86::RSP)
+ .addReg(llvm::X86::RSP)
+ .addImm(RegSizeBytes));
+ return Result;
+ }
};
} // namespace