[llvm-exegesis] Allow the randomizer to fail nicely...
authorClement Courbet <courbet@google.com>
Wed, 22 Jan 2020 14:49:10 +0000 (15:49 +0100)
committerClement Courbet <courbet@google.com>
Thu, 23 Jan 2020 10:08:44 +0000 (11:08 +0100)
Summary:
... instead of crashing.
On typical exmaple is when there are no available registers.

Reviewers: gchatelet

Subscribers: tschuett, mstojanovic, llvm-commits

Tags: #llvm

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

llvm/tools/llvm-exegesis/lib/RegisterAliasing.cpp
llvm/tools/llvm-exegesis/lib/RegisterAliasing.h
llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
llvm/tools/llvm-exegesis/lib/SnippetGenerator.h
llvm/tools/llvm-exegesis/lib/Target.cpp
llvm/tools/llvm-exegesis/lib/Target.h
llvm/tools/llvm-exegesis/lib/X86/Target.cpp
llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp

index 1db19524015547d75d92dc0cf667b12b80cbe9f6..ee612fb0dd6af69c4039000fe05dadf2fba75ed5 100644 (file)
@@ -79,5 +79,14 @@ RegisterAliasingTrackerCache::getRegisterClass(unsigned RegClassIndex) const {
   return *Found;
 }
 
+std::string debugString(const MCRegisterInfo &RegInfo, const BitVector &Regs) {
+  std::string Result;
+  for (const unsigned Reg : Regs.set_bits()) {
+    Result.append(RegInfo.getName(Reg));
+    Result.push_back(' ');
+  }
+  return Result;
+}
+
 } // namespace exegesis
 } // namespace llvm
index f269c89a3f934666c11952acd19f64f2729ed209..b2980854ba2d1efc4f17a649ce9bd92d38f603c8 100644 (file)
@@ -110,6 +110,9 @@ inline void remove(BitVector &A, const BitVector &B) {
     A.reset(I);
 }
 
+// Returns a debug string for the list of registers.
+std::string debugString(const MCRegisterInfo &RegInfo, const BitVector &Regs);
+
 } // namespace exegesis
 } // namespace llvm
 
index d1f168fe43bfdb5f5996bae2344926bd31c36ab2..af3709486ec2d9062918baf4f8956ecb1306467a 100644 (file)
@@ -72,7 +72,8 @@ Expected<std::vector<BenchmarkCode>> SnippetGenerator::generateConfigurations(
         BenchmarkCode BC;
         BC.Info = CT.Info;
         for (InstructionTemplate &IT : CT.Instructions) {
-          randomizeUnsetVariables(State.getExegesisTarget(), ForbiddenRegs, IT);
+          if (auto error = randomizeUnsetVariables(State, ForbiddenRegs, IT))
+            return std::move(error);
           BC.Key.Instructions.push_back(IT.build());
         }
         if (CT.ScratchSpacePointerInReg)
@@ -215,15 +216,53 @@ void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
   setRegisterOperandValue(randomElement(RandomConf.Uses), UseIB);
 }
 
