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

[llvm-exegesis] Cleaner design without mutable data.

Summary: Previous design was relying on the 'mutate' keyword and was quite confusing. This version separate mutable from immutable data and makes it clearer what changes and what doesn't.

Reviewers: courbet

Subscribers: tschuett, llvm-commits

Differential Revision: https://reviews.llvm.org/D48020

llvm-svn: 334596
This commit is contained in:
Guillaume Chatelet 2018-06-13 13:24:41 +00:00
parent 58b914129e
commit 8d818cafdb
12 changed files with 468 additions and 214 deletions

View File

@ -23,6 +23,9 @@
namespace exegesis {
BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S)
: llvm::StringError(S, llvm::inconvertibleErrorCode()) {}
BenchmarkRunner::InstructionFilter::~InstructionFilter() = default;
BenchmarkRunner::BenchmarkRunner(const LLVMState &State)
@ -38,14 +41,13 @@ BenchmarkRunner::run(unsigned Opcode, const InstructionFilter &Filter,
unsigned NumRepetitions) {
// Ignore instructions that we cannot run.
if (State.getInstrInfo().get(Opcode).isPseudo())
return llvm::make_error<llvm::StringError>("Unsupported opcode: isPseudo",
llvm::inconvertibleErrorCode());
return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo");
if (llvm::Error E = Filter.shouldRun(State, Opcode))
return std::move(E);
llvm::Expected<std::vector<BenchmarkConfiguration>> ConfigurationOrError =
createConfigurations(RATC, Opcode);
createConfigurations(Opcode);
if (llvm::Error E = ConfigurationOrError.takeError())
return std::move(E);

View File

@ -26,6 +26,13 @@
namespace exegesis {
// A class representing failures that happened during Benchmark, they are used
// to report informations to the user.
class BenchmarkFailure : public llvm::StringError {
public:
BenchmarkFailure(const llvm::Twine &S);
};
// A collection of instructions that are to be assembled, executed and measured.
struct BenchmarkConfiguration {
// This code is run before the Snippet is iterated. Since it is part of the
@ -67,6 +74,7 @@ protected:
const LLVMState &State;
const llvm::MCInstrInfo &MCInstrInfo;
const llvm::MCRegisterInfo &MCRegisterInfo;
const RegisterAliasingTrackerCache RATC;
private:
InstructionBenchmark runOne(const BenchmarkConfiguration &Configuration,
@ -75,8 +83,7 @@ private:
virtual InstructionBenchmark::ModeE getMode() const = 0;
virtual llvm::Expected<std::vector<BenchmarkConfiguration>>
createConfigurations(RegisterAliasingTrackerCache &RATC,
unsigned Opcode) const = 0;
createConfigurations(unsigned Opcode) const = 0;
virtual std::vector<BenchmarkMeasure>
runMeasurements(const ExecutableFunction &EF,
@ -84,8 +91,6 @@ private:
llvm::Expected<std::string>
writeObjectFile(llvm::ArrayRef<llvm::MCInst> Code) const;
RegisterAliasingTrackerCache RATC;
};
} // namespace exegesis

View File

@ -19,93 +19,113 @@
namespace exegesis {
static bool HasUnknownOperand(const llvm::MCOperandInfo &OpInfo) {
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) {
static bool hasMemoryOperand(const llvm::MCOperandInfo &OpInfo) {
return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY;
}
static bool IsInfeasible(const Instruction &Instruction, std::string &Error) {
const auto &MCInstrDesc = Instruction.Description;
if (MCInstrDesc.isPseudo()) {
Error = "is pseudo";
return true;
}
if (llvm::any_of(MCInstrDesc.operands(), HasUnknownOperand)) {
Error = "has unknown operands";
return true;
}
if (llvm::any_of(MCInstrDesc.operands(), HasMemoryOperand)) {
Error = "has memory operands";
return true;
}
return false;
}
LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
InstructionBenchmark::ModeE LatencyBenchmarkRunner::getMode() const {
return InstructionBenchmark::Latency;
}
llvm::Expected<std::vector<BenchmarkConfiguration>>
LatencyBenchmarkRunner::createConfigurations(RegisterAliasingTrackerCache &RATC,
unsigned Opcode) const {
const llvm::MCInstrDesc &MCInstrDesc = MCInstrInfo.get(Opcode);
const Instruction ThisInstruction(MCInstrDesc, RATC);
std::string Error;
if (IsInfeasible(ThisInstruction, Error))
return llvm::make_error<llvm::StringError>(
llvm::Twine("Infeasible : ").concat(Error),
llvm::inconvertibleErrorCode());
llvm::Error LatencyBenchmarkRunner::isInfeasible(
const llvm::MCInstrDesc &MCInstrDesc) const {
if (MCInstrDesc.isPseudo())
return llvm::make_error<BenchmarkFailure>("Infeasible : is pseudo");
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();
}
llvm::Expected<BenchmarkConfiguration>
LatencyBenchmarkRunner::generateSelfAliasingConfiguration(
const Instruction &Instr,
const AliasingConfigurations &SelfAliasing) const {
BenchmarkConfiguration Conf;
const AliasingConfigurations SelfAliasing(ThisInstruction, ThisInstruction);
if (!SelfAliasing.empty()) {
if (!SelfAliasing.hasImplicitAliasing()) {
Conf.Info = "explicit self cycles, selecting one aliasing Conf.";
setRandomAliasing(SelfAliasing);
} else {
Conf.Info = "implicit Self cycles, picking random values.";
}
Conf.Snippet = {randomizeUnsetVariablesAndBuild(ThisInstruction)};
return std::vector<BenchmarkConfiguration>{Conf};
InstructionInstance II(Instr);
if (SelfAliasing.hasImplicitAliasing()) {
Conf.Info = "implicit Self cycles, picking random values.";
} else {
Conf.Info = "explicit self cycles, selecting one aliasing Conf.";
// This is a self aliasing instruction so defs and uses are from the same
// instance, hence twice II in the following call.
setRandomAliasing(SelfAliasing, II, II);
}
Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()};
return Conf;
}
// Let's try to create a dependency through another opcode.
llvm::Expected<BenchmarkConfiguration>
LatencyBenchmarkRunner::generateTwoInstructionConfiguration(
const Instruction &Instr,
const AliasingConfigurations &SelfAliasing) const {
std::vector<unsigned> Opcodes;
Opcodes.resize(MCInstrInfo.getNumOpcodes());
std::iota(Opcodes.begin(), Opcodes.end(), 0U);
std::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator());
for (const unsigned OtherOpcode : Opcodes) {
clearVariableAssignments(ThisInstruction);
if (OtherOpcode == Opcode)
if (OtherOpcode == Instr.Description.Opcode)
continue;
const Instruction OtherInstruction(MCInstrInfo.get(OtherOpcode), RATC);
if (IsInfeasible(OtherInstruction, Error))
const auto &OtherInstrDesc = MCInstrInfo.get(OtherOpcode);
if (auto E = isInfeasible(OtherInstrDesc)) {
llvm::consumeError(std::move(E));
continue;
const AliasingConfigurations Forward(ThisInstruction, OtherInstruction);
const AliasingConfigurations Back(OtherInstruction, ThisInstruction);
}
const Instruction OtherInstr(OtherInstrDesc, RATC);
const AliasingConfigurations Forward(Instr, OtherInstr);
const AliasingConfigurations Back(OtherInstr, Instr);
if (Forward.empty() || Back.empty())
continue;
setRandomAliasing(Forward);
setRandomAliasing(Back);
InstructionInstance ThisII(Instr);
InstructionInstance OtherII(OtherInstr);
if (!Forward.hasImplicitAliasing())
setRandomAliasing(Forward, ThisII, OtherII);
if (!Back.hasImplicitAliasing())
setRandomAliasing(Back, OtherII, ThisII);
BenchmarkConfiguration Conf;
Conf.Info = llvm::Twine("creating cycle through ")
.concat(MCInstrInfo.getName(OtherOpcode))
.concat(".")
.str();
Conf.Snippet.push_back(randomizeUnsetVariablesAndBuild(ThisInstruction));
Conf.Snippet.push_back(randomizeUnsetVariablesAndBuild(OtherInstruction));
return std::vector<BenchmarkConfiguration>{Conf};
Conf.Snippet.push_back(ThisII.randomizeUnsetVariablesAndBuild());
Conf.Snippet.push_back(OtherII.randomizeUnsetVariablesAndBuild());
return Conf;
}
return llvm::make_error<BenchmarkFailure>(
"Infeasible : Didn't find any scheme to make the instruction serial");
}
return llvm::make_error<llvm::StringError>(
"Infeasible : Didn't find any scheme to make the instruction serial",
llvm::inconvertibleErrorCode());
llvm::Expected<BenchmarkConfiguration>
LatencyBenchmarkRunner::generateConfiguration(unsigned Opcode) const {
const auto &InstrDesc = MCInstrInfo.get(Opcode);
if (auto E = isInfeasible(InstrDesc))
return std::move(E);
const Instruction Instr(InstrDesc, RATC);
const AliasingConfigurations SelfAliasing(Instr, Instr);
if (SelfAliasing.empty()) {
// No self aliasing, trying to create a dependency through another opcode.
return generateTwoInstructionConfiguration(Instr, SelfAliasing);
} else {
return generateSelfAliasingConfiguration(Instr, SelfAliasing);
}
}
llvm::Expected<std::vector<BenchmarkConfiguration>>
LatencyBenchmarkRunner::createConfigurations(unsigned Opcode) const {
if (auto E = generateConfiguration(Opcode))
return std::vector<BenchmarkConfiguration>{E.get()};
else
return E.takeError();
}
std::vector<BenchmarkMeasure>

View File

@ -16,6 +16,7 @@
#define LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
#include "BenchmarkRunner.h"
#include "MCInstrDescView.h"
namespace exegesis {
@ -24,12 +25,24 @@ public:
using BenchmarkRunner::BenchmarkRunner;
~LatencyBenchmarkRunner() override;
llvm::Expected<BenchmarkConfiguration>
generateConfiguration(unsigned Opcode) const;
private:
llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const;
llvm::Expected<BenchmarkConfiguration> generateSelfAliasingConfiguration(
const Instruction &Instr,
const AliasingConfigurations &SelfAliasing) const;
llvm::Expected<BenchmarkConfiguration> generateTwoInstructionConfiguration(
const Instruction &Instr,
const AliasingConfigurations &SelfAliasing) const;
InstructionBenchmark::ModeE getMode() const override;
llvm::Expected<std::vector<BenchmarkConfiguration>>
createConfigurations(RegisterAliasingTrackerCache &RATC,
unsigned OpcodeIndex) const override;
createConfigurations(unsigned OpcodeIndex) const override;
std::vector<BenchmarkMeasure>
runMeasurements(const ExecutableFunction &EF,

View File

@ -17,14 +17,8 @@
namespace exegesis {
static void tie(const Operand *FromOperand, llvm::Optional<Variable> &Var) {
if (!Var)
Var.emplace();
Var->TiedOperands.push_back(FromOperand);
}
Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc,
RegisterAliasingTrackerCache &RATC)
const RegisterAliasingTrackerCache &RATC)
: Description(MCInstrDesc) {
unsigned OpIndex = 0;
for (; OpIndex < MCInstrDesc.getNumOperands(); ++OpIndex) {
@ -36,6 +30,8 @@ Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc,
// TODO(gchatelet): Handle isLookupPtrRegClass.
if (OpInfo.RegClass >= 0)
Operand.Tracker = &RATC.getRegisterClass(OpInfo.RegClass);
Operand.TiedToIndex =
MCInstrDesc.getOperandConstraint(OpIndex, llvm::MCOI::TIED_TO);
Operand.Info = &OpInfo;
Operands.push_back(Operand);
}
@ -59,24 +55,23 @@ Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc,
Operand.ImplicitReg = MCPhysReg;
Operands.push_back(Operand);
}
// Set TiedTo for operands.
for (auto &Op : Operands) {
if (Op.IsExplicit) {
const int TiedTo =
MCInstrDesc.getOperandConstraint(Op.Index, llvm::MCOI::TIED_TO);
if (TiedTo >= 0) {
Op.TiedTo = &Operands[TiedTo];
tie(&Op, Operands[TiedTo].Var);
} else {
tie(&Op, Op.Var);
}
// Assigning Variables to non tied explicit operands.
Variables.reserve(Operands.size()); // Variables.size() <= Operands.size()
for (auto &Op : Operands)
if (Op.IsExplicit && Op.TiedToIndex < 0) {
const size_t VariableIndex = Variables.size();
Op.VariableIndex = VariableIndex;
Variables.emplace_back();
Variables.back().Index = VariableIndex;
}
}
for (auto &Op : Operands) {
if (Op.Var) {
Variables.push_back(&*Op.Var);
}
}
// Assigning Variables to tied operands.
for (auto &Op : Operands)
if (Op.TiedToIndex >= 0)
Op.VariableIndex = Operands[Op.TiedToIndex].VariableIndex;
// Assigning Operands to Variables.
for (auto &Op : Operands)
if (Op.VariableIndex >= 0)
Variables[Op.VariableIndex].TiedOperands.push_back(&Op);
// Processing Aliasing.
DefRegisters = RATC.emptyRegisters();
UseRegisters = RATC.emptyRegisters();
@ -88,6 +83,35 @@ Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc,
}
}
InstructionInstance::InstructionInstance(const Instruction &Instr)
: Instr(Instr), VariableValues(Instr.Variables.size()) {}
llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) {
return VariableValues[Var.Index];
}
llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) {
assert(Op.VariableIndex >= 0);
return getValueFor(Instr.Variables[Op.VariableIndex]);
}
// forward declaration.
static void randomize(const Variable &Var, llvm::MCOperand &AssignedValue);
llvm::MCInst InstructionInstance::randomizeUnsetVariablesAndBuild() {
for (const Variable &Var : Instr.Variables) {
llvm::MCOperand &AssignedValue = getValueFor(Var);
if (!AssignedValue.isValid())
randomize(Var, AssignedValue);
}
llvm::MCInst Result;
Result.setOpcode(Instr.Description.Opcode);
for (const auto &Op : Instr.Operands)
if (Op.IsExplicit)
Result.addOperand(getValueFor(Op));
return Result;
}
bool RegisterOperandAssignment::
operator==(const RegisterOperandAssignment &Other) const {
return std::tie(Op, Reg) == std::tie(Other.Op, Other.Reg);
@ -159,7 +183,7 @@ static auto randomElement(const C &Container) -> decltype(Container[0]) {
return Container[randomIndex(Container.size())];
}
static void randomize(Variable &Var) {
static void randomize(const Variable &Var, llvm::MCOperand &AssignedValue) {
assert(!Var.TiedOperands.empty());
assert(Var.TiedOperands.front() != nullptr);
const Operand &Op = *Var.TiedOperands.front();
@ -168,12 +192,12 @@ static void randomize(Variable &Var) {
switch (OpInfo.OperandType) {
case llvm::MCOI::OperandType::OPERAND_IMMEDIATE:
// FIXME: explore immediate values too.
Var.AssignedValue = llvm::MCOperand::createImm(1);
AssignedValue = llvm::MCOperand::createImm(1);
break;
case llvm::MCOI::OperandType::OPERAND_REGISTER: {
assert(Op.Tracker);
const auto &Registers = Op.Tracker->sourceBits();
Var.AssignedValue = llvm::MCOperand::createReg(randomBit(Registers));
AssignedValue = llvm::MCOperand::createReg(randomBit(Registers));
break;
}
default:
@ -181,15 +205,16 @@ static void randomize(Variable &Var) {
}
}
static void setRegisterOperandValue(const RegisterOperandAssignment &ROV) {
const Operand *Op = ROV.Op->TiedTo ? ROV.Op->TiedTo : ROV.Op;
assert(Op->Var);
auto &AssignedValue = Op->Var->AssignedValue;
static void setRegisterOperandValue(const RegisterOperandAssignment &ROV,
InstructionInstance &II) {
assert(ROV.Op);
assert(ROV.Op->IsExplicit);
auto &AssignedValue = II.getValueFor(*ROV.Op);
if (AssignedValue.isValid()) {
assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg);
return;
}
Op->Var->AssignedValue = llvm::MCOperand::createReg(ROV.Reg);
AssignedValue = llvm::MCOperand::createReg(ROV.Reg);
}
size_t randomBit(const llvm::BitVector &Vector) {
@ -200,41 +225,13 @@ size_t randomBit(const llvm::BitVector &Vector) {
return *Itr;
}
void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations) {
void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
InstructionInstance &DefII, InstructionInstance &UseII) {
assert(!AliasingConfigurations.empty());
assert(!AliasingConfigurations.hasImplicitAliasing());
const auto &RandomConf = randomElement(AliasingConfigurations.Configurations);
setRegisterOperandValue(randomElement(RandomConf.Defs));
setRegisterOperandValue(randomElement(RandomConf.Uses));
}
void randomizeUnsetVariable(const Instruction &Instruction) {
for (auto *Var : Instruction.Variables)
if (!Var->AssignedValue.isValid())
randomize(*Var);
}
void clearVariableAssignments(const Instruction &Instruction) {
for (auto *Var : Instruction.Variables)
Var->AssignedValue = llvm::MCOperand();
}
llvm::MCInst build(const Instruction &Instruction) {
llvm::MCInst Result;
Result.setOpcode(Instruction.Description.Opcode);
for (const auto &Op : Instruction.Operands) {
if (Op.IsExplicit) {
auto &Var = Op.TiedTo ? Op.TiedTo->Var : Op.Var;
assert(Var);
Result.addOperand(Var->AssignedValue);
}
}
return Result;
}
llvm::MCInst randomizeUnsetVariablesAndBuild(const Instruction &Instruction) {
randomizeUnsetVariable(Instruction);
return build(Instruction);
setRegisterOperandValue(randomElement(RandomConf.Defs), DefII);
setRegisterOperandValue(randomElement(RandomConf.Uses), UseII);
}
void DumpMCOperand(const llvm::MCRegisterInfo &MCRegisterInfo,

View File

@ -32,11 +32,14 @@ namespace exegesis {
struct Operand; // forward declaration.
// A variable represents the value of an Operand or a set of Operands if they ar
// tied together.
// A variable represents the value associated to an Operand or a set of Operands
// if they are tied together.
struct Variable {
llvm::SmallVector<const Operand *, 2> TiedOperands;
llvm::MCOperand AssignedValue;
// The index of this Variable in Instruction.Variables and its associated
// Value in InstructionInstance.VariableValues.
unsigned Index = -1;
};
// MCOperandInfo can only represents Explicit operands. This object gives a
@ -46,35 +49,50 @@ struct Variable {
// - Tracker: is set for Register Operands and is used to keep track of possible
// registers and the registers reachable from them (aliasing registers).
// - Info: a shortcut for MCInstrDesc::operands()[Index].
// - TiedTo: a pointer to the Operand holding the value or nullptr.
// - TiedToIndex: the index of the Operand holding the value or -1.
// - ImplicitReg: a pointer to the register value when Operand is Implicit,
// nullptr otherwise.
// - Variable: The value associated with this Operand. It is only set for
// explicit operands that are not TiedTo.
// - VariableIndex: the index of the Variable holding the value for this Operand
// or -1 if this operand is implicit.
struct Operand {
uint8_t Index = 0;
unsigned Index = 0;
bool IsDef = false;
bool IsExplicit = false;
const RegisterAliasingTracker *Tracker = nullptr; // Set for Register Op.
const llvm::MCOperandInfo *Info = nullptr; // Set for Explicit Op.
const Operand *TiedTo = nullptr; // Set for Reg/Explicit Op.
int TiedToIndex = -1; // Set for Reg/Explicit Op.
const llvm::MCPhysReg *ImplicitReg = nullptr; // Set for Implicit Op.
mutable llvm::Optional<Variable> Var; // Set for Explicit Op.
int VariableIndex = -1; // Set for Reg/Explicit Op.
};
// A view over an MCInstrDesc offering a convenient interface to compute
// Register aliasing and assign values to Operands.
// Register aliasing.
struct Instruction {
Instruction(const llvm::MCInstrDesc &MCInstrDesc,
RegisterAliasingTrackerCache &ATC);
const RegisterAliasingTrackerCache &ATC);
const llvm::MCInstrDesc &Description;
llvm::SmallVector<Operand, 8> Operands;
llvm::SmallVector<Variable *, 8> Variables;
llvm::SmallVector<Variable, 4> Variables;
llvm::BitVector DefRegisters; // The union of the aliased def registers.
llvm::BitVector UseRegisters; // The union of the aliased use registers.
};
// An instance of an Instruction holding values for each of its Variables.
struct InstructionInstance {
InstructionInstance(const Instruction &Instr);
llvm::MCOperand &getValueFor(const Variable &Var);
llvm::MCOperand &getValueFor(const Operand &Op);
// Assigns a Random Value to all Variables that are still Invalid and returns
// the instance as an llvm::MCInst.
llvm::MCInst randomizeUnsetVariablesAndBuild();
const Instruction &Instr;
llvm::SmallVector<llvm::MCOperand, 4> VariableValues;
};
// Represents the assignment of a Register to an Operand.
struct RegisterOperandAssignment {
RegisterOperandAssignment(const Operand *Operand, llvm::MCPhysReg Reg)
@ -126,17 +144,10 @@ std::mt19937 &randomGenerator();
// Precondition: Vector must have at least one bit set.
size_t randomBit(const llvm::BitVector &Vector);
// Picks a random configuration, then select a random def and a random use from
// it and set the target Variables to the selected values.
// FIXME: This function mutates some nested variables in a const object, please
// fix ASAP.
void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations);
// Set all Instruction's Variables AssignedValue to Invalid.
void clearVariableAssignments(const Instruction &Instruction);
// Assigns a Random Value to all Instruction's Variables that are still Invalid.
llvm::MCInst randomizeUnsetVariablesAndBuild(const Instruction &Instruction);
// Picks a random configuration, then selects a random def and a random use from
// it and finally set the selected values in the provided InstructionInstances.
void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
InstructionInstance &DefII, InstructionInstance &UseII);
// Writes MCInst to OS.
// This is not assembly but the internal LLVM's name for instructions and

View File

@ -64,7 +64,7 @@ RegisterAliasingTrackerCache::RegisterAliasingTrackerCache(
EmptyRegisters(RegInfo.getNumRegs()) {}
const RegisterAliasingTracker &
RegisterAliasingTrackerCache::getRegister(llvm::MCPhysReg PhysReg) {
RegisterAliasingTrackerCache::getRegister(llvm::MCPhysReg PhysReg) const {
auto &Found = Registers[PhysReg];
if (!Found)
Found.reset(new RegisterAliasingTracker(RegInfo, PhysReg));
@ -72,7 +72,7 @@ RegisterAliasingTrackerCache::getRegister(llvm::MCPhysReg PhysReg) {
}
const RegisterAliasingTracker &
RegisterAliasingTrackerCache::getRegisterClass(unsigned RegClassIndex) {
RegisterAliasingTrackerCache::getRegisterClass(unsigned RegClassIndex) const {
auto &Found = RegisterClasses[RegClassIndex];
const auto &RegClass = RegInfo.getRegClass(RegClassIndex);
if (!Found)

View File

@ -87,18 +87,18 @@ struct RegisterAliasingTrackerCache {
const llvm::MCRegisterInfo &regInfo() const { return RegInfo; }
// Retrieves the RegisterAliasingTracker for this particular register.
const RegisterAliasingTracker &getRegister(llvm::MCPhysReg Reg);
const RegisterAliasingTracker &getRegister(llvm::MCPhysReg Reg) const;
// Retrieves the RegisterAliasingTracker for this particular register class.
const RegisterAliasingTracker &getRegisterClass(unsigned RegClassIndex);
const RegisterAliasingTracker &getRegisterClass(unsigned RegClassIndex) const;
private:
const llvm::MCRegisterInfo &RegInfo;
const llvm::BitVector ReservedReg;
const llvm::BitVector EmptyRegisters;
std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
mutable std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
Registers;
std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
mutable std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
RegisterClasses;
};

View File

@ -89,28 +89,24 @@ static bool hasMemoryOperand(const llvm::MCOperandInfo &OpInfo) {
return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY;
}
static bool isInfeasible(const Instruction &Instruction, std::string &Error) {
const auto &MCInstrDesc = Instruction.Description;
if (MCInstrDesc.isPseudo()) {
Error = "is pseudo";
return true;
}
if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand)) {
Error = "has unknown operands";
return true;
}
if (llvm::any_of(MCInstrDesc.operands(), hasMemoryOperand)) {
Error = "has memory operands";
return true;
}
return false;
llvm::Error
UopsBenchmarkRunner::isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const {
if (MCInstrDesc.isPseudo())
return llvm::make_error<BenchmarkFailure>("Infeasible : is pseudo");
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();
}
// Returns whether this Variable ties Use and Def operands together.
static bool hasTiedOperands(const Variable *Var) {
static bool hasTiedOperands(const Variable &Var) {
bool HasUse = false;
bool HasDef = false;
for (const Operand *Op : Var->TiedOperands) {
for (const Operand *Op : Var.TiedOperands) {
if (Op->IsDef)
HasDef = true;
else
@ -119,12 +115,12 @@ static bool hasTiedOperands(const Variable *Var) {
return HasUse && HasDef;
}
static llvm::SmallVector<Variable *, 8>
getTiedVariables(const Instruction &Instruction) {
llvm::SmallVector<Variable *, 8> Result;
for (auto *Var : Instruction.Variables)
static llvm::SmallVector<const Variable *, 8>
getTiedVariables(const Instruction &Instr) {
llvm::SmallVector<const Variable *, 8> Result;
for (const auto &Var : Instr.Variables)
if (hasTiedOperands(Var))
Result.push_back(Var);
Result.push_back(&Var);
return Result;
}
@ -140,79 +136,85 @@ InstructionBenchmark::ModeE UopsBenchmarkRunner::getMode() const {
return InstructionBenchmark::Uops;
}
llvm::Expected<std::vector<BenchmarkConfiguration>>
UopsBenchmarkRunner::createConfigurations(RegisterAliasingTrackerCache &RATC,
unsigned Opcode) const {
const llvm::MCInstrDesc &MCInstrDesc = MCInstrInfo.get(Opcode);
const Instruction Instruction(MCInstrDesc, RATC);
std::string Error;
if (isInfeasible(Instruction, Error))
return llvm::make_error<llvm::StringError>(
llvm::Twine("Infeasible : ").concat(Error),
llvm::inconvertibleErrorCode());
BenchmarkConfiguration Conf;
const AliasingConfigurations SelfAliasing(Instruction, Instruction);
llvm::Expected<BenchmarkConfiguration>
UopsBenchmarkRunner::generateConfiguration(unsigned Opcode) const {
const auto &InstrDesc = MCInstrInfo.get(Opcode);
if (auto E = isInfeasible(InstrDesc))
return std::move(E);
const Instruction Instr(InstrDesc, RATC);
const AliasingConfigurations SelfAliasing(Instr, Instr);
if (SelfAliasing.empty()) {
InstructionInstance II(Instr);
BenchmarkConfiguration Conf;
Conf.Info = "instruction is parallel, repeating a random one.";
Conf.Snippet = {randomizeUnsetVariablesAndBuild(Instruction)};
return std::vector<BenchmarkConfiguration>{Conf};
Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()};
return Conf;
}
if (SelfAliasing.hasImplicitAliasing()) {
InstructionInstance II(Instr);
BenchmarkConfiguration Conf;
Conf.Info = "instruction is serial, repeating a random one.";
Conf.Snippet = {randomizeUnsetVariablesAndBuild(Instruction)};
return std::vector<BenchmarkConfiguration>{Conf};
Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()};
return Conf;
}
const auto TiedVariables = getTiedVariables(Instruction);
const auto TiedVariables = getTiedVariables(Instr);
if (!TiedVariables.empty()) {
if (TiedVariables.size() > 1)
return llvm::make_error<llvm::StringError>(
"Infeasible : don't know how to handle several tied variables",
llvm::inconvertibleErrorCode());
BenchmarkConfiguration Conf;
Conf.Info = "instruction has tied variables using static renaming.";
Variable *Var = TiedVariables.front();
const Variable *Var = TiedVariables.front();
assert(Var);
assert(!Var->TiedOperands.empty());
const Operand &Operand = *Var->TiedOperands.front();
assert(Operand.Tracker);
for (const llvm::MCPhysReg Reg : Operand.Tracker->sourceBits().set_bits()) {
clearVariableAssignments(Instruction);
Var->AssignedValue = llvm::MCOperand::createReg(Reg);
Conf.Snippet.push_back(randomizeUnsetVariablesAndBuild(Instruction));
InstructionInstance II(Instr);
II.getValueFor(*Var) = llvm::MCOperand::createReg(Reg);
Conf.Snippet.push_back(II.randomizeUnsetVariablesAndBuild());
}
return std::vector<BenchmarkConfiguration>{Conf};
return Conf;
}
InstructionInstance II(Instr);
// No tied variables, we pick random values for defs.
llvm::BitVector Defs(MCRegisterInfo.getNumRegs());
for (const auto &Op : Instruction.Operands) {
for (const auto &Op : Instr.Operands) {
if (Op.Tracker && Op.IsExplicit && Op.IsDef) {
assert(Op.Var);
auto PossibleRegisters = Op.Tracker->sourceBits();
remove(PossibleRegisters, RATC.reservedRegisters());
assert(PossibleRegisters.any() && "No register left to choose from");
const auto RandomReg = randomBit(PossibleRegisters);
Defs.set(RandomReg);
Op.Var->AssignedValue = llvm::MCOperand::createReg(RandomReg);
II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg);
}
}
// And pick random use values that are not reserved and don't alias with defs.
const auto DefAliases = getAliasedBits(MCRegisterInfo, Defs);
for (const auto &Op : Instruction.Operands) {
for (const auto &Op : Instr.Operands) {
if (Op.Tracker && Op.IsExplicit && !Op.IsDef) {
assert(Op.Var);
auto PossibleRegisters = Op.Tracker->sourceBits();
remove(PossibleRegisters, RATC.reservedRegisters());
remove(PossibleRegisters, DefAliases);
assert(PossibleRegisters.any() && "No register left to choose from");
const auto RandomReg = randomBit(PossibleRegisters);
Op.Var->AssignedValue = llvm::MCOperand::createReg(RandomReg);
II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg);
}
}
BenchmarkConfiguration Conf;
Conf.Info =
"instruction has no tied variables picking Uses different from defs";
Conf.Snippet = {randomizeUnsetVariablesAndBuild(Instruction)};
return std::vector<BenchmarkConfiguration>{Conf};
Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()};
return Conf;
}
llvm::Expected<std::vector<BenchmarkConfiguration>>
UopsBenchmarkRunner::createConfigurations(unsigned Opcode) const {
if (auto E = generateConfiguration(Opcode))
return std::vector<BenchmarkConfiguration>{E.get()};
else
return E.takeError();
}
std::vector<BenchmarkMeasure>
@ -232,7 +234,7 @@ UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
int64_t CounterValue = 0;
llvm::SmallVector<llvm::StringRef, 2> CounterNames;
llvm::StringRef(PfmCounters).split(CounterNames, ',');
for (const auto& CounterName : CounterNames) {
for (const auto &CounterName : CounterNames) {
pfm::PerfEvent UopPerfEvent(CounterName);
if (!UopPerfEvent.valid())
llvm::report_fatal_error(

View File

@ -24,12 +24,16 @@ public:
using BenchmarkRunner::BenchmarkRunner;
~UopsBenchmarkRunner() override;
llvm::Expected<BenchmarkConfiguration>
generateConfiguration(unsigned Opcode) const;
private:
llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const;
InstructionBenchmark::ModeE getMode() const override;
llvm::Expected<std::vector<BenchmarkConfiguration>>
createConfigurations(RegisterAliasingTrackerCache &RATC,
unsigned Opcode) const override;
createConfigurations(unsigned Opcode) const override;
std::vector<BenchmarkMeasure>
runMeasurements(const ExecutableFunction &EF,

View File

@ -14,8 +14,9 @@ set(LLVM_LINK_COMPONENTS
)
add_llvm_unittest(LLVMExegesisX86Tests
RegisterAliasingTest.cpp
AssemblerTest.cpp
AnalysisTest.cpp
SnippetGeneratorTest.cpp
RegisterAliasingTest.cpp
)
target_link_libraries(LLVMExegesisX86Tests PRIVATE LLVMExegesis)

View File

@ -0,0 +1,199 @@
//===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../Common/AssemblerUtils.h"
#include "Latency.h"
#include "LlvmState.h"
#include "MCInstrDescView.h"
#include "RegisterAliasing.h"
#include "Uops.h"
#include "X86InstrInfo.h"
#include <unordered_set>
namespace exegesis {
namespace {
class X86SnippetGeneratorTest : public ::testing::Test {
protected:
X86SnippetGeneratorTest()
: MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
static void SetUpTestCase() {
LLVMInitializeX86TargetInfo();
LLVMInitializeX86TargetMC();
LLVMInitializeX86Target();
LLVMInitializeX86AsmPrinter();
}
const LLVMState State;
const llvm::MCInstrInfo &MCInstrInfo;
const llvm::MCRegisterInfo &MCRegisterInfo;
};
class LatencySnippetGeneratorTest : public X86SnippetGeneratorTest {
protected:
LatencySnippetGeneratorTest() : Runner(State) {}
BenchmarkConfiguration checkAndGetConfiguration(unsigned Opcode) {
randomGenerator().seed(0); // Initialize seed.
auto ConfOrError = Runner.generateConfiguration(Opcode);
EXPECT_FALSE(ConfOrError.takeError()); // Valid configuration.
return ConfOrError.get();
}
LatencyBenchmarkRunner Runner;
};
TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) {
// ADC16i16 self alias because of implicit use and def.
// explicit use 0 : imm
// implicit def : AX
// implicit def : EFLAGS
// implicit use : AX
// implicit use : EFLAGS
const unsigned Opcode = llvm::X86::ADC16i16;
auto Conf = checkAndGetConfiguration(Opcode);
EXPECT_THAT(Conf.Info, testing::HasSubstr("implicit"));
ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
const llvm::MCInst Instr = Conf.Snippet[0];
EXPECT_THAT(Instr.getOpcode(), Opcode);
EXPECT_THAT(Instr.getNumOperands(), 1);
EXPECT_TRUE(Instr.getOperand(0).isImm()); // Use
EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX);
EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS);
EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX);
EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS);
}
TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) {
// ADD16ri self alias because Op0 and Op1 are tied together.
// explicit def 0 : reg RegClass=GR16
// explicit use 1 : reg RegClass=GR16 | TIED_TO:0
// explicit use 2 : imm
// implicit def : EFLAGS
const unsigned Opcode = llvm::X86::ADD16ri;
auto Conf = checkAndGetConfiguration(Opcode);
EXPECT_THAT(Conf.Info, testing::HasSubstr("explicit"));
ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
const llvm::MCInst Instr = Conf.Snippet[0];
EXPECT_THAT(Instr.getOpcode(), Opcode);
EXPECT_THAT(Instr.getNumOperands(), 3);
EXPECT_TRUE(Instr.getOperand(0).isReg());
EXPECT_TRUE(Instr.getOperand(1).isReg());
EXPECT_THAT(Instr.getOperand(0).getReg(), Instr.getOperand(1).getReg())
<< "Op0 and Op1 should have the same value";
EXPECT_TRUE(Instr.getOperand(2).isImm());
EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
}
TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) {
// CMP64rr
// explicit use 0 : reg RegClass=GR64
// explicit use 1 : reg RegClass=GR64
// implicit def : EFLAGS
const unsigned Opcode = llvm::X86::CMP64rr;
auto Conf = checkAndGetConfiguration(Opcode);
EXPECT_THAT(Conf.Info, testing::HasSubstr("cycle through CMOVLE16rr"));
ASSERT_THAT(Conf.Snippet, testing::SizeIs(2));
// TODO: check that the two instructions alias each other.
}
class UopsSnippetGeneratorTest : public X86SnippetGeneratorTest {
protected:
UopsSnippetGeneratorTest() : Runner(State) {}
BenchmarkConfiguration checkAndGetConfiguration(unsigned Opcode) {
randomGenerator().seed(0); // Initialize seed.
auto ConfOrError = Runner.generateConfiguration(Opcode);
EXPECT_FALSE(ConfOrError.takeError()); // Valid configuration.
return ConfOrError.get();
}
UopsBenchmarkRunner Runner;
};
TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
// BNDCL32rr is parallelno matter what.
// explicit use 0 : reg RegClass=BNDR
// explicit use 1 : reg RegClass=GR32
const unsigned Opcode = llvm::X86::BNDCL32rr;
auto Conf = checkAndGetConfiguration(Opcode);
EXPECT_THAT(Conf.Info, testing::HasSubstr("parallel"));
ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
const llvm::MCInst Instr = Conf.Snippet[0];
EXPECT_THAT(Instr.getOpcode(), Opcode);
}
TEST_F(UopsSnippetGeneratorTest, SerialInstruction) {
// CDQ is serial no matter what.
// implicit def : EAX
// implicit def : EDX
// implicit use : EAX
const unsigned Opcode = llvm::X86::CDQ;
auto Conf = checkAndGetConfiguration(Opcode);
EXPECT_THAT(Conf.Info, testing::HasSubstr("serial"));
ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
const llvm::MCInst Instr = Conf.Snippet[0];
EXPECT_THAT(Instr.getOpcode(), Opcode);
}
TEST_F(UopsSnippetGeneratorTest, StaticRenaming) {
// CMOVA32rr has tied variables, we enumarate the possible values to execute
// as many in parallel as possible.
// explicit def 0 : reg RegClass=GR32
// explicit use 1 : reg RegClass=GR32 | TIED_TO:0
// explicit use 2 : reg RegClass=GR32
// implicit use : EFLAGS
const unsigned Opcode = llvm::X86::CMOVA32rr;
auto Conf = checkAndGetConfiguration(Opcode);
EXPECT_THAT(Conf.Info, testing::HasSubstr("static renaming"));
constexpr const unsigned kInstructionCount = 15;
ASSERT_THAT(Conf.Snippet, testing::SizeIs(kInstructionCount));
std::unordered_set<unsigned> AllDefRegisters;
for (const auto &Inst : Conf.Snippet)
AllDefRegisters.insert(Inst.getOperand(0).getReg());
EXPECT_THAT(AllDefRegisters, testing::SizeIs(kInstructionCount))
<< "Each instruction writes to a different register";
}
TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
// CMOV_GR32 has no tied variables, we make sure def and use are different
// from each other.
// explicit def 0 : reg RegClass=GR32
// explicit use 1 : reg RegClass=GR32
// explicit use 2 : reg RegClass=GR32
// explicit use 3 : imm
// implicit use : EFLAGS
const unsigned Opcode = llvm::X86::CMOV_GR32;
auto Conf = checkAndGetConfiguration(Opcode);
EXPECT_THAT(Conf.Info, testing::HasSubstr("no tied variables"));
ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
const llvm::MCInst Instr = Conf.Snippet[0];
EXPECT_THAT(Instr.getOpcode(), Opcode);
EXPECT_THAT(Instr.getNumOperands(), 4);
EXPECT_THAT(Instr.getOperand(0).getReg(),
testing::Not(Instr.getOperand(1).getReg()))
<< "Def is different from first Use";
EXPECT_THAT(Instr.getOperand(0).getReg(),
testing::Not(Instr.getOperand(2).getReg()))
<< "Def is different from second Use";
EXPECT_THAT(Instr.getOperand(3).getImm(), 1);
}
} // namespace
} // namespace exegesis