ConstantOp are only supported in the ModulePass because they require a GlobalCreator object that must be constructed from a ModuleOp.
If the standlaone FunctionPass encounters a ConstantOp, bufferization fails.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Can you just delete the function pass and have the module pass be the only registered pass?
mlir/lib/Dialect/Linalg/Transforms/ComprehensiveBufferize.cpp | ||
---|---|---|
2187–2196 |
mlir/test/Dialect/Linalg/comprehensive-func-bufferize-invalid.mlir | ||
---|---|---|
31–32 ↗ | (On Diff #355383) | But for constants defined inside of functions, why should one fail bufferization and kill parallelism (using module pass instead of function pass)? Is the pass also transparently promoting the constants to global - something not immediately expected/implied from bufferization? And in any case, if this is what is being done, is there a motivation to even support the FunctionPass? |
I don't find that there is a clear unambiguous way of moving forward here, only somewhat unsatisfactory options.
Forcing everything to be a ModulePass is premature atm: others have expressed a wish for generalization to "more generic ops with regions" than just func, this is still unclear.
The parallelism granularity decision and implications is a particular tradeoff in performance vs simplicity/what pass writers have to care about vs how passes must be written.
I greatly appreciate the "parallelism for free" aspect that comes with FuncOp granularity so I don't imagine this is going to change (ever?) to give users more control as it would be a pretty big footgun.
I wonder however whether ModuleOp could gain a "synchronized region" for things that are not functions?
@herhut also mentioned wishes for transactional updates du ModuleOp.
The "transparently promoting the constants to global" part is debatable.
Atm, tensor constant bufferization is a specific independent ModuleOp pass.
Going to memref.global easily turns this into readonly memory and is pretty clean; the alternatives I see are either to unroll constants or plop them into a vector + vector.store, both of which have large drawbacks.
Another alternative that I'm leaning towards is to allow memref.global to be constructible within a FuncOp but illegal to lower to LLVM.
During conversion to LLVM, legalization would consist in hoisting to the first enclosing ModuleOp.
I would personally like to see this as a nice clean module pass, and then make targeted changes informed by concrete use cases (actual need for more parallelism, need to be applied locally within a particular kind of enclosing op, etc.). Really for more advanced use cases this is going to be accessed via a direct C++ API, so the pass structure we choose here should be clean and simple and have nice invariants that make it easy to reason about extracting a targeted C++ API for new use cases. For example, the C++ API could take a std::function<Value(ConstantOp op)> for creating memref values from constants, which for the module pass would use a GlobalCreator, but for other users would be implemented otherwise. I don't want to promote uses of ComprehensiveBufferize with this weird non-support for constants.
I don't think we want to allow memref.global inside a function. That just seems like a huge layering violation (e.g. it is a Symbol but a FuncOp is not a SymbolTable).