Skip to content

Conversation

@lakshayk-nv
Copy link
Contributor

@lakshayk-nv lakshayk-nv commented Jun 3, 2025

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.

Just a good to have change for dev experience. :)

Please review: @sjoerdmeijer, @boomanaiden154, @davemgreen

@llvmbot
Copy link
Member

llvmbot commented Jun 3, 2025

@llvm/pr-subscribers-tools-llvm-exegesis

Author: Lakshay Kumar (lakshayk-nv)

Changes

Debug generated disassembly by passing argument debug-only="exegesis-benchmark-runner" of exegesis call. Calling /usr/bin/objdump -d on generated object file for debugging generated disassembly.
Just a good to have change for dev experience. :)


Full diff: https://github.com/llvm/llvm-project/pull/142540.diff

1 Files Affected:

  • (modified) llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp (+34)
diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
index a7771b99e97b1..7c2a6c966106a 100644
--- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
+++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
@@ -20,6 +20,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"
@@ -29,6 +30,7 @@
 #include <cmath>
 #include <memory>
 #include <string>
+#define DEBUG_TYPE "exegesis-benchmark-runner"
 
 #ifdef __linux__
 #ifdef HAVE_LIBPFM
@@ -709,6 +711,38 @@ std::pair<Error, Benchmark> BenchmarkRunner::runConfiguration(
     }
     outs() << "Check generated assembly with: /usr/bin/objdump -d "
            << *ObjectFilePath << "\n";
+
+#ifdef __linux__
+    int StdOutFD, StdErrFD;
+    SmallString<128> StdOutFile, StdErrFile;
+    sys::fs::createTemporaryFile("temp-objdump-out", "txt", StdOutFD,
+                                 StdOutFile);
+    sys::fs::createTemporaryFile("temp-objdump-err", "txt", StdErrFD,
+                                 StdErrFile);
+    std::vector<std::optional<StringRef>> Redirects = {
+        std::nullopt,          // stdin
+        StringRef(StdOutFile), // stdout
+        StringRef(StdErrFile)  // stderr
+    };
+
+    std::string ErrMsg;
+    int Result = sys::ExecuteAndWait(
+        "/usr/bin/objdump", {"/usr/bin/objdump", "-d", *ObjectFilePath},
+        std::nullopt, Redirects, 0, 0, &ErrMsg);
+    auto StdOutBuf = MemoryBuffer::getFile(StdOutFile);
+    if (StdOutBuf && !(*StdOutBuf)->getBuffer().empty())
+      LLVM_DEBUG(dbgs() << "[llvm-exegesis][objdump] Generated assembly:\n"
+                        << (*StdOutBuf)->getBuffer() << '\n');
+    auto StdErrBuf = MemoryBuffer::getFile(StdErrFile);
+    if (StdErrBuf && !(*StdErrBuf)->getBuffer().empty())
+      LLVM_DEBUG(dbgs() << "[llvm-exegesis][objdump] stderr:\n"
+                        << (*StdErrBuf)->getBuffer() << '\n');
+    if (!ErrMsg.empty())
+      LLVM_DEBUG(dbgs() << "[llvm-exegesis][objdump] process error: " << ErrMsg
+                        << '\n');
+    sys::fs::remove(StdOutFile);
+    sys::fs::remove(StdErrFile);
+#endif
   }
 
   if (BenchmarkPhaseSelector < BenchmarkPhaseSelectorE::Measure) {

@RKSimon RKSimon requested a review from boomanaiden154 June 3, 2025 07:00
Copy link
Contributor

@boomanaiden154 boomanaiden154 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't exactly see the point of this. Running objdump (or llvm-objdump/whatever version) on the output of -dump-object-to-disk is intended to cover this exact use case. I don't think the slight convenience of running it automatically by passing -debug-only or -debug outweights the extra code complexity and other problems that will come with this.

It's hard to test because now you're relying on /usr/bin/objdump existing, you have no idea what /usr/bin/objdump actually does or if it even exists, and creating temporary files to invoke an external command is the wrong way to go about this. If we want to print instructions we can spin up a MCInstPrinter to print the vector of MCInsts that we have earlier. That case is still covered by -dump-object-to-disk though.

@sjoerdmeijer
Copy link
Collaborator

I don't exactly see the point of this. Running objdump (or llvm-objdump/whatever version) on the output of -dump-object-to-disk is intended to cover this exact use case. I don't think the slight convenience of running it automatically by passing -debug-only or -debug outweights the extra code complexity and other problems that will come with this.

This would be a very welcome debug feature for me. I agree that this can be achieved in the way you described it, but it would be so much more efficient for me if I can flip a switch and don't need to do this all manually. Analysing the snippets is really important, i.e. analysing them to see if they truly test what they want to test is important and time consuming, and anything that helps is welcome.

I agree with the trade-off in general, a debug feature shouldn't lead to massive churn in the code, or pose maintainance problems now or later, but this really doesn't seem to be the case here. I.e., it is self-contained, and not a lot of code, partly because it already relies on the infrastructure that is mostly there already.

Long story short, I fully support the change, but I would like to see 2 changes:

  • I agree we don't want hard-code paths, so we want to remove /usr/bin/objdump, and we want to change that to llvm-objdump. That is already used in some test cases, so that is not a new dependency,
  • and we need a test-case, because we need one, and also so that we can see how this looks like.

@sjoerdmeijer
Copy link
Collaborator

I had actually not looked at the implementation very carefully, but now looking and thinking about it, I think I would like to have this available under and option, so that I can toggle this on or off. For example, add an option -print-snippet-assembly or something along those lines.

In terms of implementation, this could be a wrapper around "-dump-object-to-disk" and then disassembling and printing it to stdout, or the current implementation.

Does this (and my previous comment) help with your concerns, @boomanaiden154 ?

@boomanaiden154
Copy link
Contributor

This would be a very welcome debug feature for me. I agree that this can be achieved in the way you described it, but it would be so much more efficient for me if I can flip a switch and don't need to do this all manually. Analysing the snippets is really important, i.e. analysing them to see if they truly test what they want to test is important and time consuming, and anything that helps is welcome.

The snippet (not repeated) is already shown in the benchmark key:

key:
  instructions:
    - 'CMP64rr RSI RSI'
    - 'ADC64ri32_ND RSI RDI i_0x1'

I don't see how this patch will provide significant benefit over that, other than maybe printing everything in a more familiar format.

I agree we don't want hard-code paths, so we want to remove /usr/bin/objdump, and we want to change that to llvm-objdump. That is already used in some test cases, so that is not a new dependency,

I don't see how this is preferrable to a MCInstPrinter.

@github-actions
Copy link

github-actions bot commented Jul 1, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@sjoerdmeijer
Copy link
Collaborator

I was just looking at this, and noticed the update, and was wondering why all the pointer authentication stuff is included here. I am assuming that was included by mistake?

@lakshayk-nv
Copy link
Contributor Author

lakshayk-nv commented Jul 1, 2025

I was just looking at this, and noticed the update, and was wondering why all the pointer authentication stuff is included here. I am assuming that was included by mistake?

Apologies, I messed up this branch. I just wanted to add the testcase commit.
PS: Reverted back those bad commits. Please review the changes and test cases, Thanks.

@lakshayk-nv lakshayk-nv force-pushed the llvm-exegesis-disassembly branch from a8160ae to b57c3ab Compare July 1, 2025 09:28
};

