[RFC] Add SCF-to-Affine Conversion Pass in MLIR (original) (raw)
August 25, 2025, 3:08am 1
This SCF-to-Affine pass is inspired by the Polygeist project, which implemented similar transformations.
Motivation
- Some MLIR frontends (e.g., ClangIR, Flang) and downstream projects generate SCF loops by default.
- Promoting analyzable SCF loops (e.g.,
scf.for) to Affine loops unlocks advanced analysis and polyhedral-style optimizations available in the Affine dialect. - This enables MLIR itself (not limited to specific projects such as Polygeist) to provide stronger optimization opportunities at the core dialect level.
- With this pass in MLIR core, any project generating SCF can reuse it without depending on Polygeist or reimplementing similar logic.
Inspiration
- The Polygeist project implemented SCF->Affine conversion as part of its optimization pipeline.
- This RFC proposes to bring a similar but independent, general-purpose implementation into MLIR, so it can be maintained and evolved together with the core dialects.
I have submitted an initial patch that implements the conversion from scf.for to affine.for.
PR: [SCFToAffine] Add a pass to raise scf to affine ops. by NexMing · Pull Request #152925 · llvm/llvm-project · GitHub
ftynse August 25, 2025, 6:39pm 2
Why reimplement instead of cleaning up the transformation in polygeist?
yanming August 26, 2025, 10:10am 3
@ftynse
Whether by porting Polygeist or reimplementing it, I believe our goals are aligned. From my research, MLIR already provides the --affine-raise-from-memref pass to handle the conversion of memref.load/memref.store. In practice, there is not much additional work needed in MLIR. At present, implementing scf.for and scf.if covers the vast majority of use cases, and reimplementing them does not require much effort. Therefore, I decided to implement it directly in MLIR.
I also used the Polybench benchmark suite to validate the effectiveness of my implementation.
In my view, the best approach is still to refactor the code to use the affine dialect. Although --affine-raise-from-memref sounds promising.
It seems highly likely that both the affine dialect and the SCF dialect will coexist simultaneously. Probably not all SCF IR can be converted to affine IR, which doesn’t seem very elegant.
Beyond that, the cases in the PR don’t appear to show much difference whether using affine.for or scf.for. I don’t see any significant benefits. I am not an expert on affine.
ftynse August 27, 2025, 7:28pm 5
I see the following in the commit message:
This patch is the first step in an incremental effort
- Extend support for converting other scf operations.
- Extend support for more complex loop forms and bounds.
What is the community this is serving / who is going to maintain it?
Polygeist is an LLVM incubator project. It is perfectly reasonable and expected to “promote” code from the incubator into a mainline project, which may require some modernization, cleanup and documentation effort, but arguably less than writing the same logic from scratch, hitting the same bugs, etc. And that would also solve the dependency issues should some project not want to depend on the incubator.
I’d like to understand why the proposed way forward is to re-implement something that already exists under the LLVM umbrella. If this is somehow more general-purpose, please provide specific examples how so. I would like to avoid the situation where we have two divergent instances of the same transformation logic in the overall ecosystem.
yanming August 28, 2025, 1:06pm 6
I am not opposed to being “promoted” from the incubator to the main project. I initially chose to reimplement because I thought the work required in the main project was easy and straightforward. From your explanation, I realized that I may have misjudged the actual situation. I will next try to port the Polygeist code to the main project and submit a new PR. Thank you for your reply. @ftynse
This is actually possible by making use of scf.execute_region ops where and when needed, i.e., around affine nests or loop bodies with non-affine subscripts (e.g. data dependent or non-linear) and adding the AffineScope trait to the scf.execute_region op.