diff --git a/llvm/test/tools/llvm-exegesis/AArch64/all-opcodes.test b/llvm/test/tools/llvm-exegesis/AArch64/all-opcodes.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/AArch64/all-opcodes.test @@ -0,0 +1,10 @@ +# Test that we can just use --opcode-index=-1 to generate snippets for +# all supported opcodes and gracefully handle unsupported ones. + +# RUN: llvm-exegesis --mtriple=aarch64-linux-gnu --mcpu=cortex-a55 --benchmark-phase=prepare-and-assemble-snippet \ +# RUN: --mode=latency --opcode-index=-1 | FileCheck %s +# RUN: llvm-exegesis --mtriple=aarch64-linux-gnu --mcpu=cortex-a55 --benchmark-phase=prepare-and-assemble-snippet \ +# RUN: --mode=uops --opcode-index=-1 | FileCheck %s + +# 100 means "quite a lot" +# CHECK-COUNT-100: assembled_snippet: {{.*}} diff --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h --- a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h +++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.h @@ -107,6 +107,9 @@ const BitVector &ForbiddenRegs, InstructionTemplate &IT); +// Sanity check generated instruction. +Error validateGeneratedInstruction(const LLVMState &State, const MCInst &Inst); + } // namespace exegesis } // namespace llvm diff --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp --- a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp +++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Program.h" @@ -77,9 +78,12 @@ BC.Info = CT.Info; BC.Key.Instructions.reserve(CT.Instructions.size()); for (InstructionTemplate &IT : CT.Instructions) { - if (auto error = randomizeUnsetVariables(State, ForbiddenRegs, IT)) - return error; - BC.Key.Instructions.push_back(IT.build()); + if (auto Error = randomizeUnsetVariables(State, ForbiddenRegs, IT)) + return Error; + MCInst Inst = IT.build(); + if (auto Error = validateGeneratedInstruction(State, Inst)) + return Error; + BC.Key.Instructions.push_back(Inst); } if (CT.ScratchSpacePointerInReg) BC.LiveIns.push_back(CT.ScratchSpacePointerInReg); @@ -282,5 +286,21 @@ return Error::success(); } +Error validateGeneratedInstruction(const LLVMState &State, const MCInst &Inst) { + for (const auto &Operand : Inst) { + if (!Operand.isValid()) { + // Mention the particular opcode - it is not necessarily the "main" + // opcode being benchmarked by this snippet. For example, serial snippet + // generator uses one more opcode when in SERIAL_VIA_NON_MEMORY_INSTR + // execution mode. + const auto OpcodeName = State.getInstrInfo().getName(Inst.getOpcode()); + return make_error("Not all operands were initialized by the " + "snippet generator for " + + OpcodeName + " opcode."); + } + } + return Error::success(); +} + } // namespace exegesis } // namespace llvm