1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[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

llvm-svn: 335465
This commit is contained in:
Clement Courbet 2018-06-25 13:12:02 +00:00
parent b594513f85
commit 3af012a962
13 changed files with 324 additions and 31 deletions

View File

@ -28,6 +28,21 @@ namespace exegesis {
static constexpr const char ModuleID[] = "ExegesisInfoTest"; static constexpr const char ModuleID[] = "ExegesisInfoTest";
static constexpr const char FunctionID[] = "foo"; 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. // Small utility function to add named passes.
static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName, static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName,
llvm::TargetPassConfig &TPC) { llvm::TargetPassConfig &TPC) {
@ -123,7 +138,9 @@ llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM) {
return MF.getSubtarget().getRegisterInfo()->getReservedRegs(MF); 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::ArrayRef<llvm::MCInst> Instructions,
llvm::raw_pwrite_stream &AsmStream) { llvm::raw_pwrite_stream &AsmStream) {
std::unique_ptr<llvm::LLVMContext> Context = std::unique_ptr<llvm::LLVMContext> Context =
@ -140,9 +157,20 @@ void assembleToStream(std::unique_ptr<llvm::LLVMTargetMachine> TM,
auto &Properties = MF.getProperties(); auto &Properties = MF.getProperties();
Properties.set(llvm::MachineFunctionProperties::Property::NoVRegs); Properties.set(llvm::MachineFunctionProperties::Property::NoVRegs);
Properties.reset(llvm::MachineFunctionProperties::Property::IsSSA); Properties.reset(llvm::MachineFunctionProperties::Property::IsSSA);
// FIXME: Remove this when we assign all used registers as config step. This std::vector<llvm::MCInst> SnippetWithSetup;
// essentially disables checks that used registers are def'ed somewhere. bool IsSnippetSetupComplete;
Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness); 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 // prologue/epilogue pass needs the reserved registers to be frozen, this
// is usually done by the SelectionDAGISel pass. // is usually done by the SelectionDAGISel pass.
MF.getRegInfo().freezeReservedRegs(MF); MF.getRegInfo().freezeReservedRegs(MF);
@ -162,7 +190,7 @@ void assembleToStream(std::unique_ptr<llvm::LLVMTargetMachine> TM,
PM.add(MMI.release()); PM.add(MMI.release());
TPC->printAndVerify("MachineFunctionGenerator::assemble"); TPC->printAndVerify("MachineFunctionGenerator::assemble");
// Add target-specific passes. // Add target-specific passes.
if (const auto *ET = ExegesisTarget::lookup(TM->getTargetTriple())) { if (ET) {
ET->addTargetSpecificPasses(PM); ET->addTargetSpecificPasses(PM);
TPC->printAndVerify("After ExegesisTarget::addTargetSpecificPasses"); TPC->printAndVerify("After ExegesisTarget::addTargetSpecificPasses");
} }

View File

@ -33,6 +33,8 @@
namespace exegesis { namespace exegesis {
class ExegesisTarget;
// Gather the set of reserved registers (depends on function's calling // Gather the set of reserved registers (depends on function's calling
// convention and target machine). // convention and target machine).
llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM); 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 // Instructions. Runs a set of llvm Passes to provide correct prologue and
// epilogue. Once the MachineFunction is ready, it is assembled for TM to // epilogue. Once the MachineFunction is ready, it is assembled for TM to
// AsmStream, the temporary function is eventually discarded. // 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::ArrayRef<llvm::MCInst> Instructions,
llvm::raw_pwrite_stream &AsmStream); llvm::raw_pwrite_stream &AsmStream);

View File

@ -78,10 +78,11 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
// Repeat the snippet until there are at least NumInstructions in the // Repeat the snippet until there are at least NumInstructions in the
// resulting code. The snippet is always repeated at least once. // resulting code. The snippet is always repeated at least once.
const auto GenerateInstructions = [&Snippet](const int MinInstructions) { const auto GenerateInstructions = [&Configuration](
std::vector<llvm::MCInst> Code = Snippet; const int MinInstructions) {
std::vector<llvm::MCInst> Code = Configuration.Snippet;
for (int I = 0; I < MinInstructions; ++I) 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; return Code;
}; };
@ -91,7 +92,8 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
constexpr const int kMinInstructionsForSnippet = 16; constexpr const int kMinInstructionsForSnippet = 16;
{ {
auto ObjectFilePath = auto ObjectFilePath =
writeObjectFile(GenerateInstructions(kMinInstructionsForSnippet)); writeObjectFile(Configuration.SnippetSetup,
GenerateInstructions(kMinInstructionsForSnippet));
if (llvm::Error E = ObjectFilePath.takeError()) { if (llvm::Error E = ObjectFilePath.takeError()) {
InstrBenchmark.Error = llvm::toString(std::move(E)); InstrBenchmark.Error = llvm::toString(std::move(E));
return InstrBenchmark; return InstrBenchmark;
@ -105,7 +107,8 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
// Assemble NumRepetitions instructions repetitions of the snippet for // Assemble NumRepetitions instructions repetitions of the snippet for
// measurements. // measurements.
auto ObjectFilePath = auto ObjectFilePath =
writeObjectFile(GenerateInstructions(InstrBenchmark.NumRepetitions)); writeObjectFile(Configuration.SnippetSetup,
GenerateInstructions(InstrBenchmark.NumRepetitions));
if (llvm::Error E = ObjectFilePath.takeError()) { if (llvm::Error E = ObjectFilePath.takeError()) {
InstrBenchmark.Error = llvm::toString(std::move(E)); InstrBenchmark.Error = llvm::toString(std::move(E));
return InstrBenchmark; return InstrBenchmark;
@ -126,22 +129,68 @@ BenchmarkRunner::generateConfigurations(unsigned Opcode) const {
// TODO: Generate as many configurations as needed here. // TODO: Generate as many configurations as needed here.
BenchmarkConfiguration Configuration; BenchmarkConfiguration Configuration;
Configuration.Info = Prototype.Explanation; Configuration.Info = Prototype.Explanation;
for (InstructionInstance &II : Prototype.Snippet) for (InstructionInstance &II : Prototype.Snippet) {
Configuration.Snippet.push_back(II.randomizeUnsetVariablesAndBuild()); II.randomizeUnsetVariables();
Configuration.Snippet.push_back(II.build());
}
Configuration.SnippetSetup.RegsToDef = computeRegsToDef(Prototype.Snippet);
return std::vector<BenchmarkConfiguration>{Configuration}; return std::vector<BenchmarkConfiguration>{Configuration};
} else } else
return E.takeError(); 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> 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; int ResultFD = 0;
llvm::SmallString<256> ResultPath; llvm::SmallString<256> ResultPath;
if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile( if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile(
"snippet", "o", ResultFD, ResultPath))) "snippet", "o", ResultFD, ResultPath)))
return std::move(E); return std::move(E);
llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); 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(); return ResultPath.str();
} }

