The FunctionPass is similar to the MIRPass. It works at the
function-level. The ModulePass has a global view. It sees all globals
and functions.
LLVM is building a new inliner as a module pass with ML and PGO. It
will be more flexible in the order in which it can inline
functions. There is also a new function specialization module pass.
I am wondering whether there is interest in a GlobalMIRPass. Similar
to LLVM's ModulePass, it has a global view. It would enable new
optimizations on MIR.
I don't think so. It is pretty much impossible to have global passes without breaking incremental compilation or making it utterly ineffective. MIR passes exist mostly to make the backend churn through less IR which in turn improves compilation speed. Breaking incremental compilation is completely counter to that.
Note that you still can use incremental for release builds, though it does necessarily preclude some optimization.
Depending on what exactly (cg unit) global passes see, they are at least possible to incrementalize with careful recording of inputs to only rerun on changed input.
E.g. the global const load substitution could be structured such that it reruns on all code when the const changes but otherwise only reruns on changed code (and salsa-style incrementalize such that unchanged output doesn't rerun later derivations).
But this example is a poor one; uses of a const can (and I think may already sometime) be inlined with procedure-local analysis because the value is const-known already.
I think it would be helpful to provide some specific examples of where a global MIR optimization pass would be useful. Writing and maintaining correct MIR optimizations is complicated and, at least for right now, the additional complexity is really only worthwhile if they provide improved compiler throughput, better code quality or some combination thereof.
It sounds like you're interested in improving the quality of optimized code. Are there specific cases where LLVM currently generates low quality code? What optimizations would need to be implemented to improve them and why do the optimizations need to be global?
I am mostly interested in optimisation for the release mode. I am not the guy writing the IPOs. I want to enable people to write IPOs.
My main motivation is that optimisations (IPO or not) on MIR are more precise and powerful than generic LLVM optimisations. In LLVM function parameters are either by value or a pointer. In Rust, you have by value, borrowed, mutable borrowed, and maybe pointers. This distinction becomes only valuable with global optimisations.
LLVM also has metadata which allows encoding some of those properties into the IR. rustc will generate aliasing metadata for references in recent versions (godbolt).
This information is given to LLVM (e.g. for its IPO). Specifically, the current translation is (using the eventually-default opaque pointers mode) [godbolt]
I may have missed some combinations that translate to different LLVM IR attributes. This mapping may change in the future, and tighter bounds (e.g. readnone and a lot of function-level attributes) can be used/inferred if a function body is present (I used extern to suppress this).
That's great, but the difference between a local and global inliner should be obvious. I still believe that IPO on MIR could help. Getting the infrastructure into rustc should not take much effort. Then we can decide whether the new passes have an impact on performance.
The thing is... it's not. Inlining itself is a highly local decision.
There is a split between top-down inlining (don't lose semantic guarantees lost by transformations) and bottom-up inlining (inlined chunk is smaller thus a better candidate for inlining; includes discovered consts), but both are still localized decisions rather than global decisions.
I think the disconnect may be that from within procedure X, asking "is called procedure Y suitable for inlining" is a local decision and not a global one. You could get slightly better results by doing loop_until_fixpoint { inlining_pass; all_other_optimization; }, but this still doesn't require structuring as IPO.
Note that even e.g. global value numbering typically isn't an IPO; the "global" there refers to working accross multiple SSA basic blocks, not being interprocedural.
I agree that GVN is not IPO. I disagree with the inliner. Have a look at my first post. LLVM is designing a new global inliner with ML and PGO. You need to know the call graph, the sizes of the functions, and maybe the hot and cold functions.