Skip to content

Commit 37792b4

Browse files
committed
[MLIR][Flang][OpenMP] Remove omp.parallel from loop wrapper ops
This patch updates the `omp.parallel` operation according to the results of the discussion in [this RFC](https://discourse.llvm.org/t/rfc-disambiguation-between-loop-and-block-associated-omp-parallelop/79972). It is removed from the set of loop wrapper operations, changing the expected MLIR representation for composite `distribute parallel do/for` into the following: ```mlir omp.parallel { ... omp.distribute { omp.wsloop { omp.loop_nest ... { ... } omp.terminator } omp.terminator } ... omp.terminator } ``` MLIR verifiers for operations impacted by this representation change are updated, as well as related tests. The `LoopWrapperInterface` is also updated, since it's no longer representing an optional "role" of an operation but a mandatory set of restrictions instead.
1 parent 60e9fb9 commit 37792b4

File tree

6 files changed

+126
-156
lines changed

6 files changed

+126
-156
lines changed

flang/lib/Lower/OpenMP/DataSharingProcessor.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,7 @@ void DataSharingProcessor::insertBarrier() {
231231
void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
232232
mlir::omp::LoopNestOp loopOp;
233233
if (auto wrapper = mlir::dyn_cast<mlir::omp::LoopWrapperInterface>(op))
234-
loopOp = wrapper.isWrapper()
235-
? mlir::cast<mlir::omp::LoopNestOp>(wrapper.getWrappedLoop())
236-
: nullptr;
234+
loopOp = mlir::cast<mlir::omp::LoopNestOp>(wrapper.getWrappedLoop());
237235

238236
bool cmpCreated = false;
239237
mlir::OpBuilder::InsertionGuard guard(firOpBuilder);

mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove, RecipeInterface]>
129129
def ParallelOp : OpenMP_Op<"parallel", traits = [
130130
AttrSizedOperandSegments, AutomaticAllocationScope,
131131
DeclareOpInterfaceMethods<ComposableOpInterface>,
132-
DeclareOpInterfaceMethods<LoopWrapperInterface>,
133132
DeclareOpInterfaceMethods<OutlineableOpenMPOpInterface>,
134133
RecursiveMemoryEffects
135134
], clauses = [

mlir/include/mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,23 @@ def ReductionClauseInterface : OpInterface<"ReductionClauseInterface"> {
7171

7272
def LoopWrapperInterface : OpInterface<"LoopWrapperInterface"> {
7373
let description = [{
74-
OpenMP operations that can wrap a single loop nest. When taking a wrapper
75-
role, these operations must only contain a single region with a single block
76-
in which there's a single operation and a terminator. That nested operation
77-
must be another loop wrapper or an `omp.loop_nest`.
74+
OpenMP operations that wrap a single loop nest. They must only contain a
75+
single region with a single block in which there's a single operation and a
76+
terminator. That nested operation must be another loop wrapper or an
77+
`omp.loop_nest`.
7878
}];
7979

8080
let cppNamespace = "::mlir::omp";
8181

8282
let methods = [
8383
InterfaceMethod<
8484
/*description=*/[{
85-
Tell whether the operation could be taking the role of a loop wrapper.
86-
That is, it has a single region with a single block in which there are
87-
two operations: another wrapper (also taking a loop wrapper role) or
88-
`omp.loop_nest` operation and a terminator.
85+
Check whether the operation is a valid loop wrapper. That is, it has a
86+
single region with a single block in which there are two operations:
87+
another loop wrapper or `omp.loop_nest` operation and a terminator.
8988
}],
9089
/*retTy=*/"bool",
91-
/*methodName=*/"isWrapper",
90+
/*methodName=*/"isValidWrapper",
9291
(ins ), [{}], [{
9392
if ($_op->getNumRegions() != 1)
9493
return false;
@@ -106,34 +105,31 @@ def LoopWrapperInterface : OpInterface<"LoopWrapperInterface"> {
106105
if (!secondOp.hasTrait<OpTrait::IsTerminator>())
107106
return false;
108107

109-
if (auto wrapper = ::llvm::dyn_cast<LoopWrapperInterface>(firstOp))
110-
return wrapper.isWrapper();
111-
112-
return ::llvm::isa<LoopNestOp>(firstOp);
108+
return ::llvm::isa<LoopNestOp, LoopWrapperInterface>(firstOp);
113109
}]
114110
>,
115111
InterfaceMethod<
116112
/*description=*/[{
117113
If there is another loop wrapper immediately nested inside, return that
118-
operation. Assumes this operation is taking a loop wrapper role.
114+
operation. Assumes this operation is a valid loop wrapper.
119115
}],
120116
/*retTy=*/"::mlir::omp::LoopWrapperInterface",
121117
/*methodName=*/"getNestedWrapper",
122118
(ins), [{}], [{
123-
assert($_op.isWrapper() && "Unexpected non-wrapper op");
119+
assert($_op.isValidWrapper() && "Unexpected non-wrapper op");
124120
Operation *nested = &*$_op->getRegion(0).op_begin();
125121
return ::llvm::dyn_cast<LoopWrapperInterface>(nested);
126122
}]
127123
>,
128124
InterfaceMethod<
129125
/*description=*/[{
130126
Return the loop nest nested directly or indirectly inside of this loop
131-
wrapper. Assumes this operation is taking a loop wrapper role.
127+
wrapper. Assumes this operation is a valid loop wrapper.
132128
}],
133129
/*retTy=*/"::mlir::Operation *",
134130
/*methodName=*/"getWrappedLoop",
135131
(ins), [{}], [{
136-
assert($_op.isWrapper() && "Unexpected non-wrapper op");
132+
assert($_op.isValidWrapper() && "Unexpected non-wrapper op");
137133
if (LoopWrapperInterface nested = $_op.getNestedWrapper())
138134
return nested.getWrappedLoop();
139135
return &*$_op->getRegion(0).op_begin();

mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,26 +1541,25 @@ static LogicalResult verifyPrivateVarList(OpType &op) {
15411541
}
15421542

15431543
LogicalResult ParallelOp::verify() {
1544-
// Check that it is a valid loop wrapper if it's taking that role.
1545-
if (isa<DistributeOp>((*this)->getParentOp())) {
1546-
if (!isWrapper())
1547-
return emitOpError() << "must take a loop wrapper role if nested inside "
1548-
"of 'omp.distribute'";
1544+
auto distributeChildOps = getOps<DistributeOp>();
1545+
if (!distributeChildOps.empty()) {
15491546
if (!isComposite())
15501547
return emitError()
1551-
<< "'omp.composite' attribute missing from composite wrapper";
1548+
<< "'omp.composite' attribute missing from composite operation";
15521549

1553-
if (LoopWrapperInterface nested = getNestedWrapper()) {
1554-
// Check for the allowed leaf constructs that may appear in a composite
1555-
// construct directly after PARALLEL.
1556-
if (!isa<WsloopOp>(nested))
1557-
return emitError() << "only supported nested wrapper is 'omp.wsloop'";
1558-
} else {
1559-
return emitOpError() << "must not wrap an 'omp.loop_nest' directly";
1550+
auto *ompDialect = getContext()->getLoadedDialect<OpenMPDialect>();
1551+
Operation &distributeOp = **distributeChildOps.begin();
1552+
for (Operation &childOp : getOps()) {
1553+
if (&childOp == &distributeOp || ompDialect != childOp.getDialect())
1554+
continue;
1555+
1556+
if (!childOp.hasTrait<OpTrait::IsTerminator>())
1557+
return emitError() << "unexpected OpenMP operation inside of composite "
1558+
"'omp.parallel'";
15601559
}
15611560
} else if (isComposite()) {
15621561
return emitError()
1563-
<< "'omp.composite' attribute present in non-composite wrapper";
1562+
<< "'omp.composite' attribute present in non-composite operation";
15641563
}
15651564

15661565
if (getAllocateVars().size() != getAllocatorVars().size())
@@ -1751,15 +1750,12 @@ void WsloopOp::build(OpBuilder &builder, OperationState &state,
17511750
}
17521751

17531752
LogicalResult WsloopOp::verify() {
1754-
if (!isWrapper())
1755-
return emitOpError() << "must be a loop wrapper";
1753+
if (!isValidWrapper())
1754+
return emitOpError() << "must be a valid loop wrapper";
17561755

1757-
auto wrapper =
1758-
llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());
17591756
bool isCompositeChildLeaf =
1760-
wrapper && wrapper.isWrapper() &&
1761-
(!llvm::isa<ParallelOp>(wrapper) ||
1762-
llvm::isa_and_present<DistributeOp>(wrapper->getParentOp()));
1757+
llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());
1758+
17631759
if (LoopWrapperInterface nested = getNestedWrapper()) {
17641760
if (!isComposite())
17651761
return emitError()
@@ -1813,18 +1809,14 @@ LogicalResult SimdOp::verify() {
18131809
if (verifyNontemporalClause(*this, getNontemporalVars()).failed())
18141810
return failure();
18151811

1816-
if (!isWrapper())
1817-
return emitOpError() << "must be a loop wrapper";
1812+
if (!isValidWrapper())
1813+
return emitOpError() << "must be a valid loop wrapper";
18181814

18191815
if (getNestedWrapper())
18201816
return emitOpError() << "must wrap an 'omp.loop_nest' directly";
18211817

1822-
auto wrapper =
1823-
llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());
18241818
bool isCompositeChildLeaf =
1825-
wrapper && wrapper.isWrapper() &&
1826-
(!llvm::isa<ParallelOp>(wrapper) ||
1827-
llvm::isa_and_present<DistributeOp>(wrapper->getParentOp()));
1819+
llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());
18281820

18291821
if (!isComposite() && isCompositeChildLeaf)
18301822
return emitError()
@@ -1859,18 +1851,22 @@ LogicalResult DistributeOp::verify() {
18591851
return emitError(
18601852
"expected equal sizes for allocate and allocator variables");
18611853

1862-
if (!isWrapper())
1863-
return emitOpError() << "must be a loop wrapper";
1854+
if (!isValidWrapper())
1855+
return emitOpError() << "must be a valid loop wrapper";
18641856

18651857
if (LoopWrapperInterface nested = getNestedWrapper()) {
18661858
if (!isComposite())
18671859
return emitError()
18681860
<< "'omp.composite' attribute missing from composite wrapper";
18691861
// Check for the allowed leaf constructs that may appear in a composite
18701862
// construct directly after DISTRIBUTE.
1871-
if (!isa<ParallelOp, SimdOp>(nested))
1872-
return emitError() << "only supported nested wrappers are 'omp.parallel' "
1873-
"and 'omp.simd'";
1863+
if (isa<WsloopOp>(nested)) {
1864+
if (!llvm::dyn_cast_if_present<ParallelOp>((*this)->getParentOp()))
1865+
return emitError() << "an 'omp.wsloop' nested wrapper is only allowed "
1866+
"when 'omp.parallel' is the direct parent";
1867+
} else if (!isa<SimdOp>(nested))
1868+
return emitError() << "only supported nested wrappers are 'omp.simd' and "
1869+
"'omp.wsloop'";
18741870
} else if (isComposite()) {
18751871
return emitError()
18761872
<< "'omp.composite' attribute present in non-composite wrapper";
@@ -2063,8 +2059,8 @@ LogicalResult TaskloopOp::verify() {
20632059
"may not appear on the same taskloop directive");
20642060
}
20652061

2066-
if (!isWrapper())
2067-
return emitOpError() << "must be a loop wrapper";
2062+
if (!isValidWrapper())
2063+
return emitOpError() << "must be a valid loop wrapper";
20682064

20692065
if (LoopWrapperInterface nested = getNestedWrapper()) {
20702066
if (!isComposite())
@@ -2161,11 +2157,8 @@ LogicalResult LoopNestOp::verify() {
21612157
<< "range argument type does not match corresponding IV type";
21622158
}
21632159

2164-
auto wrapper =
2165-
llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());
2166-
2167-
if (!wrapper || !wrapper.isWrapper())
2168-
return emitOpError() << "expects parent op to be a valid loop wrapper";
2160+
if (!llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp()))
2161+
return emitOpError() << "expects parent op to be a loop wrapper";
21692162

21702163
return success();
21712164
}
@@ -2175,8 +2168,6 @@ void LoopNestOp::gatherWrappers(
21752168
Operation *parent = (*this)->getParentOp();
21762169
while (auto wrapper =
21772170
llvm::dyn_cast_if_present<LoopWrapperInterface>(parent)) {
2178-
if (!wrapper.isWrapper())
2179-
break;
21802171
wrappers.push_back(wrapper);
21812172
parent = parent->getParentOp();
21822173
}

0 commit comments

Comments
 (0)