Skip to content

Conversation

@gbaraldi
Copy link
Member

@gbaraldi gbaraldi commented Jan 15, 2026

Add the ability to pass LLVM pass manager options directly to code_llvm() for debugging and analysis purposes. This allows users to inspect IR at various stages of the optimization pipeline without needing to set environment variables.

Supported options include:

  • -print-after-all: Print IR after each pass
  • -print-before-all: Print IR before each pass
  • -print-after=<passname>: Print IR after a specific pass
  • -print-before=<passname>: Print IR before a specific pass
  • -print-module-scope: Print entire module instead of just the function
  • -filter-print-funcs=<name>: Filter output to specific functions

Example usage:
code_llvm(+, (Int, Int); llvm_options="-print-after=loop-vectorize")

@gbaraldi gbaraldi requested review from vchuravy and vtjnash January 15, 2026 12:22
@gbaraldi gbaraldi force-pushed the gb/print-options-llvm branch 5 times, most recently from 9be33d6 to e363be3 Compare January 15, 2026 15:39
@oscardssmith
Copy link
Member

This is amazing! Thank you so much!

@oscardssmith
Copy link
Member

The only UI question IMO is whether we want the string interface or to use a kwarg interface (e.g. print-after=:allocopt)

@gbaraldi
Copy link
Member Author

I initially had something like that but to me it felt weird. This is very much a LLVM api and I think users of this are probably familiar with that syntax. (it's also much simpler to plumb through)

@gbaraldi gbaraldi force-pushed the gb/print-options-llvm branch from e363be3 to 6e72b9d Compare January 15, 2026 19:06
…tion

Add the ability to pass LLVM pass manager options directly to code_llvm()
for debugging and analysis purposes. This allows users to inspect IR at
various stages of the optimization pipeline without needing to set
environment variables.

Supported options include:
- `-print-after-all`: Print IR after each pass
- `-print-before-all`: Print IR before each pass
- `-print-after=<passname>`: Print IR after a specific pass
- `-print-before=<passname>`: Print IR before a specific pass
- `-print-module-scope`: Print entire module instead of just the function
- `-print-changed`: Print IR only when it changes
- `-filter-print-funcs=<name>`: Filter output to specific functions

Example usage:
  code_llvm(+, (Int, Int); llvm_options="-print-after=loop-vectorize")

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@gbaraldi gbaraldi force-pushed the gb/print-options-llvm branch from 6e72b9d to 12d1b7e Compare January 15, 2026 19:14
Comment on lines 350 to 358
The `llvm_options` keyword argument allows passing LLVM options to control the optimization pipeline output.
Supported options include:
- `-print-after-all`: Print IR after each pass
- `-print-before-all`: Print IR before each pass
- `-print-after=<passname>`: Print IR after a specific pass (e.g., `-print-after=loop-vectorize`)
- `-print-before=<passname>`: Print IR before a specific pass
- `-print-module-scope`: Print entire module instead of just the function
- `-print-changed`: Print IR only when it changes
- `-filter-print-funcs=<name>`: Only print IR for functions matching the name
Copy link
Member

Choose a reason for hiding this comment

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

I makes me wonder if we want to support more (all) of the opt pipeline?

In particular remarks? But perhaps custom opt pipeline would also make sense

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah. I want this now, but technically we can extend this to pretty much all of the things (triple/target/pipeline etc)

Copy link
Member

Choose a reason for hiding this comment

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

Most of those options are static global variables that we cannot safely use, because llvm is not actually very usable as a library when it comes to options. Already very annoying that the StandardInstrumentations pass has to be copied here and reimplemented since that pass relies heavily on those global variables.

str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool,
dump_module::Bool, debuginfo::Ptr{UInt8})::Ref{String}
return str
# Prepend pass output to final IR
Copy link
Member

@vtjnash vtjnash Jan 15, 2026

Choose a reason for hiding this comment

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

Should we prepend ; to each newline in the annotations stream? I think normally in LLVM it might go to stderr while the final output goes to stdout, so they are normally separated that way instead?

str = replace(str, '\n' => "\n;")

Copy link
Member Author

Choose a reason for hiding this comment

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

So this is implemented, but then I slept on it. I kind of think that we shouldn't make it a comment. Most of the time I'll get this output and put it in viscose or godbolt, maybe give it to an llvm tool. Rarely it's just something to look at, so having the comments might be helpful

Copy link
Member

Choose a reason for hiding this comment

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

It looks like Claude still mentioned this in the commit, but didn't implement it. I'm neutral on which version is better. It should be pretty clear to a human where the real module starts and the debug output ended, or pretty easy to mechanically strip off ; from the start of every line

Copy link
Member Author

Choose a reason for hiding this comment

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

I decided on no prepend because it gets syntax highlighting (even if not great ones)

@gbaraldi gbaraldi force-pushed the gb/print-options-llvm branch from 7678a81 to 7ddc6e3 Compare January 16, 2026 19:20
// --- native code info, and dump function to IR and ASM ---
// Get pointer to llvm::Function instance, compiling if necessary
// for use in reflection from Julia.
// This is paired with jl_dump_function_ir and jl_dump_function_asm, either of which will free all memory allocated here
Copy link
Member

Choose a reason for hiding this comment

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

Looks like jl_dump_function_asm isn't freeing the newly added memory?

src/pipeline.cpp Outdated
Comment on lines 782 to 783
} else if (Arg.starts_with("-print-after")) {
StringRef val = getNextValue(i, Arg, "-print-after");
Copy link
Member

Choose a reason for hiding this comment

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

Can you ask Claude to rewrite this so that is isn't doing just a prefix check, but can have getNextValue implement the exact checks:

Suggested change
} else if (Arg.starts_with("-print-after")) {
StringRef val = getNextValue(i, Arg, "-print-after");
} else if (StringRef val = getNextValue(i, Arg, "-print-after=")) {

If you were going to do a prefix comparison scan only, the getopt standard requires that you do the prefix in the other direction (e.g. StringRef("-print-after").starts_with(Arg.split('=').first)) but that is getting fairly tedious to implement by hand and not something the custom parsers for llvm or gcc arguments currently does

@gbaraldi gbaraldi force-pushed the gb/print-options-llvm branch from 7ddc6e3 to 47a88d4 Compare January 16, 2026 19:58
Use LLVM's TokenizeGNUCommandLine for parsing instead of custom logic.
Support both "-option=value" and "-option value" syntax with proper
bounds checking. Format pass instrumentation output as IR comments by
prepending "; " to each line. Simplify memory management by having
jl_dump_function_ir handle freeing pass_output internally.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@gbaraldi gbaraldi force-pushed the gb/print-options-llvm branch from 47a88d4 to 2bc0119 Compare January 16, 2026 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants