Skip to content

Commit 571df01

Browse files
committed
[OpenMP][MLIR] Refactor and extend current map support by adding MapInfoOp and DataBoundsOp operations to the OpenMP Dialect
This patch adds two new operations: The first is the DataBoundsOp, which is based on OpenACC's DataBoundsOp, which holds stride, index, extent, lower bound and upper bounds which will be used in future follow up patches to perform initial array sectioning of mapped arrays, and Fortran pointer and allocatable mapping. Similarly to OpenACC, this new OpenMP DataBoundsOp also comes with a new OpenMP type, which helps to restrict operations to accepting only DataBoundsOp as an input or output where necessary (or other related operations that implement this type as a return). The patch also adds the MapInfoOp which rolls up some of the old map information stored in target operations into this new operation, and adds new information that will be utilised in the lowering of mapped variables, e.g. the aforementioned DataBoundsOp, but also a new ByCapture OpenMP MLIR attribute, and isImplicit boolean attribute. Both the ByCapture and isImplicit arguments will affect the lowering from the OpenMP dialect to LLVM-IR in minor but important ways, such as shifting the final maptype or generating different load/store combinations to maintain semantics with the OpenMP standard and alignment with the current Clang OpenMP output as best as possible. This MapInfoOp operation is slightly based on OpenACC's DataEntryOp, the main difference other than some slightly different fields (e,g, isImplicit/MapType/ByCapture) is that OpenACC's data operations "inherit" (the MLIR ODS equivalent) from this operation, whereas in OpenMP operations that utilise MapInfoOp's are composed of/contain them. A series of these MapInfoOp (one per map clause list item) is now held by target operations that represent OpenMP directives that utilise map clauses, e.g. TargetOp. MapInfoOp's do not have their own specialised lowering to LLVM-IR, instead the lowering is dependent on the particular container of the MapInfoOp's, e.g. TargetOp has its own lowering to LLVM-IR which utilised the information stored inside of MapInfoOp's to affect it's lowering and the end result of the LLVM-IR generated, which in turn can differ for host and device. This patch contains these operations, minor changes to the printing and parsing to support them, changes to tests (only those relevant to this segment of the patch, other test additions and changes are in other dependent patches in this series) and some alterations to the OpenMPToLLVM rewriter to support the new OpenMP type and operations. This patch is one in a series that are dependent on each other: https://reviews.llvm.org/D158734 https://reviews.llvm.org/D158735 https://reviews.llvm.org/D158737 Reviewers: kiranchandramohan, TIFitis, razvanlupusoru Differential Revision: https://reviews.llvm.org/D158732
1 parent 9120e85 commit 571df01

File tree

8 files changed

+587
-202
lines changed

8 files changed

+587
-202
lines changed

mlir/include/mlir/Dialect/OpenMP/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ mlir_tablegen(OpenMPOpsDialect.h.inc -gen-dialect-decls -dialect=omp)
77
mlir_tablegen(OpenMPOpsDialect.cpp.inc -gen-dialect-defs -dialect=omp)
88
mlir_tablegen(OpenMPOps.h.inc -gen-op-decls)
99
mlir_tablegen(OpenMPOps.cpp.inc -gen-op-defs)
10+
mlir_tablegen(OpenMPOpsTypes.h.inc -gen-typedef-decls -typedefs-dialect=omp)
11+
mlir_tablegen(OpenMPOpsTypes.cpp.inc -gen-typedef-defs -typedefs-dialect=omp)
1012
mlir_tablegen(OpenMPOpsEnums.h.inc -gen-enum-decls)
1113
mlir_tablegen(OpenMPOpsEnums.cpp.inc -gen-enum-defs)
1214
mlir_tablegen(OpenMPOpsAttributes.h.inc -gen-attrdef-decls -attrdefs-dialect=omp)

mlir/include/mlir/Dialect/OpenMP/OpenMPDialect.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#include "mlir/Interfaces/ControlFlowInterfaces.h"
2323
#include "mlir/Interfaces/SideEffectInterfaces.h"
2424

25+
#define GET_TYPEDEF_CLASSES
26+
#include "mlir/Dialect/OpenMP/OpenMPOpsTypes.h.inc"
27+
2528
#include "mlir/Dialect/OpenMP/OpenMPOpsDialect.h.inc"
2629
#include "mlir/Dialect/OpenMP/OpenMPOpsEnums.h.inc"
2730
#include "mlir/Dialect/OpenMP/OpenMPTypeInterfaces.h.inc"

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

