@@ -293,3 +293,117 @@ been implemented, but it is closely linked to the `omp.canonical_loop` work.
293293Nevertheless, loop transformation that the ` collapse ` clause for loop-associated
294294worksharing constructs defines can be represented by introducing multiple
295295bounds, step and induction variables to the ` omp.loop_nest ` operation.
296+
297+ ## Compound Construct Representation
298+
299+ The OpenMP specification defines certain shortcuts that allow specifying
300+ multiple constructs in a single directive, which are referred to as compound
301+ constructs (e.g. ` parallel do ` contains the ` parallel ` and ` do ` constructs).
302+ These can be further classified into [ combined] ( #combined-constructs ) and
303+ [ composite] ( #composite-constructs ) constructs. This section describes how they
304+ are represented in the dialect.
305+
306+ When clauses are specified for compound constructs, the OpenMP specification
307+ defines a set of rules to decide to which leaf constructs they apply, as well as
308+ potentially introducing some other implicit clauses. These rules must be taken
309+ into account by those creating the MLIR representation, since it is a per-leaf
310+ representation that expects these rules to have already been followed.
311+
312+ ### Combined Constructs
313+
314+ Combined constructs are semantically equivalent to specifying one construct
315+ immediately nested inside another. This property is used to simplify the dialect
316+ by representing them through the operations associated to each leaf construct.
317+ For example, ` target teams ` would be represented as follows:
318+
319+ ``` mlir
320+ omp.target ... {
321+ ...
322+ omp.teams ... {
323+ ...
324+ omp.terminator
325+ }
326+ ...
327+ omp.terminator
328+ }
329+ ```
330+
331+ ### Composite Constructs
332+
333+ Composite constructs are similar to combined constructs in that they specify the
334+ effect of one construct being applied immediately after another. However, they
335+ group together constructs that cannot be directly nested into each other.
336+ Specifically, they group together multiple loop-associated constructs that apply
337+ to the same collapsed loop nest.
338+
339+ As of version 5.2 of the OpenMP specification, the list of composite constructs
340+ is the following:
341+ - ` {do,for} simd ` ;
342+ - ` distribute simd ` ;
343+ - ` distribute parallel {do,for} ` ;
344+ - ` distribute parallel {do,for} simd ` ; and
345+ - ` taskloop simd ` .
346+
347+ Even though the list of composite constructs is relatively short and it would
348+ also be possible to create dialect operations for each, it was decided to
349+ allow attaching multiple loop wrappers to a single loop instead. This minimizes
350+ redundancy in the dialect and maximizes its modularity, since there is a single
351+ operation for each leaf construct regardless of whether it can be part of a
352+ composite construct. On the other hand, this means the ` omp.loop_nest ` operation
353+ will have to be interpreted differently depending on how many and which loop
354+ wrappers are attached to it.
355+
356+ To simplify the detection of operations taking part in the representation of a
357+ composite construct, the ` ComposableOpInterface ` was introduced. Its purpose is
358+ to handle the ` omp.composite ` discardable dialect attribute that can optionally
359+ be attached to these operations. Operation verifiers will ensure its presence is
360+ consistent with the context the operation appears in, so that it is valid when
361+ the attribute is present if and only if it represents a leaf of a composite
362+ construct.
363+
364+ For example, the ` distribute simd ` composite construct is represented as
365+ follows:
366+
367+ ``` mlir
368+ omp.distribute ... {
369+ omp.simd ... {
370+ omp.loop_nest (%i) : index = (%lb) to (%ub) step (%step) {
371+ ...
372+ omp.yield
373+ }
374+ omp.terminator
375+ } {omp.composite}
376+ omp.terminator
377+ } {omp.composite}
378+ ```
379+
380+ One exception to this is the representation of the
381+ ` distribute parallel {do,for} ` composite construct. The presence of a
382+ block-associated ` parallel ` leaf construct would introduce many problems if it
383+ was allowed to work as a loop wrapper. In this case, the "hoisted ` omp.parallel `
384+ representation" is used instead. This consists in making ` omp.parallel ` the
385+ parent operation, with a nested ` omp.loop_nest ` wrapped by ` omp.distribute ` and
386+ ` omp.wsloop ` (and ` omp.simd ` , in the ` distribute parallel {do,for} simd ` case).
387+
388+ This approach works because ` parallel ` is a parallelism-generating construct,
389+ whereas ` distribute ` is a worksharing construct impacting the higher level
390+ ` teams ` construct, making the ordering between these constructs not cause
391+ semantic mismatches. This property is also exploited by LLVM's SPMD-mode.
392+
393+ ``` mlir
394+ omp.parallel ... {
395+ ...
396+ omp.distribute ... {
397+ omp.wsloop ... {
398+ omp.loop_nest (%i) : index = (%lb) to (%ub) step (%step) {
399+ ...
400+ omp.yield
401+ }
402+ omp.terminator
403+ } {omp.composite}
404+ omp.terminator
405+ } {omp.composite}
406+ ...
407+ omp.terminator
408+ } {omp.composite}
409+ ```
0 commit comments