-
Couldn't load subscription status.
- Fork 15k
[mlir][OpenACC] add unstructured attributes for acc.loop with early exits #164990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| @llvm/pr-subscribers-openacc @llvm/pr-subscribers-mlir-openacc Author: None (jeanPerier) Changes"!$acc loop" directive may be placed above loops with early exits. Currently flang lowers loop with early exits to explicit control flow (this may be revisited when MLIR allows early exits in structured region). The acc loop directive cannot simply be ignored in such case in lowering because it may hold data clauses that should be applied when reaching that point. This patch adds an "unstructured" attribute to acc.loop to support that case. Such acc.loop is just a container and the loop with early exit will be executed sequentially. Full diff: https://github.com/llvm/llvm-project/pull/164990.diff 4 Files Affected:
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td index 2f87975ebaa04..0a2b3f9e5e601 100644 --- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td @@ -2487,6 +2487,12 @@ def OpenACC_LoopOp : OpenACC_Op<"loop", device-type-aware getter methods. When modifying these operands, the corresponding `device_type` attributes must be updated to maintain consistency between operands and their target device types. + + The `unstructured` attribute indicates that the loops inside the OpenACC + construct contain early exits and cannot be lowered to structured MLIR + operations. When this flag is set, the acc.loop should have no induction + variables and the loop must be implemented via explicit control flow + inside its body. }]; let arguments = (ins @@ -2520,7 +2526,8 @@ def OpenACC_LoopOp : OpenACC_Op<"loop", OptionalAttr<SymbolRefArrayAttr>:$firstprivatizationRecipes, Variadic<AnyType>:$reductionOperands, OptionalAttr<SymbolRefArrayAttr>:$reductionRecipes, - OptionalAttr<OpenACC_CombinedConstructsAttr>:$combined + OptionalAttr<OpenACC_CombinedConstructsAttr>:$combined, + UnitAttr:$unstructured ); let results = (outs Variadic<AnyType>:$results); diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index ca46629919dba..ceb6a92de3772 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -3065,8 +3065,12 @@ LogicalResult acc::LoopOp::verify() { if (getRegion().empty()) return emitError("expected non-empty body."); - // When it is container-like - it is expected to hold a loop-like operation. - if (isContainerLike()) { + if (getUnstructured()) { + if (!isContainerLike()) + return emitError( + "unstructured acc.loop must not have induction variables"); + } else if (isContainerLike()) { + // When it is container-like - it is expected to hold a loop-like operation. // Obtain the maximum collapse count - we use this to check that there // are enough loops contained. uint64_t collapseCount = getCollapseValue().value_or(1); diff --git a/mlir/test/Dialect/OpenACC/invalid.mlir b/mlir/test/Dialect/OpenACC/invalid.mlir index 26b63fbe182ea..0e75894eaeceb 100644 --- a/mlir/test/Dialect/OpenACC/invalid.mlir +++ b/mlir/test/Dialect/OpenACC/invalid.mlir @@ -492,6 +492,15 @@ func.func @fct1(%0 : !llvm.ptr) -> () { // ----- +%i1 = arith.constant 1 : i32 +%i2 = arith.constant 10 : i32 +// expected-error@+1 {{unstructured acc.loop must not have induction variables}} +acc.loop control(%iv : i32) = (%i1 : i32) to (%i2 : i32) step (%i1 : i32) { + acc.yield +} attributes {independent = [#acc.device_type<none>], unstructured} + +// ----- + // expected-error@+1 {{expect at least one of num, dim or static values}} acc.loop gang({}) { "test.openacc_dummy_op"() : () -> () diff --git a/mlir/test/Dialect/OpenACC/ops.mlir b/mlir/test/Dialect/OpenACC/ops.mlir index 77d18da49276a..6e63efc59fe57 100644 --- a/mlir/test/Dialect/OpenACC/ops.mlir +++ b/mlir/test/Dialect/OpenACC/ops.mlir @@ -2143,6 +2143,20 @@ func.func @acc_loop_container() { // ----- +func.func @acc_unstructured_loop() { + acc.loop { + acc.yield + } attributes {independent = [#acc.device_type<none>], unstructured} + return +} + +// CHECK-LABEL: func.func @acc_unstructured_loop +// CHECK: acc.loop +// CHECK: acc.yield +// CHECK: } attributes {independent = [#acc.device_type<none>], unstructured} + +// ----- + // Test private recipe with data bounds for array slicing acc.private.recipe @privatization_memref_slice : memref<10x10xf32> init { ^bb0(%arg0: memref<10x10xf32>, %bounds0: !acc.data_bounds_ty, %bounds1: !acc.data_bounds_ty): |
| @llvm/pr-subscribers-mlir Author: None (jeanPerier) Changes"!$acc loop" directive may be placed above loops with early exits. Currently flang lowers loop with early exits to explicit control flow (this may be revisited when MLIR allows early exits in structured region). The acc loop directive cannot simply be ignored in such case in lowering because it may hold data clauses that should be applied when reaching that point. This patch adds an "unstructured" attribute to acc.loop to support that case. Such acc.loop is just a container and the loop with early exit will be executed sequentially. Full diff: https://github.com/llvm/llvm-project/pull/164990.diff 4 Files Affected:
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td index 2f87975ebaa04..0a2b3f9e5e601 100644 --- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td @@ -2487,6 +2487,12 @@ def OpenACC_LoopOp : OpenACC_Op<"loop", device-type-aware getter methods. When modifying these operands, the corresponding `device_type` attributes must be updated to maintain consistency between operands and their target device types. + + The `unstructured` attribute indicates that the loops inside the OpenACC + construct contain early exits and cannot be lowered to structured MLIR + operations. When this flag is set, the acc.loop should have no induction + variables and the loop must be implemented via explicit control flow + inside its body. }]; let arguments = (ins @@ -2520,7 +2526,8 @@ def OpenACC_LoopOp : OpenACC_Op<"loop", OptionalAttr<SymbolRefArrayAttr>:$firstprivatizationRecipes, Variadic<AnyType>:$reductionOperands, OptionalAttr<SymbolRefArrayAttr>:$reductionRecipes, - OptionalAttr<OpenACC_CombinedConstructsAttr>:$combined + OptionalAttr<OpenACC_CombinedConstructsAttr>:$combined, + UnitAttr:$unstructured ); let results = (outs Variadic<AnyType>:$results); diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index ca46629919dba..ceb6a92de3772 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -3065,8 +3065,12 @@ LogicalResult acc::LoopOp::verify() { if (getRegion().empty()) return emitError("expected non-empty body."); - // When it is container-like - it is expected to hold a loop-like operation. - if (isContainerLike()) { + if (getUnstructured()) { + if (!isContainerLike()) + return emitError( + "unstructured acc.loop must not have induction variables"); + } else if (isContainerLike()) { + // When it is container-like - it is expected to hold a loop-like operation. // Obtain the maximum collapse count - we use this to check that there // are enough loops contained. uint64_t collapseCount = getCollapseValue().value_or(1); diff --git a/mlir/test/Dialect/OpenACC/invalid.mlir b/mlir/test/Dialect/OpenACC/invalid.mlir index 26b63fbe182ea..0e75894eaeceb 100644 --- a/mlir/test/Dialect/OpenACC/invalid.mlir +++ b/mlir/test/Dialect/OpenACC/invalid.mlir @@ -492,6 +492,15 @@ func.func @fct1(%0 : !llvm.ptr) -> () { // ----- +%i1 = arith.constant 1 : i32 +%i2 = arith.constant 10 : i32 +// expected-error@+1 {{unstructured acc.loop must not have induction variables}} +acc.loop control(%iv : i32) = (%i1 : i32) to (%i2 : i32) step (%i1 : i32) { + acc.yield +} attributes {independent = [#acc.device_type<none>], unstructured} + +// ----- + // expected-error@+1 {{expect at least one of num, dim or static values}} acc.loop gang({}) { "test.openacc_dummy_op"() : () -> () diff --git a/mlir/test/Dialect/OpenACC/ops.mlir b/mlir/test/Dialect/OpenACC/ops.mlir index 77d18da49276a..6e63efc59fe57 100644 --- a/mlir/test/Dialect/OpenACC/ops.mlir +++ b/mlir/test/Dialect/OpenACC/ops.mlir @@ -2143,6 +2143,20 @@ func.func @acc_loop_container() { // ----- +func.func @acc_unstructured_loop() { + acc.loop { + acc.yield + } attributes {independent = [#acc.device_type<none>], unstructured} + return +} + +// CHECK-LABEL: func.func @acc_unstructured_loop +// CHECK: acc.loop +// CHECK: acc.yield +// CHECK: } attributes {independent = [#acc.device_type<none>], unstructured} + +// ----- + // Test private recipe with data bounds for array slicing acc.private.recipe @privatization_memref_slice : memref<10x10xf32> init { ^bb0(%arg0: memref<10x10xf32>, %bounds0: !acc.data_bounds_ty, %bounds1: !acc.data_bounds_ty): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
| Hi Jean! Thank you for this work! Can you please make sure that CIR build still works? I am guessing that with LoopOp constructor changes there will be a few spots needing to be fixed. |
I just did a pull of this and it compiles/runs my tests. For LoopOp I'm using the |
"!$acc loop" directive may be placed above loops with early exits.
Currently flang lowers loop with early exits to explicit control flow (this may be revisited when MLIR allows early exits in structured region). The acc loop directive cannot simply be ignored in such case in lowering because it may hold data clauses that should be applied when reaching that point.
This patch adds an "unstructured" attribute to acc.loop to support that case.
An acc.loop with such attributes may hold data operands but must have no controls. It is expected that the loop logic is implemented in its body in a way that the acc dialect may not understand.
Such acc.loop is just a container and the loop with early exit will be executed sequentially.