LatencySnippetGenerator::~LatencySnippetGenerator() = default;
-llvm::Expected<CodeTemplate>
-LatencySnippetGenerator::generateTwoInstructionPrototype(
- const Instruction &Instr) const {
+llvm::Expected<std::vector<CodeTemplate>>
+generateTwoInstructionPrototypes(const LLVMState &State,
+ const Instruction &Instr) {
std::vector<unsigned> Opcodes;
Opcodes.resize(State.getInstrInfo().getNumOpcodes());
std::iota(Opcodes.begin(), Opcodes.end(), 0U);
State.getInstrInfo().getName(OtherOpcode));
CT.Instructions.push_back(std::move(ThisIT));
CT.Instructions.push_back(std::move(OtherIT));
- return std::move(CT);
+ return getSingleton(CT);
}
return llvm::make_error<BenchmarkFailure>(
"Infeasible : Didn't find any scheme to make the instruction serial");
}
-llvm::Expected<CodeTemplate>
-LatencySnippetGenerator::generateCodeTemplate(const Instruction &Instr) const {
+llvm::Expected<std::vector<CodeTemplate>>
+LatencySnippetGenerator::generateCodeTemplates(const Instruction &Instr) const {
if (Instr.hasMemoryOperands())
return llvm::make_error<BenchmarkFailure>(
"Infeasible : has memory operands");
- if (auto CT = generateSelfAliasingCodeTemplate(Instr))
- return CT;
- else
- llvm::consumeError(CT.takeError());
- // No self aliasing, trying to create a dependency through another opcode.
- return generateTwoInstructionPrototype(Instr);
+ return llvm::handleExpected( //
+ generateSelfAliasingCodeTemplates(Instr),
+ [this, &Instr]() {
+ return generateTwoInstructionPrototypes(State, Instr);
+ },
+ [](const BenchmarkFailure &) { /*Consume Error*/ });
}
const char *LatencyBenchmarkRunner::getCounterName() const {
LatencySnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {}
~LatencySnippetGenerator() override;
- llvm::Expected<CodeTemplate>
- generateCodeTemplate(const Instruction &Instr) const override;
-
-private:
- llvm::Expected<CodeTemplate>
- generateTwoInstructionPrototype(const Instruction &Instr) const;
+ llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const override;
};
class LatencyBenchmarkRunner : public BenchmarkRunner {
namespace exegesis {
+std::vector<CodeTemplate> getSingleton(CodeTemplate &CT) {
+ std::vector<CodeTemplate> Result;
+ Result.push_back(std::move(CT));
+ return Result;
+}
+
SnippetGeneratorFailure::SnippetGeneratorFailure(const llvm::Twine &S)
: llvm::StringError(S, llvm::inconvertibleErrorCode()) {}
llvm::Expected<std::vector<BenchmarkCode>>
SnippetGenerator::generateConfigurations(const Instruction &Instr) const {
- if (auto E = generateCodeTemplate(Instr)) {
- CodeTemplate &CT = E.get();
+ if (auto E = generateCodeTemplates(Instr)) {
const auto &RATC = State.getRATC();
- const llvm::BitVector &ForbiddenRegs =
- CT.ScratchSpacePointerInReg
- ? RATC.getRegister(CT.ScratchSpacePointerInReg).aliasedBits()
- : RATC.emptyRegisters();
std::vector<BenchmarkCode> Output;
- // TODO: Generate as many BenchmarkCode as needed.
- {
- BenchmarkCode BC;
- BC.Info = CT.Info;
- for (InstructionTemplate &IT : CT.Instructions) {
- randomizeUnsetVariables(ForbiddenRegs, IT);
- BC.Instructions.push_back(IT.build());
+ for (CodeTemplate &CT : E.get()) {
+ const llvm::BitVector &ForbiddenRegs =
+ CT.ScratchSpacePointerInReg
+ ? RATC.getRegister(CT.ScratchSpacePointerInReg).aliasedBits()
+ : RATC.emptyRegisters();
+ // TODO: Generate as many BenchmarkCode as needed.
+ {
+ BenchmarkCode BC;
+ BC.Info = CT.Info;
+ for (InstructionTemplate &IT : CT.Instructions) {
+ randomizeUnsetVariables(ForbiddenRegs, IT);
+ BC.Instructions.push_back(IT.build());
+ }
+ if (CT.ScratchSpacePointerInReg)
+ BC.LiveIns.push_back(CT.ScratchSpacePointerInReg);
+ BC.RegisterInitialValues =
+ computeRegisterInitialValues(CT.Instructions);
+ Output.push_back(std::move(BC));
}
- if (CT.ScratchSpacePointerInReg)
- BC.LiveIns.push_back(CT.ScratchSpacePointerInReg);
- BC.RegisterInitialValues = computeRegisterInitialValues(CT.Instructions);
- Output.push_back(std::move(BC));
}
return Output;
} else
return RIV;
}
-llvm::Expected<CodeTemplate> SnippetGenerator::generateSelfAliasingCodeTemplate(
- const Instruction &Instr) const {
+llvm::Expected<std::vector<CodeTemplate>>
+generateSelfAliasingCodeTemplates(const Instruction &Instr) {
const AliasingConfigurations SelfAliasing(Instr, Instr);
- if (SelfAliasing.empty()) {
+ if (SelfAliasing.empty())
return llvm::make_error<SnippetGeneratorFailure>("empty self aliasing");
- }
- CodeTemplate CT;
+ std::vector<CodeTemplate> Result;
+ Result.emplace_back();
+ CodeTemplate &CT = Result.back();
InstructionTemplate IT(Instr);
if (SelfAliasing.hasImplicitAliasing()) {
CT.Info = "implicit Self cycles, picking random values.";
setRandomAliasing(SelfAliasing, IT, IT);
}
CT.Instructions.push_back(std::move(IT));
- return std::move(CT);
+ return Result;
}
-llvm::Expected<CodeTemplate>
-SnippetGenerator::generateUnconstrainedCodeTemplate(const Instruction &Instr,
- llvm::StringRef Msg) const {
- CodeTemplate CT;
+llvm::Expected<std::vector<CodeTemplate>>
+generateUnconstrainedCodeTemplates(const Instruction &Instr,
+ llvm::StringRef Msg) {
+ std::vector<CodeTemplate> Result;
+ Result.emplace_back();
+ CodeTemplate &CT = Result.back();
CT.Info = llvm::formatv("{0}, repeating an unconstrained assignment", Msg);
CT.Instructions.emplace_back(Instr);
- return std::move(CT);
+ return Result;
}
std::mt19937 &randomGenerator() {
namespace exegesis {
+std::vector<CodeTemplate> getSingleton(CodeTemplate &CT);
+
+// Generates code templates that has a self-dependency.
+llvm::Expected<std::vector<CodeTemplate>>
+generateSelfAliasingCodeTemplates(const Instruction &Instr);
+
+// Generates code templates without assignment constraints.
+llvm::Expected<std::vector<CodeTemplate>>
+generateUnconstrainedCodeTemplates(const Instruction &Instr,
+ llvm::StringRef Msg);
+
// A class representing failures that happened during Benchmark, they are used
// to report informations to the user.
class SnippetGeneratorFailure : public llvm::StringError {
protected:
const LLVMState &State;
- // Generates a single code template that has a self-dependency.
- llvm::Expected<CodeTemplate>
- generateSelfAliasingCodeTemplate(const Instruction &Instr) const;
- // Generates a single code template without assignment constraints.
- llvm::Expected<CodeTemplate>
- generateUnconstrainedCodeTemplate(const Instruction &Instr,
- llvm::StringRef Msg) const;
-
private:
// API to be implemented by subclasses.
- virtual llvm::Expected<CodeTemplate>
- generateCodeTemplate(const Instruction &Instr) const = 0;
+ virtual llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const = 0;
};
// A global Random Number Generator to randomize configurations.
"not enough scratch space");
}
-llvm::Expected<CodeTemplate>
-UopsSnippetGenerator::generateCodeTemplate(const Instruction &Instr) const {
+llvm::Expected<std::vector<CodeTemplate>>
+UopsSnippetGenerator::generateCodeTemplates(const Instruction &Instr) const {
CodeTemplate CT;
const llvm::BitVector *ScratchSpaceAliasedRegs = nullptr;
if (Instr.hasMemoryOperands()) {
CT.Info = "instruction is parallel, repeating a random one.";
CT.Instructions.push_back(std::move(IT));
instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
- return std::move(CT);
+ return getSingleton(CT);
}
if (SelfAliasing.hasImplicitAliasing()) {
CT.Info = "instruction is serial, repeating a random one.";
CT.Instructions.push_back(std::move(IT));
instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
- return std::move(CT);
+ return getSingleton(CT);
}
const auto TiedVariables = getVariablesWithTiedOperands(Instr);
if (!TiedVariables.empty()) {
CT.Instructions.push_back(std::move(TmpIT));
}
instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
- return std::move(CT);
+ return getSingleton(CT);
}
const auto &ReservedRegisters = State.getRATC().reservedRegisters();
// No tied variables, we pick random values for defs.
"instruction has no tied variables picking Uses different from defs";
CT.Instructions.push_back(std::move(IT));
instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
- return std::move(CT);
+ return getSingleton(CT);
}
std::vector<BenchmarkMeasure>
UopsSnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {}
~UopsSnippetGenerator() override;
- llvm::Expected<CodeTemplate>
- generateCodeTemplate(const Instruction &Instr) const override;
+ llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const override;
static constexpr const size_t kMinNumDifferentAddresses = 6;
public:
using LatencySnippetGenerator::LatencySnippetGenerator;
- llvm::Expected<CodeTemplate>
- generateCodeTemplate(const Instruction &Instr) const override {
+ llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const override {
if (auto E = IsInvalidOpcode(Instr))
return std::move(E);
switch (GetX86FPFlags(Instr)) {
case llvm::X86II::NotFP:
- return LatencySnippetGenerator::generateCodeTemplate(Instr);
+ return LatencySnippetGenerator::generateCodeTemplates(Instr);
case llvm::X86II::ZeroArgFP:
case llvm::X86II::OneArgFP:
case llvm::X86II::SpecialFP:
// - `ST(0) = fsqrt(ST(0))` (OneArgFPRW)
// - `ST(0) = ST(0) + ST(i)` (TwoArgFP)
// They are intrinsically serial and do not modify the state of the stack.
- return generateSelfAliasingCodeTemplate(Instr);
+ return generateSelfAliasingCodeTemplates(Instr);
default:
llvm_unreachable("Unknown FP Type!");
}
public:
using UopsSnippetGenerator::UopsSnippetGenerator;
- llvm::Expected<CodeTemplate>
- generateCodeTemplate(const Instruction &Instr) const override {
+ llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const override {
if (auto E = IsInvalidOpcode(Instr))
return std::move(E);
switch (GetX86FPFlags(Instr)) {
case llvm::X86II::NotFP:
- return UopsSnippetGenerator::generateCodeTemplate(Instr);
+ return UopsSnippetGenerator::generateCodeTemplates(Instr);
case llvm::X86II::ZeroArgFP:
case llvm::X86II::OneArgFP:
case llvm::X86II::SpecialFP:
// - `ST(0) = ST(0) + ST(i)` (TwoArgFP)
// They are intrinsically serial and do not modify the state of the stack.
// We generate the same code for latency and uops.
- return generateSelfAliasingCodeTemplate(Instr);
+ return generateSelfAliasingCodeTemplates(Instr);
case llvm::X86II::CompareFP:
case llvm::X86II::CondMovFP:
// We can compute uops for any FP instruction that does not grow or shrink
// the stack (either do not touch the stack or push as much as they pop).
- return generateUnconstrainedCodeTemplate(
+ return generateUnconstrainedCodeTemplates(
Instr, "instruction does not grow/shrink the FP stack");
default:
llvm_unreachable("Unknown FP Type!");
CodeTemplate checkAndGetCodeTemplate(unsigned Opcode) {
randomGenerator().seed(0); // Initialize seed.
const Instruction Instr(State, Opcode);
- auto CodeTemplateOrError = Generator.generateCodeTemplate(Instr);
+ auto CodeTemplateOrError = Generator.generateCodeTemplates(Instr);
EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.
- return std::move(CodeTemplateOrError.get());
+ auto &CodeTemplate = CodeTemplateOrError.get();
+ EXPECT_EQ(CodeTemplate.size(), 1U);
+ return std::move(CodeTemplate.front());
}
SnippetGeneratorT Generator;
// MOVSB writes to scratch memory register.
const unsigned Opcode = llvm::X86::MOVSB;
const Instruction Instr(State, Opcode);
- auto Error = Generator.generateCodeTemplate(Instr).takeError();
+ auto Error = Generator.generateCodeTemplates(Instr).takeError();
EXPECT_TRUE((bool)Error);
llvm::consumeError(std::move(Error));
}
}
private:
- llvm::Expected<CodeTemplate>
- generateCodeTemplate(const Instruction &Instr) const override {
+ llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const override {
return llvm::make_error<llvm::StringError>("not implemented",
llvm::inconvertibleErrorCode());
}