[llvm] d35686b - [llvm-exegesis] Print generated assembly snippet (#142540)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 13 02:37:27 PDT 2025


Author: Lakshay Kumar
Date: 2025-08-13T10:37:24+01:00
New Revision: d35686b25cc040d79d526008d1ddedfc3e572429

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

LOG: [llvm-exegesis] Print generated assembly snippet (#142540)

Debug generated disassembly by passing argument
`debug-only="print-gen-assembly"` or `debug-only=preview-gen-assembly`
of exegesis call.
`--debug-only="print-gen-assembly"` debugs the whole generated assembly
snippet .
`--debug-only=preview-gen-assembly` debugs the initial 10 instructions
and ending 3 lines.
Thus, We can in glance see the initial setup code like registers setup
and instruction followed by truncated middle and finally print out the
last 3 instructions.

This helps us look into assembly that exegesis is execution in hardware,
Thus, it is simply functionally alias to separate objdump command on the
dumped object file.

Added: 
    llvm/test/tools/llvm-exegesis/AArch64/debug-gen-asm.s

Modified: 
    llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
    llvm/tools/llvm-exegesis/llvm-exegesis.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-exegesis/AArch64/debug-gen-asm.s b/llvm/test/tools/llvm-exegesis/AArch64/debug-gen-asm.s
new file mode 100644
index 0000000000000..7cd088fa7f20e
--- /dev/null
+++ b/llvm/test/tools/llvm-exegesis/AArch64/debug-gen-asm.s
@@ -0,0 +1,21 @@
+REQUIRES: aarch64-registered-target, asserts
+
+RUN: llvm-exegesis -mcpu=neoverse-v2 --use-dummy-perf-counters --min-instructions=100 --mode=latency --debug-only=preview-gen-assembly --opcode-name=ADDVv4i16v 2>&1 | FileCheck %s -check-prefix=PREVIEW
+
+PREVIEW: Generated assembly snippet:
+PREVIEW-NEXT: ```
+PREVIEW:      {{[04]}}: {{.*}}      movi    d{{[0-9]+}}, #0000000000000000
+PREVIEW-NEXT: {{[48]}}: {{.*}}      addv    h{{[0-9]+}}, v{{[0-9]+}}.4h
+PREVIEW:      ...      ({{[0-9]+}} more instructions)
+PREVIEW-NEXT: {{.*}}   addv    h{{[0-9]+}}, v{{[0-9]+}}.4h
+PREVIEW:      {{.*}}   ret
+PREVIEW-NEXT:```
+
+RUN: llvm-exegesis -mcpu=neoverse-v2 --use-dummy-perf-counters --min-instructions=100 --mode=latency --debug-only=print-gen-assembly  --opcode-name=ADDVv4i16v 2>&1 | FileCheck %s -check-prefix=PRINT
+PRINT: Generated assembly snippet:
+PRINT-NEXT: ```
+PRINT:      {{[04]}}: {{.*}}      movi    d{{[0-9]+}}, #0000000000000000
+PRINT-NEXT: {{[48]}}: {{.*}}      addv    h{{[0-9]+}}, v{{[0-9]+}}.4h
+PRINT-NEXT: {{.*}}   addv    h{{[0-9]+}}, v{{[0-9]+}}.4h
+PRINT:      {{.*}}   ret
+PRINT-NEXT:```

diff  --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
index a7771b99e97b1..2c9d6dc4ba13a 100644
--- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
+++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
@@ -8,6 +8,7 @@
 
 #include "BenchmarkRunner.h"
 #include "Assembler.h"
+#include "DisassemblerHelper.h"
 #include "Error.h"
 #include "MCInstrDescView.h"
 #include "MmapUtils.h"
@@ -20,6 +21,7 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX
 #include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/Debug.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -588,6 +590,89 @@ class SubProcessFunctionExecutorImpl
   const std::optional<int> BenchmarkProcessCPU;
 };
 #endif // __linux__
+
+// Structure to hold instruction information for assembly printing
+struct InstructionInfo {
+  std::string Text;
+  uint64_t Address;
+  std::string HexBytes;
+};
+
+// Helper function to print generated assembly snippets
+void printInstructions(const std::vector<InstructionInfo> &Instructions,
+                       int InitialLinesCount, int LastLinesCount) {
+  int N = Instructions.size();
+  dbgs() << "Generated assembly snippet:\n```\n";
+
+  // Print initial lines
+  for (int i = 0; i < InitialLinesCount; ++i)
+    dbgs() << format_hex_no_prefix(Instructions[i].Address, 0) << ":\t"
+           << Instructions[i].HexBytes << Instructions[i].Text << '\n';
+
+  // Show truncation message if needed
+  int SkippedInstructions = N - InitialLinesCount - LastLinesCount;
+  if (SkippedInstructions > 0)
+    dbgs() << "...\t(" << SkippedInstructions << " more instructions)\n";
+
+  // Print last min(PreviewLast, N - PreviewFirst) lines
+  int LastLinesToPrint = std::min(
+      LastLinesCount, N > InitialLinesCount ? N - InitialLinesCount : 0);
+  for (int i = N - LastLinesToPrint; i < N; ++i)
+    dbgs() << format_hex_no_prefix(Instructions[i].Address, 0) << ":\t"
+           << Instructions[i].HexBytes << Instructions[i].Text << '\n';
+  dbgs() << "```\n";
+}
+
+// Function to extract and print assembly from snippet
+Error printAssembledSnippet(const LLVMState &State,
+                            const SmallString<0> &Snippet) {
+  // Extract the actual function bytes from the object file
+  std::vector<uint8_t> FunctionBytes;
+  if (auto Err = getBenchmarkFunctionBytes(Snippet, FunctionBytes))
+    return make_error<Failure>("Failed to extract function bytes: " +
+                               toString(std::move(Err)));
+
+  // Decode all instructions first
+  DisassemblerHelper DisHelper(State);
+  uint64_t Address = 0;
+  std::vector<InstructionInfo> Instructions;
+  const size_t FunctionBytesSize = FunctionBytes.size();
+
+  while (Address < FunctionBytesSize) {
+    MCInst Inst;
+    uint64_t Size;
+    ArrayRef<uint8_t> Bytes(FunctionBytes.data() + Address,
+                            FunctionBytesSize - Address);
+
+    if (!DisHelper.decodeInst(Inst, Size, Bytes)) {
+      Instructions.push_back({"<decode error>", Address, ""});
+      break;
+    }
+
+    // Format instruction text
+    std::string InstStr;
+    raw_string_ostream OS(InstStr);
+    DisHelper.printInst(&Inst, OS);
+
+    // Create hex string for this instruction (big-endian order)
+    std::string HexStr;
+    raw_string_ostream HexOS(HexStr);
+    for (int i = Size - 1; i >= 0; --i)
+      HexOS << format_hex_no_prefix(Bytes[i], 2);
+
+    Instructions.push_back({OS.str(), Address, HexOS.str()});
+    Address += Size;
+  }
+
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "preview-gen-assembly"
+  LLVM_DEBUG(printInstructions(Instructions, 10, 3));
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "print-gen-assembly"
+  LLVM_DEBUG(printInstructions(Instructions, Instructions.size(), 0));
+#undef DEBUG_TYPE
+  return Error::success();
+}
 } // namespace
 
 Expected<SmallString<0>> BenchmarkRunner::assembleSnippet(
@@ -655,6 +740,10 @@ BenchmarkRunner::getRunnableConfiguration(
     if (Error E = Snippet.takeError())
       return std::move(E);
     RC.ObjectFile = getObjectFromBuffer(*Snippet);
+
+    // Print the assembled snippet by disassembling the binary data
+    if (Error E = printAssembledSnippet(State, *Snippet))
+      return std::move(E);
   }
 
   return std::move(RC);

diff  --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
index babcffeb9666a..b5820f8669f1d 100644
--- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
+++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
@@ -482,7 +482,8 @@ void benchmarkMain() {
   InitializeAllExegesisTargets();
 #define LLVM_EXEGESIS(TargetName)                                              \
   LLVMInitialize##TargetName##AsmPrinter();                                    \
-  LLVMInitialize##TargetName##AsmParser();
+  LLVMInitialize##TargetName##AsmParser();                                     \
+  LLVMInitialize##TargetName##Disassembler();
 #include "llvm/Config/TargetExegesis.def"
 
   const LLVMState State = ExitOnErr(


        


More information about the llvm-commits mailing list