-void randomizeUnsetVariables(const ExegesisTarget &Target,
-                             const BitVector &ForbiddenRegs,
-                             InstructionTemplate &IT) {
+static Error randomizeMCOperand(const LLVMState &State,
+                                const Instruction &Instr, const Variable &Var,
+                                MCOperand &AssignedValue,
+                                const BitVector &ForbiddenRegs) {
+  const Operand &Op = Instr.getPrimaryOperand(Var);
+  if (Op.getExplicitOperandInfo().OperandType >=
+      MCOI::OperandType::OPERAND_FIRST_TARGET)
+    return State.getExegesisTarget().randomizeTargetMCOperand(
+        Instr, Var, AssignedValue, ForbiddenRegs);
+  switch (Op.getExplicitOperandInfo().OperandType) {
+  case MCOI::OperandType::OPERAND_IMMEDIATE:
+    // FIXME: explore immediate values too.
+    AssignedValue = MCOperand::createImm(1);
+    break;
+  case MCOI::OperandType::OPERAND_REGISTER: {
+    assert(Op.isReg());
+    auto AllowedRegs = Op.getRegisterAliasing().sourceBits();
+    assert(AllowedRegs.size() == ForbiddenRegs.size());
+    for (auto I : ForbiddenRegs.set_bits())
+      AllowedRegs.reset(I);
+    if (!AllowedRegs.any())
+      return make_error<Failure>(
+          Twine("no available registers:\ncandidates:\n")
+              .concat(debugString(State.getRegInfo(),
+                                  Op.getRegisterAliasing().sourceBits()))
+              .concat("\nforbidden:\n")
+              .concat(debugString(State.getRegInfo(), ForbiddenRegs)));
+    AssignedValue = MCOperand::createReg(randomBit(AllowedRegs));
+    break;
+  }
+  default:
+    break;
+  }
+  return Error::success();
+}
+
+Error randomizeUnsetVariables(const LLVMState &State,
+                              const BitVector &ForbiddenRegs,
+                              InstructionTemplate &IT) {
   for (const Variable &Var : IT.getInstr().Variables) {
     MCOperand &AssignedValue = IT.getValueFor(Var);
     if (!AssignedValue.isValid())
-      Target.randomizeMCOperand(IT.getInstr(), Var, AssignedValue,
-                                ForbiddenRegs);
+      if (auto Err = randomizeMCOperand(State, IT.getInstr(), Var,
+                                        AssignedValue, ForbiddenRegs))
+        return Err;
   }
+  return Error::success();
 }
 
 } // namespace exegesis
index 70f81f942be41cc02355334c2f929dc0b8c9f69d..7e7f6e487e18d03f55e1eb2cd5ed6bd1af9de440 100644 (file)
@@ -97,9 +97,9 @@ void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
 
 // Assigns a Random Value to all Variables in IT that are still Invalid.
 // Do not use any of the registers in `ForbiddenRegs`.
-void randomizeUnsetVariables(const ExegesisTarget &Target,
-                             const BitVector &ForbiddenRegs,
-                             InstructionTemplate &IT);
+Error randomizeUnsetVariables(const LLVMState &State,
+                              const BitVector &ForbiddenRegs,
+                              InstructionTemplate &IT);
 
 } // namespace exegesis
 } // namespace llvm
index 40021e2931cb02692d80d5ace5470cf9be0009f1..701ff14be2093ce27083c918a9eb5181db0544a4 100644 (file)
@@ -99,30 +99,6 @@ ExegesisTarget::createUopsBenchmarkRunner(const LLVMState &State) const {
   return std::make_unique<UopsBenchmarkRunner>(State);
 }
 