// Preview generated assembly snippet
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this work now? Does this mean the snippet is printed twice if option preview-gen-assembly is provided and it is a debug build, is that right?

And --debug-only="preview-gen-assembly" only works with a debug build?

I would prefer to just flip a switch and option, similar to --dump-object-to-disk, so that this works in any build.

Copy link
Contributor Author

@lakshayk-nv lakshayk-nv Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How to use:
--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.
(Updated PR Description accordingly)

No, Given argument of --debug-only="print-gen-assembly" exegesis will print out the generated assembly only once with truncated middle part, so not to fill up whole terminal for large value of min-instructions

$ build/bin/llvm-exegesis -mcpu=neoverse-v2 --dump-object-to-disk=%d --benchmark-phase=measure --min-instructions=10000 --mode=latency --opcode-name=ADCXr --debug-only="preview-gen-assembly"
setRegTo is not implemented, results will be unreliable
setRegTo is not implemented, results will be unreliable
Generated assembly snippet:
'''
0:      f81f0ff7        str     x23, [sp, #-16]!
4:      d2800017        mov     x23, #0
8:      d280000d        mov     x13, #0
c:      9a0d02ed        adc     x13, x23, x13
10:     9a0d02ed        adc     x13, x23, x13
14:     9a0d02ed        adc     x13, x23, x13
18:     9a0d02ed        adc     x13, x23, x13
1c:     9a0d02ed        adc     x13, x23, x13
20:     9a0d02ed        adc     x13, x23, x13
24:     9a0d02ed        adc     x13, x23, x13
...     (9992 more instructions)
9c48:   9a0d02ed        adc     x13, x23, x13
9c4c:   f84107f7        ldr     x23, [sp], #16
9c50:   d65f03c0        ret
'''
Check generated assembly with: /usr/bin/objdump -d %d
---
mode:            latency
key:
  instructions:
    - 'ADCXr X13 X23 X13'
  config:          ''
  register_initial_values:
    - 'X23=0x0'
    - 'X13=0x0'
    - 'NZCV=0x0'
cpu_name:        neoverse-v2
llvm_triple:     aarch64-unknown-linux-gnu
min_instructions: 10000
measurements:
  - { key: latency, value: 1.0036, per_snippet_value: 1.0036, validation_counters: {} }
error:           ''
info:            Repeating a single explicitly serial instruction
assembled_snippet: F70F1FF8170080D20D0080D2ED020D9AED020D9AED020D9AED020D9AF70741F8C0035FD6
...

If we explicitly give argument --debug-only="preview-gen-assembly,print-gen-assembly" to exegesis, both preview and print debug will result in duplicate print.

Copy link
Contributor Author

@lakshayk-nv lakshayk-nv Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And --debug-only="preview-gen-assembly" only works with a debug build?

No, I checked this work for both Debug and Release build (UPDATE:with assertion on).

I would prefer to just flip a switch and option, similar to --dump-object-to-disk, so that this works in any build.

Given that, Can you reconsider this (additional argument) because we will require two arguments if not for --debug-only one for whole assembly print and another for preview.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Currently, Given the argument of --dump-object-to-disk=<filename> exegesis just stdout
Check generated assembly with: /usr/bin/objdump -d %d. This object file is to be objdump in next command outside exegesis)

In terms of implementation, this could be a wrapper around "-dump-object-to-disk"

I know you previously also asked for this. If this seems necessary, can you please expand on wrapper thing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarifications.

What I have suggested could have been an alternative way to implement this, but I am happy with the implementation here as it is nice and concise and doesn't depend on external tools. So, ignore that suggestion.

Copy link
Contributor

@boomanaiden154 boomanaiden154 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update, this looks a lot better.

}
if (N > (PreviewFirst + PreviewLast)) {
if (Preview) {
dbgs() << "...\t(" << (N - PreviewFirst - PreviewLast)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So yeah, debug vs. release builds had me confused earlier. I guess this debug stream is available even in non-Debug mode, but does it make sense to use outs() instead? I see that being used elsewhere, and guess that is the slightly more appropriate output stream for this?

Copy link
Collaborator

@sjoerdmeijer sjoerdmeijer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the output and think this will be very useful to me. Ideally I would like to have this available in a release build without assertions, but let's get some experience first before we promote this to an option, so LGTM.

I've left two nits that don't need another review if addressed.

@boomanaiden154
Copy link
Contributor

Ideally I would like to have this available in a release build without assertions, but let's get some experience first before we promote this to an option, so LGTM.

Why the preference for enabling this without assertions? This seems almost exclusively a debug feature. llvm-exegesis is also pretty much exclusively an LLVM development tool, so there's not really a need to ship a release build anywhere. At least that's how I see it, not sure if others use it much differently.

@sjoerdmeijer
Copy link
Collaborator

Sorry for the confusion and putting noise on the line, but @boomanaiden154 was happy with this as a debug feature. Let's go with that. Hopefully it's not too much work to restore the LLVM_DEBUG and dbgs() things.

@lakshayk-nv
Copy link
Contributor Author

Introduced back this as debug feature.
--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.

Thanks !

@sjoerdmeijer
Copy link
Collaborator

Introduced back this as debug feature. --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.

A last nit: it looks like we are only testing --debug-only=preview-gen-assembly, for completeness we should also test --debug-only="print-gen-assembly". Can you write and add a test case for that one?

@lakshayk-nv lakshayk-nv changed the title [llvm-exegesis] Debug generated disassembly [llvm-exegesis] Print generated assembly snippet Aug 8, 2025
@lakshayk-nv
Copy link
Contributor Author

for completeness we should also test --debug-only="print-gen-assembly"

Added the same, Thanks!

@@ -0,0 +1,21 @@
REQUIRES: aarch64-registered-target
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also needs to require an assertions enabled build since you're using debug macros.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added asserts in testfile. Thanks!

Copy link
Contributor

@boomanaiden154 boomanaiden154 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One minor change that is needed, otherwise LGTM.

@@ -0,0 +1,21 @@
REQUIRES: aarch64-registered-target, asserts

RUN: llvm-exegesis -mcpu=neoverse-v2 --benchmark-phase=measure --min-instructions=100 --mode=latency --debug-only=preview-gen-assembly --opcode-name=ADDVv4i16v 2>&1 | FileCheck %s -check-prefix=PREVIEW
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you have to progress to --benchmark-phase=measure, you also need a requires for being able to use perf counters. You might be able to get away with --use-dummy-perf-counters though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@sjoerdmeijer sjoerdmeijer merged commit d35686b into llvm:main Aug 13, 2025
9 checks passed
@kazutakahirata
Copy link
Contributor

@sjoerdmeijer I've landed 0f77887 to fix a warning from this PR. Thanks!

@sjoerdmeijer
Copy link
Collaborator

@sjoerdmeijer I've landed 0f77887 to fix a warning from this PR. Thanks!

Oopsy, thanks a lot @kazutakahirata

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants