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:
parent
b594513f85
commit
3af012a962
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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"
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user