View File

@ -39,7 +39,10 @@ struct BenchmarkConfiguration {
// This code is run before the Snippet is iterated. Since it is part of the // 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 // measurement it should be as short as possible. It is usually used to setup
// the content of the Registers. // 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. // The sequence of instructions that are to be repeated.
std::vector<llvm::MCInst> Snippet; std::vector<llvm::MCInst> Snippet;
@ -71,6 +74,10 @@ public:
run(unsigned Opcode, const InstructionFilter &Filter, run(unsigned Opcode, const InstructionFilter &Filter,
unsigned NumRepetitions); 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: protected:
const LLVMState &State; const LLVMState &State;
const llvm::MCInstrInfo &MCInstrInfo; const llvm::MCInstrInfo &MCInstrInfo;
@ -96,9 +103,8 @@ private:
const unsigned NumRepetitions) const = 0; const unsigned NumRepetitions) const = 0;
llvm::Expected<std::string> llvm::Expected<std::string>
writeObjectFile(llvm::ArrayRef<llvm::MCInst> Code) const; writeObjectFile(const BenchmarkConfiguration::Setup &Setup,
llvm::Expected<ExecutableFunction> llvm::ArrayRef<llvm::MCInst> Code) const;
createExecutableFunction(llvm::ArrayRef<llvm::MCInst> Code) const;
}; };
} // namespace exegesis } // namespace exegesis