-void ExegesisTarget::randomizeMCOperand(const Instruction &Instr,
-                                        const Variable &Var,
-                                        MCOperand &AssignedValue,
-                                        const BitVector &ForbiddenRegs) const {
-  const Operand &Op = Instr.getPrimaryOperand(Var);
-  switch (Op.getExplicitOperandInfo().OperandType) {
-  case MCOI::OperandType::OPERAND_IMMEDIATE:
-    // FIXME: explore immediate values too.
-    AssignedValue = MCOperand::createImm(1);
-    break;
-  case MCOI::OperandType::OPERAND_REGISTER: {
-    assert(Op.isReg());
-    auto AllowedRegs = Op.getRegisterAliasing().sourceBits();
-    assert(AllowedRegs.size() == ForbiddenRegs.size());
-    for (auto I : ForbiddenRegs.set_bits())
-      AllowedRegs.reset(I);
-    AssignedValue = MCOperand::createReg(randomBit(AllowedRegs));
-    break;
-  }
-  default:
-    break;
-  }
-}
-
 static_assert(std::is_pod<PfmCountersInfo>::value,
               "We shouldn't have dynamic initialization here");
 const PfmCountersInfo PfmCountersInfo::Default = {nullptr, nullptr, nullptr,
index bd43009041054ff98ff7af4af0591b8bd5df245a..d9bddc56e8241d9bdd23607859601c2105243338 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "BenchmarkResult.h"
 #include "BenchmarkRunner.h"
+#include "Error.h"
 #include "LlvmState.h"
 #include "SnippetGenerator.h"
 #include "llvm/ADT/Triple.h"
@@ -108,12 +109,15 @@ public:
   virtual unsigned getMaxMemoryAccessSize() const { return 0; }
 
   // Assigns a random operand of the right type to variable Var.
-  // The default implementation only handles generic operand types.
-  // The target is responsible for handling any operand
-  // starting from OPERAND_FIRST_TARGET.
-  virtual void randomizeMCOperand(const Instruction &Instr, const Variable &Var,
-                                  MCOperand &AssignedValue,
-                                  const BitVector &ForbiddenRegs) const;
+  // The target is responsible for handling any operand starting from
+  // OPERAND_FIRST_TARGET.
+  virtual Error randomizeTargetMCOperand(const Instruction &Instr,
+                                         const Variable &Var,
+                                         MCOperand &AssignedValue,
+                                         const BitVector &ForbiddenRegs) const {
+    return make_error<Failure>(
+        "targets with target-specific operands should implement this");
+  }
 
   // Returns true if this instruction is supported as a back-to-back
   // instructions.
index cea8af0cf69c0e2a837e9baed49d68e719d129f1..a76e590cfa2c1eddb73f1f96c5b24a714e16ac11 100644 (file)
@@ -566,9 +566,9 @@ private:
 
   unsigned getMaxMemoryAccessSize() const override { return 64; }
 
-  void randomizeMCOperand(const Instruction &Instr, const Variable &Var,
-                          MCOperand &AssignedValue,
-                          const BitVector &ForbiddenRegs) const override;
+  Error randomizeTargetMCOperand(const Instruction &Instr, const Variable &Var,
+                                 MCOperand &AssignedValue,
+                                 const BitVector &ForbiddenRegs) const override;
 
   void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg,
                           unsigned Offset) const override;
@@ -644,24 +644,25 @@ unsigned ExegesisX86Target::getLoopCounterRegister(const Triple &TT) const {
   return kLoopCounterReg;
 }
 
-void ExegesisX86Target::randomizeMCOperand(
+Error ExegesisX86Target::randomizeTargetMCOperand(
     const Instruction &Instr, const Variable &Var, MCOperand &AssignedValue,
     const BitVector &ForbiddenRegs) const {
-  ExegesisTarget::randomizeMCOperand(Instr, Var, AssignedValue, ForbiddenRegs);
-
   const Operand &Op = Instr.getPrimaryOperand(Var);
   switch (Op.getExplicitOperandInfo().OperandType) {
   case X86::OperandType::OPERAND_ROUNDING_CONTROL:
     AssignedValue =
         MCOperand::createImm(randomIndex(X86::STATIC_ROUNDING::NO_EXC));
-    break;
+    return Error::success();
   case X86::OperandType::OPERAND_COND_CODE:
     AssignedValue =
         MCOperand::createImm(randomIndex(X86::CondCode::LAST_VALID_COND));
-    break;
+    return Error::success();
   default:
     break;
   }
+  return make_error<Failure>(
+      Twine("unimplemented operand type ")
+          .concat(Twine(Op.getExplicitOperandInfo().OperandType)));
 }
 
 void ExegesisX86Target::fillMemoryOperands(InstructionTemplate &IT,
index 808ea34e743722ec3f1eb0941ead91a7cc923df2..a833e1e22793b51444a1236cfc5553992db35f4a 100644 (file)
@@ -354,6 +354,17 @@ TEST_F(ParallelSnippetGeneratorTest, MemoryUse) {
   EXPECT_EQ(IT.getVariableValues()[5].getReg(), 0u);
 }
 
+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();
+  EXPECT_TRUE((bool)Err);
+  EXPECT_THAT(toString(std::move(Err)),
+              testing::HasSubstr("no available registers"));
+}
+
 class FakeSnippetGenerator : public SnippetGenerator {
 public:
   FakeSnippetGenerator(const LLVMState &State, const Options &Opts)