The new construct represents a generic loop with two regions: one executed
before the loop condition is verifier and another after that. This construct
can be used to express both a "while" loop and a "do-while" loop, depending on
where the main payload is located. It is intended as an intermediate
abstraction for lowering, which will be added later. This form is relatively
easy to target from higher-level abstractions and supports transformations such
as loop rotation and LICM.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
mlir/include/mlir/Dialect/SCF/SCFOps.td | ||
---|---|---|
498 | Unclear what "match" means in this first sentence (match with each other? match with the outer op?). Maybe make it explicit "The two regions need not have the same argument list." | |
mlir/lib/Dialect/SCF/SCF.cpp | ||
1029 | A lot of this verification is subsumed by implementing RegionBranchOpInterface and calling RegionBranchOpInterface::verifyTypes (this would also reduce the testing burden, as we generally don't require testing properties verified by traits/interfaces) |
Thanks for adding the while!
mlir/include/mlir/Dialect/SCF/SCFOps.td | ||
---|---|---|
438 | .... represents a generic ... | |
469 | Label this as "After" region | |
mlir/lib/Dialect/SCF/SCF.cpp | ||
1032 | You switch a bit between first/second and before/after region in your terminology. Perhaps use one name consistently throughout doc and messages? |
mlir/include/mlir/Dialect/SCF/SCFOps.td | ||
---|---|---|
440–441 | Random question: we tend to easily add this single-block restriction, but I'm not sure why is it the right thing to do? |
mlir/include/mlir/Dialect/SCF/SCFOps.td | ||
---|---|---|
435 | Can you add the RegionControlFlowInterface to this operation? | |
448 | nit: The two regions ? | |
mlir/lib/Dialect/SCF/SCF.cpp | ||
976 | parser.resolveOperands? | |
1005 | nit: Add braces to the outer loop if the inner statement also has braces. | |
1043 | nit: Add braces around this, it isn't really trivial. |
Nice, thanks for implementing this ! :)
mlir/include/mlir/Dialect/SCF/SCFOps.td | ||
---|---|---|
466 | You probably want to be very loud in this part of the doc to make the textual part more intuitive. /* Compute values for "after" block*/ %foo#2 = call @compute(%arg1) : (f32) -> (i42, vector<3xf32>) /* "Before" region. */ %condition = call @evaluate_condition(%arg1, %foo#1) : (f32, vector<3xf32>) -> i1 /* Forward the argument (as result or "after" region argument). */ scf.yield %condition, %foo#0, %foo#1, %arg1 : i1, i42, vector<3xf32>, f32 | |
473 | I'd add a comment: /* Must match enclosing scf.while operand types */ for in the same spirit of making the textual part more intuitive. | |
mlir/lib/Dialect/SCF/SCF.cpp | ||
1029 | +1 |
Partially address review
mlir/include/mlir/Dialect/SCF/SCFOps.td | ||
---|---|---|
440–441 | I am considering to relax this, but this should be done with a proper RFC and apply to all SCF loops IMO. Allowing scf.yield in multiple blocks is equivalent to implementing a continue, and we may also want a break. Loop transformations currently rely on the single-block property. | |
466 | Added another example. I want to keep the simple examples simple. | |
mlir/lib/Dialect/SCF/SCF.cpp | ||
1029 | Not as much actually as I would have liked. We cannot use it to verify the yield of the 'before' block because it takes the condition operand that it does not forward. Looking at this closer, I am considering to introduce a different terminator for this region, something like scf.while_condition(%i1) %arg1, %arg2 : types to avoid breaking the "return-like" semantics of scf.yield. WDYT? |
mlir/lib/Dialect/SCF/SCF.cpp | ||
---|---|---|
1029 | About the yield "return semantics": I see the terminator as always terminating the region and transferring the control to the parent op, yield fits here to me just like other loops. I was also expecting the region branch op interface to support this, but it may haven't been implemented yet. @jurahul worked on this at some point? |
lowering next? ;-)
mlir/include/mlir/Dialect/SCF/SCFOps.td | ||
---|---|---|
438 | The a is now at the wrong place :-) This operation represents a generic .... |
mlir/lib/Dialect/SCF/SCF.cpp | ||
---|---|---|
1029 | If I read the code correctly, there is an assumption that all operands of a ReturnLike terminator are forwarded somewhere.
This is what we wrote in the spec. But I interpret the RegionBranchOpInterface API as "after receiving the flow of control from region X, the op can forwarded it to region Y or to its natural control-flow successor (next op in the block)". In the former case, there is an expectation that types match exactly AFAICS. |
mlir/lib/Dialect/SCF/SCF.cpp | ||
---|---|---|
1029 | Yes, I had spec'd an extension to RegionBranchOpInterface to support more extensive type checking, specifically around "internal" values that op's may produce and pass into attached regions. The main feature there was the interface declares, across all control flow edges, the types of values that flow across them, separately from the values. So its then becomes possible to typecheck say a region inputs when they feed not from the operation inputs but some internally generated values (which have no direct IR representation). And similarly typecheck region outputs that are consumed internally by the operation (and not directly feeding into the output). However, I have not yet had time to actually follow that up with a discourse RFC and actual implementation. |
mlir/lib/Dialect/SCF/SCF.cpp | ||
---|---|---|
1029 | I don't understand how you quote from the RegionBranchOpInterface contradicts the quote you're answering from me? On the contrary it seems to say the same thing here? Can we have a TODO somewhere for the missing RegionBranchOpInterface feature? |
mlir/lib/Dialect/SCF/SCF.cpp | ||
---|---|---|
1029 | I don't think there is a contradiction on RegionBranchOpInterface. However, there is a "ReturnLike" trait that RegionBranchOpInterface uses to understand that all operands are sent back to the parent op for dispatch. A potentially useful addition could be an interface method that specifies which operands instead of just taking all of them. That being said, "ReturnLike" seems to have some other semantics attached to it, and I am not willing to revise those semantics here or remove them from YieldOp for the purpose of this patch. Hence the new op. |
mlir/lib/Dialect/SCF/SCF.cpp | ||
---|---|---|
1029 |
I still don't understand why yield can't be directly used here? |
mlir/lib/Dialect/SCF/SCF.cpp | ||
---|---|---|
1029 | Because it has the ReturnLike trait, which disallows to take the leading i1 operand without the next region also having the leading i1 argument. ReturnLike is also connected to other concepts, so I am reluctant to change it without a proper discussion. |
Can you add the RegionControlFlowInterface to this operation?