View File

@ -29,6 +29,7 @@ LLVMState::LLVMState(const std::string &Triple, const std::string &CpuName) {
TargetMachine.reset(static_cast<llvm::LLVMTargetMachine *>( TargetMachine.reset(static_cast<llvm::LLVMTargetMachine *>(
TheTarget->createTargetMachine(Triple, CpuName, /*Features*/ "", Options, TheTarget->createTargetMachine(Triple, CpuName, /*Features*/ "", Options,
llvm::Reloc::Model::Static))); llvm::Reloc::Model::Static)));
TheExegesisTarget = ExegesisTarget::lookup(TargetMachine->getTargetTriple());
} }
LLVMState::LLVMState() LLVMState::LLVMState()

View File

@ -15,6 +15,7 @@
#ifndef LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H #ifndef LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
#define LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H #define LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
#include "Target.h"
#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCInst.h" #include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCInstrInfo.h"

View File

@ -99,11 +99,22 @@ llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) {
return VariableValues[Var.Index]; return VariableValues[Var.Index];
} }
const llvm::MCOperand &
InstructionInstance::getValueFor(const Variable &Var) const {
return VariableValues[Var.Index];
}
llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) { llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) {
assert(Op.VariableIndex >= 0); assert(Op.VariableIndex >= 0);
return getValueFor(Instr.Variables[Op.VariableIndex]); 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. // forward declaration.
static void randomize(const Instruction &Instr, const Variable &Var, static void randomize(const Instruction &Instr, const Variable &Var,
llvm::MCOperand &AssignedValue); llvm::MCOperand &AssignedValue);
@ -118,12 +129,15 @@ bool InstructionInstance::hasImmediateVariables() const {
}); });
} }
llvm::MCInst InstructionInstance::randomizeUnsetVariablesAndBuild() { void InstructionInstance::randomizeUnsetVariables() {
for (const Variable &Var : Instr.Variables) { for (const Variable &Var : Instr.Variables) {
llvm::MCOperand &AssignedValue = getValueFor(Var); llvm::MCOperand &AssignedValue = getValueFor(Var);
if (!AssignedValue.isValid()) if (!AssignedValue.isValid())
randomize(Instr, Var, AssignedValue); randomize(Instr, Var, AssignedValue);
} }
}
llvm::MCInst InstructionInstance::build() const {
llvm::MCInst Result; llvm::MCInst Result;
Result.setOpcode(Instr.Description->Opcode); Result.setOpcode(Instr.Description->Opcode);
for (const auto &Op : Instr.Operands) for (const auto &Op : Instr.Operands)

View File

@ -93,12 +93,17 @@ struct InstructionInstance {
unsigned getOpcode() const; unsigned getOpcode() const;
llvm::MCOperand &getValueFor(const Variable &Var); llvm::MCOperand &getValueFor(const Variable &Var);
const llvm::MCOperand &getValueFor(const Variable &Var) const;
llvm::MCOperand &getValueFor(const Operand &Op); llvm::MCOperand &getValueFor(const Operand &Op);
const llvm::MCOperand &getValueFor(const Operand &Op) const;
bool hasImmediateVariables() const; bool hasImmediateVariables() const;
// Assigns a Random Value to all Variables that are still Invalid and returns // Assigns a Random Value to all Variables that are still Invalid.
// the instance as an llvm::MCInst. void randomizeUnsetVariables();
llvm::MCInst randomizeUnsetVariablesAndBuild();
// Returns the instance as an llvm::MCInst. The InstructionInstance must be
// fully allocated (no invalid variables).
llvm::MCInst build() const;
Instruction Instr; Instruction Instr;
llvm::SmallVector<llvm::MCOperand, 4> VariableValues; llvm::SmallVector<llvm::MCOperand, 4> VariableValues;

View File

@ -20,6 +20,8 @@
#include "llvm/ADT/Triple.h" #include "llvm/ADT/Triple.h"
#include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassManager.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCRegisterInfo.h"
namespace exegesis { namespace exegesis {
@ -28,6 +30,11 @@ public:
// Targets can use this to add target-specific passes in assembleToStream(); // Targets can use this to add target-specific passes in assembleToStream();
virtual void addTargetSpecificPasses(llvm::PassManagerBase &PM) const {} 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 // Returns the ExegesisTarget for the given triple or nullptr if the target
// does not exist. // does not exist.
static const ExegesisTarget *lookup(llvm::Triple TT); static const ExegesisTarget *lookup(llvm::Triple TT);

View File

@ -8,7 +8,10 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "../Target.h" #include "../Target.h"
#include "MCTargetDesc/X86MCTargetDesc.h"
#include "X86.h" #include "X86.h"
#include "X86RegisterInfo.h"
#include "llvm/MC/MCInstBuilder.h"
namespace exegesis { namespace exegesis {
@ -22,9 +25,88 @@ class ExegesisX86Target : public ExegesisTarget {
// PM.add(llvm::createX86FloatingPointStackifierPass()); // 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 { bool matchesArch(llvm::Triple::ArchType Arch) const override {
return Arch == llvm::Triple::x86_64 || Arch == llvm::Triple::x86; 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 } // namespace

View File

@ -36,9 +36,16 @@ protected:
} }
template <class... Bs> inline void Check(llvm::MCInst MCInst, Bs... Bytes) { template <class... Bs> inline void Check(llvm::MCInst MCInst, Bs... Bytes) {
ExecutableFunction Function = (MCInst.getOpcode() == 0) CheckWithSetup(nullptr, {}, MCInst, Bytes...);
? assembleToFunction({}) }
: assembleToFunction({MCInst});
template <class... Bs>
inline void CheckWithSetup(const ExegesisTarget *ET,
llvm::ArrayRef<unsigned> RegsToDef,
llvm::MCInst MCInst, Bs... Bytes) {
ExecutableFunction Function =
(MCInst.getOpcode() == 0) ? assembleToFunction(ET, RegsToDef, {})
: assembleToFunction(ET, RegsToDef, {MCInst});
ASSERT_THAT(Function.getFunctionBytes().str(), ASSERT_THAT(Function.getFunctionBytes().str(),
testing::ElementsAre(Bytes...)); testing::ElementsAre(Bytes...));
if (CanExecute) if (CanExecute)
@ -60,10 +67,13 @@ private:
} }
ExecutableFunction ExecutableFunction
assembleToFunction(llvm::ArrayRef<llvm::MCInst> Instructions) { assembleToFunction(const ExegesisTarget *ET,
llvm::ArrayRef<unsigned> RegsToDef,
llvm::ArrayRef<llvm::MCInst> Instructions) {
llvm::SmallString<256> Buffer; llvm::SmallString<256> Buffer;
llvm::raw_svector_ostream AsmStream(Buffer); llvm::raw_svector_ostream AsmStream(Buffer);
assembleToStream(createTargetMachine(), Instructions, AsmStream); assembleToStream(ET, createTargetMachine(), RegsToDef, Instructions,
AsmStream);
return ExecutableFunction(createTargetMachine(), return ExecutableFunction(createTargetMachine(),
getObjectFromBuffer(AsmStream.str())); getObjectFromBuffer(AsmStream.str()));
} }

View File

@ -25,6 +25,7 @@ using testing::ElementsAre;
using testing::HasSubstr; using testing::HasSubstr;
using testing::Not; using testing::Not;
using testing::SizeIs; using testing::SizeIs;
using testing::UnorderedElementsAre;
MATCHER(IsInvalid, "") { return !arg.isValid(); } MATCHER(IsInvalid, "") { return !arg.isValid(); }
MATCHER(IsReg, "") { return arg.isReg(); } MATCHER(IsReg, "") { return arg.isReg(); }
@ -214,5 +215,74 @@ TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
EXPECT_THAT(II.VariableValues[3], IsInvalid()); EXPECT_THAT(II.VariableValues[3], IsInvalid());
} }
class FakeBenchmarkRunner : public BenchmarkRunner {
public:
using BenchmarkRunner::BenchmarkRunner;
Instruction createInstruction(unsigned Opcode) {
return Instruction(MCInstrInfo.get(Opcode), RATC);
}
private:
InstructionBenchmark::ModeE getMode() const override {
return InstructionBenchmark::Unknown;
}
llvm::Expected<SnippetPrototype>
generatePrototype(unsigned Opcode) const override {
return llvm::make_error<llvm::StringError>("not implemented",
llvm::inconvertibleErrorCode());
}
std::vector<BenchmarkMeasure>
runMeasurements(const ExecutableFunction &EF,
const unsigned NumRepetitions) const override {
return {};
}
};
using FakeSnippetGeneratorTest = SnippetGeneratorTest<FakeBenchmarkRunner>;
TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd16ri) {
// ADD16ri:
// explicit def 0 : reg RegClass=GR16
// explicit use 1 : reg RegClass=GR16 | TIED_TO:0
// explicit use 2 : imm
// implicit def : EFLAGS
InstructionInstance II(Runner.createInstruction(llvm::X86::ADD16ri));
II.getValueFor(II.Instr.Variables[0]) =
llvm::MCOperand::createReg(llvm::X86::AX);
std::vector<InstructionInstance> Snippet;
Snippet.push_back(std::move(II));
const auto RegsToDef = Runner.computeRegsToDef(Snippet);
EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::AX));
}
TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd64rr) {
// ADD64rr:
// mov64ri rax, 42
// add64rr rax, rax, rbx
// -> only rbx needs defining.
std::vector<InstructionInstance> Snippet;
{
InstructionInstance Mov(Runner.createInstruction(llvm::X86::MOV64ri));
Mov.getValueFor(Mov.Instr.Variables[0]) =
llvm::MCOperand::createReg(llvm::X86::RAX);
Mov.getValueFor(Mov.Instr.Variables[1]) = llvm::MCOperand::createImm(42);
Snippet.push_back(std::move(Mov));
}
{
InstructionInstance Add(Runner.createInstruction(llvm::X86::ADD64rr));
Add.getValueFor(Add.Instr.Variables[0]) =
llvm::MCOperand::createReg(llvm::X86::RAX);
Add.getValueFor(Add.Instr.Variables[1]) =
llvm::MCOperand::createReg(llvm::X86::RBX);
Snippet.push_back(std::move(Add));
}
const auto RegsToDef = Runner.computeRegsToDef(Snippet);
EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::RBX));
}
} // namespace } // namespace
} // namespace exegesis } // namespace exegesis

View File

@ -3,6 +3,7 @@
#include <cassert> #include <cassert>
#include <memory> #include <memory>
#include "MCTargetDesc/X86MCTargetDesc.h"
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
@ -12,16 +13,31 @@ void InitializeX86ExegesisTarget();
namespace { namespace {
using testing::Gt;
using testing::NotNull; using testing::NotNull;
using testing::SizeIs;
class X86TargetTest : public ::testing::Test { class X86TargetTest : public ::testing::Test {
protected: protected:
X86TargetTest()
: Target_(ExegesisTarget::lookup(llvm::Triple("x86_64-unknown-linux"))) {
EXPECT_THAT(Target_, NotNull());
}
static void SetUpTestCase() { InitializeX86ExegesisTarget(); } static void SetUpTestCase() { InitializeX86ExegesisTarget(); }
const ExegesisTarget *const Target_;
}; };
TEST_F(X86TargetTest, Lookup) { TEST_F(X86TargetTest, SetRegToConstantGPR) {
EXPECT_THAT(ExegesisTarget::lookup(llvm::Triple("x86_64-unknown-linux")), const auto Insts = Target_->setRegToConstant(llvm::X86::EAX);
NotNull()); EXPECT_THAT(Insts, SizeIs(1));
EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::MOV32ri);
EXPECT_EQ(Insts[0].getOperand(0).getReg(), llvm::X86::EAX);
}
TEST_F(X86TargetTest, SetRegToConstantXMM) {
const auto Insts = Target_->setRegToConstant(llvm::X86::XMM1);
EXPECT_THAT(Insts, SizeIs(Gt(0)));
} }
} // namespace } // namespace