[flang-commits] [flang] [flang][hlfir] Add MLIR op for `do concurrent` (PR #130893)
Tom Eccles via flang-commits
flang-commits at lists.llvm.org
Wed Mar 12 02:55:43 PDT 2025
================
@@ -1863,5 +1864,120 @@ def hlfir_EvaluateInMemoryOp : hlfir_Op<"eval_in_mem", [AttrSizedOperandSegments
let hasVerifier = 1;
}
+def hlfir_DoConcurrentOp : hlfir_Op<"do_concurrent", [SingleBlock]> {
+ let summary = "do concurrent loop wrapper";
+
+ let description = [{
+ A wrapper operation for the actual op modeling `do concurrent` loops:
+ `hlfir.do_concurrent.loop` (see op declaration below for more info about it).
+
+ The `hlfir.do_concurrent` wrapper op consists of one single-block region with
+ the following properties:
+ - The first ops in the region are responsible for allocating storage for the
+ loop's iteration variables. This is property is **not** enforced by the op
+ verifier, but expected to be respected when building the op.
+ - The terminator of the region is an instance of `hlfir.do_concurrent.loop`.
+
+ For example, a 2D loop nest would be represented as follows:
+ ```
+ hlfir.do_concurrent {
+ %i = fir.alloca i32
+ %j = fir.alloca i32
+ hlfir.do_concurrent.loop ...
+ }
+ ```
+ }];
+
+ let regions = (region SizedRegion<1>:$region);
+
+ let assemblyFormat = "$region attr-dict";
+ let hasVerifier = 1;
+}
+
+def hlfir_DoConcurrentLoopOp : hlfir_Op<"do_concurrent.loop",
+ [AttrSizedOperandSegments, DeclareOpInterfaceMethods<LoopLikeOpInterface>,
+ Terminator, NoTerminator, SingleBlock, ParentOneOf<["DoConcurrentOp"]>]> {
+ let summary = "do concurrent loop";
+
+ let description = [{
+ An operation that models a Fortran `do concurrent` loop's header and block.
+ This is a single-region single-block terminator op that is expected to
+ terminate the region of a `omp.do_concurrent` wrapper op.
+
+ This op borrows from both `scf.parallel` and `fir.do_loop` ops. Similar to
+ `scf.parallel`, a loop nest takes 3 groups of SSA values as operands that
+ represent the lower bounds, upper bounds, and steps. Similar to `fir.do_loop`
+ the op takes one additional group of SSA values to represent reductions.
+
+ The body region **does not** have a terminator.
+
+ For example, a 2D loop nest with 2 reductions (sum and max) would be
+ represented as follows:
+ ```
+ // The wrapper of the loop
+ hlfir.do_concurrent {
+ %i = fir.alloca i32
+ %j = fir.alloca i32
+
+ // The actual `do concurrent` loop
+ hlfir.do_concurrent.loop
+ (%i_iv, %j_iv) = (%i_lb, %j_lb) to (%i_ub, %j_ub) step (%i_st, %j_st)
+ reduce(#fir.reduce_attr<add> -> %sum : !fir.ref<i32>,
+ #fir.reduce_attr<max> -> %max : !fir.ref<f32>) {
+
+ %0 = fir.convert %i_iv : (index) -> i32
+ fir.store %0 to %i : !fir.ref<i32>
+
+ %1 = fir.convert %j_iv : (index) -> i32
+ fir.store %1 to %j : !fir.ref<i32>
+
+ // ... loop body goes here ...
+ }
+ }
+ ```
+
+ Description of arguments:
+ - `lowerBound`: The group of SSA values for the nest's lower bounds.
+ - `upperBound`: The group of SSA values for the nest's upper bounds.
+ - `step`: The group of SSA values for the nest's steps.
+ - `reduceOperands`: The reduction SSA values, if any.
+ - `reduceAttrs`: Attributes to store reduction operations, if any.
+ - `loopAnnotation`: Loop metadata to be passed down the compiler pipeline to
+ LLVM.
+ }];
+
+ let arguments = (ins
+ Variadic<Index>:$lowerBound,
+ Variadic<Index>:$upperBound,
+ Variadic<Index>:$step,
+ Variadic<AnyType>:$reduceOperands,
+ OptionalAttr<ArrayAttr>:$reduceAttrs,
+ OptionalAttr<LoopAnnotationAttr>:$loopAnnotation
+ );
+
+ let regions = (region SizedRegion<1>:$region);
+
+ let hasCustomAssemblyFormat = 1;
+ let hasVerifier = 1;
+
+ let extraClassDeclaration = [{
+ /// Get Number of variadic operands
+ unsigned getNumOperands(unsigned segmentIdx) {
+ auto segments = (*this)->getAttrOfType<mlir::DenseI32ArrayAttr>(
+ getOperandSegmentSizeAttr());
+ return static_cast<unsigned>(segments[segmentIdx]);
+ }
+
+ // Get Number of reduction operands
+ unsigned getNumReduceOperands() {
+ return getNumOperands(3);
+ }
----------------
tblah wrote:
nit: I am worried that `getNumOperands` is an interface where it could be easy to get the wrong segment idx. There are easier ways to achieve this.
```suggestion
// Get Number of reduction operands
unsigned getNumReduceOperands() {
return getReduceOperands.size();
}
```
https://github.com/llvm/llvm-project/pull/130893
More information about the flang-commits
mailing list