Hey, I built (with AI) a web-based tool for inspecting how InstCombine rewrites LLVM IR snippets:
https://xuhongxu.com/instcombine-instrumentor/
The motivation is that the before/after IR often does not explain how an InstCombine result was reached. I often want to know which fold created a value, where a replacement happened, and how later rewrites were enabled.
The tool runs an (automatically-)instrumented InstCombine WebAsm build in the browser and shows:
It is intended mainly for small IR snippets, such as debugging rewrites, triaging missed optimizations, or helping new contributors understand InstCombine.
I would appreciate feedback from LLVM/InstCombine developers:
-
Is this kind of trace useful?
-
Is the source-location attribution clear, or could it be misleading?
-
What other information would make it more useful?
Known limitations: it uses a patched LLVM build with ONLY InstCombine pass, and the reported source location may not always correspond to a clean high-level optimization rule.
It is also open-sourced at: GitHub - xuhongxu96/instcombine-instrumentor: Instrument LLVM InstCombine pass to record every new instruction and every RAUW replacement · GitHub
Thanks!
4 Likes
Great job! I think this tool is very useful to help us understand peephole optimizations and debug with miscompilation bugs.
Is the source-location attribution clear, or could it be misleading?
InstSimplify/ConstantFolding should also be instrumented. If the callee returns a nonnull Value*, some optimizations may be triggered. InstCombine fold debugger
I’d suggest also adding support for other peephole passes like VectorCombine/AggressiveInstCombine.
What other information would make it more useful?
- Activated conditions are useful. But it might be too expensive.
- Activated ValueTracking calls associated with this transformation. For example, InstCombine converts a sext into a zext nneg when the operand is known non-negative. I’d like to see a call to
computeKnownBits. (Some typical ValueTracking calls: computeKnownBits/computeConstantRange/computeKnownFPClass/isGuaranteedNotToBeUndefOrPoison)
- Remaining insts in the worklist. It helps us to understand why the fixpoint check in InstCombine fails.
Thanks for the valuable feedback. I’ve noted these suggestions as GitHub issues and will definitely try to support them in the future!
I think this is a (highly) desirable feature. And personally I think we should just integrate these instrumentations into InstCombine in the future, instead of patching LLVM out-of-line. But that brings a crucial question: how well does this scale on large codebases (use LLVM to compile large codebases)? For example, what is the compilation time impact when this feature is enabled? Similarly, how well does the trace file scale? It seems like you’re exporting the trace in JSON file but I’m a bit worried that it’ll fill up my disk in minutes under this scenario.
1 Like
Thanks for the feedback!
Yeah, the current implementation definitely does not scale, which is why I limit the tool’s scope to debugging peephole optimizations. This narrower scope also makes it somewhat easier to port to WebAsm. Ideally, though, we should reuse LLVM’s existing tracing or instrumentation infrastructure if possible.
For integration, I am not sure whether there is a non-intrusive way to instrument InstCombine while still obtaining the same level of debugging information. The current instrumentation, unless automated, also hurts code readability, and developers could easily forget to add instrumentation at some transformation sites.
I am also wondering whether XRay could be used for this purpose. I do not have much experience with it, so I am not sure what its performance overhead would look like. I assume WebAssembly is not supported, but that should not be a major concern, since we could always run the analysis on a server, similar to Godbolt.
Unless InstCombine rules are described as a DSL (like tablegen), I personally don’t like to make this feature in-tree.
3 Likes