Skip to content

Commit e8f590e

Browse files
[mlir][acc] Improve acc.loop support as a container (#137887)
Dialects which have their own loop representation not representable with numeric bounds + steps cannot be represented cleanly with acc.loop. In such a case, we permit the dialects representation with acc.loop merely encompasing its loop representation. This limitation became obvious when looking at range / random iterator C++ loops. The API of acc.loop was updated to test for this differentiation. Additionally, the verifier was updated to check for consistent bounds and whether inner-loops are contained when it works as a container.
1 parent 4a6c81d commit e8f590e

File tree

4 files changed

+142
-6
lines changed

4 files changed

+142
-6
lines changed

mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,11 @@ def OpenACC_LoopOp : OpenACC_Op<"loop",
21272127
/// Used to retrieve the block inside the op's region.
21282128
Block &getBody() { return getLoopRegions().front()->front(); }
21292129

2130+
/// Used to determine if this operation is merely a container for a loop
2131+
/// operation instead of being loop-like itself.
2132+
bool isLoopLike() { return !getLowerbound().empty(); }
2133+
bool isContainerLike() { return !isLoopLike(); }
2134+
21302135
/// Return true if the op has the auto attribute for the
21312136
/// mlir::acc::DeviceType::None device_type.
21322137
bool hasAuto();

mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2298,6 +2298,14 @@ LogicalResult checkDeviceTypes(mlir::ArrayAttr deviceTypes) {
22982298
}
22992299

23002300
LogicalResult acc::LoopOp::verify() {
2301+
if (getUpperbound().size() != getStep().size())
2302+
return emitError() << "number of upperbounds expected to be the same as "
2303+
"number of steps";
2304+
2305+
if (getUpperbound().size() != getLowerbound().size())
2306+
return emitError() << "number of upperbounds expected to be the same as "
2307+
"number of lowerbounds";
2308+
23012309
if (!getUpperbound().empty() && getInclusiveUpperbound() &&
23022310
(getUpperbound().size() != getInclusiveUpperbound()->size()))
23032311
return emitError() << "inclusiveUpperbound size is expected to be the same"
@@ -2415,6 +2423,49 @@ LogicalResult acc::LoopOp::verify() {
24152423
if (getRegion().empty())
24162424
return emitError("expected non-empty body.");
24172425

2426+
// When it is container-like - it is expected to hold a loop-like operation.
2427+
if (isContainerLike()) {
2428+
// Obtain the maximum collapse count - we use this to check that there
2429+
// are enough loops contained.
2430+
uint64_t collapseCount = getCollapseValue().value_or(1);
2431+
if (getCollapseAttr()) {
2432+
for (auto collapseEntry : getCollapseAttr()) {
2433+
auto intAttr = mlir::dyn_cast<IntegerAttr>(collapseEntry);
2434+
if (intAttr.getValue().getZExtValue() > collapseCount)
2435+
collapseCount = intAttr.getValue().getZExtValue();
2436+
}
2437+
}
2438+
2439+
// We want to check that we find enough loop-like operations inside.
2440+
// PreOrder walk allows us to walk in a breadth-first manner at each nesting
2441+
// level.
2442+
mlir::Operation *expectedParent = this->getOperation();
2443+
bool foundSibling = false;
2444+
getRegion().walk<WalkOrder::PreOrder>([&](mlir::Operation *op) {
2445+
if (mlir::isa<mlir::LoopLikeOpInterface>(op)) {
2446+
// This effectively checks that we are not looking at a sibling loop.
2447+
if (op->getParentOfType<mlir::LoopLikeOpInterface>() !=
2448+
expectedParent) {
2449+
foundSibling = true;
2450+
return mlir::WalkResult::interrupt();
2451+
}
2452+
2453+
collapseCount--;
2454+
expectedParent = op;
2455+
}
2456+
// We found enough contained loops.
2457+
if (collapseCount == 0)
2458+
return mlir::WalkResult::interrupt();
2459+
return mlir::WalkResult::advance();
2460+
});
2461+
2462+
if (foundSibling)
2463+
return emitError("found sibling loops inside container-like acc.loop");
2464+
if (collapseCount != 0)
2465+
return emitError("failed to find enough loop-like operations inside "
2466+
"container-like acc.loop");
2467+
}
2468+
24182469
return success();
24192470
}
24202471

mlir/test/Dialect/OpenACC/invalid.mlir

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,6 @@ func.func @acc_combined() {
752752
// expected-error @below {{expected 'loop'}}
753753
acc.parallel combined() {
754754
}
755-
756755
return
757756
}
758757

@@ -762,7 +761,6 @@ func.func @acc_combined() {
762761
// expected-error @below {{expected compute construct name}}
763762
acc.loop combined(loop) {
764763
}
765-
766764
return
767765
}
768766

@@ -772,7 +770,6 @@ func.func @acc_combined() {
772770
// expected-error @below {{expected 'loop'}}
773771
acc.parallel combined(parallel loop) {
774772
}
775-
776773
return
777774
}
778775

@@ -782,6 +779,43 @@ func.func @acc_combined() {
782779
// expected-error @below {{expected ')'}}
783780
acc.loop combined(parallel loop) {
784781
}
782+
return
783+
}
784+
785+
// -----
785786

787+
func.func @acc_loop_container() {
788+
%c0 = arith.constant 0 : index
789+
%c10 = arith.constant 10 : index
790+
%c1 = arith.constant 1 : index
791+
// expected-error @below {{found sibling loops inside container-like acc.loop}}
792+
acc.loop {
793+
scf.for %arg4 = %c0 to %c10 step %c1 {
794+
scf.yield
795+
}
796+
scf.for %arg5 = %c0 to %c10 step %c1 {
797+
scf.yield
798+
}
799+
acc.yield
800+
} attributes { collapse = [2], collapseDeviceType = [#acc.device_type<none>] }
801+
return
802+
}
803+
804+
// -----
805+
806+
func.func @acc_loop_container() {
807+
%c0 = arith.constant 0 : index
808+
%c10 = arith.constant 10 : index
809+
%c1 = arith.constant 1 : index
810+
// expected-error @below {{failed to find enough loop-like operations inside container-like acc.loop}}
811+
acc.loop {
812+
scf.for %arg4 = %c0 to %c10 step %c1 {
813+
scf.for %arg5 = %c0 to %c10 step %c1 {
814+
scf.yield
815+
}
816+
scf.yield
817+
}
818+
acc.yield
819+
} attributes { collapse = [3], collapseDeviceType = [#acc.device_type<none>] }
786820
return
787821
}

mlir/test/Dialect/OpenACC/ops.mlir

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,22 +1862,26 @@ func.func @acc_num_gangs() {
18621862

18631863
// CHECK-LABEL: func.func @acc_combined
18641864
func.func @acc_combined() {
1865+
%c0 = arith.constant 0 : index
1866+
%c10 = arith.constant 10 : index
1867+
%c1 = arith.constant 1 : index
1868+
18651869
acc.parallel combined(loop) {
1866-
acc.loop combined(parallel) {
1870+
acc.loop combined(parallel) control(%arg3 : index) = (%c0 : index) to (%c10 : index) step (%c1 : index) {
18671871
acc.yield
18681872
}
18691873
acc.terminator
18701874
}
18711875

18721876
acc.kernels combined(loop) {
1873-
acc.loop combined(kernels) {
1877+
acc.loop combined(kernels) control(%arg3 : index) = (%c0 : index) to (%c10 : index) step (%c1 : index) {
18741878
acc.yield
18751879
}
18761880
acc.terminator
18771881
}
18781882

18791883
acc.serial combined(loop) {
1880-
acc.loop combined(serial) {
1884+
acc.loop combined(serial) control(%arg3 : index) = (%c0 : index) to (%c10 : index) step (%c1 : index) {
18811885
acc.yield
18821886
}
18831887
acc.terminator
@@ -1933,3 +1937,45 @@ acc.private.recipe @privatization_memref_i32 : memref<i32> init {
19331937

19341938
// CHECK-LABEL: acc.private.recipe @privatization_memref_i32
19351939
// CHECK: memref.alloca
1940+
1941+
// -----
1942+
1943+
func.func @acc_loop_container() {
1944+
%c0 = arith.constant 0 : index
1945+
%c10 = arith.constant 10 : index
1946+
%c1 = arith.constant 1 : index
1947+
acc.loop {
1948+
scf.for %arg4 = %c0 to %c10 step %c1 {
1949+
scf.yield
1950+
}
1951+
acc.yield
1952+
}
1953+
return
1954+
}
1955+
1956+
// CHECK-LABEL: func.func @acc_loop_container
1957+
// CHECK: acc.loop
1958+
// CHECK: scf.for
1959+
1960+
// -----
1961+
1962+
func.func @acc_loop_container() {
1963+
%c0 = arith.constant 0 : index
1964+
%c10 = arith.constant 10 : index
1965+
%c1 = arith.constant 1 : index
1966+
acc.loop {
1967+
scf.for %arg4 = %c0 to %c10 step %c1 {
1968+
scf.for %arg5 = %c0 to %c10 step %c1 {
1969+
scf.yield
1970+
}
1971+
scf.yield
1972+
}
1973+
acc.yield
1974+
} attributes { collapse = [2], collapseDeviceType = [#acc.device_type<none>] }
1975+
return
1976+
}
1977+
1978+
// CHECK-LABEL: func.func @acc_loop_container
1979+
// CHECK: acc.loop
1980+
// CHECK: scf.for
1981+
// CHECK: scf.for

0 commit comments

Comments
 (0)