[llvm] 6030fe0 - [llvm-exegesis] Exploring X86::OperandType::OPERAND_COND_CODE

Roman Lebedev via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 12 10:34:18 PST 2020


Author: Roman Lebedev
Date: 2020-02-12T21:33:52+03:00
New Revision: 6030fe01f4ee9a65d92effe44749bee11b36d346

URL: https://github.com/llvm/llvm-project/commit/6030fe01f4ee9a65d92effe44749bee11b36d346
DIFF: https://github.com/llvm/llvm-project/commit/6030fe01f4ee9a65d92effe44749bee11b36d346.diff

LOG: [llvm-exegesis] Exploring X86::OperandType::OPERAND_COND_CODE

Summary:
Currently, we only have nice exploration for LEA instruction,
while for the rest, we rely on `randomizeUnsetVariables()`
to sometimes generate something interesting.
While that works, it isn't very reliable in coverage :)

Here, i'm making an assumption that while we may want to explore
multi-instruction configs, we are most interested in the
characteristics of the main instruction we were asked about.

Which we can do, by taking the existing `randomizeMCOperand()`,
and turning it on it's head - instead of relying on it to randomly fill
one of the interesting values, let's pregenerate all the possible interesting
values for the variable, and then generate as much `InstructionTemplate`
combinations of these possible values for variables as needed/possible.

Of course, that requires invasive changes to no longer pass just the
naked `Instruction`, but sometimes partially filled `InstructionTemplate`.

As it can be seen from the test, this allows us to explore
`X86::OperandType::OPERAND_COND_CODE` for instructions
that take such an operand.
I'm hoping this will greatly simplify exploration.

Reviewers: courbet, gchatelet

Reviewed By: gchatelet

Subscribers: orodley, mgorny, sdardis, tschuett, jrtc27, atanasyan, mstojanovic, andreadb, RKSimon, llvm-commits

Tags: #llvm

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

Added: 
    llvm/test/tools/llvm-exegesis/X86/latency-SETCCr-cond-codes-sweep.s
    llvm/unittests/tools/llvm-exegesis/SnippetGeneratorTest.cpp

Modified: 
    llvm/tools/llvm-exegesis/lib/CodeTemplate.h
    llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp
    llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h
    llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
    llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.h
    llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
    llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
    llvm/tools/llvm-exegesis/lib/Target.h
    llvm/tools/llvm-exegesis/lib/X86/Target.cpp
    llvm/tools/llvm-exegesis/llvm-exegesis.cpp
    llvm/unittests/tools/llvm-exegesis/CMakeLists.txt
    llvm/unittests/tools/llvm-exegesis/Mips/SnippetGeneratorTest.cpp
    llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-exegesis/X86/latency-SETCCr-cond-codes-sweep.s b/llvm/test/tools/llvm-exegesis/X86/latency-SETCCr-cond-codes-sweep.s
new file mode 100644
index 000000000000..3b17ffd73699
--- /dev/null
+++ b/llvm/test/tools/llvm-exegesis/X86/latency-SETCCr-cond-codes-sweep.s
@@ -0,0 +1,25 @@
+# RUN: llvm-exegesis -mode=latency -opcode-name=SETCCr --max-configs-per-opcode=1 | FileCheck %s --check-prefix=CHECK
+# RUN: llvm-exegesis -mode=latency -opcode-name=SETCCr --max-configs-per-opcode=256 | FileCheck %s --check-prefix=SWEEP
+
+CHECK:      ---
+CHECK-NEXT: mode: latency
+CHECK-NEXT: key:
+CHECK-NEXT:   instructions:
+CHECK-NEXT:     'SETCCr {{.*}} i_0x{{[0-9a-f]}}'
+
+SWEEP-DAG:      'SETCCr {{.*}} i_0x0'
+SWEEP-DAG:      'SETCCr {{.*}} i_0x1'
+SWEEP-DAG:      'SETCCr {{.*}} i_0x2'
+SWEEP-DAG:      'SETCCr {{.*}} i_0x3'
+SWEEP-DAG:      'SETCCr {{.*}} i_0x4'
+SWEEP-DAG:      'SETCCr {{.*}} i_0x5'
+SWEEP-DAG:      'SETCCr {{.*}} i_0x6'
+SWEEP-DAG:      'SETCCr {{.*}} i_0x7'
+SWEEP-DAG:      'SETCCr {{.*}} i_0x8'
+SWEEP-DAG:      'SETCCr {{.*}} i_0x9'
+SWEEP-DAG:      'SETCCr {{.*}} i_0xa'
+SWEEP-DAG:      'SETCCr {{.*}} i_0xb'
+SWEEP-DAG:      'SETCCr {{.*}} i_0xc'
+SWEEP-DAG:      'SETCCr {{.*}} i_0xd'
+SWEEP-DAG:      'SETCCr {{.*}} i_0xe'
+SWEEP-DAG:      'SETCCr {{.*}} i_0xf'

diff  --git a/llvm/tools/llvm-exegesis/lib/CodeTemplate.h b/llvm/tools/llvm-exegesis/lib/CodeTemplate.h
index 51978147ba09..bea10304cba9 100644
--- a/llvm/tools/llvm-exegesis/lib/CodeTemplate.h
+++ b/llvm/tools/llvm-exegesis/lib/CodeTemplate.h
@@ -38,6 +38,11 @@ struct InstructionTemplate {
   bool hasImmediateVariables() const;
   const Instruction &getInstr() const { return *Instr; }
   ArrayRef<MCOperand> getVariableValues() const { return VariableValues; }
+  void setVariableValues(ArrayRef<MCOperand> NewVariableValues) {
+    assert(VariableValues.size() == NewVariableValues.size() &&
+           "Value count mismatch");
+    VariableValues.assign(NewVariableValues.begin(), NewVariableValues.end());
+  }
 
   // Builds an MCInst from this InstructionTemplate setting its operands
   // to the corresponding variable values. Precondition: All VariableValues must

diff  --git a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp
index 86e7fda63f3a..ca5e5c9191d5 100644
--- a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp
+++ b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp
@@ -154,8 +154,10 @@ static std::vector<InstructionTemplate> generateSnippetUsingStaticRenaming(
   }
 }
 
-Expected<std::vector<CodeTemplate>> ParallelSnippetGenerator::generateCodeTemplates(
-    const Instruction &Instr, const BitVector &ForbiddenRegisters) const {
+Expected<std::vector<CodeTemplate>>
+ParallelSnippetGenerator::generateCodeTemplates(
+    InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
+  const Instruction &Instr = Variant.getInstr();
   CodeTemplate CT;
   CT.ScratchSpacePointerInReg =
       Instr.hasMemoryOperands()
@@ -163,16 +165,15 @@ Expected<std::vector<CodeTemplate>> ParallelSnippetGenerator::generateCodeTempla
                 State.getTargetMachine().getTargetTriple())
           : 0;
   const AliasingConfigurations SelfAliasing(Instr, Instr);
-  InstructionTemplate IT(&Instr);
   if (SelfAliasing.empty()) {
     CT.Info = "instruction is parallel, repeating a random one.";
-    CT.Instructions.push_back(std::move(IT));
+    CT.Instructions.push_back(std::move(Variant));
     instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
     return getSingleton(std::move(CT));
   }
   if (SelfAliasing.hasImplicitAliasing()) {
     CT.Info = "instruction is serial, repeating a random one.";
-    CT.Instructions.push_back(std::move(IT));
+    CT.Instructions.push_back(std::move(Variant));
     instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
     return getSingleton(std::move(CT));
   }
@@ -180,7 +181,7 @@ Expected<std::vector<CodeTemplate>> ParallelSnippetGenerator::generateCodeTempla
   if (!TiedVariables.empty()) {
     CT.Info = "instruction has tied variables, using static renaming.";
     CT.Instructions = generateSnippetUsingStaticRenaming(
-        State, IT, TiedVariables, ForbiddenRegisters);
+        State, Variant, TiedVariables, ForbiddenRegisters);
     instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
     return getSingleton(std::move(CT));
   }
@@ -194,7 +195,7 @@ Expected<std::vector<CodeTemplate>> ParallelSnippetGenerator::generateCodeTempla
       assert(PossibleRegisters.any() && "No register left to choose from");
       const auto RandomReg = randomBit(PossibleRegisters);
       Defs.set(RandomReg);
-      IT.getValueFor(Op) = MCOperand::createReg(RandomReg);
+      Variant.getValueFor(Op) = MCOperand::createReg(RandomReg);
     }
   }
   // And pick random use values that are not reserved and don't alias with defs.
@@ -206,12 +207,12 @@ Expected<std::vector<CodeTemplate>> ParallelSnippetGenerator::generateCodeTempla
       remove(PossibleRegisters, DefAliases);
       assert(PossibleRegisters.any() && "No register left to choose from");
       const auto RandomReg = randomBit(PossibleRegisters);
-      IT.getValueFor(Op) = MCOperand::createReg(RandomReg);
+      Variant.getValueFor(Op) = MCOperand::createReg(RandomReg);
     }
   }
   CT.Info =
       "instruction has no tied variables picking Uses 
diff erent from defs";
-  CT.Instructions.push_back(std::move(IT));
+  CT.Instructions.push_back(std::move(Variant));
   instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
   return getSingleton(std::move(CT));
 }