Lines changed: 231 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def OpenMP_Dialect : Dialect {
2929
let cppNamespace = "::mlir::omp";
3030
let dependentDialects = ["::mlir::LLVM::LLVMDialect, ::mlir::func::FuncDialect"];
3131
let useDefaultAttributePrinterParser = 1;
32+
let useDefaultTypePrinterParser = 1;
3233
}
3334

3435
// OmpCommon requires definition of OpenACC_Dialect.
@@ -89,6 +90,10 @@ def IntLikeType : AnyTypeOf<[AnyInteger, Index]>;
8990
def OpenMP_PointerLikeType : TypeAlias<OpenMP_PointerLikeTypeInterface,
9091
"OpenMP-compatible variable type">;
9192

93+
class OpenMP_Type<string name, string typeMnemonic> : TypeDef<OpenMP_Dialect, name> {
94+
let mnemonic = typeMnemonic;
95+
}
96+
9297
//===----------------------------------------------------------------------===//
9398
// 2.12.7 Declare Target Directive
9499
//===----------------------------------------------------------------------===//
@@ -1004,6 +1009,220 @@ def FlushOp : OpenMP_Op<"flush"> {
10041009
}];
10051010
}
10061011

1012+
//===----------------------------------------------------------------------===//
1013+
// Map related constructs
1014+
//===----------------------------------------------------------------------===//
1015+
1016+
def CaptureThis : I32EnumAttrCase<"This", 0>;
1017+
def CaptureByRef : I32EnumAttrCase<"ByRef", 1>;
1018+
def CaptureByCopy : I32EnumAttrCase<"ByCopy", 2>;
1019+
def CaptureVLAType : I32EnumAttrCase<"VLAType", 3>;
1020+
1021+
def VariableCaptureKind : I32EnumAttr<
1022+
"VariableCaptureKind",
1023+
"variable capture kind",
1024+
[CaptureThis, CaptureByRef, CaptureByCopy, CaptureVLAType]> {
1025+
let genSpecializedAttr = 0;
1026+
let cppNamespace = "::mlir::omp";
1027+
}
1028+
1029+
def VariableCaptureKindAttr : EnumAttr<OpenMP_Dialect, VariableCaptureKind,
1030+
"variable_capture_kind"> {
1031+
let assemblyFormat = "`(` $value `)`";
1032+
}
1033+
1034+
def DataBoundsType : OpenMP_Type<"DataBounds", "data_bounds_ty"> {
1035+
let summary = "Type for representing omp data clause bounds information";
1036+
}
1037+
1038+
def DataBoundsOp : OpenMP_Op<"bounds",
1039+
[AttrSizedOperandSegments, NoMemoryEffect]> {
1040+
let summary = "Represents normalized bounds information for map clauses.";
1041+
1042+
let description = [{
1043+
This operation is a variation on the OpenACC dialects DataBoundsOp. Within
1044+
the OpenMP dialect it stores the bounds/range of data to be mapped to a
1045+
device specified by map clauses on target directives. Within,
1046+
the OpenMP dialect the DataBoundsOp is associated with MapInfoOp,
1047+
helping to store bounds information for the mapped variable.
1048+
1049+
It is used to support OpenMP array sectioning, Fortran pointer and
1050+
allocatable mapping and pointer/allocatable member of derived types.
1051+
In all cases the DataBoundsOp holds information on the section of
1052+
data to be mapped. Such as the upper bound and lower bound of the
1053+
section of data to be mapped. This information is currently
1054+
utilised by the LLVM-IR lowering to help generate instructions to
1055+
copy data to and from the device when processing target operations.
1056+
1057+
The example below copys a section of a 10-element array; all except the
1058+
first element, utilising OpenMP array sectioning syntax where array
1059+
subscripts are provided to specify the bounds to be mapped to device.
1060+
To simplify the examples, the constants are used directly, in reality
1061+
they will be MLIR SSA values.
1062+
1063+
C++:
1064+
```
1065+
int array[10];
1066+
#pragma target map(array[1:9])
1067+
```
1068+
=>
1069+
```mlir
1070+
omp.bounds lower_bound(1) upper_bound(9) extent(9) start_idx(0)
1071+
```
1072+
1073+
Fortran:
1074+
```
1075+
integer :: array(1:10)
1076+
!$target map(array(2:10))
1077+
```
1078+
=>
1079+
```mlir
1080+
omp.bounds lower_bound(1) upper_bound(9) extent(9) start_idx(1)
1081+
```
1082+
1083+
For Fortran pointers and allocatables (as well as those that are
1084+
members of derived types) the bounds information is provided by
1085+
the Fortran compiler and runtime through descriptor information.
1086+
1087+
A basic pointer example can be found below (constants again
1088+
provided for simplicity, where in reality SSA values will be
1089+
used, in this case that point to data yielded by Fortran's
1090+
descriptors):
1091+
1092+
Fortran:
1093+
```
1094+
integer, pointer :: ptr(:)
1095+
allocate(ptr(10))
1096+
!$target map(ptr)
1097+
```
1098+
=>
1099+
```mlir
1100+
omp.bounds lower_bound(0) upper_bound(9) extent(10) start_idx(1)
1101+
```
1102+
1103+
This operation records the bounds information in a normalized fashion
1104+
(zero-based). This works well with the `PointerLikeType`
1105+
requirement in data clauses - since a `lower_bound` of 0 means looking
1106+
at data at the zero offset from pointer.
1107+
1108+
This operation must have an `upper_bound` or `extent` (or both are allowed -
1109+
but not checked for consistency). When the source language's arrays are
1110+
not zero-based, the `start_idx` must specify the zero-position index.
1111+
}];
1112+
1113+
let arguments = (ins Optional<IntLikeType>:$lower_bound,
1114+
Optional<IntLikeType>:$upper_bound,
1115+
Optional<IntLikeType>:$extent,
1116+
Optional<IntLikeType>:$stride,
1117+
DefaultValuedAttr<BoolAttr, "false">:$stride_in_bytes,
1118+
Optional<IntLikeType>:$start_idx);
1119+
let results = (outs DataBoundsType:$result);
1120+
1121+
let assemblyFormat = [{
1122+
oilist(
1123+
`lower_bound` `(` $lower_bound `:` type($lower_bound) `)`
1124+
| `upper_bound` `(` $upper_bound `:` type($upper_bound) `)`
1125+
| `extent` `(` $extent `:` type($extent) `)`
1126+
| `stride` `(` $stride `:` type($stride) `)`
1127+
| `start_idx` `(` $start_idx `:` type($start_idx) `)`
1128+
) attr-dict
1129+
}];
1130+
1131+
let extraClassDeclaration = [{
1132+
/// The number of variable operands.
1133+
unsigned getNumVariableOperands() {
1134+
return getNumOperands();
1135+
}
1136+
1137+
/// The i-th variable operand passed.
1138+
Value getVariableOperand(unsigned i) {
1139+
return getOperands()[i];
1140+
}
1141+
}];
1142+
1143+
let hasVerifier = 1;
1144+
}
1145+
1146+
def MapInfoOp : OpenMP_Op<"map_info", [AttrSizedOperandSegments]> {
1147+
let arguments = (ins OpenMP_PointerLikeType:$var_ptr,
1148+
Optional<OpenMP_PointerLikeType>:$var_ptr_ptr,
1149+
Variadic<DataBoundsType>:$bounds, /* rank-0 to rank-{n-1} */
1150+
OptionalAttr<UI64Attr>:$map_type,
1151+
OptionalAttr<VariableCaptureKindAttr>:$map_capture_type,
1152+
DefaultValuedAttr<BoolAttr, "false">:$implicit,
1153+
OptionalAttr<StrAttr>:$name);
1154+
let results = (outs OpenMP_PointerLikeType:$omp_ptr);
1155+
1156+
let description = [{
1157+
The MapInfoOp captures information relating to individual OpenMP map clauses
1158+
that are applied to certain OpenMP directives such as Target and Target Data.
1159+
1160+
For example, the map type modifier; such as from, tofrom and to, the variable
1161+
being captured or the bounds of an array section being mapped.
1162+
1163+
It can be used to capture both implicit and explicit map information, where
1164+
explicit is an argument directly specified to an OpenMP map clause or implicit
1165+
where a variable is utilised in a target region but is defined externally to
1166+
the target region.
1167+
1168+
This map information is later used to aid the lowering of the target operations
1169+
they are attached to providing argument input and output context for kernels
1170+
generated or the target data mapping environment.
1171+
1172+
Example (Fortran):
1173+
1174+
```
1175+
integer :: index
1176+
!$target map(to: index)
1177+
```
1178+
=>
1179+
```mlir
1180+
omp.map_info var_ptr(%index_ssa) map_type(to) map_capture_type(ByRef) implicit(false)
1181+
name(index)
1182+
```
1183+
1184+
Description of arguments:
1185+
- `var_ptr`: The address of variable to copy.
1186+
- `var_ptr_ptr`: Used when the variable copied is a member of a class, structure
1187+
or derived type and refers to the originating struct.
1188+
- `bounds`: Used when copying slices of array's, pointers or pointer members of
1189+
objects (e.g. derived types or classes), indicates the bounds to be copied
1190+
of the variable. When it's an array slice it is in rank order where rank 0
1191+
is the inner-most dimension.
1192+
- `implicit`: indicates where the map item has been specified explicitly in a
1193+
map clause or captured implicitly by being used in a target region with no
1194+
map or other data mapping construct.
1195+
- 'map_clauses': OpenMP map type for this map capture, for example: from, to and
1196+
always. It's a bitfield composed of the OpenMP runtime flags stored in
1197+
OpenMPOffloadMappingFlags.
1198+
- 'map_capture_type': Capture type for the variable e.g. this, byref, byvalue, byvla
1199+
this can affect how the variable is lowered.
1200+
- `name`: Holds the name of variable as specified in user clause (including bounds).
1201+
}];
1202+
1203+
let assemblyFormat = [{
1204+
`var_ptr` `(` $var_ptr `:` type($var_ptr) `)`
1205+
oilist(
1206+
`var_ptr_ptr` `(` $var_ptr_ptr `:` type($var_ptr_ptr) `)`
1207+
| `map_clauses` `(` custom<MapClause>($map_type) `)`
1208+
| `capture` `(` custom<CaptureType>($map_capture_type) `)`
1209+
| `bounds` `(` $bounds `)`
1210+
) `->` type($omp_ptr) attr-dict
1211+
}];
1212+
1213+
let extraClassDeclaration = [{
1214+
/// The number of variable operands.
1215+
unsigned getNumVariableOperands() {
1216+
return getNumOperands();
1217+
}
1218+
1219+
/// The i-th variable operand passed.
1220+
Value getVariableOperand(unsigned i) {
1221+
return getOperands()[i];
1222+
}
1223+
}];
1224+
}
1225+
10071226
//===---------------------------------------------------------------------===//
10081227
// 2.14.2 target data Construct
10091228
//===---------------------------------------------------------------------===//
@@ -1044,16 +1263,14 @@ def Target_DataOp: OpenMP_Op<"target_data", [AttrSizedOperandSegments]>{
10441263
Optional<AnyInteger>:$device,
10451264
Variadic<OpenMP_PointerLikeType>:$use_device_ptr,
10461265
Variadic<OpenMP_PointerLikeType>:$use_device_addr,
1047-
Variadic<OpenMP_PointerLikeType>:$map_operands,
1048-
OptionalAttr<I64ArrayAttr>:$map_types);
1266+
Variadic<OpenMP_PointerLikeType>:$map_operands);
10491267

