diff --git a/llvm/docs/CommandGuide/llvm-exegesis.rst b/llvm/docs/CommandGuide/llvm-exegesis.rst index d357c2ceea4189..e036757c69bdec 100644 --- a/llvm/docs/CommandGuide/llvm-exegesis.rst +++ b/llvm/docs/CommandGuide/llvm-exegesis.rst @@ -303,6 +303,22 @@ OPTIONS * ``measure``: Same as ``assemble-measured-code``, but also runs the measurement. * ``dry-run-measurement``: Same as measure, but does not actually execute the snippet. +.. option:: --serialize-benchmarks + + Generate a fully serialized benchmarks file, including the assembled object + files. This is useful to resume the measurement later with ``--run-measurement``. + +.. option:: --force-serialized-obj-compress-format=[zlib|zstd] + + When serializing benchmarks with ``--serialize-benchmarks``, always use the + compression format designated by this flag. + +.. option:: --run-measurement= + + Given a fully serialized benchmarks file generated after the + ``assembly-measured-code`` phase with ``--serialize-benchmarks``, resume the + measurement phase from it. + .. option:: --x86-lbr-sample-period= Specify the LBR sampling period - how many branches before we take a sample. diff --git a/llvm/include/llvm/Support/Compression.h b/llvm/include/llvm/Support/Compression.h index 2a8da9e96d356f..4441c3481e984c 100644 --- a/llvm/include/llvm/Support/Compression.h +++ b/llvm/include/llvm/Support/Compression.h @@ -15,6 +15,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/DataTypes.h" +#include "llvm/Support/YAMLTraits.h" namespace llvm { template class SmallVectorImpl; @@ -126,6 +127,16 @@ Error decompress(DebugCompressionType T, ArrayRef Input, } // End of namespace compression +namespace yaml { +// Related YAML traits. +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &Io, compression::Format &Format) { + Io.enumCase(Format, "zstd", compression::Format::Zstd); + Io.enumCase(Format, "zlib", compression::Format::Zlib); + } +}; +} // namespace yaml + } // End of namespace llvm #endif diff --git a/llvm/test/tools/llvm-exegesis/serialize-obj-file.test b/llvm/test/tools/llvm-exegesis/serialize-obj-file.test new file mode 100644 index 00000000000000..5b5a68e36035a5 --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/serialize-obj-file.test @@ -0,0 +1,41 @@ +# RUN: llvm-exegesis -mtriple=riscv64 -mcpu=sifive-p470 --opcode-name=SH3ADD --serialize-benchmarks --benchmark-phase=assemble-measured-code \ +# RUN: --mode=latency --benchmarks-file=%t.yaml +# RUN: FileCheck --input-file=%t.yaml %s --check-prefixes=CHECK,SERIALIZE +# RUN: llvm-exegesis -mtriple=riscv64 -mcpu=sifive-p470 --run-measurement=%t.yaml --mode=latency --benchmark-phase=dry-run-measurement --use-dummy-perf-counters \ +# RUN: --dump-object-to-disk=%t.o | FileCheck %s --check-prefixes=CHECK,DESERIALIZE +# RUN: llvm-objdump -d %t.o | FileCheck %s --check-prefix=OBJDUMP + +# We should not serialie benchmarks by default. +# RUN: llvm-exegesis -mtriple=riscv64 -mcpu=sifive-p470 --opcode-name=SH3ADD --benchmark-phase=assemble-measured-code --mode=latency | \ +# RUN: FileCheck %s --check-prefix=NO-SERIALIZE + +# We currently don't support serialization for repetition modes that require more than one snippets. +# RUN: not llvm-exegesis -mtriple=riscv64 -mcpu=sifive-p470 --opcode-name=SH3ADD --mode=latency --serialize-benchmarks --benchmark-phase=assemble-measured-code \ +# RUN: --repetition-mode=min 2>&1 | FileCheck %s --check-prefix=NOT-SUPPORTED +# RUN: not llvm-exegesis -mtriple=riscv64 -mcpu=sifive-p470 --opcode-name=SH3ADD --mode=latency --serialize-benchmarks --benchmark-phase=assemble-measured-code \ +# RUN: --repetition-mode=middle-half-loop 2>&1 | FileCheck %s --check-prefix=NOT-SUPPORTED +# RUN: not llvm-exegesis -mtriple=riscv64 -mcpu=sifive-p470 --opcode-name=SH3ADD --mode=latency --serialize-benchmarks --benchmark-phase=assemble-measured-code \ +# RUN: --repetition-mode=middle-half-duplicate 2>&1 | FileCheck %s --check-prefix=NOT-SUPPORTED + +# REQUIRES: riscv-registered-target && native-registered-exegesis-target +# REQUIRES: zlib || zstd + +# A round-trip test for serialize/deserialize benchmarks. + +# CHECK: mode: latency +# CHECK: instructions: +# CHECK-NEXT: - 'SH3ADD X{{.*}} X{{.*}} X{{.*}}' +# CHECK: cpu_name: sifive-p470 +# CHECK-NEXT: llvm_triple: riscv64 +# CHECK-NEXT: min_instructions: 10000 +# CHECK-NEXT: measurements: [] +# SERIALIZE: error: actual measurements skipped. +# DESERIALIZE: error: '' +# CHECK: info: Repeating a single explicitly serial instruction + +# OBJDUMP: sh3add + +# Negative tests. + +# NOT-SUPPORTED: -serialize-benchmarks currently only supports -repetition-mode of loop and duplicate. +# NO-SERIALIZE-NOT: object_file: diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp index 84dc23b343c6c0..cdda71d6431948 100644 --- a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp @@ -15,10 +15,13 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/bit.h" #include "llvm/ObjectYAML/YAML.h" +#include "llvm/Support/Base64.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" static constexpr const char kIntegerPrefix[] = "i_0x"; @@ -27,6 +30,13 @@ static constexpr const char kInvalidOperand[] = "INVALID"; namespace llvm { +static cl::opt ForceObjectFileCompressionFormat( + "force-serialized-obj-compress-format", cl::Hidden, + cl::desc( + "Force to use this compression format for serialized object files."), + cl::values(clEnumValN(compression::Format::Zstd, "zstd", "Using Zstandard"), + clEnumValN(compression::Format::Zlib, "zlib", "Using LibZ"))); + namespace { // A mutable struct holding an LLVMState that can be passed through the @@ -288,6 +298,33 @@ template <> struct MappingContextTraits { } }; +template <> struct MappingTraits { + struct NormalizedBase64Binary { + std::string Base64Str; + + NormalizedBase64Binary(IO &) {} + NormalizedBase64Binary(IO &, const std::vector &Data) + : Base64Str(llvm::encodeBase64(Data)) {} + + std::vector denormalize(IO &) { + std::vector Buffer; + if (Error E = llvm::decodeBase64(Base64Str, Buffer)) + report_fatal_error(std::move(E)); + + StringRef Data(Buffer.data(), Buffer.size()); + return std::vector(Data.bytes_begin(), Data.bytes_end()); + } + }; + + static void mapping(IO &Io, exegesis::Benchmark::ObjectFile &Obj) { + Io.mapRequired("compression", Obj.CompressionFormat); + Io.mapRequired("original_size", Obj.UncompressedSize); + MappingNormalization> + ObjFileString(Io, Obj.CompressedBytes); + Io.mapRequired("compressed_bytes", ObjFileString->Base64Str); + } +}; + template <> struct MappingContextTraits { struct NormalizedBinary { NormalizedBinary(IO &io) {} @@ -325,9 +362,11 @@ template <> struct MappingContextTraits { Io.mapRequired("error", Obj.Error); Io.mapOptional("info", Obj.Info); // AssembledSnippet - MappingNormalization> BinaryString( + MappingNormalization> SnippetString( Io, Obj.AssembledSnippet); - Io.mapOptional("assembled_snippet", BinaryString->Binary); + Io.mapOptional("assembled_snippet", SnippetString->Binary); + // ObjectFile + Io.mapOptional("object_file", Obj.ObjFile); } }; @@ -364,6 +403,52 @@ Benchmark::readTriplesAndCpusFromYamls(MemoryBufferRef Buffer) { return Result; } +Error Benchmark::setObjectFile(StringRef RawBytes) { + SmallVector CompressedBytes; + llvm::compression::Format CompressionFormat; + + auto isFormatAvailable = [](llvm::compression::Format F) -> bool { + switch (F) { + case compression::Format::Zstd: + return compression::zstd::isAvailable(); + case compression::Format::Zlib: + return compression::zlib::isAvailable(); + } + }; + if (ForceObjectFileCompressionFormat.getNumOccurrences() > 0) { + CompressionFormat = ForceObjectFileCompressionFormat; + if (!isFormatAvailable(CompressionFormat)) + return make_error( + "The designated compression format is not available.", + inconvertibleErrorCode()); + } else if (isFormatAvailable(compression::Format::Zstd)) { + // Try newer compression algorithm first. + CompressionFormat = compression::Format::Zstd; + } else if (isFormatAvailable(compression::Format::Zlib)) { + CompressionFormat = compression::Format::Zlib; + } else { + return make_error( + "None of the compression methods is available.", + inconvertibleErrorCode()); + } + + switch (CompressionFormat) { + case compression::Format::Zstd: + compression::zstd::compress({RawBytes.bytes_begin(), RawBytes.bytes_end()}, + CompressedBytes); + break; + case compression::Format::Zlib: + compression::zlib::compress({RawBytes.bytes_begin(), RawBytes.bytes_end()}, + CompressedBytes); + break; + } + + ObjFile = {CompressionFormat, + RawBytes.size(), + {CompressedBytes.begin(), CompressedBytes.end()}}; + return Error::success(); +} + Expected Benchmark::readYaml(const LLVMState &State, MemoryBufferRef Buffer) { yaml::Input Yin(Buffer); diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h index 5480d856168784..2094334d754fa0 100644 --- a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h @@ -21,6 +21,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/YAMLTraits.h" #include #include @@ -77,6 +78,11 @@ struct BenchmarkKey { uintptr_t SnippetAddress = 0; // The register that should be used to hold the loop counter. unsigned LoopRegister; + + bool operator==(const BenchmarkKey &RHS) const { + return Config == RHS.Config && + Instructions[0].getOpcode() == RHS.Instructions[0].getOpcode(); + } }; struct BenchmarkMeasure { @@ -123,6 +129,16 @@ struct Benchmark { std::string Error; std::string Info; std::vector AssembledSnippet; + + struct ObjectFile { + llvm::compression::Format CompressionFormat; + size_t UncompressedSize = 0; + std::vector CompressedBytes; + + bool isValid() const { return UncompressedSize && CompressedBytes.size(); } + }; + std::optional ObjFile; + // How to aggregate measurements. enum ResultAggregationModeE { Min, Max, Mean, MinVariance }; @@ -133,6 +149,10 @@ struct Benchmark { Benchmark &operator=(const Benchmark &) = delete; Benchmark &operator=(Benchmark &&) = delete; + // Compress raw object file bytes and assign the result and compression type + // to CompressedObjectFile and ObjFileCompression, respectively. + class Error setObjectFile(StringRef RawBytes); + // Read functions. static Expected readYaml(const LLVMState &State, MemoryBufferRef Buffer); diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp index cc46f7feb6cf7f..aaf8dac8945d8f 100644 --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX +#include "llvm/Support/CommandLine.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" @@ -51,6 +52,14 @@ #endif // __linux__ namespace llvm { + +static cl::opt + SerializeBenchmarks("serialize-benchmarks", + cl::desc("Generate fully-serialized benchmarks " + "that can later be deserialized and " + "resuming the measurement."), + cl::init(false)); + namespace exegesis { BenchmarkRunner::BenchmarkRunner(const LLVMState &State, Benchmark::ModeE Mode, @@ -619,6 +628,7 @@ Expected> BenchmarkRunner::assembleSnippet( Expected BenchmarkRunner::getRunnableConfiguration( const BenchmarkCode &BC, unsigned MinInstructions, unsigned LoopBodySize, + Benchmark::RepetitionModeE RepetitionMode, const SnippetRepetitor &Repetitor) const { RunnableConfiguration RC; @@ -663,12 +673,57 @@ BenchmarkRunner::getRunnableConfiguration( LoopBodySize, GenerateMemoryInstructions); if (Error E = Snippet.takeError()) return std::move(E); + + // Generate fully-serialized benchmarks. + if (SerializeBenchmarks) { + if (RepetitionMode != Benchmark::Loop && + RepetitionMode != Benchmark::Duplicate) + return make_error( + "-serialize-benchmarks currently only supports -repetition-mode " + "of loop and duplicate."); + + if (Error E = BenchmarkResult.setObjectFile(*Snippet)) + return std::move(E); + } + RC.ObjectFile = getObjectFromBuffer(*Snippet); } return std::move(RC); } +Expected +BenchmarkRunner::getRunnableConfiguration(Benchmark &&B) const { + assert(B.ObjFile.has_value() && B.ObjFile->isValid() && + "No serialized obejct file is attached?"); + const Benchmark::ObjectFile &ObjFile = *B.ObjFile; + SmallVector DecompressedObjFile; + switch (ObjFile.CompressionFormat) { + case compression::Format::Zstd: + if (!compression::zstd::isAvailable()) + return make_error("zstd is not available for decompression.", + inconvertibleErrorCode()); + if (Error E = compression::zstd::decompress(ObjFile.CompressedBytes, + DecompressedObjFile, + ObjFile.UncompressedSize)) + return std::move(E); + break; + case compression::Format::Zlib: + if (!compression::zlib::isAvailable()) + return make_error("zlib is not available for decompression.", + inconvertibleErrorCode()); + if (Error E = compression::zlib::decompress(ObjFile.CompressedBytes, + DecompressedObjFile, + ObjFile.UncompressedSize)) + return std::move(E); + break; + } + + StringRef Buffer(reinterpret_cast(DecompressedObjFile.begin()), + DecompressedObjFile.size()); + return RunnableConfiguration{std::move(B), getObjectFromBuffer(Buffer)}; +} + Expected> BenchmarkRunner::createFunctionExecutor( object::OwningBinary ObjectFile, diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h index e688b814d1c83d..ef9446bdd5bbe8 100644 --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -54,18 +54,25 @@ class BenchmarkRunner { RunnableConfiguration &operator=(RunnableConfiguration &&) = delete; RunnableConfiguration &operator=(const RunnableConfiguration &) = delete; + Benchmark BenchmarkResult; + object::OwningBinary ObjectFile; + private: RunnableConfiguration() = default; - Benchmark BenchmarkResult; - object::OwningBinary ObjectFile; + RunnableConfiguration(Benchmark &&B, + object::OwningBinary &&OF) + : BenchmarkResult(std::move(B)), ObjectFile(std::move(OF)) {} }; Expected getRunnableConfiguration(const BenchmarkCode &Configuration, unsigned MinInstructions, unsigned LoopUnrollFactor, + Benchmark::RepetitionModeE RepetitionMode, const SnippetRepetitor &Repetitor) const; + Expected getRunnableConfiguration(Benchmark &&B) const; + std::pair runConfiguration(RunnableConfiguration &&RC, const std::optional &DumpFile, diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp index 07bd44ee64f1f2..4b18eb96f02e71 100644 --- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -114,8 +114,7 @@ static cl::opt BenchmarkMeasurementsPrintProgress( static cl::opt BenchmarkPhaseSelector( "benchmark-phase", - cl::desc( - "it is possible to stop the benchmarking process after some phase"), + cl::desc("Stop the benchmarking process after some phase"), cl::cat(BenchmarkOptions), cl::values( clEnumValN(BenchmarkPhaseSelectorE::PrepareSnippet, "prepare-snippet", @@ -138,6 +137,13 @@ static cl::opt BenchmarkPhaseSelector( "Same as measure, but does not actually execute the snippet")), cl::init(BenchmarkPhaseSelectorE::Measure)); +static cl::opt RunMeasurement( + "run-measurement", + cl::desc( + "Run measurement phase with a benchmarks file generated previously"), + cl::cat(BenchmarkOptions), cl::value_desc(""), + cl::init("")); + static cl::opt UseDummyPerfCounters("use-dummy-perf-counters", cl::desc("Do not read real performance counters, use " @@ -400,11 +406,55 @@ generateSnippets(const LLVMState &State, unsigned Opcode, return Benchmarks; } -static void runBenchmarkConfigurations( - const LLVMState &State, ArrayRef Configurations, +static void deserializeRunnableConfigurations( + std::vector &Benchmarks, const BenchmarkRunner &Runner, + std::vector &RunnableConfigs, + SmallVectorImpl &Repetitions) { + for (unsigned I = 0U, E = Benchmarks.size(); I < E; ++I) { + // Reset any previous error. + Benchmarks[I].Error.clear(); + + RunnableConfigs.emplace_back( + ExitOnErr(Runner.getRunnableConfiguration(std::move(Benchmarks[I])))); + if (I > 0 && RunnableConfigs[I].BenchmarkResult.Key == + RunnableConfigs[I - 1].BenchmarkResult.Key) { + // Extend the current end index in Repetitions. + Repetitions.back() = RunnableConfigs.size(); + } else { + // Append a new entry into Repetitions. + Repetitions.push_back(RunnableConfigs.size()); + } + } +} + +static void collectRunnableConfigurations( + ArrayRef Configurations, ArrayRef> Repetitors, - const BenchmarkRunner &Runner) { - assert(!Configurations.empty() && "Don't have any configurations to run."); + const BenchmarkRunner &Runner, + std::vector &RunnableConfigs, + SmallVectorImpl &Repetitions) { + + SmallVector MinInstructionCounts = {MinInstructions}; + if (RepetitionMode == Benchmark::MiddleHalfDuplicate || + RepetitionMode == Benchmark::MiddleHalfLoop) + MinInstructionCounts.push_back(MinInstructions * 2); + + for (const BenchmarkCode &Conf : Configurations) { + for (const auto &Repetitor : Repetitors) { + for (unsigned IterationRepetitions : MinInstructionCounts) + RunnableConfigs.emplace_back(ExitOnErr(Runner.getRunnableConfiguration( + Conf, IterationRepetitions, LoopBodySize, RepetitionMode, + *Repetitor))); + } + Repetitions.emplace_back(RunnableConfigs.size()); + } +} + +static void runBenchmarkConfigurations( + const LLVMState &State, + std::vector &RunnableConfigs, + ArrayRef Repetitions, const BenchmarkRunner &Runner) { + assert(!RunnableConfigs.empty() && "Don't have any configurations to run."); std::optional FileOstr; if (BenchmarkFile != "-") { int ResultFD = 0; @@ -418,43 +468,38 @@ static void runBenchmarkConfigurations( std::optional> Meter; if (BenchmarkMeasurementsPrintProgress) - Meter.emplace(Configurations.size()); + Meter.emplace(RunnableConfigs.size()); - SmallVector MinInstructionCounts = {MinInstructions}; - if (RepetitionMode == Benchmark::MiddleHalfDuplicate || - RepetitionMode == Benchmark::MiddleHalfLoop) - MinInstructionCounts.push_back(MinInstructions * 2); + std::optional DumpFile; + if (DumpObjectToDisk.getNumOccurrences()) + DumpFile = DumpObjectToDisk; - for (const BenchmarkCode &Conf : Configurations) { + const std::optional BenchmarkCPU = + BenchmarkProcessCPU == -1 ? std::nullopt + : std::optional(BenchmarkProcessCPU.getValue()); + + unsigned StartIdx = 0; + for (unsigned EndIdx : Repetitions) { ProgressMeter<>::ProgressMeterStep MeterStep(Meter ? &*Meter : nullptr); SmallVector AllResults; - for (const std::unique_ptr &Repetitor : - Repetitors) { - for (unsigned IterationRepetitions : MinInstructionCounts) { - auto RC = ExitOnErr(Runner.getRunnableConfiguration( - Conf, IterationRepetitions, LoopBodySize, *Repetitor)); - std::optional DumpFile; - if (DumpObjectToDisk.getNumOccurrences()) - DumpFile = DumpObjectToDisk; - const std::optional BenchmarkCPU = - BenchmarkProcessCPU == -1 - ? std::nullopt - : std::optional(BenchmarkProcessCPU.getValue()); - auto [Err, BenchmarkResult] = - Runner.runConfiguration(std::move(RC), DumpFile, BenchmarkCPU); - if (Err) { - // Errors from executing the snippets are fine. - // All other errors are a framework issue and should fail. - if (!Err.isA()) - ExitOnErr(std::move(Err)); - - BenchmarkResult.Error = toString(std::move(Err)); + for (unsigned Idx = StartIdx; Idx < EndIdx; ++Idx) { + auto RC = std::move(RunnableConfigs[Idx]); + auto [Err, BenchmarkResult] = + Runner.runConfiguration(std::move(RC), DumpFile, BenchmarkCPU); + if (Err) { + // Errors from executing the snippets are fine. + // All other errors are a framework issue and should fail. + if (!Err.isA()) { + llvm::errs() << "llvm-exegesis error: " << toString(std::move(Err)); + exit(1); } - AllResults.push_back(std::move(BenchmarkResult)); + BenchmarkResult.Error = toString(std::move(Err)); } - } + AllResults.push_back(std::move(BenchmarkResult)); + } + StartIdx = EndIdx; Benchmark &Result = AllResults.front(); // If any of our measurements failed, pretend they all have failed. @@ -520,77 +565,94 @@ void benchmarkMain() { ExitWithError("cannot create benchmark runner"); } - const auto Opcodes = getOpcodesOrDie(State); - std::vector Configurations; - - unsigned LoopRegister = - State.getExegesisTarget().getDefaultLoopCounterRegister( - State.getTargetMachine().getTargetTriple()); - - if (Opcodes.empty()) { - Configurations = ExitOnErr(readSnippets(State, SnippetsFile)); - for (const auto &Configuration : Configurations) { - if (ExecutionMode != BenchmarkRunner::ExecutionModeE::SubProcess && - (Configuration.Key.MemoryMappings.size() != 0 || - Configuration.Key.MemoryValues.size() != 0 || - Configuration.Key.SnippetAddress != 0)) - ExitWithError("Memory and snippet address annotations are only " - "supported in subprocess " - "execution mode"); - } - LoopRegister = Configurations[0].Key.LoopRegister; - } + std::vector RunnableConfigs; + SmallVector Repetitions; - SmallVector, 2> Repetitors; - if (RepetitionMode != Benchmark::RepetitionModeE::AggregateMin) - Repetitors.emplace_back( - SnippetRepetitor::Create(RepetitionMode, State, LoopRegister)); - else { - for (Benchmark::RepetitionModeE RepMode : - {Benchmark::RepetitionModeE::Duplicate, - Benchmark::RepetitionModeE::Loop}) - Repetitors.emplace_back( - SnippetRepetitor::Create(RepMode, State, LoopRegister)); - } + // Write to standard output if file is not set. + if (BenchmarkFile.empty()) + BenchmarkFile = "-"; - BitVector AllReservedRegs; - for (const std::unique_ptr &Repetitor : Repetitors) - AllReservedRegs |= Repetitor->getReservedRegs(); - - if (!Opcodes.empty()) { - for (const unsigned Opcode : Opcodes) { - // Ignore instructions without a sched class if - // -ignore-invalid-sched-class is passed. - if (IgnoreInvalidSchedClass && - State.getInstrInfo().get(Opcode).getSchedClass() == 0) { - errs() << State.getInstrInfo().getName(Opcode) - << ": ignoring instruction without sched class\n"; - continue; + if (!RunMeasurement.empty()) { + // Right now we only support resuming before the measurement phase. + auto ErrOrBuffer = + MemoryBuffer::getFileOrSTDIN(RunMeasurement, /*IsText=*/true); + if (!ErrOrBuffer) + report_fatal_error(errorCodeToError(ErrOrBuffer.getError())); + + std::vector Benchmarks = + ExitOnErr(Benchmark::readYamls(State, **ErrOrBuffer)); + deserializeRunnableConfigurations(Benchmarks, *Runner, RunnableConfigs, + Repetitions); + } else { + const auto Opcodes = getOpcodesOrDie(State); + std::vector Configurations; + + unsigned LoopRegister = + State.getExegesisTarget().getDefaultLoopCounterRegister( + State.getTargetMachine().getTargetTriple()); + + if (Opcodes.empty()) { + Configurations = ExitOnErr(readSnippets(State, SnippetsFile)); + for (const auto &Configuration : Configurations) { + if (ExecutionMode != BenchmarkRunner::ExecutionModeE::SubProcess && + (Configuration.Key.MemoryMappings.size() != 0 || + Configuration.Key.MemoryValues.size() != 0 || + Configuration.Key.SnippetAddress != 0)) + ExitWithError("Memory and snippet address annotations are only " + "supported in subprocess " + "execution mode"); } + LoopRegister = Configurations[0].Key.LoopRegister; + } + SmallVector, 2> Repetitors; + if (RepetitionMode != Benchmark::RepetitionModeE::AggregateMin) + Repetitors.emplace_back( + SnippetRepetitor::Create(RepetitionMode, State, LoopRegister)); + else { + for (Benchmark::RepetitionModeE RepMode : + {Benchmark::RepetitionModeE::Duplicate, + Benchmark::RepetitionModeE::Loop}) + Repetitors.emplace_back( + SnippetRepetitor::Create(RepMode, State, LoopRegister)); + } - auto ConfigsForInstr = generateSnippets(State, Opcode, AllReservedRegs); - if (!ConfigsForInstr) { - logAllUnhandledErrors( - ConfigsForInstr.takeError(), errs(), - Twine(State.getInstrInfo().getName(Opcode)).concat(": ")); - continue; + BitVector AllReservedRegs; + for (const std::unique_ptr &Repetitor : Repetitors) + AllReservedRegs |= Repetitor->getReservedRegs(); + + if (!Opcodes.empty()) { + for (const unsigned Opcode : Opcodes) { + // Ignore instructions without a sched class if + // -ignore-invalid-sched-class is passed. + if (IgnoreInvalidSchedClass && + State.getInstrInfo().get(Opcode).getSchedClass() == 0) { + errs() << State.getInstrInfo().getName(Opcode) + << ": ignoring instruction without sched class\n"; + continue; + } + + auto ConfigsForInstr = generateSnippets(State, Opcode, AllReservedRegs); + if (!ConfigsForInstr) { + logAllUnhandledErrors( + ConfigsForInstr.takeError(), errs(), + Twine(State.getInstrInfo().getName(Opcode)).concat(": ")); + continue; + } + std::move(ConfigsForInstr->begin(), ConfigsForInstr->end(), + std::back_inserter(Configurations)); } - std::move(ConfigsForInstr->begin(), ConfigsForInstr->end(), - std::back_inserter(Configurations)); } - } + if (MinInstructions == 0) { + ExitOnErr.setBanner("llvm-exegesis: "); + ExitWithError("--min-instructions must be greater than zero"); + } - if (MinInstructions == 0) { - ExitOnErr.setBanner("llvm-exegesis: "); - ExitWithError("--min-instructions must be greater than zero"); + collectRunnableConfigurations(Configurations, Repetitors, *Runner, + RunnableConfigs, Repetitions); } - // Write to standard output if file is not set. - if (BenchmarkFile.empty()) - BenchmarkFile = "-"; - - if (!Configurations.empty()) - runBenchmarkConfigurations(State, Configurations, Repetitors, *Runner); + if (!RunnableConfigs.empty()) + runBenchmarkConfigurations(State, RunnableConfigs, Repetitions, *Runner); pfm::pfmTerminate(); }