diff  --git a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h
index 617bf51ba941..94eb4e26eb58 100644
--- a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h
+++ b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h
@@ -25,7 +25,7 @@ class ParallelSnippetGenerator : public SnippetGenerator {
   ~ParallelSnippetGenerator() override;
 
   Expected<std::vector<CodeTemplate>>
-  generateCodeTemplates(const Instruction &Instr,
+  generateCodeTemplates(InstructionTemplate Variant,
                         const BitVector &ForbiddenRegisters) const override;
 
   static constexpr const size_t kMinNumDifferentAddresses = 6;

diff  --git a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
index fea6dc95ce82..a0d227d8c4bc 100644
--- a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
+++ b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
@@ -87,7 +87,7 @@ static ExecutionMode getExecutionModes(const Instruction &Instr,
 }
 
 static void appendCodeTemplates(const LLVMState &State,
-                                const Instruction *Instr,
+                                InstructionTemplate Variant,
                                 const BitVector &ForbiddenRegisters,
                                 ExecutionMode ExecutionModeBit,
                                 StringRef ExecutionClassDescription,
@@ -103,7 +103,7 @@ static void appendCodeTemplates(const LLVMState &State,
     CodeTemplate CT;
     CT.Execution = ExecutionModeBit;
     CT.Info = std::string(ExecutionClassDescription);
-    CT.Instructions.push_back(Instr);
+    CT.Instructions.push_back(std::move(Variant));
     CodeTemplates.push_back(std::move(CT));
     return;
   }
@@ -115,27 +115,28 @@ static void appendCodeTemplates(const LLVMState &State,
   case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS: {
     // Making the execution of this instruction serial by selecting one def
     // register to alias with one use register.
-    const AliasingConfigurations SelfAliasing(*Instr, *Instr);
+    const AliasingConfigurations SelfAliasing(Variant.getInstr(),
+                                              Variant.getInstr());
     assert(!SelfAliasing.empty() && !SelfAliasing.hasImplicitAliasing() &&
            "Instr must alias itself explicitly");
-    InstructionTemplate IT(Instr);
     // This is a self aliasing instruction so defs and uses are from the same
-    // instance, hence twice IT in the following call.
-    setRandomAliasing(SelfAliasing, IT, IT);
+    // instance, hence twice Variant in the following call.
+    setRandomAliasing(SelfAliasing, Variant, Variant);
     CodeTemplate CT;
     CT.Execution = ExecutionModeBit;
     CT.Info = std::string(ExecutionClassDescription);
-    CT.Instructions.push_back(std::move(IT));
+    CT.Instructions.push_back(std::move(Variant));
     CodeTemplates.push_back(std::move(CT));
     return;
   }
   case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR: {
+    const Instruction &Instr = Variant.getInstr();
     // Select back-to-back non-memory instruction.
     for (const auto *OtherInstr : computeAliasingInstructions(
-             State, Instr, kMaxAliasingInstructions, ForbiddenRegisters)) {
-      const AliasingConfigurations Forward(*Instr, *OtherInstr);
-      const AliasingConfigurations Back(*OtherInstr, *Instr);
-      InstructionTemplate ThisIT(Instr);
+             State, &Instr, kMaxAliasingInstructions, ForbiddenRegisters)) {
+      const AliasingConfigurations Forward(Instr, *OtherInstr);
+      const AliasingConfigurations Back(*OtherInstr, Instr);
+      InstructionTemplate ThisIT(Variant);
       InstructionTemplate OtherIT(OtherInstr);
       if (!Forward.hasImplicitAliasing())
         setRandomAliasing(Forward, ThisIT, OtherIT);
@@ -159,12 +160,13 @@ SerialSnippetGenerator::~SerialSnippetGenerator() = default;
 
 Expected<std::vector<CodeTemplate>>
 SerialSnippetGenerator::generateCodeTemplates(
-    const Instruction &Instr, const BitVector &ForbiddenRegisters) const {
+    InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
   std::vector<CodeTemplate> Results;
-  const ExecutionMode EM = getExecutionModes(Instr, ForbiddenRegisters);
+  const ExecutionMode EM =
+      getExecutionModes(Variant.getInstr(), ForbiddenRegisters);
   for (const auto EC : kExecutionClasses) {
     for (const auto ExecutionModeBit : getExecutionModeBits(EM & EC.Mask))
-      appendCodeTemplates(State, &Instr, ForbiddenRegisters, ExecutionModeBit,
+      appendCodeTemplates(State, Variant, ForbiddenRegisters, ExecutionModeBit,
                           EC.Description, Results);
     if (!Results.empty())
       break;

diff  --git a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.h b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.h
index 292b1bc947c9..42a1ed38b503 100644
--- a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.h
+++ b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.h
@@ -27,7 +27,7 @@ class SerialSnippetGenerator : public SnippetGenerator {
   ~SerialSnippetGenerator() override;
 
   Expected<std::vector<CodeTemplate>>
-  generateCodeTemplates(const Instruction &Instr,
+  generateCodeTemplates(InstructionTemplate Variant,
                         const BitVector &ForbiddenRegisters) const override;
 };
 

diff  --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
index 84ef4c63598a..b3a7118115c1 100644
--- a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
+++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
@@ -38,13 +38,14 @@ SnippetGenerator::SnippetGenerator(const LLVMState &State, const Options &Opts)
 
 SnippetGenerator::~SnippetGenerator() = default;
 
-Expected<std::vector<BenchmarkCode>> SnippetGenerator::generateConfigurations(
-    const Instruction &Instr, const BitVector &ExtraForbiddenRegs) const {
+Error SnippetGenerator::generateConfigurations(
+    const InstructionTemplate &Variant, std::vector<BenchmarkCode> &Benchmarks,
+    const BitVector &ExtraForbiddenRegs) const {
   BitVector ForbiddenRegs = State.getRATC().reservedRegisters();
   ForbiddenRegs |= ExtraForbiddenRegs;
   // If the instruction has memory registers, prevent the generator from
   // using the scratch register and its aliasing registers.
-  if (Instr.hasMemoryOperands()) {
+  if (Variant.getInstr().hasMemoryOperands()) {
     const auto &ET = State.getExegesisTarget();
     unsigned ScratchSpacePointerInReg =
         ET.getScratchMemoryRegister(State.getTargetMachine().getTargetTriple());
@@ -55,7 +56,7 @@ Expected<std::vector<BenchmarkCode>> SnippetGenerator::generateConfigurations(
         State.getRATC().getRegister(ScratchSpacePointerInReg).aliasedBits();
     // If the instruction implicitly writes to ScratchSpacePointerInReg , abort.
     // FIXME: We could make a copy of the scratch register.
-    for (const auto &Op : Instr.Operands) {
+    for (const auto &Op : Variant.getInstr().Operands) {
       if (Op.isDef() && Op.isImplicitReg() &&
           ScratchRegAliases.test(Op.getImplicitReg()))
         return make_error<Failure>(
@@ -64,16 +65,19 @@ Expected<std::vector<BenchmarkCode>> SnippetGenerator::generateConfigurations(
     ForbiddenRegs |= ScratchRegAliases;
   }
 
-  if (auto E = generateCodeTemplates(Instr, ForbiddenRegs)) {
-    std::vector<BenchmarkCode> Output;
-    for (CodeTemplate &CT : E.get()) {
+  if (auto E = generateCodeTemplates(Variant, ForbiddenRegs)) {
+    MutableArrayRef<CodeTemplate> Templates = E.get();
+
+    // Avoid reallocations in the loop.
+    Benchmarks.reserve(Benchmarks.size() + Templates.size());
+    for (CodeTemplate &CT : Templates) {
       // TODO: Generate as many BenchmarkCode as needed.
       {
         BenchmarkCode BC;
         BC.Info = CT.Info;
         for (InstructionTemplate &IT : CT.Instructions) {
           if (auto error = randomizeUnsetVariables(State, ForbiddenRegs, IT))
-            return std::move(error);
+            return error;
           BC.Key.Instructions.push_back(IT.build());
         }
         if (CT.ScratchSpacePointerInReg)
@@ -81,13 +85,14 @@ Expected<std::vector<BenchmarkCode>> SnippetGenerator::generateConfigurations(
         BC.Key.RegisterInitialValues =
             computeRegisterInitialValues(CT.Instructions);
         BC.Key.Config = CT.Config;
-        Output.push_back(std::move(BC));
-        if (Output.size() >= Opts.MaxConfigsPerOpcode)
-          return Output; // Early exit if we exceeded the number of allowed
-                         // configs.
+        Benchmarks.emplace_back(std::move(BC));
+        if (Benchmarks.size() >= Opts.MaxConfigsPerOpcode) {
+          // We reached the number of  allowed configs and return early.
+          return Error::success();
+        }
       }
     }
-    return Output;
+    return Error::success();
   } else
     return E.takeError();
 }
@@ -135,34 +140,35 @@ std::vector<RegisterValue> SnippetGenerator::computeRegisterInitialValues(
 }
 
 Expected<std::vector<CodeTemplate>>
-generateSelfAliasingCodeTemplates(const Instruction &Instr) {
-  const AliasingConfigurations SelfAliasing(Instr, Instr);
+generateSelfAliasingCodeTemplates(InstructionTemplate Variant) {
+  const AliasingConfigurations SelfAliasing(Variant.getInstr(),
+                                            Variant.getInstr());
   if (SelfAliasing.empty())
     return make_error<SnippetGeneratorFailure>("empty self aliasing");
   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.";
   } else {
     CT.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 IT in the following call.
-    setRandomAliasing(SelfAliasing, IT, IT);
+    // instance, hence twice Variant in the following call.
+    setRandomAliasing(SelfAliasing, Variant, Variant);
   }
-  CT.Instructions.push_back(std::move(IT));
+  CT.Instructions.push_back(std::move(Variant));
   return std::move(Result);
 }
 
 Expected<std::vector<CodeTemplate>>
-generateUnconstrainedCodeTemplates(const Instruction &Instr, StringRef Msg) {
+generateUnconstrainedCodeTemplates(const InstructionTemplate &Variant,
+                                   StringRef Msg) {
   std::vector<CodeTemplate> Result;
   Result.emplace_back();
   CodeTemplate &CT = Result.back();
   CT.Info =
       std::string(formatv("{0}, repeating an unconstrained assignment", Msg));
-  CT.Instructions.emplace_back(&Instr);
+  CT.Instructions.push_back(std::move(Variant));
   return std::move(Result);
 }
 

diff  --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
index 7e7f6e487e18..ebea886d3c7d 100644
--- a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
+++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
@@ -34,11 +34,12 @@ std::vector<CodeTemplate> getSingleton(CodeTemplate &&CT);
 
 // Generates code templates that has a self-dependency.
 Expected<std::vector<CodeTemplate>>
-generateSelfAliasingCodeTemplates(const Instruction &Instr);
+generateSelfAliasingCodeTemplates(InstructionTemplate Variant);
 
 // Generates code templates without assignment constraints.
 Expected<std::vector<CodeTemplate>>
-generateUnconstrainedCodeTemplates(const Instruction &Instr, StringRef Msg);
+generateUnconstrainedCodeTemplates(const InstructionTemplate &Variant,
+                                   StringRef Msg);
 
 // A class representing failures that happened during Benchmark, they are used
 // to report informations to the user.
@@ -59,9 +60,9 @@ class SnippetGenerator {
   virtual ~SnippetGenerator();
 
   // Calls generateCodeTemplate and expands it into one or more BenchmarkCode.
-  Expected<std::vector<BenchmarkCode>>
-  generateConfigurations(const Instruction &Instr,
-                         const BitVector &ExtraForbiddenRegs) const;
+  Error generateConfigurations(const InstructionTemplate &Variant,
+                               std::vector<BenchmarkCode> &Benchmarks,
+                               const BitVector &ExtraForbiddenRegs) const;
 
   // Given a snippet, computes which registers the setup code needs to define.
   std::vector<RegisterValue> computeRegisterInitialValues(
@@ -74,7 +75,7 @@ class SnippetGenerator {
 private:
   // API to be implemented by subclasses.
   virtual Expected<std::vector<CodeTemplate>>
-  generateCodeTemplates(const Instruction &Instr,
+  generateCodeTemplates(InstructionTemplate Variant,
                         const BitVector &ForbiddenRegisters) const = 0;
 };
 
@@ -101,6 +102,132 @@ Error randomizeUnsetVariables(const LLVMState &State,
                               const BitVector &ForbiddenRegs,
                               InstructionTemplate &IT);
 
+// Combination generator.
+//
+// Example: given input {{0, 1}, {2}, {3, 4}} it will produce the following
+// combinations: {0, 2, 3}, {0, 2, 4}, {1, 2, 3}, {1, 2, 4}.
+//
+// It is important to think of input as vector-of-vectors, where the
+// outer vector is the variable space, and inner vector is choice space.
+// The number of choices for each variable can be 
diff erent.
+//
+// As for implementation, it is useful to think of this as a weird number,
+// where each digit (==variable) may have 
diff erent base (==number of choices).
+// Thus modelling of 'produce next combination' is exactly analogous to the
+// incrementing of an number - increment lowest digit (pick next choice for the
+// variable), and if it wrapped to the beginning then increment next digit.
+template <typename choice_type, typename choices_storage_type,
+          int variable_smallsize>
+class CombinationGenerator {
+  template <typename T> struct WrappingIterator {
+    using value_type = T;
+
+    const ArrayRef<value_type> Range;
+    typename decltype(Range)::const_iterator Position;
+
+    // Rewind the tape, placing the position to again point at the beginning.
+    void rewind() { Position = Range.begin(); }
+
+    // Advance position forward, possibly wrapping to the beginning.
+    // Returns whether the wrap happened.
+    bool operator++() {
+      ++Position;
+      bool Wrapped = Position == Range.end();
+      if (Wrapped)
+        rewind();
+      return Wrapped;
+    }
+
+    // Get the value at which we are currently pointing.
+    operator const value_type &() const { return *Position; }
+
+    WrappingIterator(ArrayRef<value_type> Range_) : Range(Range_) {
+      assert(!Range.empty() && "The range must not be empty.");
+      rewind();
+    }
+
+    // Only allow using our custom constructor.
+    WrappingIterator() = delete;
+    WrappingIterator(const WrappingIterator &) = delete;
+    WrappingIterator(WrappingIterator &&) = delete;
+    WrappingIterator &operator=(WrappingIterator) = delete;
+    WrappingIterator &operator=(const WrappingIterator &) = delete;
+    WrappingIterator &operator=(WrappingIterator &&) = delete;
+  };
+
+  const ArrayRef<choices_storage_type> VariablesChoices;
+  const function_ref<bool(ArrayRef<choice_type>)> &Callback;
+
+  void performGeneration() const {
+    SmallVector<WrappingIterator<choice_type>, variable_smallsize>
+        VariablesState;
+
+    // Initialize the per-variable state to refer to the possible choices for
+    // that variable.
+    VariablesState.reserve(VariablesChoices.size());
+    for (ArrayRef<choice_type> VariablesChoices : VariablesChoices)
+      VariablesState.emplace_back(VariablesChoices);
+
+    // Temporary buffer to store each combination before performing Callback.
+    SmallVector<choice_type, variable_smallsize> CurrentCombination;
+    CurrentCombination.resize(VariablesState.size());
+
+    while (true) {
+      // Gather the currently-selected variable choices into a vector.
+      for (auto I : llvm::zip(VariablesState, CurrentCombination))
+        std::get<1>(I) = std::get<0>(I);
+      // And pass the new combination into callback, as intended.
+      if (/*Abort=*/Callback(CurrentCombination))
+        return;
+
+      // 'increment' the whole VariablesState, much like you would increment
+      // a number: starting from the least significant element, increment it,
+      // and if it wrapped, then propagate that carry by also incrementing next
+      // (more significant) element.
+      for (WrappingIterator<choice_type> &VariableState :
+           llvm::reverse(VariablesState)) {
+        bool Wrapped = ++VariableState;
+        if (!Wrapped)
+          break;
+
+        if (VariablesState.begin() == &VariableState)
+          return; // The "most significant" variable has wrapped, which means
+                  // that we have produced all the combinations.
+
+        // We have carry - increment more significant variable next..
+      }
+    }
+  };
+
+public:
+  CombinationGenerator(ArrayRef<choices_storage_type> VariablesChoices_,
+                       const function_ref<bool(ArrayRef<choice_type>)> &Cb_)
+      : VariablesChoices(VariablesChoices_), Callback(Cb_) {
+#ifndef NDEBUG
+    assert(!VariablesChoices.empty() && "There should be some variables.");
+    llvm::for_each(VariablesChoices, [](ArrayRef<choice_type> VariableChoices) {
+      assert(!VariableChoices.empty() &&
+             "There must always be some choice, at least a placeholder one.");
+    });
+#endif
+  }
+
+  // How many combinations can we produce, max?
+  // This is at most how many times the callback will be called.
+  size_t numCombinations() const {
+    size_t NumVariants = 1;
+    for (ArrayRef<choice_type> VariableChoices : VariablesChoices)
+      NumVariants *= VariableChoices.size();
+    assert(NumVariants >= 1 &&
+           "We should always end up producing at least one combination");
+    return NumVariants;
+  }
+
+  // Actually perform exhaustive combination generation.
+  // Each result will be passed into the callback.
+  void generate() { performGeneration(); }
+};
+
 } // namespace exegesis
 } // namespace llvm
 

diff  --git a/llvm/tools/llvm-exegesis/lib/Target.h b/llvm/tools/llvm-exegesis/lib/Target.h
index 1f35bfeed34c..298cfac23e81 100644
--- a/llvm/tools/llvm-exegesis/lib/Target.h
+++ b/llvm/tools/llvm-exegesis/lib/Target.h
@@ -126,6 +126,16 @@ class ExegesisTarget {
     return true;
   }
 
+  // For some instructions, it is interesting to measure how it's performance
+  // characteristics 
diff er depending on it's operands.
+  // This allows us to produce all the interesting variants.
+  virtual std::vector<InstructionTemplate>
+  generateInstructionVariants(const Instruction &Instr,
+                              unsigned MaxConfigsPerOpcode) const {
+    // By default, we're happy with whatever randomizer will give us.
+    return {&Instr};
+  }
+
   // Creates a snippet generator for the given mode.
   std::unique_ptr<SnippetGenerator>
   createSnippetGenerator(InstructionBenchmark::ModeE Mode,

diff  --git a/llvm/tools/llvm-exegesis/lib/X86/Target.cpp b/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
index 72553af823f1..3dcac2553a02 100644
--- a/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
+++ b/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
@@ -8,14 +8,15 @@
 #include "../Target.h"
 
 #include "../Error.h"
+#include "../ParallelSnippetGenerator.h"
 #include "../SerialSnippetGenerator.h"
 #include "../SnippetGenerator.h"
-#include "../ParallelSnippetGenerator.h"
 #include "MCTargetDesc/X86BaseInfo.h"
 #include "MCTargetDesc/X86MCTargetDesc.h"
 #include "X86.h"
 #include "X86RegisterInfo.h"
 #include "X86Subtarget.h"
+#include "llvm/ADT/Sequence.h"
 #include "llvm/MC/MCInstBuilder.h"
 #include "llvm/Support/FormatVariadic.h"
 
@@ -256,14 +257,16 @@ class X86SerialSnippetGenerator : public SerialSnippetGenerator {
   using SerialSnippetGenerator::SerialSnippetGenerator;
 
   Expected<std::vector<CodeTemplate>>
-  generateCodeTemplates(const Instruction &Instr,
+  generateCodeTemplates(InstructionTemplate Variant,
                         const BitVector &ForbiddenRegisters) const override;
 };
 } // namespace
 
 Expected<std::vector<CodeTemplate>>
 X86SerialSnippetGenerator::generateCodeTemplates(
-    const Instruction &Instr, const BitVector &ForbiddenRegisters) const {
+    InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
+  const Instruction &Instr = Variant.getInstr();
+
   if (const auto reason = isInvalidOpcode(Instr))
     return make_error<Failure>(reason);
 
@@ -287,8 +290,8 @@ X86SerialSnippetGenerator::generateCodeTemplates(
 
   switch (getX86FPFlags(Instr)) {
   case X86II::NotFP:
-    return SerialSnippetGenerator::generateCodeTemplates(Instr,
-                                                          ForbiddenRegisters);
+    return SerialSnippetGenerator::generateCodeTemplates(Variant,
+                                                         ForbiddenRegisters);
   case X86II::ZeroArgFP:
   case X86II::OneArgFP:
   case X86II::SpecialFP:
@@ -301,7 +304,7 @@ X86SerialSnippetGenerator::generateCodeTemplates(
     //   - `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 generateSelfAliasingCodeTemplates(Instr);
+    return generateSelfAliasingCodeTemplates(Variant);
   default:
     llvm_unreachable("Unknown FP Type!");
   }
@@ -313,7 +316,7 @@ class X86ParallelSnippetGenerator : public ParallelSnippetGenerator {
   using ParallelSnippetGenerator::ParallelSnippetGenerator;
 
   Expected<std::vector<CodeTemplate>>
-  generateCodeTemplates(const Instruction &Instr,
+  generateCodeTemplates(InstructionTemplate Variant,
                         const BitVector &ForbiddenRegisters) const override;
 };
 
@@ -321,7 +324,9 @@ class X86ParallelSnippetGenerator : public ParallelSnippetGenerator {
 
 Expected<std::vector<CodeTemplate>>
 X86ParallelSnippetGenerator::generateCodeTemplates(
-    const Instruction &Instr, const BitVector &ForbiddenRegisters) const {
+    InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
+  const Instruction &Instr = Variant.getInstr();
+
   if (const auto reason = isInvalidOpcode(Instr))
     return make_error<Failure>(reason);
 
@@ -342,8 +347,8 @@ X86ParallelSnippetGenerator::generateCodeTemplates(
 
   switch (getX86FPFlags(Instr)) {
   case X86II::NotFP:
-    return ParallelSnippetGenerator::generateCodeTemplates(Instr,
-                                                       ForbiddenRegisters);
+    return ParallelSnippetGenerator::generateCodeTemplates(Variant,
+                                                           ForbiddenRegisters);
   case X86II::ZeroArgFP:
   case X86II::OneArgFP:
   case X86II::SpecialFP:
@@ -355,13 +360,13 @@ X86ParallelSnippetGenerator::generateCodeTemplates(
     //   - `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 generateSelfAliasingCodeTemplates(Instr);
+    return generateSelfAliasingCodeTemplates(Variant);
   case X86II::CompareFP:
   case 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 generateUnconstrainedCodeTemplates(
-        Instr, "instruction does not grow/shrink the FP stack");
+        Variant, "instruction does not grow/shrink the FP stack");
   default:
     llvm_unreachable("Unknown FP Type!");
   }
@@ -592,6 +597,10 @@ class ExegesisX86Target : public ExegesisTarget {
            Opcode != X86::LEA64_32r && Opcode != X86::LEA16r;
   }
 
+  std::vector<InstructionTemplate>
+  generateInstructionVariants(const Instruction &Instr,
+                              unsigned MaxConfigsPerOpcode) const override;
+
   std::unique_ptr<SnippetGenerator> createSerialSnippetGenerator(
       const LLVMState &State,
       const SnippetGenerator::Options &Opts) const override {
@@ -653,10 +662,6 @@ Error ExegesisX86Target::randomizeTargetMCOperand(
     AssignedValue =
         MCOperand::createImm(randomIndex(X86::STATIC_ROUNDING::TO_ZERO));
     return Error::success();
-  case X86::OperandType::OPERAND_COND_CODE:
-    AssignedValue =
-        MCOperand::createImm(randomIndex(X86::CondCode::LAST_VALID_COND));
-    return Error::success();
   default:
     break;
   }
@@ -741,6 +746,63 @@ std::vector<MCInst> ExegesisX86Target::setRegTo(const MCSubtargetInfo &STI,
   return {}; // Not yet implemented.
 }
 
+// Instruction can have some variable operands, and we may want to see how
+// 
diff erent operands affect performance. So for each operand position,
+// precompute all the possible choices we might care about,
+// and greedily generate all the possible combinations of choices.
+std::vector<InstructionTemplate> ExegesisX86Target::generateInstructionVariants(
+    const Instruction &Instr, unsigned MaxConfigsPerOpcode) const {
+  bool Exploration = false;
+  SmallVector<SmallVector<MCOperand, 1>, 4> VariableChoices;
+  VariableChoices.resize(Instr.Variables.size());
+  for (auto I : llvm::zip(Instr.Variables, VariableChoices)) {
+    const Variable &Var = std::get<0>(I);
+    SmallVectorImpl<MCOperand> &Choices = std::get<1>(I);
+
+    switch (Instr.getPrimaryOperand(Var).getExplicitOperandInfo().OperandType) {
+    default:
+      // We don't wish to explicitly explore this variable.
+      Choices.emplace_back(); // But add invalid MCOperand to simplify logic.
+      continue;
+    case X86::OperandType::OPERAND_COND_CODE: {
+      Exploration = true;
+      auto CondCodes = seq((int)X86::CondCode::COND_O,
+                           1 + (int)X86::CondCode::LAST_VALID_COND);
+      Choices.reserve(std::distance(CondCodes.begin(), CondCodes.end()));
+      for (int CondCode : CondCodes)
+        Choices.emplace_back(MCOperand::createImm(CondCode));
+      break;
+    }
+    }
+  }
+
+  // If we don't wish to explore any variables, defer to the baseline method.
+  if (!Exploration)
+    return ExegesisTarget::generateInstructionVariants(Instr,
+                                                       MaxConfigsPerOpcode);
+
+  std::vector<InstructionTemplate> Variants;
+  size_t NumVariants;
+  CombinationGenerator<MCOperand, decltype(VariableChoices)::value_type, 4> G(
+      VariableChoices, [&](ArrayRef<MCOperand> State) -> bool {
+        Variants.emplace_back(&Instr);
+        Variants.back().setVariableValues(State);
+        // Did we run out of space for variants?
+        return Variants.size() >= NumVariants;
+      });
+
+  // How many operand combinations can we produce, within the limit?
+  NumVariants = std::min(G.numCombinations(), (size_t)MaxConfigsPerOpcode);
+  // And actually produce all the wanted operand combinations.
+  Variants.reserve(NumVariants);
+  G.generate();
+
+  assert(Variants.size() == NumVariants &&
+         Variants.size() <= MaxConfigsPerOpcode &&
+         "Should not produce too many variants");
+  return Variants;
+}
+
 static ExegesisTarget *getTheExegesisX86Target() {
   static ExegesisX86Target Target;
   return &Target;

diff  --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
index 49b01f369073..c086abbb80df 100644
--- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
+++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
@@ -238,6 +238,10 @@ generateSnippets(const LLVMState &State, unsigned Opcode,
   if (InstrDesc.isCall() || InstrDesc.isReturn())
     return make_error<Failure>("Unsupported opcode: isCall/isReturn");
 
+  const std::vector<InstructionTemplate> InstructionVariants =
+      State.getExegesisTarget().generateInstructionVariants(
+          Instr, MaxConfigsPerOpcode);
+
   SnippetGenerator::Options SnippetOptions;
   SnippetOptions.MaxConfigsPerOpcode = MaxConfigsPerOpcode;
   const std::unique_ptr<SnippetGenerator> Generator =
@@ -245,7 +249,16 @@ generateSnippets(const LLVMState &State, unsigned Opcode,
                                                        SnippetOptions);
   if (!Generator)
     ExitWithError("cannot create snippet generator");
-  return Generator->generateConfigurations(Instr, ForbiddenRegs);
+
+  std::vector<BenchmarkCode> Benchmarks;
+  for (const InstructionTemplate &Variant : InstructionVariants) {
+    if (Benchmarks.size() >= MaxConfigsPerOpcode)
+      break;
+    if (auto Err = Generator->generateConfigurations(Variant, Benchmarks,
+                                                     ForbiddenRegs))
+      return std::move(Err);
+  }
+  return Benchmarks;
 }
 
 void benchmarkMain() {

diff  --git a/llvm/unittests/tools/llvm-exegesis/CMakeLists.txt b/llvm/unittests/tools/llvm-exegesis/CMakeLists.txt
index d6d2a2d743cb..17d38ffe4d11 100644
--- a/llvm/unittests/tools/llvm-exegesis/CMakeLists.txt
+++ b/llvm/unittests/tools/llvm-exegesis/CMakeLists.txt
@@ -15,6 +15,7 @@ add_llvm_unittest(LLVMExegesisTests
   ClusteringTest.cpp
   PerfHelperTest.cpp
   RegisterValueTest.cpp
+  SnippetGeneratorTest.cpp
   )
 target_link_libraries(LLVMExegesisTests PRIVATE LLVMExegesis)
 

diff  --git a/llvm/unittests/tools/llvm-exegesis/Mips/SnippetGeneratorTest.cpp b/llvm/unittests/tools/llvm-exegesis/Mips/SnippetGeneratorTest.cpp
index 9cb2e3b1ed1a..f837454803ca 100644
--- a/llvm/unittests/tools/llvm-exegesis/Mips/SnippetGeneratorTest.cpp
+++ b/llvm/unittests/tools/llvm-exegesis/Mips/SnippetGeneratorTest.cpp
@@ -40,7 +40,7 @@ class SnippetGeneratorTest : public MipsSnippetGeneratorTest {
     randomGenerator().seed(0); // Initialize seed.
     const Instruction &Instr = State.getIC().getInstr(Opcode);
     auto CodeTemplateOrError = Generator.generateCodeTemplates(
-        Instr, State.getRATC().emptyRegisters());
+        &Instr, State.getRATC().emptyRegisters());
     EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.
     return std::move(CodeTemplateOrError.get());
   }
@@ -91,7 +91,8 @@ TEST_F(SerialSnippetGeneratorTest,
   const Instruction &Instr = State.getIC().getInstr(Mips::XOR);
   auto AllRegisters = State.getRATC().emptyRegisters();
   AllRegisters.flip();
-  auto Error = Generator.generateCodeTemplates(Instr, AllRegisters).takeError();
+  auto Error =
+      Generator.generateCodeTemplates(&Instr, AllRegisters).takeError();
   EXPECT_TRUE((bool)Error);
   consumeError(std::move(Error));
 }

diff  --git a/llvm/unittests/tools/llvm-exegesis/SnippetGeneratorTest.cpp b/llvm/unittests/tools/llvm-exegesis/SnippetGeneratorTest.cpp
new file mode 100644
index 000000000000..9feff27f40dd
--- /dev/null
+++ b/llvm/unittests/tools/llvm-exegesis/SnippetGeneratorTest.cpp
@@ -0,0 +1,183 @@
+//===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SnippetGenerator.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <initializer_list>
+
+namespace llvm {
+namespace exegesis {
+
+namespace {
+
+TEST(CombinationGenerator, Square) {
+  const std::vector<std::vector<int>> Choices{{0, 1}, {2, 3}};
+
+  std::vector<std::vector<int>> Variants;
+  CombinationGenerator<int, std::vector<int>, 4> G(
+      Choices, [&](ArrayRef<int> State) -> bool {
+        Variants.emplace_back(State);
+        return false; // keep going
+      });
+  const size_t NumVariants = G.numCombinations();
+  G.generate();
+
+  const std::vector<std::vector<int>> ExpectedVariants{
+      {0, 2},
+      {0, 3},
+      {1, 2},
+      {1, 3},
+  };
+  ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));
+  ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));
+}
+
+TEST(CombinationGenerator, MiddleColumn) {
+  const std::vector<std::vector<int>> Choices{{0}, {1, 2}, {3}};
+
+  std::vector<std::vector<int>> Variants;
+  CombinationGenerator<int, std::vector<int>, 4> G(
+      Choices, [&](ArrayRef<int> State) -> bool {
+        Variants.emplace_back(State);
+        return false; // keep going
+      });
+  const size_t NumVariants = G.numCombinations();
+  G.generate();
+
+  const std::vector<std::vector<int>> ExpectedVariants{
+      {0, 1, 3},
+      {0, 2, 3},
+  };
+  ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));
+  ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));
+}
+
+TEST(CombinationGenerator, SideColumns) {
+  const std::vector<std::vector<int>> Choices{{0, 1}, {2}, {3, 4}};
+
+  std::vector<std::vector<int>> Variants;
+  CombinationGenerator<int, std::vector<int>, 4> G(
+      Choices, [&](ArrayRef<int> State) -> bool {
+        Variants.emplace_back(State);
+        return false; // keep going
+      });
+  const size_t NumVariants = G.numCombinations();
+  G.generate();
+
+  const std::vector<std::vector<int>> ExpectedVariants{
+      {0, 2, 3},
+      {0, 2, 4},
+      {1, 2, 3},
+      {1, 2, 4},
+  };
+  ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));
+  ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));
+}
+
+TEST(CombinationGenerator, LeftColumn) {
+  const std::vector<std::vector<int>> Choices{{0, 1}, {2}};
+
+  std::vector<std::vector<int>> Variants;
+  CombinationGenerator<int, std::vector<int>, 4> G(
+      Choices, [&](ArrayRef<int> State) -> bool {
+        Variants.emplace_back(State);
+        return false; // keep going
+      });
+  const size_t NumVariants = G.numCombinations();
+  G.generate();
+
+  const std::vector<std::vector<int>> ExpectedVariants{
+      {0, 2},
+      {1, 2},
+  };
+  ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));
+  ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));
+}
+
+TEST(CombinationGenerator, RightColumn) {
+  const std::vector<std::vector<int>> Choices{{0}, {1, 2}};
+
+  std::vector<std::vector<int>> Variants;
+  CombinationGenerator<int, std::vector<int>, 4> G(
+      Choices, [&](ArrayRef<int> State) -> bool {
+        Variants.emplace_back(State);
+        return false; // keep going
+      });
+  const size_t NumVariants = G.numCombinations();
+  G.generate();
+
+  const std::vector<std::vector<int>> ExpectedVariants{
+      {0, 1},
+      {0, 2},
+  };
+  ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));
+  ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));
+}
+
+TEST(CombinationGenerator, Column) {
+  const std::vector<std::vector<int>> Choices{{0, 1}};
+
+  std::vector<std::vector<int>> Variants;
+  CombinationGenerator<int, std::vector<int>, 4> G(
+      Choices, [&](ArrayRef<int> State) -> bool {
+        Variants.emplace_back(State);
+        return false; // keep going
+      });
+  const size_t NumVariants = G.numCombinations();
+  G.generate();
+
+  const std::vector<std::vector<int>> ExpectedVariants{
+      {0},
+      {1},
+  };
+  ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));
+  ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));
+}
+
+TEST(CombinationGenerator, Row) {
+  const std::vector<std::vector<int>> Choices{{0}, {1}};
+
+  std::vector<std::vector<int>> Variants;
+  CombinationGenerator<int, std::vector<int>, 4> G(
+      Choices, [&](ArrayRef<int> State) -> bool {
+        Variants.emplace_back(State);
+        return false; // keep going
+      });
+  const size_t NumVariants = G.numCombinations();
+  G.generate();
+
+  const std::vector<std::vector<int>> ExpectedVariants{
+      {0, 1},
+  };
+  ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));
+  ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));
+}
+
+TEST(CombinationGenerator, Singleton) {
+  const std::vector<std::vector<int>> Choices{{0}};
+
+  std::vector<std::vector<int>> Variants;
+  CombinationGenerator<int, std::vector<int>, 4> G(
+      Choices, [&](ArrayRef<int> State) -> bool {
+        Variants.emplace_back(State);
+        return false; // keep going
+      });
+  const size_t NumVariants = G.numCombinations();
+  G.generate();
+
+  const std::vector<std::vector<int>> ExpectedVariants{
+      {0},
+  };
+  ASSERT_THAT(Variants, ::testing::SizeIs(NumVariants));
+  ASSERT_THAT(Variants, ::testing::ContainerEq(ExpectedVariants));
+}
+
+} // namespace
+} // namespace exegesis
+} // namespace llvm

diff  --git a/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp b/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp
index a833e1e22793..81e5f01effea 100644
--- a/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp
+++ b/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp
@@ -51,7 +51,7 @@ class SnippetGeneratorTest : public X86SnippetGeneratorTest {
     randomGenerator().seed(0); // Initialize seed.
     const Instruction &Instr = State.getIC().getInstr(Opcode);
     auto CodeTemplateOrError = Generator.generateCodeTemplates(
-        Instr, State.getRATC().emptyRegisters());
+        &Instr, State.getRATC().emptyRegisters());
     EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.
     return std::move(CodeTemplateOrError.get());
   }
@@ -153,7 +153,8 @@ TEST_F(SerialSnippetGeneratorTest,
   const Instruction &Instr = State.getIC().getInstr(Opcode);
   auto AllRegisters = State.getRATC().emptyRegisters();
   AllRegisters.flip();
-  auto Error = Generator.generateCodeTemplates(Instr, AllRegisters).takeError();
+  auto Error =
+      Generator.generateCodeTemplates(&Instr, AllRegisters).takeError();
   EXPECT_TRUE((bool)Error);
   consumeError(std::move(Error));
 }
@@ -207,11 +208,12 @@ TEST_F(SerialSnippetGeneratorTest, VCVTUSI642SDZrrb_Int) {
   // - Op4 Implicit Use Reg(MXSCR)
   const unsigned Opcode = X86::VCVTUSI642SDZrrb_Int;
   const Instruction &Instr = State.getIC().getInstr(Opcode);
-  auto Configs =
-      Generator.generateConfigurations(Instr, State.getRATC().emptyRegisters());
-  ASSERT_FALSE(Configs.takeError());
-  ASSERT_THAT(*Configs, SizeIs(1));
-  const BenchmarkCode &BC = (*Configs)[0];
+  std::vector<BenchmarkCode> Configs;
+  auto Error = Generator.generateConfigurations(
+      &Instr, Configs, State.getRATC().emptyRegisters());
+  ASSERT_FALSE(Error);
+  ASSERT_THAT(Configs, SizeIs(1));
+  const BenchmarkCode &BC = Configs[0];
   ASSERT_THAT(BC.Key.Instructions, SizeIs(1));
   ASSERT_TRUE(BC.Key.Instructions[0].getOperand(3).isImm());
 }
@@ -357,9 +359,9 @@ TEST_F(ParallelSnippetGeneratorTest, MemoryUse) {
 TEST_F(ParallelSnippetGeneratorTest, MOV16ms) {
   const unsigned Opcode = X86::MOV16ms;
   const Instruction &Instr = State.getIC().getInstr(Opcode);
-  auto Err =
-      Generator.generateConfigurations(Instr, State.getRATC().emptyRegisters())
-          .takeError();
+  std::vector<BenchmarkCode> Benchmarks;
+  auto Err = Generator.generateConfigurations(&Instr, Benchmarks,
+                                              State.getRATC().emptyRegisters());
   EXPECT_TRUE((bool)Err);
   EXPECT_THAT(toString(std::move(Err)),
               testing::HasSubstr("no available registers"));
@@ -380,7 +382,7 @@ class FakeSnippetGenerator : public SnippetGenerator {
 
 private:
   Expected<std::vector<CodeTemplate>>
-  generateCodeTemplates(const Instruction &, const BitVector &) const override {
+  generateCodeTemplates(InstructionTemplate, const BitVector &) const override {
     return make_error<StringError>("not implemented", inconvertibleErrorCode());
   }
 };
@@ -412,9 +414,9 @@ TEST_F(FakeSnippetGeneratorTest, MemoryUse_Movsb) {
   // - hasAliasingRegisters
   const unsigned Opcode = X86::MOVSB;
   const Instruction &Instr = State.getIC().getInstr(Opcode);
-  auto Error =
-      Generator.generateConfigurations(Instr, State.getRATC().emptyRegisters())
-          .takeError();
+  std::vector<BenchmarkCode> Benchmarks;
+  auto Error = Generator.generateConfigurations(
+      &Instr, Benchmarks, State.getRATC().emptyRegisters());
   EXPECT_TRUE((bool)Error);
   consumeError(std::move(Error));
 }


        


More information about the llvm-commits mailing list