-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
codegen: Add llvm_options parameter to code_llvm for pass instrumentation #60698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
9be33d6 to
e363be3
Compare
|
This is amazing! Thank you so much! |
|
The only UI question IMO is whether we want the string interface or to use a kwarg interface (e.g. |
|
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) |
e363be3 to
6e72b9d
Compare
…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]>
6e72b9d to
12d1b7e
Compare
| 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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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;")There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
7678a81 to
7ddc6e3
Compare
| // --- 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 |
There was a problem hiding this comment.
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
| } else if (Arg.starts_with("-print-after")) { | ||
| StringRef val = getNextValue(i, Arg, "-print-after"); |
There was a problem hiding this comment.
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:
| } 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
7ddc6e3 to
47a88d4
Compare
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]>
47a88d4 to
2bc0119
Compare
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 functionsExample usage:
code_llvm(+, (Int, Int); llvm_options="-print-after=loop-vectorize")