diff --git a/llvm/docs/CommandGuide/llvm-exegesis.rst b/llvm/docs/CommandGuide/llvm-exegesis.rst --- a/llvm/docs/CommandGuide/llvm-exegesis.rst +++ b/llvm/docs/CommandGuide/llvm-exegesis.rst @@ -357,10 +357,11 @@ were measured for, but if you want to analyze them for some other combination (specified via `-mtriple`/`-mcpu`), you can pass this flag. -.. option:: --dump-object-to-disk=true +.. option:: --dump-object-to-disk= - If set, llvm-exegesis will dump the generated code to a temporary file to - enable code inspection. Disabled by default. + If set, llvm-exegesis will dump the generated code to file. If multiple files + are to be written, they will have names /path/to/file-OPCODE-REPETITOR.o with + `-OPCODE` and `-REPETITOR` parts omitted if always the same. .. option:: --use-dummy-perf-counters diff --git a/llvm/test/tools/llvm-exegesis/X86/latency/dump-object-to-disk.s b/llvm/test/tools/llvm-exegesis/X86/latency/dump-object-to-disk.s --- a/llvm/test/tools/llvm-exegesis/X86/latency/dump-object-to-disk.s +++ b/llvm/test/tools/llvm-exegesis/X86/latency/dump-object-to-disk.s @@ -1,24 +1,102 @@ -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=ADDPSrr -repetition-mode=duplicate -dump-object-to-disk 2>&1 | FileCheck %s --check-prefix=CHECK-ON -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=ADDPSrr -repetition-mode=loop -dump-object-to-disk 2>&1 | FileCheck %s --check-prefix=CHECK-ON -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=ADDPSrr -repetition-mode=duplicate 2>&1 | FileCheck %s --check-prefix=CHECK-OFF -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=ADDPSrr -repetition-mode=loop 2>&1 | FileCheck %s --check-prefix=CHECK-OFF -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=ADDPSrr -repetition-mode=duplicate 2>&1 | FileCheck %s --check-prefix=CHECK-OFF -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=ADDPSrr -repetition-mode=loop 2>&1 | FileCheck %s --check-prefix=CHECK-OFF - -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=prepare-and-assemble-snippet -opcode-name=ADDPSrr -repetition-mode=duplicate 2>&1 | FileCheck %s --check-prefix=CHECK-OFF -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=prepare-and-assemble-snippet -opcode-name=ADDPSrr -repetition-mode=loop 2>&1 | FileCheck %s --check-prefix=CHECK-OFF -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=prepare-and-assemble-snippet -opcode-name=ADDPSrr -repetition-mode=duplicate 2>&1 | FileCheck %s --check-prefix=CHECK-OFF -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=prepare-and-assemble-snippet -opcode-name=ADDPSrr -repetition-mode=loop 2>&1 | FileCheck %s --check-prefix=CHECK-OFF - -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=prepare-snippet -opcode-name=ADDPSrr -repetition-mode=duplicate 2>&1 | FileCheck %s --check-prefix=CHECK-OFF -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=prepare-snippet -opcode-name=ADDPSrr -repetition-mode=loop 2>&1 | FileCheck %s --check-prefix=CHECK-OFF -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=prepare-snippet -opcode-name=ADDPSrr -repetition-mode=duplicate 2>&1 | FileCheck %s --check-prefix=CHECK-OFF -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=prepare-snippet -opcode-name=ADDPSrr -repetition-mode=loop 2>&1 | FileCheck %s --check-prefix=CHECK-OFF - -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=ADDPSrr -repetition-mode=duplicate -dump-object-to-disk=%t.mydmpfile.o 2>&1 | FileCheck %s --check-prefix=DUMP-FILE-NAME -# RUN: llvm-objdump -d %t.mydmpfile.o | FileCheck %s --check-prefix=ASM -CHECK-ON: Check generated assembly with -CHECK-OFF-NOT: Check generated assembly with -DUMP-FILE-NAME: Check generated assembly with: {{.*}}objdump -d {{.*}}mydmpfile +# This test requires -debug-only=... command line option. +# This test uses "mkdir -p". +REQUIRES: asserts, system-linux + +# Check that nothing is dumped if not requested. + +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -opcode-name=ADDPSrr -benchmark-phase=assemble-measured-code 2>&1 \ +RUN: | FileCheck %s --check-prefix=CHECK-OFF + +# Check various benchmark phases. +# An object is never dumped if benchmark phase is below "assemble-measured-code". + +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.o -benchmark-phase=prepare-snippet -opcode-name=ADDPSrr 2>&1 \ +RUN: | FileCheck %s --check-prefix=CHECK-OFF +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.o -benchmark-phase=prepare-and-assemble-snippet -opcode-name=ADDPSrr 2>&1 \ +RUN: | FileCheck %s --check-prefix=CHECK-OFF + +# Check generating multiple output names. +# In the most general case, -dump-object-to-disk=/tmp/snippet.o results in /tmp/snippet--.o names. +# If only one OPCODE or only one REPETITOR is dumped, the corresponding infix is omitted. + +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.o -benchmark-phase=assemble-measured-code \ +RUN: -opcode-name=ADDPSrr -repetition-mode=loop 2>&1 \ +RUN: | FileCheck %s --implicit-check-not="Output file name is" --check-prefix=CHECK-ONE + +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.o -benchmark-phase=assemble-measured-code \ +RUN: -opcode-name=ADDPSrr -repetition-mode=duplicate 2>&1 \ +RUN: | FileCheck %s --implicit-check-not="Output file name is" --check-prefix=CHECK-ONE + +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.o -benchmark-phase=assemble-measured-code \ +RUN: -opcode-name=ADDPSrr -repetition-mode=min 2>&1 \ +RUN: | FileCheck %s --implicit-check-not="Output file name is" --check-prefix=CHECK-REPETITORS + +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.o -benchmark-phase=assemble-measured-code \ +RUN: -opcode-name=ADDPSrr,ADDPDrr -repetition-mode=loop 2>&1 \ +RUN: | FileCheck %s --implicit-check-not="Output file name is" --check-prefix=CHECK-OPCODES + +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.o -benchmark-phase=assemble-measured-code \ +RUN: -opcode-name=ADDPSrr,ADDPDrr -repetition-mode=duplicate 2>&1 \ +RUN: | FileCheck %s --implicit-check-not="Output file name is" --check-prefix=CHECK-OPCODES + +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.o -benchmark-phase=assemble-measured-code \ +RUN: -opcode-name=ADDPSrr,ADDPDrr -repetition-mode=min 2>&1 \ +RUN: | FileCheck %s --implicit-check-not="Output file name is" --check-prefix=CHECK-OPCODES-REPETITORS + +# Check handling of multiple configs per opcode +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.o -benchmark-phase=assemble-measured-code \ +RUN: -opcode-name=SETCCr -max-configs-per-opcode=3 -repetition-mode=min 2>&1 \ +RUN: | FileCheck %s --implicit-check-not="Output file name is" --check-prefix=CHECK-CONFIGS-REPETITORS + +# Corner case: cannot ensure no dots in %t, so only check the dot-in-directory-name case for now. +RUN: mkdir -p "%t-snippet.dir" +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t-snippet.dir/file -benchmark-phase=assemble-measured-code \ +RUN: -opcode-name=ADDPSrr,ADDPDrr -repetition-mode=loop 2>&1 \ +RUN: | FileCheck %s --implicit-check-not="Output file name is" --check-prefix=CHECK-DOT-IN-DIR + +CHECK-OFF-NOT: Output file name is + +CHECK-ONE: Output file name is {{.*}}-snippet.o + +CHECK-OPCODES: Output file name is {{.*}}-snippet-ADDPSrr.o +CHECK-OPCODES: Output file name is {{.*}}-snippet-ADDPDrr.o + +CHECK-REPETITORS: Output file name is {{.*}}-snippet-dup.o +CHECK-REPETITORS: Output file name is {{.*}}-snippet-loop.o + +CHECK-OPCODES-REPETITORS: Output file name is {{.*}}-snippet-ADDPSrr-dup.o +CHECK-OPCODES-REPETITORS: Output file name is {{.*}}-snippet-ADDPSrr-loop.o +CHECK-OPCODES-REPETITORS: Output file name is {{.*}}-snippet-ADDPDrr-dup.o +CHECK-OPCODES-REPETITORS: Output file name is {{.*}}-snippet-ADDPDrr-loop.o + +CHECK-CONFIGS-REPETITORS: Output file name is {{.*}}-snippet-SETCCr-dup.o +CHECK-CONFIGS-REPETITORS: Output file name is {{.*}}-snippet-SETCCr-loop.o +CHECK-CONFIGS-REPETITORS: Output file name is {{.*}}-snippet-SETCCr-1-dup.o +CHECK-CONFIGS-REPETITORS: Output file name is {{.*}}-snippet-SETCCr-1-loop.o +CHECK-CONFIGS-REPETITORS: Output file name is {{.*}}-snippet-SETCCr-2-dup.o +CHECK-CONFIGS-REPETITORS: Output file name is {{.*}}-snippet-SETCCr-2-loop.o + +CHECK-DOT-IN-DIR: Output file name is {{.*}}-snippet.dir/file-ADDPSrr +CHECK-DOT-IN-DIR: Output file name is {{.*}}-snippet.dir/file-ADDPDrr + +# Check the file contents +RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -debug-only=exegesis-name-generator \ +RUN: -dump-object-to-disk=%t.mydmpfile.o -benchmark-phase=assemble-measured-code \ +RUN: -opcode-name=ADDPSrr -repetition-mode=duplicate 2>&1 \ +RUN: | FileCheck %s --check-prefix=DUMP-FILE-NAME +RUN: llvm-objdump -d %t.mydmpfile.o | FileCheck %s --check-prefix=ASM + +DUMP-FILE-NAME: Output file name is {{.*}}.mydmpfile.o ASM: addps ASM: retq diff --git a/llvm/test/tools/llvm-exegesis/X86/uops/uops-HLT.s b/llvm/test/tools/llvm-exegesis/X86/uops/uops-HLT.s --- a/llvm/test/tools/llvm-exegesis/X86/uops/uops-HLT.s +++ b/llvm/test/tools/llvm-exegesis/X86/uops/uops-HLT.s @@ -1,4 +1,4 @@ -# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=uops --benchmark-phase=assemble-measured-code --dump-object-to-disk=0 --repetition-mode=loop --loop-body-size=1000 --result-aggregation-mode=min --opcode-name=HLT --max-configs-per-opcode=8192 | FileCheck %s +# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=uops --benchmark-phase=assemble-measured-code --repetition-mode=loop --loop-body-size=1000 --result-aggregation-mode=min --opcode-name=HLT --max-configs-per-opcode=8192 | FileCheck %s # By definition, loop repetitor can not be used for terminator instructions. # Just check that we do not crash. diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h --- a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h @@ -51,6 +51,9 @@ // An opaque configuration, that can be used to separate several benchmarks of // the same instruction under different configurations. std::string Config; + // Index of this benchmark configuration among all benchmarks generated + // for the same instruction. + int Index; }; struct BenchmarkMeasure { diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -51,6 +51,10 @@ RunnableConfiguration &operator=(RunnableConfiguration &&) = delete; RunnableConfiguration &operator=(const RunnableConfiguration &) = delete; + const object::ObjectFile *getObjectFile() const { + return ObjectFile.getBinary(); + } + private: RunnableConfiguration() = default; @@ -63,9 +67,7 @@ unsigned NumRepetitions, unsigned LoopUnrollFactor, const SnippetRepetitor &Repetitor) const; - Expected - runConfiguration(RunnableConfiguration &&RC, - const std::optional &DumpFile) const; + Expected runConfiguration(RunnableConfiguration &&RC) const; // Scratch space to run instructions that touch memory. struct ScratchSpace { @@ -115,9 +117,6 @@ unsigned MinInstructions, unsigned LoopBodySize) const; - Expected writeObjectFile(StringRef Buffer, - StringRef FileName) const; - const std::unique_ptr Scratch; }; diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -193,24 +193,11 @@ return std::move(RC); } -Expected BenchmarkRunner::runConfiguration( - RunnableConfiguration &&RC, - const std::optional &DumpFile) const { +Expected +BenchmarkRunner::runConfiguration(RunnableConfiguration &&RC) const { Benchmark &InstrBenchmark = RC.InstrBenchmark; object::OwningBinary &ObjectFile = RC.ObjectFile; - if (DumpFile && BenchmarkPhaseSelector > - BenchmarkPhaseSelectorE::PrepareAndAssembleSnippet) { - auto ObjectFilePath = - writeObjectFile(ObjectFile.getBinary()->getData(), *DumpFile); - if (Error E = ObjectFilePath.takeError()) { - InstrBenchmark.Error = toString(std::move(E)); - return std::move(InstrBenchmark); - } - outs() << "Check generated assembly with: /usr/bin/objdump -d " - << *ObjectFilePath << "\n"; - } - if (BenchmarkPhaseSelector < BenchmarkPhaseSelectorE::Measure) { InstrBenchmark.Error = "actual measurements skipped."; return std::move(InstrBenchmark); @@ -239,23 +226,6 @@ return std::move(InstrBenchmark); } -Expected -BenchmarkRunner::writeObjectFile(StringRef Buffer, StringRef FileName) const { - int ResultFD = 0; - SmallString<256> ResultPath = FileName; - if (Error E = errorCodeToError( - FileName.empty() ? sys::fs::createTemporaryFile("snippet", "o", - ResultFD, ResultPath) - : sys::fs::openFileForReadWrite( - FileName, ResultFD, sys::fs::CD_CreateAlways, - sys::fs::OF_None))) - return std::move(E); - raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); - OFS.write(Buffer.data(), Buffer.size()); - OFS.flush(); - return std::string(ResultPath.str()); -} - BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {} } // namespace exegesis diff --git a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt --- a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt +++ b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt @@ -53,6 +53,7 @@ CodeTemplate.cpp DisassemblerHelper.cpp Error.cpp + FileNameGenerator.cpp LatencyBenchmarkRunner.cpp LlvmState.cpp MCInstrDescView.cpp diff --git a/llvm/tools/llvm-exegesis/lib/FileNameGenerator.h b/llvm/tools/llvm-exegesis/lib/FileNameGenerator.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-exegesis/lib/FileNameGenerator.h @@ -0,0 +1,75 @@ +//===-- FileNameGenerator.h -------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file declares a helper class for generating distinct stable names for +/// multiple output files based on a single file name passed on command line. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_FILE_NAME_GENERATOR_H +#define LLVM_TOOLS_LLVM_EXEGESIS_FILE_NAME_GENERATOR_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +#include + +namespace llvm { +namespace exegesis { + +/// Helper class to generate stable output file names. +/// +/// Based on the file name passed via command line option and information +/// whether multiple opcode and/or repetitor infixes will be requested, this +/// class generates output file names, so that it is exactly as passed on the +/// command line in the trivial case (when only a single file is written) and +/// preventing one output file from overwriting another one dumped during the +/// same llvm-exegesis run (if multiple files are written). +class FileNameGenerator { +public: + /// Construct file name generator. + /// + /// `FileName` is the name passed via command line (empty string means + /// output is not requested). + /// `HasManyOpcodes` and `HasManyRepetitors` arguments control whether + /// output file name should contain corresponding infix. + FileNameGenerator(StringRef FileName, bool HasManyOpcodes, + bool HasManyRepetitors) + : FileNameGenerator(FileName, {HasManyOpcodes, HasManyRepetitors}) {} + + /// Returns output file name. + std::string getFileName(StringRef OpcodeLabel, + StringRef RepetitorLabel) const { + return getFileName({OpcodeLabel, RepetitorLabel}); + } + + /// Returns whether output files should be produced at all. + bool isEnabled() const { return IsEnabled; } + +private: + FileNameGenerator(StringRef FileName, ArrayRef IsVariableInfix); + + std::string getFileName(ArrayRef Infixes) const; + + bool IsEnabled; + std::string FileNamePrefix; + std::string FileNameSuffix; + SmallVector IsVariableInfix; + +#ifndef NDEBUG + // Infixes passed to the first getFileName() call. + mutable SmallVector FirstInfixes; +#endif +}; + +} // namespace exegesis +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_FILE_NAME_GENERATOR_H diff --git a/llvm/tools/llvm-exegesis/lib/FileNameGenerator.cpp b/llvm/tools/llvm-exegesis/lib/FileNameGenerator.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-exegesis/lib/FileNameGenerator.cpp @@ -0,0 +1,66 @@ +//===-- FileNameGenerator.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 "FileNameGenerator.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUG_TYPE "exegesis-name-generator" + +namespace llvm { +namespace exegesis { + +FileNameGenerator::FileNameGenerator(StringRef FileName, + ArrayRef IsVariableInfix) + : IsVariableInfix(IsVariableInfix) { + IsEnabled = !FileName.empty(); + if (!IsEnabled) + return; + + size_t DotPosition = FileName.rfind('.'); + size_t SlashPosition = FileName.find_last_of("/\\"); + + if (DotPosition == StringRef::npos || + (SlashPosition != StringRef::npos && DotPosition < SlashPosition)) { + // Two corner cases: + // 1) "/path/to/file" - no dots at all + // 2) "/dir/another_dir.ext/file" - the rightmost dot is in directory name + FileNamePrefix = FileName; + FileNameSuffix = ""; + } else { + // "/path/to/file.ext" -> "/path/to/file", ".ext" + FileNamePrefix = FileName.take_front(DotPosition); + FileNameSuffix = FileName.drop_front(DotPosition); + } +} + +std::string FileNameGenerator::getFileName(ArrayRef Infixes) const { + SmallVector Parts; + + Parts.push_back(FileNamePrefix); + for (unsigned I = 0, N = IsVariableInfix.size(); I < N; ++I) + if (IsVariableInfix[I]) + Parts.push_back(Infixes[I]); + +#ifndef NDEBUG + if (FirstInfixes.empty()) + FirstInfixes.assign(Infixes.begin(), Infixes.end()); + + for (unsigned I = 0, N = IsVariableInfix.size(); I < N; ++I) + assert(IsVariableInfix[I] || FirstInfixes[I] == Infixes[I]); +#endif + + const auto Result = join(Parts, "-") + FileNameSuffix; + LLVM_DEBUG(errs() << "Output file name is " << Result << "\n"); + return Result; +} + +} // 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 @@ -86,6 +86,7 @@ BC.Key.RegisterInitialValues = computeRegisterInitialValues(CT.Instructions); BC.Key.Config = CT.Config; + BC.Key.Index = Benchmarks.size(); Benchmarks.emplace_back(std::move(BC)); if (Benchmarks.size() >= Opts.MaxConfigsPerOpcode) { // We reached the number of allowed configs and return early. diff --git a/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h b/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h --- a/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h +++ b/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h @@ -42,6 +42,8 @@ unsigned MinInstructions, unsigned LoopBodySize) const = 0; + virtual std::string getName() const = 0; + explicit SnippetRepetitor(const LLVMState &State) : State(State) {} protected: diff --git a/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp b/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp --- a/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp +++ b/llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp @@ -44,6 +44,8 @@ // We're using no additional registers. return State.getRATC().emptyRegisters(); } + + std::string getName() const override { return "dup"; } }; class LoopSnippetRepetitor : public SnippetRepetitor { @@ -122,6 +124,8 @@ return State.getRATC().getRegister(LoopCounter).aliasedBits(); } + std::string getName() const override { return "loop"; } + private: const unsigned LoopCounter; }; diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp --- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -16,6 +16,7 @@ #include "lib/BenchmarkRunner.h" #include "lib/Clustering.h" #include "lib/Error.h" +#include "lib/FileNameGenerator.h" #include "lib/LlvmState.h" #include "lib/PerfHelper.h" #include "lib/ProgressMeter.h" @@ -36,6 +37,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" #include "llvm/Support/SourceMgr.h" @@ -242,11 +244,11 @@ cl::desc("Target a specific cpu type (-mcpu=help for details)"), cl::value_desc("cpu-name"), cl::cat(Options), cl::init("native")); -static cl::opt - DumpObjectToDisk("dump-object-to-disk", - cl::desc("dumps the generated benchmark object to disk " - "and prints a message to access it"), - cl::ValueOptional, cl::cat(BenchmarkOptions)); +static cl::opt DumpObjectToDisk( + "dump-object-to-disk", + cl::desc("Dump generated benchmark object to file (opcode and repetitor " + "names are inserted if writing mupliple files)"), + cl::cat(BenchmarkOptions), cl::init("")); static ExitOnError ExitOnErr("llvm-exegesis error: "); @@ -354,11 +356,36 @@ return Benchmarks; } +static SmallString<16> formatOpcodeLabel(const MCInstrInfo &MCII, + const BenchmarkKey &Key) { + unsigned Opcode = Key.Instructions[0].getOpcode(); + const StringRef OpcodeName = MCII.getName(Opcode); + + // It is likely to have only a single configuration per opcode, so handle + // Index == 0 differently and return just "OPC" instead of "OPC-0". + if (Key.Index == 0) + return OpcodeName; + + return formatv("{0}-{1}", OpcodeName, Key.Index); +} + +static void dumpObjectFile(const object::ObjectFile *Obj, + const StringRef DumpFileName) { + const StringRef Buffer = Obj->getData(); + std::error_code EC; + raw_fd_ostream OFS(DumpFileName, EC); + ExitOnFileError(DumpFileName, errorCodeToError(EC)); + OFS.write(Buffer.data(), Buffer.size()); + OFS.flush(); + ExitOnFileError(DumpFileName, errorCodeToError(OFS.error())); +} + static void runBenchmarkConfigurations( const LLVMState &State, ArrayRef Configurations, ArrayRef> Repetitors, const BenchmarkRunner &Runner) { assert(!Configurations.empty() && "Don't have any configurations to run."); + const MCInstrInfo &MCII = State.getInstrInfo(); std::optional FileOstr; if (BenchmarkFile != "-") { int ResultFD = 0; @@ -370,6 +397,11 @@ } raw_ostream &Ostr = FileOstr ? *FileOstr : outs(); + bool ManyConfigurations = Configurations.size() > 1; + bool ManyRepetitors = Repetitors.size() > 1; + FileNameGenerator DumpedObjectName(DumpObjectToDisk, ManyConfigurations, + ManyRepetitors); + std::optional> Meter; if (BenchmarkMeasurementsPrintProgress) Meter.emplace(Configurations.size()); @@ -377,15 +409,23 @@ ProgressMeter<>::ProgressMeterStep MeterStep(Meter ? &*Meter : nullptr); SmallVector AllResults; + const auto OpcodeLabel = formatOpcodeLabel(MCII, Conf.Key); + for (const std::unique_ptr &Repetitor : Repetitors) { auto RC = ExitOnErr(Runner.getRunnableConfiguration( Conf, NumRepetitions, LoopBodySize, *Repetitor)); - std::optional DumpFile; - if (DumpObjectToDisk.getNumOccurrences()) - DumpFile = DumpObjectToDisk; + + if (BenchmarkPhaseSelector >= + BenchmarkPhaseSelectorE::AssembleMeasuredCode && + DumpedObjectName.isEnabled()) { + dumpObjectFile( + RC.getObjectFile(), + DumpedObjectName.getFileName(OpcodeLabel, Repetitor->getName())); + } + AllResults.emplace_back( - ExitOnErr(Runner.runConfiguration(std::move(RC), DumpFile))); + ExitOnErr(Runner.runConfiguration(std::move(RC)))); } Benchmark &Result = AllResults.front(); diff --git a/llvm/unittests/tools/llvm-exegesis/Mips/BenchmarkResultTest.cpp b/llvm/unittests/tools/llvm-exegesis/Mips/BenchmarkResultTest.cpp --- a/llvm/unittests/tools/llvm-exegesis/Mips/BenchmarkResultTest.cpp +++ b/llvm/unittests/tools/llvm-exegesis/Mips/BenchmarkResultTest.cpp @@ -62,6 +62,7 @@ .addReg(Mips::T1) .addReg(Mips::T2)); ToDisk.Key.Config = "config"; + ToDisk.Key.Index = 0; ToDisk.Key.RegisterInitialValues = { RegisterValue{Mips::T1, APInt(8, "123", 10)}, RegisterValue{Mips::T2, APInt(8, "456", 10)}}; diff --git a/llvm/unittests/tools/llvm-exegesis/X86/BenchmarkResultTest.cpp b/llvm/unittests/tools/llvm-exegesis/X86/BenchmarkResultTest.cpp --- a/llvm/unittests/tools/llvm-exegesis/X86/BenchmarkResultTest.cpp +++ b/llvm/unittests/tools/llvm-exegesis/X86/BenchmarkResultTest.cpp @@ -71,6 +71,7 @@ .addImm(123) .addDFPImm(bit_cast(0.5))); ToDisk.Key.Config = "config"; + ToDisk.Key.Index = 0; ToDisk.Key.RegisterInitialValues = { RegisterValue{X86::AL, APInt(8, "-1", 10)}, RegisterValue{X86::AH, APInt(8, "123", 10)}};