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.
@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.
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.
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.
I lead a (still very young) quantum compiler project (open source) and we are in a situation as described in the motivation section. We have to support some (kind of) external frontend which emits scf , but we want to start our pipeline with high-level dialects like affine (and others). For this such a raising pass sounds useful as affine seems to contain passes which we need (like -affine-loop-unroll).
For now we are probably going to implement our own raising pass but we would happily drop it if the pass was supported upstream.
I’m not trying to block this RFC; I’m just asking out of curiosity. In my understanding, if you are emitting the scf dialect, why not target the affine dialect directly?
I totally understand this question. The problem for us is that we do not control this particular frontend. We are part of a consortium and agreed to support said frontend. We definitely urge them to produce affine in the future but this may likely take an extremely long time (maybe they will never do that), and we cannot halt the project for this reason. This raising pass is a somewhat elegant fix for this admittedly sub-optimal situation.
That being said: We will definitely push for a cleaner situation on our side. It is really just a way to unstuck ourselves - right now. We totally understand that our rationale (alone) is no justification for MLIR to add such a pass. On the other hand, seeing this RFC gave us some hope that the community interest is high enough. If not, it would be good to know for us.
A good compiler frontend is incredibly important, because it impacts how the rest of the compiler is designed and implemented.
If not, it would be good to know for us.
I can’t give you any great advice on this, but if that PR has stalled, maybe you guys could look into it and implement the feature yourselves. If it turns out well, you could write an RFC to discuss whether it can be upstreamed.
Yes we will likely implement such a pass on our end. Concerning upstreaming it: In principle we could do this. On the other hand there is already this RFC, and a related open PR. Hence we wanted to first check the status on these. From what I read elsewhere review capabilities are fairly limited in the LLVM project. We suspect that this is the reason why the PR went stale.
We would be glad to do this. Our plan would be like this
First implement a minimal version in our own repo (coming days).
Come back here and see if still nobody else is working on it.
If not: open a PR on a complete version.
The first step is needed to unstuck other work in our repo ASAP. This way we have time for the upstream PR.
@ftynse is it ok if the PR has two authors? We are first time contributors and want to get things right. We would do pair programming and review each others work in detail to reduce the work of the actual reviewer. Anything else a first time contributor should know? (We will search for the docs of course.)
Thanks @ftynse we will definitely start from the polygeist implementation.
@yanming from our side we will be two people working on it. We already have an llvm fork for some other reason inside the organization of our project. We intend to open the PR from there. But we would be happy to join forces and work on this together (3 people). If this sounds good to you we can probably find a way to communicate how to work together on this.
Hi, I’d be happy to collaborate on this project. As long as the solution is reasonable and elegant, I don’t mind which branch we start submitting PRs from.
I imagine we’ll probably want to upstream more than just scf-to-affine from polygeist. If you guys don’t mind waiting a few days, let me make a skeleton PR to add a polygeist dialect that we can add this and other passes too, along with the corresponding operations that will be required.