10501268
let regions = (region AnyRegion:$region);
10511269

10521270
let assemblyFormat = [{
10531271
oilist(`if` `(` $if_expr `:` type($if_expr) `)`
10541272
| `device` `(` $device `:` type($device) `)`
1055-
| `map`
1056-
`(` custom<MapClause>($map_operands, type($map_operands), $map_types) `)`
1273+
| `map_entries` `(` $map_operands `:` type($map_operands) `)`
10571274
| `use_device_ptr` `(` $use_device_ptr `:` type($use_device_ptr) `)`
10581275
| `use_device_addr` `(` $use_device_addr `:` type($use_device_addr) `)`)
10591276
$region attr-dict
@@ -1095,15 +1312,14 @@ def Target_EnterDataOp: OpenMP_Op<"target_enter_data",
10951312
let arguments = (ins Optional<I1>:$if_expr,
10961313
Optional<AnyInteger>:$device,
10971314
UnitAttr:$nowait,
1098-
Variadic<OpenMP_PointerLikeType>:$map_operands,
1099-
I64ArrayAttr:$map_types);
1315+
Variadic<OpenMP_PointerLikeType>:$map_operands);
11001316

11011317
let assemblyFormat = [{
11021318
oilist(`if` `(` $if_expr `:` type($if_expr) `)`
11031319
| `device` `(` $device `:` type($device) `)`
1104-
| `nowait` $nowait)
1105-
`map` `(` custom<MapClause>($map_operands, type($map_operands), $map_types) `)`
1106-
attr-dict
1320+
| `nowait` $nowait
1321+
| `map_entries` `(` $map_operands `:` type($map_operands) `)`
1322+
) attr-dict
11071323
}];
11081324

