Index: tools/llvm-exegesis/lib/Latency.h =================================================================== --- tools/llvm-exegesis/lib/Latency.h +++ tools/llvm-exegesis/lib/Latency.h @@ -23,13 +23,15 @@ class LatencySnippetGenerator : public SnippetGenerator { public: - LatencySnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {} + LatencySnippetGenerator(const LLVMState &State) : State(State) {} ~LatencySnippetGenerator() override; llvm::Expected generateCodeTemplate(const Instruction &Instr) const override; private: + const LLVMState &State; + llvm::Expected generateTwoInstructionPrototype(const Instruction &Instr) const; }; Index: tools/llvm-exegesis/lib/SnippetGenerator.h =================================================================== --- tools/llvm-exegesis/lib/SnippetGenerator.h +++ tools/llvm-exegesis/lib/SnippetGenerator.h @@ -30,41 +30,37 @@ namespace exegesis { -// A class representing failures that happened during Benchmark, they are used -// to report informations to the user. +// Given a snippet, computes which registers the setup code needs to define. +std::vector computeRegisterInitialValues( + const RegisterAliasingTrackerCache &RATC, + const std::vector &Instructions); + +// Expands a CodeTemplate into one or more BenchmarkCode. +std::vector +generateConfigurations(const RegisterAliasingTrackerCache &RATC, + CodeTemplate &CT); + +// Generates a single code template that has a self-dependency. +llvm::Expected +generateSelfAliasingCodeTemplate(const Instruction &Instr); + +// Generates a single code template without assignment constraints. +llvm::Expected +generateUnconstrainedCodeTemplate(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 { public: SnippetGeneratorFailure(const llvm::Twine &S); }; -// Common code for all benchmark modes. +// Interface for generating code templates. class SnippetGenerator { public: - explicit SnippetGenerator(const LLVMState &State); - virtual ~SnippetGenerator(); - // Calls generateCodeTemplate and expands it into one or more BenchmarkCode. - llvm::Expected> - generateConfigurations(const Instruction &Instr) const; - - // Given a snippet, computes which registers the setup code needs to define. - std::vector computeRegisterInitialValues( - const std::vector &Snippet) const; - -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; }; Index: tools/llvm-exegesis/lib/SnippetGenerator.cpp =================================================================== --- tools/llvm-exegesis/lib/SnippetGenerator.cpp +++ tools/llvm-exegesis/lib/SnippetGenerator.cpp @@ -22,48 +22,14 @@ namespace exegesis { -SnippetGeneratorFailure::SnippetGeneratorFailure(const llvm::Twine &S) - : llvm::StringError(S, llvm::inconvertibleErrorCode()) {} - -SnippetGenerator::SnippetGenerator(const LLVMState &State) : State(State) {} - -SnippetGenerator::~SnippetGenerator() = default; - -llvm::Expected> -SnippetGenerator::generateConfigurations(const Instruction &Instr) const { - if (auto E = generateCodeTemplate(Instr)) { - CodeTemplate &CT = E.get(); - 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()); - } - if (CT.ScratchSpacePointerInReg) - BC.LiveIns.push_back(CT.ScratchSpacePointerInReg); - BC.RegisterInitialValues = computeRegisterInitialValues(CT.Instructions); - Output.push_back(std::move(BC)); - } - return Output; - } else - return E.takeError(); -} - -std::vector SnippetGenerator::computeRegisterInitialValues( - const std::vector &Instructions) const { +std::vector computeRegisterInitialValues( + const RegisterAliasingTrackerCache &RATC, + const std::vector &Instructions) { // Collect all register uses and create an assignment for each of them. // Ignore memory operands which are handled separately. // Loop invariant: DefinedRegs[i] is true iif it has been set at least once // before the current instruction. - llvm::BitVector DefinedRegs = State.getRATC().emptyRegisters(); + llvm::BitVector DefinedRegs = RATC.emptyRegisters(); std::vector RIV; for (const InstructionTemplate &IT : Instructions) { // Returns the register that this Operand sets or uses, or 0 if this is not @@ -99,8 +65,33 @@ return RIV; } -llvm::Expected SnippetGenerator::generateSelfAliasingCodeTemplate( - const Instruction &Instr) const { +std::vector +generateConfigurations(const RegisterAliasingTrackerCache &RATC, + CodeTemplate &CT) { + 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()); + } + if (CT.ScratchSpacePointerInReg) + BC.LiveIns.push_back(CT.ScratchSpacePointerInReg); + BC.RegisterInitialValues = + computeRegisterInitialValues(RATC, CT.Instructions); + Output.push_back(std::move(BC)); + } + return Output; +} + +llvm::Expected +generateSelfAliasingCodeTemplate(const Instruction &Instr) { const AliasingConfigurations SelfAliasing(Instr, Instr); if (SelfAliasing.empty()) { return llvm::make_error("empty self aliasing"); @@ -120,14 +111,19 @@ } 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); } +SnippetGeneratorFailure::SnippetGeneratorFailure(const llvm::Twine &S) + : llvm::StringError(S, llvm::inconvertibleErrorCode()) {} + +SnippetGenerator::~SnippetGenerator() = default; + 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 @@ -22,7 +22,7 @@ class UopsSnippetGenerator : public SnippetGenerator { public: - UopsSnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {} + UopsSnippetGenerator(const LLVMState &State) : State(State) {} ~UopsSnippetGenerator() override; llvm::Expected @@ -31,6 +31,7 @@ static constexpr const size_t kMinNumDifferentAddresses = 6; private: + const LLVMState &State; // Instantiates memory operands within a snippet. // To make computations as parallel as possible, we generate independant // memory locations for instructions that load and store. If there are less Index: tools/llvm-exegesis/lib/X86/Target.cpp =================================================================== --- tools/llvm-exegesis/lib/X86/Target.cpp +++ tools/llvm-exegesis/lib/X86/Target.cpp @@ -22,18 +22,20 @@ namespace { // Common code for X86 Uops and Latency runners. -template class X86SnippetGenerator : public Impl { - using Impl::Impl; +template +class X86SnippetGenerator : public SnippetGenerator, private Policy { +public: + X86SnippetGenerator(const LLVMState &State) + : Delegate(Policy::Create(State)) {} llvm::Expected generateCodeTemplate(const Instruction &Instr) const override { // Test whether we can generate a snippet for this instruction. const auto OpcodeName = Instr.Name; if (OpcodeName.startswith("POPF") || OpcodeName.startswith("PUSHF") || - OpcodeName.startswith("ADJCALLSTACK")) { + OpcodeName.startswith("ADJCALLSTACK")) return llvm::make_error( "Unsupported opcode: Push/Pop/AdjCallStack"); - } // Handle X87. const unsigned FPInstClass = @@ -52,12 +54,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 this->generateSelfAliasingCodeTemplate(Instr); + return generateSelfAliasingCodeTemplate(Instr); } case llvm::X86II::CompareFP: - return Impl::handleCompareFP(Instr); + return Policy::handleCompareFP(Instr); case llvm::X86II::CondMovFP: - return Impl::handleCondMovFP(Instr); + return Policy::handleCondMovFP(Instr); case llvm::X86II::SpecialFP: return llvm::make_error("Unsupported x87 SpecialFP"); default: @@ -65,14 +67,18 @@ } // Fallback to generic implementation. - return Impl::Base::generateCodeTemplate(Instr); + return Delegate->generateCodeTemplate(Instr); } + +private: + std::unique_ptr Delegate; }; -class X86LatencyImpl : public LatencySnippetGenerator { +class X86LatencyPolicy { protected: - using Base = LatencySnippetGenerator; - using Base::Base; + static SnippetGenerator *Create(const LLVMState &State) { + return new LatencySnippetGenerator(State); + } llvm::Expected handleCompareFP(const Instruction &Instr) const { return llvm::make_error( "Unsupported x87 CompareFP"); @@ -83,10 +89,11 @@ } }; -class X86UopsImpl : public UopsSnippetGenerator { +class X86UopsPolicy { protected: - using Base = UopsSnippetGenerator; - using Base::Base; + static SnippetGenerator *Create(const LLVMState &State) { + return new UopsSnippetGenerator(State); + } // 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). llvm::Expected handleCompareFP(const Instruction &Instr) const { @@ -330,12 +337,12 @@ std::unique_ptr createLatencySnippetGenerator(const LLVMState &State) const override { - return llvm::make_unique>(State); + return llvm::make_unique>(State); } std::unique_ptr createUopsSnippetGenerator(const LLVMState &State) const override { - return llvm::make_unique>(State); + return llvm::make_unique>(State); } bool matchesArch(llvm::Triple::ArchType Arch) const override { Index: tools/llvm-exegesis/llvm-exegesis.cpp =================================================================== --- tools/llvm-exegesis/llvm-exegesis.cpp +++ tools/llvm-exegesis/llvm-exegesis.cpp @@ -140,7 +140,10 @@ State.getExegesisTarget().createSnippetGenerator(BenchmarkMode, State); if (!Generator) llvm::report_fatal_error("cannot create snippet generator"); - return Generator->generateConfigurations(Instr); + if (auto E = Generator->generateCodeTemplate(Instr)) + return generateConfigurations(State.getRATC(), E.get()); + else + return E.takeError(); } namespace { Index: unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp +++ unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp @@ -247,13 +247,15 @@ class FakeSnippetGenerator : public SnippetGenerator { public: - FakeSnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {} + FakeSnippetGenerator(const LLVMState &State) : State(State) {} Instruction createInstruction(unsigned Opcode) { return Instruction(State, Opcode); } private: + const LLVMState &State; + llvm::Expected generateCodeTemplate(const Instruction &Instr) const override { return llvm::make_error("not implemented", @@ -280,7 +282,7 @@ llvm::MCOperand::createReg(llvm::X86::AX); std::vector Snippet; Snippet.push_back(std::move(IT)); - const auto RIV = Generator.computeRegisterInitialValues(Snippet); + const auto RIV = computeRegisterInitialValues(State.getRATC(), Snippet); EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(llvm::X86::AX, llvm::APInt()))); } @@ -306,7 +308,7 @@ Snippet.push_back(std::move(Add)); } - const auto RIV = Generator.computeRegisterInitialValues(Snippet); + const auto RIV = computeRegisterInitialValues(State.getRATC(), Snippet); EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(llvm::X86::RBX, llvm::APInt()))); }