@@ -293,3 +293,117 @@ been implemented, but it is closely linked to the `omp.canonical_loop` work.
293
293
Nevertheless, loop transformation that the ` collapse ` clause for loop-associated
294
294
worksharing constructs defines can be represented by introducing multiple
295
295
bounds, 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