11091325
let hasVerifier = 1;
@@ -1142,15 +1358,14 @@ def Target_ExitDataOp: OpenMP_Op<"target_exit_data",
11421358
let arguments = (ins Optional<I1>:$if_expr,
11431359
Optional<AnyInteger>:$device,
11441360
UnitAttr:$nowait,
1145-
Variadic<OpenMP_PointerLikeType>:$map_operands,
1146-
I64ArrayAttr:$map_types);
1361+
Variadic<OpenMP_PointerLikeType>:$map_operands);
11471362

11481363
let assemblyFormat = [{
11491364
oilist(`if` `(` $if_expr `:` type($if_expr) `)`
11501365
| `device` `(` $device `:` type($device) `)`
1151-
| `nowait` $nowait)
1152-
`map` `(` custom<MapClause>($map_operands, type($map_operands), $map_types) `)`
1153-
attr-dict
1366+
| `nowait` $nowait
1367+
| `map_entries` `(` $map_operands `:` type($map_operands) `)`
1368+
) attr-dict
11541369
}];
11551370

11561371
let hasVerifier = 1;
@@ -1186,8 +1401,7 @@ def TargetOp : OpenMP_Op<"target",[AttrSizedOperandSegments]> {
11861401
Optional<AnyInteger>:$device,
11871402
Optional<AnyInteger>:$thread_limit,
11881403
UnitAttr:$nowait,
1189-
Variadic<OpenMP_PointerLikeType>:$map_operands,
1190-
OptionalAttr<I64ArrayAttr>:$map_types);
1404+
Variadic<OpenMP_PointerLikeType>:$map_operands);
11911405

