Index: tools/llvm-exegesis/lib/Latency.h =================================================================== --- tools/llvm-exegesis/lib/Latency.h +++ tools/llvm-exegesis/lib/Latency.h @@ -26,12 +26,8 @@ LatencySnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {} ~LatencySnippetGenerator() override; - llvm::Expected - generateCodeTemplate(const Instruction &Instr) const override; - -private: - llvm::Expected - generateTwoInstructionPrototype(const Instruction &Instr) const; + llvm::Expected> + generateCodeTemplates(const Instruction &Instr) const override; }; class LatencyBenchmarkRunner : public BenchmarkRunner { Index: tools/llvm-exegesis/lib/Latency.cpp =================================================================== --- tools/llvm-exegesis/lib/Latency.cpp +++ tools/llvm-exegesis/lib/Latency.cpp @@ -22,9 +22,9 @@ LatencySnippetGenerator::~LatencySnippetGenerator() = default; -llvm::Expected -LatencySnippetGenerator::generateTwoInstructionPrototype( - const Instruction &Instr) const { +llvm::Expected> +generateTwoInstructionPrototypes(const LLVMState &State, + const Instruction &Instr) { std::vector Opcodes; Opcodes.resize(State.getInstrInfo().getNumOpcodes()); std::iota(Opcodes.begin(), Opcodes.end(), 0U); @@ -50,23 +50,23 @@ 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( "Infeasible : Didn't find any scheme to make the instruction serial"); } -llvm::Expected -LatencySnippetGenerator::generateCodeTemplate(const Instruction &Instr) const { +llvm::Expected> +LatencySnippetGenerator::generateCodeTemplates(const Instruction &Instr) const { if (Instr.hasMemoryOperands()) return llvm::make_error( "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 { Index: tools/llvm-exegesis/lib/SnippetGenerator.h =================================================================== --- tools/llvm-exegesis/lib/SnippetGenerator.h +++ tools/llvm-exegesis/lib/SnippetGenerator.h @@ -30,6 +30,17 @@ namespace exegesis { +std::vector getSingleton(CodeTemplate &CT); + +// Generates code templates that has a self-dependency. +llvm::Expected> +generateSelfAliasingCodeTemplates(const Instruction &Instr); + +// Generates code templates without assignment constraints. +llvm::Expected> +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 { @@ -55,18 +66,10 @@ protected: const LLVMState &State; - // Generates a single code template that has a self-dependency. - llvm::Expected - generateSelfAliasingCodeTemplate(const Instruction &Instr) const; - // Generates a single code template without assignment constraints. - llvm::Expected - generateUnconstrainedCodeTemplate(const Instruction &Instr, - llvm::StringRef Msg) const; - private: // API to be implemented by subclasses. - virtual llvm::Expected - generateCodeTemplate(const Instruction &Instr) const = 0; + virtual llvm::Expected> + generateCodeTemplates(const Instruction &Instr) const = 0; }; // A global Random Number Generator to randomize configurations. Index: tools/llvm-exegesis/lib/SnippetGenerator.cpp =================================================================== --- tools/llvm-exegesis/lib/SnippetGenerator.cpp +++ tools/llvm-exegesis/lib/SnippetGenerator.cpp @@ -22,6 +22,12 @@ namespace exegesis { +std::vector getSingleton(CodeTemplate &CT) { + std::vector Result; + Result.push_back(std::move(CT)); + return Result; +} + SnippetGeneratorFailure::SnippetGeneratorFailure(const llvm::Twine &S) : llvm::StringError(S, llvm::inconvertibleErrorCode()) {} @@ -31,26 +37,28 @@ llvm::Expected> 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 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 @@ -99,8 +107,8 @@ return RIV; } -llvm::Expected SnippetGenerator::generateSelfAliasingCodeTemplate( - const Instruction &Instr) const { +llvm::Expected +generateSelfAliasingCodeTemplate(const Instruction &Instr) { const AliasingConfigurations SelfAliasing(Instr, Instr); if (SelfAliasing.empty()) { return llvm::make_error("empty self aliasing"); @@ -119,15 +127,32 @@ return std::move(CT); } +llvm::Expected> +generateSelfAliasingCodeTemplates(const Instruction &Instr) { + if (auto E = generateSelfAliasingCodeTemplate(Instr)) + return getSingleton(E.get()); + else + return E.takeError(); +} + llvm::Expected -SnippetGenerator::generateUnconstrainedCodeTemplate(const Instruction &Instr, - llvm::StringRef Msg) const { +generateUnconstrainedCodeTemplate(const Instruction &Instr, + llvm::StringRef Msg) { CodeTemplate CT; CT.Info = llvm::formatv("{0}, repeating an unconstrained assignment", Msg); CT.Instructions.emplace_back(Instr); return std::move(CT); } +llvm::Expected> +generateUnconstrainedCodeTemplates(const Instruction &Instr, + llvm::StringRef Msg) { + if (auto E = generateUnconstrainedCodeTemplate(Instr, Msg)) + return getSingleton(E.get()); + else + return E.takeError(); +} + std::mt19937 &randomGenerator() { static std::random_device RandomDevice; static std::mt19937 RandomGenerator(RandomDevice()); Index: tools/llvm-exegesis/lib/Uops.h =================================================================== --- tools/llvm-exegesis/lib/Uops.h +++ tools/llvm-exegesis/lib/Uops.h @@ -25,8 +25,8 @@ UopsSnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {} ~UopsSnippetGenerator() override; - llvm::Expected - generateCodeTemplate(const Instruction &Instr) const override; + llvm::Expected> + generateCodeTemplates(const Instruction &Instr) const override; static constexpr const size_t kMinNumDifferentAddresses = 6; Index: tools/llvm-exegesis/lib/Uops.cpp =================================================================== --- tools/llvm-exegesis/lib/Uops.cpp +++ tools/llvm-exegesis/lib/Uops.cpp @@ -124,8 +124,8 @@ "not enough scratch space"); } -llvm::Expected -UopsSnippetGenerator::generateCodeTemplate(const Instruction &Instr) const { +llvm::Expected> +UopsSnippetGenerator::generateCodeTemplates(const Instruction &Instr) const { CodeTemplate CT; const llvm::BitVector *ScratchSpaceAliasedRegs = nullptr; if (Instr.hasMemoryOperands()) { @@ -153,13 +153,13 @@ 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()) { @@ -181,7 +181,7 @@ 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. @@ -218,7 +218,7 @@ "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 Index: tools/llvm-exegesis/lib/X86/Target.cpp =================================================================== --- tools/llvm-exegesis/lib/X86/Target.cpp +++ tools/llvm-exegesis/lib/X86/Target.cpp @@ -38,14 +38,14 @@ public: using LatencySnippetGenerator::LatencySnippetGenerator; - llvm::Expected - generateCodeTemplate(const Instruction &Instr) const override { + llvm::Expected> + 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: @@ -58,7 +58,7 @@ // - `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!"); } @@ -69,14 +69,14 @@ public: using UopsSnippetGenerator::UopsSnippetGenerator; - llvm::Expected - generateCodeTemplate(const Instruction &Instr) const override { + llvm::Expected> + 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: @@ -88,12 +88,12 @@ // - `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!"); Index: unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp +++ unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp @@ -60,9 +60,11 @@ 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; @@ -240,7 +242,7 @@ // 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)); } @@ -254,8 +256,8 @@ } private: - llvm::Expected - generateCodeTemplate(const Instruction &Instr) const override { + llvm::Expected> + generateCodeTemplates(const Instruction &Instr) const override { return llvm::make_error("not implemented", llvm::inconvertibleErrorCode()); }