Skip to content

Commit 2c8e9c7

Browse files
committed
[MLIR][OpenMP] Add omp.simd operation
This patch introduces the `omp.simd` operation. In contrast to the existing `omp.simdloop` operation, it is intended to hold SIMD information within worksharing loops, rather than representing a SIMD-only loop. Some examples of such loops are "omp do/for simd", "omp distribute simd", "omp target teams distribute parallel do/for simd", etc. For more context on this work, refer to PR #79559. This operation must always be nested within an `omp.wsloop` operation as its only non-terminator child. It follows the same approach as the `omp.distribute` operation, by serving as a simple wrapper operation holding clause information.
1 parent c9a6e99 commit 2c8e9c7

File tree

4 files changed

+515
-12
lines changed

4 files changed

+515
-12
lines changed

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

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ def WsLoopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments,
505505

506506
/// Returns the number of reduction variables.
507507
unsigned getNumReductionVars() { return getReductionVars().size(); }
508+
509+
/// Returns its nested 'omp.simd' operation, if present.
510+
SimdOp getNestedSimd();
508511
}];
509512
let hasCustomAssemblyFormat = 1;
510513
let assemblyFormat = [{
@@ -617,11 +620,84 @@ def SimdLoopOp : OpenMP_Op<"simdloop", [AttrSizedOperandSegments,
617620
let hasVerifier = 1;
618621
}
619622

623+
def SimdOp : OpenMP_Op<"simd",
624+
[AttrSizedOperandSegments, MemoryEffects<[MemWrite]>,
625+
HasParent<"WsLoopOp">]> {
626+
let summary = "simd construct";
627+
let description = [{
628+
The simd construct can be applied to a loop to indicate that the loop can be
629+
transformed into a SIMD loop (that is, multiple iterations of the loop can
630+
be executed concurrently using SIMD instructions).
631+
632+
This operation is intended to hold SIMD information for a worksharing loop
633+
(i.e. "omp for simd"), so it must always be nested inside of a parent
634+
"omp.wsloop" operation as its only child. For SIMD loops not combined with a
635+
worksharing loop (i.e. "omp simd"), the "omp.simdloop" is used instead.
636+
637+
The body region can contain any number of blocks. The region is terminated
638+
by "omp.yield" instruction without operands.
639+
640+
The `alignment_values` attribute additionally specifies alignment of each
641+
corresponding aligned operand. Note that `aligned_vars` and
642+
`alignment_values` should contain the same number of elements.
643+
644+
When an if clause is present and evaluates to false, the preferred number of
645+
iterations to be executed concurrently is one, regardless of whether
646+
a simdlen clause is specified.
647+
648+
The optional `nontemporal` attribute specifies variables which have low
649+
temporal locality across the iterations where they are accessed.
650+
651+
The optional `order` attribute specifies which order the iterations of the
652+
associate loops are executed in. Currently the only option for this
653+
attribute is "concurrent".
654+
655+
When a simdlen clause is present, the preferred number of iterations to be
656+
executed concurrently is the value provided to the simdlen clause.
657+
658+
The safelen clause specifies that no two concurrent iterations within a
659+
SIMD chunk can have a distance in the logical iteration space that is
660+
greater than or equal to the value given in the clause.
661+
```
662+
omp.wsloop for (%i) : index = (%c0) to (%c10) step (%c1) {
663+
omp.simd <clauses> {
664+
// block operations
665+
omp.yield
666+
}
667+
omp.yield
668+
```
669+
}];
670+
671+
// TODO: Add other clauses
672+
let arguments = (ins Variadic<OpenMP_PointerLikeType>:$aligned_vars,
673+
OptionalAttr<I64ArrayAttr>:$alignment_values,
674+
Optional<I1>:$if_expr,
675+
Variadic<OpenMP_PointerLikeType>:$nontemporal_vars,
676+
OptionalAttr<OrderKindAttr>:$order_val,
677+
ConfinedAttr<OptionalAttr<I64Attr>, [IntPositive]>:$simdlen,
678+
ConfinedAttr<OptionalAttr<I64Attr>, [IntPositive]>:$safelen
679+
);
680+
681+
let regions = (region AnyRegion:$region);
682+
let assemblyFormat = [{
683+
oilist(`aligned` `(`
684+
custom<AlignedClause>($aligned_vars, type($aligned_vars),
685+
$alignment_values) `)`
686+
|`if` `(` $if_expr `)`
687+
|`nontemporal` `(` $nontemporal_vars `:` type($nontemporal_vars) `)`
688+
|`order` `(` custom<ClauseAttr>($order_val) `)`
689+
|`simdlen` `(` $simdlen `)`
690+
|`safelen` `(` $safelen `)`
691+
) $region attr-dict
692+
}];
693+
694+
let hasVerifier = 1;
695+
}
620696

621697
def YieldOp : OpenMP_Op<"yield",
622698
[Pure, ReturnLike, Terminator,
623699
ParentOneOf<["WsLoopOp", "ReductionDeclareOp",
624-
"AtomicUpdateOp", "SimdLoopOp"]>]> {
700+
"AtomicUpdateOp", "SimdLoopOp", "SimdOp"]>]> {
625701
let summary = "loop yield and termination operation";
626702
let description = [{
627703
"omp.yield" yields SSA values from the OpenMP dialect op region and

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

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,28 +1131,33 @@ void printLoopControl(OpAsmPrinter &p, Operation *op, Region &region,
11311131
}
11321132

11331133
//===----------------------------------------------------------------------===//
1134-
// Verifier for Simd construct [2.9.3.1]
1134+
// Verifier for Simd constructs [2.9.3.1]
11351135
//===----------------------------------------------------------------------===//
11361136

1137-
LogicalResult SimdLoopOp::verify() {
1138-
if (this->getLowerBound().empty()) {
1139-
return emitOpError() << "empty lowerbound for simd loop operation";
1140-
}
1141-
if (this->getSimdlen().has_value() && this->getSafelen().has_value() &&
1142-
this->getSimdlen().value() > this->getSafelen().value()) {
1143-
return emitOpError()
1137+
template <typename OpTy>
1138+
static LogicalResult verifySimdOp(OpTy op) {
1139+
if (op.getSimdlen().has_value() && op.getSafelen().has_value() &&
1140+
op.getSimdlen().value() > op.getSafelen().value()) {
1141+
return op.emitOpError()
11441142
<< "simdlen clause and safelen clause are both present, but the "
11451143
"simdlen value is not less than or equal to safelen value";
11461144
}
1147-
if (verifyAlignedClause(*this, this->getAlignmentValues(),
1148-
this->getAlignedVars())
1145+
if (verifyAlignedClause(op, op.getAlignmentValues(), op.getAlignedVars())
11491146
.failed())
11501147
return failure();
1151-
if (verifyNontemporalClause(*this, this->getNontemporalVars()).failed())
1148+
if (verifyNontemporalClause(op, op.getNontemporalVars()).failed())
11521149
return failure();
11531150
return success();
11541151
}
11551152

1153+
LogicalResult SimdLoopOp::verify() {
1154+
if (this->getLowerBound().empty())
1155+
return emitOpError() << "empty lowerbound for simd loop operation";
1156+
return verifySimdOp(*this);
1157+
}
1158+
1159+
LogicalResult SimdOp::verify() { return verifySimdOp(*this); }
1160+
11561161
//===----------------------------------------------------------------------===//
11571162
// Verifier for Distribute construct [2.9.4.1]
11581163
//===----------------------------------------------------------------------===//
@@ -1329,7 +1334,34 @@ void WsLoopOp::build(OpBuilder &builder, OperationState &state,
13291334
state.addAttributes(attributes);
13301335
}
13311336

1337+
SimdOp WsLoopOp::getNestedSimd() {
1338+
auto ops = this->getOps<SimdOp>();
1339+
assert(std::distance(ops.begin(), ops.end()) <= 1 &&
1340+
"There can only be a single omp.simd child at most");
1341+
return ops.empty() ? SimdOp() : *ops.begin();
1342+
}
1343+
13321344
LogicalResult WsLoopOp::verify() {
1345+
// Check that, if it has an omp.simd child, it must be the only one.
1346+
bool hasSimd = false, hasOther = false;
1347+
for (auto &op : this->getOps()) {
1348+
if (isa<SimdOp>(op)) {
1349+
if (hasSimd)
1350+
return emitOpError() << "cannot have multiple 'omp.simd' child ops";
1351+
hasSimd = true;
1352+
1353+
if (hasOther)
1354+
break;
1355+
} else if (!op.hasTrait<OpTrait::IsTerminator>()) {
1356+
hasOther = true;
1357+
if (hasSimd)
1358+
break;
1359+
}
1360+
}
1361+
if (hasSimd && hasOther)
1362+
return emitOpError() << "if 'omp.simd' is a child, it must be the only "
1363+
"non-terminator child op";
1364+
13331365
return verifyReductionVarList(*this, getReductions(), getReductionVars());
13341366
}
13351367

0 commit comments

Comments
 (0)