11921406
let regions = (region AnyRegion:$region);
11931407

@@ -1196,7 +1410,7 @@ def TargetOp : OpenMP_Op<"target",[AttrSizedOperandSegments]> {
11961410
| `device` `(` $device `:` type($device) `)`
11971411
| `thread_limit` `(` $thread_limit `:` type($thread_limit) `)`
11981412
| `nowait` $nowait
1199-
| `map` `(` custom<MapClause>($map_operands, type($map_operands), $map_types) `)`
1413+
| `map_entries` `(` $map_operands `:` type($map_operands) `)`
12001414
) $region attr-dict
12011415
}];
12021416

mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,10 @@ void mlir::configureOpenMPToLLVMConversionLegality(
207207
typeConverter.isLegal(op->getOperandTypes()) &&
208208
typeConverter.isLegal(op->getResultTypes());
209209
});
210-
target.addDynamicallyLegalOp<mlir::omp::AtomicReadOp,
211-
mlir::omp::AtomicWriteOp, mlir::omp::FlushOp,
212-
mlir::omp::ThreadprivateOp, mlir::omp::YieldOp,
213-
mlir::omp::EnterDataOp, mlir::omp::ExitDataOp>(
210+
target.addDynamicallyLegalOp<
211+
mlir::omp::AtomicReadOp, mlir::omp::AtomicWriteOp, mlir::omp::FlushOp,
212+
mlir::omp::ThreadprivateOp, mlir::omp::YieldOp, mlir::omp::EnterDataOp,
213+
mlir::omp::ExitDataOp, mlir::omp::DataBoundsOp, mlir::omp::MapInfoOp>(
214214
[&](Operation *op) {
215215
return typeConverter.isLegal(op->getOperandTypes()) &&
216216
typeConverter.isLegal(op->getResultTypes());
@@ -230,6 +230,12 @@ void mlir::configureOpenMPToLLVMConversionLegality(
230230

231231
void mlir::populateOpenMPToLLVMConversionPatterns(LLVMTypeConverter &converter,
232232
RewritePatternSet &patterns) {
233+
// This type is allowed when converting OpenMP to LLVM Dialect, it carries
234+
// bounds information for map clauses and the operation and type are
235+
// discarded on lowering to LLVM-IR from the OpenMP dialect.
236+
converter.addConversion(
237+
[&](omp::DataBoundsType type) -> Type { return type; });
238+
233239
patterns.add<
234240
AtomicReadOpConversion, ReductionOpConversion,
235241
ReductionDeclareOpConversion, RegionOpConversion<omp::CriticalOp>,
@@ -246,7 +252,9 @@ void mlir::populateOpenMPToLLVMConversionPatterns(LLVMTypeConverter &converter,
246252
RegionLessOpWithVarOperandsConversion<omp::ThreadprivateOp>,
247253
RegionLessOpConversion<omp::YieldOp>,
248254
RegionLessOpConversion<omp::EnterDataOp>,
249-
RegionLessOpConversion<omp::ExitDataOp>>(converter);
255+
RegionLessOpConversion<omp::ExitDataOp>,
256+
RegionLessOpWithVarOperandsConversion<omp::DataBoundsOp>,
257+
RegionLessOpWithVarOperandsConversion<omp::MapInfoOp>>(converter);
250258
}
251259

252260
namespace {

0 commit comments

Comments
 (0)