[llvm] [LLVM][MC] Add tracing support to llvm-mc to measure decode times (PR #151149)

Rahul Joshi via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 29 06:27:55 PDT 2025


https://github.com/jurahul created https://github.com/llvm/llvm-project/pull/151149

Add support for measuring decode times in llvm-mc tool. Add command line options to enable time-trace profiling (similar to llc or opt) and add option `num-benchmark-runs` to run the decoder N times on each instruction.


>From 6c49bdd429968a7e59332e122fea5c0015cfa25b Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Sat, 21 Jun 2025 10:32:11 -0700
Subject: [PATCH] LLVM-MC profiling to measure disassembly times

---
 llvm/tools/llvm-mc/Disassembler.cpp | 49 +++++++++++++++++++----------
 llvm/tools/llvm-mc/Disassembler.h   |  3 +-
 llvm/tools/llvm-mc/llvm-mc.cpp      | 46 ++++++++++++++++++++++++---
 3 files changed, 76 insertions(+), 22 deletions(-)

diff --git a/llvm/tools/llvm-mc/Disassembler.cpp b/llvm/tools/llvm-mc/Disassembler.cpp
index 86727931067a5..00dee06e847ce 100644
--- a/llvm/tools/llvm-mc/Disassembler.cpp
+++ b/llvm/tools/llvm-mc/Disassembler.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Disassembler.h"
+#include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/MC/MCAsmInfo.h"
 #include "llvm/MC/MCContext.h"
@@ -24,6 +25,7 @@
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TargetParser/Triple.h"
 
@@ -32,24 +34,25 @@ using namespace llvm;
 typedef std::pair<std::vector<unsigned char>, std::vector<const char *>>
     ByteArrayTy;
 
-static bool PrintInsts(const MCDisassembler &DisAsm, const ByteArrayTy &Bytes,
+static bool printInsts(const MCDisassembler &DisAsm, const ByteArrayTy &Bytes,
                        SourceMgr &SM, MCStreamer &Streamer, bool InAtomicBlock,
-                       const MCSubtargetInfo &STI) {
+                       const MCSubtargetInfo &STI, unsigned BenchmarkNumRuns) {
   ArrayRef<uint8_t> Data(Bytes.first);
 
   // Disassemble it to strings.
   uint64_t Size;
-  uint64_t Index;
 
-  for (Index = 0; Index < Bytes.first.size(); Index += Size) {
-    MCInst Inst;
+  for (uint64_t Index = 0; Index < Bytes.first.size(); Index += Size) {
+    auto getInstruction = [&](MCInst &Inst) -> MCDisassembler::DecodeStatus {
+      if (STI.getTargetTriple().getArch() == Triple::hexagon)
+        return DisAsm.getInstructionBundle(Inst, Size, Data.slice(Index), Index,
+                                           nulls());
+      return DisAsm.getInstruction(Inst, Size, Data.slice(Index), Index,
+                                   nulls());
+    };
 
-    MCDisassembler::DecodeStatus S;
-    if (STI.getTargetTriple().getArch() == Triple::hexagon)
-      S = DisAsm.getInstructionBundle(Inst, Size, Data.slice(Index), Index,
-                                      nulls());
-    else
-      S = DisAsm.getInstruction(Inst, Size, Data.slice(Index), Index, nulls());
+    MCInst Inst;
+    MCDisassembler::DecodeStatus S = getInstruction(Inst);
     switch (S) {
     case MCDisassembler::Fail:
       SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]),
@@ -74,12 +77,24 @@ static bool PrintInsts(const MCDisassembler &DisAsm, const ByteArrayTy &Bytes,
       Streamer.emitInstruction(Inst, STI);
       break;
     }
+
+    if (S == MCDisassembler::Success && BenchmarkNumRuns != 0) {
+      // Benchmark mode, collect timing for decoding the instruction several
+      // times.
+      MCInst BMInst;
+      TimeTraceScope timeScope("getInstruction");
+      for (unsigned _ : llvm::seq<unsigned>(BenchmarkNumRuns)) {
+        BMInst.clear();
+        BMInst.setOpcode(0);
+        S = getInstruction(BMInst);
+      }
+    }
   }
 
   return false;
 }
 
-static bool SkipToToken(StringRef &Str) {
+static bool skipToToken(StringRef &Str) {
   for (;;) {
     if (Str.empty())
       return false;
@@ -101,7 +116,7 @@ static bool SkipToToken(StringRef &Str) {
 
 static bool byteArrayFromString(ByteArrayTy &ByteArray, StringRef &Str,
                                 SourceMgr &SM, bool HexBytes) {
-  while (SkipToToken(Str)) {
+  while (skipToToken(Str)) {
     // Handled by higher level
     if (Str[0] == '[' || Str[0] == ']')
       return false;
@@ -151,7 +166,7 @@ int Disassembler::disassemble(const Target &T, const std::string &Triple,
                               MCSubtargetInfo &STI, MCStreamer &Streamer,
                               MemoryBuffer &Buffer, SourceMgr &SM,
                               MCContext &Ctx, const MCTargetOptions &MCOptions,
-                              bool HexBytes) {
+                              bool HexBytes, unsigned BenchmarkNumRuns) {
   std::unique_ptr<const MCRegisterInfo> MRI(T.createMCRegInfo(Triple));
   if (!MRI) {
     errs() << "error: no register info for target " << Triple << "\n";
@@ -179,7 +194,7 @@ int Disassembler::disassemble(const Target &T, const std::string &Triple,
   StringRef Str = Buffer.getBuffer();
   bool InAtomicBlock = false;
 
-  while (SkipToToken(Str)) {
+  while (skipToToken(Str)) {
     ByteArray.first.clear();
     ByteArray.second.clear();
 
@@ -207,8 +222,8 @@ int Disassembler::disassemble(const Target &T, const std::string &Triple,
     ErrorOccurred |= byteArrayFromString(ByteArray, Str, SM, HexBytes);
 
     if (!ByteArray.first.empty())
-      ErrorOccurred |=
-          PrintInsts(*DisAsm, ByteArray, SM, Streamer, InAtomicBlock, STI);
+      ErrorOccurred |= printInsts(*DisAsm, ByteArray, SM, Streamer,
+                                  InAtomicBlock, STI, BenchmarkNumRuns);
   }
 
   if (InAtomicBlock) {
diff --git a/llvm/tools/llvm-mc/Disassembler.h b/llvm/tools/llvm-mc/Disassembler.h
index 5efffca1e9926..334b72af8b522 100644
--- a/llvm/tools/llvm-mc/Disassembler.h
+++ b/llvm/tools/llvm-mc/Disassembler.h
@@ -32,7 +32,8 @@ class Disassembler {
   static int disassemble(const Target &T, const std::string &Triple,
                          MCSubtargetInfo &STI, MCStreamer &Streamer,
                          MemoryBuffer &Buffer, SourceMgr &SM, MCContext &Ctx,
-                         const MCTargetOptions &MCOptions, bool HexBytes);
+                         const MCTargetOptions &MCOptions, bool HexBytes,
+                         unsigned BenchmarkNumRuns);
 };
 
 } // namespace llvm
diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp
index da89af71bbbe8..4f3ff1569c158 100644
--- a/llvm/tools/llvm-mc/llvm-mc.cpp
+++ b/llvm/tools/llvm-mc/llvm-mc.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Disassembler.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/DWARFCFIChecker/DWARFCFIFunctionFrameAnalyzer.h"
 #include "llvm/DWARFCFIChecker/DWARFCFIFunctionFrameStreamer.h"
 #include "llvm/MC/MCAsmBackend.h"
@@ -37,6 +38,7 @@
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/ToolOutputFile.h"
 #include "llvm/Support/WithColor.h"
 #include "llvm/TargetParser/Host.h"
@@ -240,6 +242,24 @@ static cl::opt<ActionType> Action(
                           "Colored disassembly of strings of hex bytes")),
     cl::cat(MCCategory));
 
+static cl::opt<unsigned>
+    BenchmarkNumRuns("benchmark-num-runs",
+                     cl::desc("Number of runs for decoder benchmarking"),
+                     cl::cat(MCCategory));
+
+static cl::opt<bool> TimeTrace("time-trace", cl::desc("Record time trace"));
+
+static cl::opt<unsigned> TimeTraceGranularity(
+    "time-trace-granularity",
+    cl::desc(
+        "Minimum time granularity (in microseconds) traced by time profiler"),
+    cl::init(500), cl::Hidden);
+
+static cl::opt<std::string>
+    TimeTraceFile("time-trace-file",
+                  cl::desc("Specify time trace file destination"),
+                  cl::value_desc("filename"));
+
 static const Target *GetTarget(const char *ProgName) {
   // Figure out the target triple.
   if (TripleName.empty())
@@ -371,6 +391,22 @@ int main(int argc, char **argv) {
 
   cl::HideUnrelatedOptions({&MCCategory, &getColorCategory()});
   cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n");
+
+  if (TimeTrace)
+    timeTraceProfilerInitialize(TimeTraceGranularity, argv[0]);
+
+  auto TimeTraceScopeExit = make_scope_exit([]() {
+    if (!TimeTrace)
+      return;
+    if (auto E = timeTraceProfilerWrite(TimeTraceFile, OutputFilename)) {
+      handleAllErrors(std::move(E), [&](const StringError &SE) {
+        errs() << SE.getMessage() << "\n";
+      });
+      return;
+    }
+    timeTraceProfilerCleanup();
+  });
+
   MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();
   MCOptions.CompressDebugSections = CompressDebugSections.getValue();
   MCOptions.ShowMCInst = ShowInst;
@@ -603,7 +639,7 @@ int main(int argc, char **argv) {
   }
 
   int Res = 1;
-  bool disassemble = false;
+  bool Disassemble = false;
   switch (Action) {
   case AC_AsLex:
     Res = AsLexInput(SrcMgr, *MAI, Out->os());
@@ -615,12 +651,13 @@ int main(int argc, char **argv) {
   case AC_MDisassemble:
   case AC_CDisassemble:
   case AC_Disassemble:
-    disassemble = true;
+    Disassemble = true;
     break;
   }
-  if (disassemble)
+  if (Disassemble)
     Res = Disassembler::disassemble(*TheTarget, TripleName, *STI, *Str, *Buffer,
-                                    SrcMgr, Ctx, MCOptions, HexBytes);
+                                    SrcMgr, Ctx, MCOptions, HexBytes,
+                                    BenchmarkNumRuns);
 
   // Keep output if no errors.
   if (Res == 0) {
@@ -628,5 +665,6 @@ int main(int argc, char **argv) {
     if (DwoOut)
       DwoOut->keep();
   }
+
   return Res;
 }



More information about the llvm-commits mailing list