xref: /llvm-project/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td (revision db791b278a414fb6df1acc1799adcf11d8fb9169)
1//===- DirectiveAtomicInterfaces.td - atomic interfaces ----*- tablegen -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Defines the operation interface for atomic operations used in OpenACC and
10// OpenMP.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef OPENACC_MP_COMMON_INTERFACES_ATOMICINTERFACES
15#define OPENACC_MP_COMMON_INTERFACES_ATOMICINTERFACES
16
17include "mlir/IR/OpBase.td"
18include "mlir/Interfaces/ControlFlowInterfaces.td"
19
20def AtomicReadOpInterface : OpInterface<"AtomicReadOpInterface"> {
21  let description = [{
22    This interface is used for OpenACC/OpenMP dialect operation that performs an
23    atomic read.
24
25    The interface terminology uses `x` and `v` like the directive
26    specifications:
27      `v = x;`
28    `x` is the address from where the value is atomically read.
29    `v` is the address where the value is stored after reading.
30  }];
31  let cppNamespace = "::mlir::accomp";
32
33  let methods = [
34    InterfaceMethod<[{
35        Common verifier for operation that implements atomic read interface.
36      }],
37      /*retTy=*/"::llvm::LogicalResult",
38      /*methodName=*/"verifyCommon",
39      /*args=*/(ins),
40      /*methodBody=*/"",
41      /*defaultImplementation=*/[{
42        if ($_op.getX() == $_op.getV()) {
43          return $_op.emitError(
44            "read and write must not be to the same location for atomic reads");
45        }
46        return mlir::success();
47      }]
48    >,
49    InterfaceMethod<[{
50        Obtains `x` which is the address from where the value is atomically
51        read.
52      }],
53      /*retTy=*/"::mlir::Value",
54      /*methodName=*/"getX",
55      /*args=*/(ins)
56    >,
57    InterfaceMethod<[{
58        Obtains `v` which is the address where the value is stored after
59        reading.
60      }],
61      /*retTy=*/"::mlir::Value",
62      /*methodName=*/"getV",
63      /*args=*/(ins)
64    >,
65  ];
66}
67
68def AtomicWriteOpInterface : OpInterface<"AtomicWriteOpInterface"> {
69  let description = [{
70    This interface is used for OpenACC/OpenMP dialect operation that performs an
71    atomic write.
72
73    The interface terminology uses `x` and `expr` like the directive
74    specifications:
75      `x = expr;`
76    `x` is the address to where the `expr` is atomically written.
77  }];
78  let cppNamespace = "::mlir::accomp";
79
80  let methods = [
81    InterfaceMethod<[{
82        Common verifier for operation that implements atomic write interface.
83      }],
84      /*retTy=*/"::llvm::LogicalResult",
85      /*methodName=*/"verifyCommon",
86      /*args=*/(ins),
87      /*methodBody=*/"",
88      /*defaultImplementation=*/[{
89        mlir::Type elementType = $_op.getX().getType().getElementType();
90        if (elementType && elementType != $_op.getExpr().getType())
91          return $_op.emitError("address must dereference to value type");
92        return mlir::success();
93      }]
94    >,
95    InterfaceMethod<[{
96        Obtains `x` which is the address to which the value is atomically
97        written to.
98      }],
99      /*retTy=*/"::mlir::Value",
100      /*methodName=*/"getX",
101      /*args=*/(ins)
102    >,
103    InterfaceMethod<[{
104        Obtains `expr` which corresponds to the expression whose value is
105        written to `x`.
106      }],
107      /*retTy=*/"::mlir::Value",
108      /*methodName=*/"getExpr",
109      /*args=*/(ins)
110    >,
111  ];
112}
113
114def AtomicUpdateOpInterface : OpInterface<"AtomicUpdateOpInterface"> {
115  let description = [{
116    This interface is used for OpenACC/OpenMP dialect operation that performs an
117    atomic update.
118
119    The interface terminology uses `x` to specify the address where a value
120    is atomically written/read.
121
122    Since atomic update expression comes in many forms, this interface requires
123    that the operation uses a region with a single argument to capture the
124    expression.
125
126    The region describes how to update the value of `x`. It takes the value at
127    `x` as an input and must yield the updated value. Only the update to `x` is
128    atomic. Generally the region must have only one instruction, but can
129    potentially have more than one instructions too. The update is semantically
130    similar to a compare-exchange loop based atomic update.
131  }];
132  let cppNamespace = "::mlir::accomp";
133
134  let methods = [
135    InterfaceMethod<[{
136        Obtains `x` which is the address to which the value is atomically
137        written to / read from.
138      }],
139      /*retTy=*/"::mlir::Value",
140      /*methodName=*/"getX",
141      /*args=*/(ins)
142    >,
143    InterfaceMethod<[{
144        Returns the first operation in atomic update region.
145      }],
146      /*retTy=*/"::mlir::Operation *",
147      /*methodName=*/"getFirstOp",
148      /*args=*/(ins),
149      /*methodBody=*/"",
150      /*defaultImplementation=*/[{
151        return &($_op.getRegion().front().getOperations().front());
152      }]
153    >,
154    InterfaceMethod<[{
155        Returns true if the new value is same as old value and the operation is
156        a no-op, false otherwise.
157      }],
158      /*retTy=*/"bool",
159      /*methodName=*/"isNoOp",
160      /*args=*/(ins),
161      /*methodBody=*/"",
162      /*defaultImplementation=*/[{
163        // The atomic update is a no-op if the terminator is the first and only
164        // operation in its region.
165        mlir::Operation* terminator =
166          llvm::dyn_cast<mlir::RegionBranchTerminatorOpInterface>($_op.getFirstOp());
167        return terminator && terminator->getOperands().front() ==
168          $_op.getRegion().front().getArgument(0);
169      }]
170    >,
171    InterfaceMethod<[{
172        Returns the new value if the operation is equivalent to just a write
173        operation. Otherwise, returns nullptr.
174      }],
175      /*retTy=*/"::mlir::Value",
176      /*methodName=*/"getWriteOpVal",
177      /*args=*/(ins),
178      /*methodBody=*/"",
179      /*defaultImplementation=*/[{
180        mlir::Operation* terminator =
181          llvm::dyn_cast<mlir::RegionBranchTerminatorOpInterface>($_op.getFirstOp());
182        if (terminator && terminator->getOperands().front() !=
183          $_op.getRegion().front().getArgument(0)) {
184          return terminator->getOperands().front();
185        }
186        return nullptr;
187      }]
188    >,
189    InterfaceMethod<[{
190        Common verifier for operation that implements atomic update interface.
191      }],
192      /*retTy=*/"::llvm::LogicalResult",
193      /*methodName=*/"verifyCommon",
194      /*args=*/(ins),
195      /*methodBody=*/"",
196      /*defaultImplementation=*/[{
197        if ($_op.getRegion().getNumArguments() != 1)
198          return $_op.emitError("the region must accept exactly one argument");
199
200        Type elementType = $_op.getX().getType().getElementType();
201        if (elementType && elementType != $_op.getRegion().getArgument(0).getType()) {
202          return $_op.emitError("the type of the operand must be a pointer type whose "
203                          "element type is the same as that of the region argument");
204        }
205
206        return mlir::success();
207      }]
208    >,
209    InterfaceMethod<[{
210        Common verifier of the required region for operation that implements
211        atomic update interface.
212      }],
213      /*retTy=*/"::llvm::LogicalResult",
214      /*methodName=*/"verifyRegionsCommon",
215      /*args=*/(ins),
216      /*methodBody=*/"",
217      /*defaultImplementation=*/[{
218        mlir::Operation *terminator = $_op.getRegion().front().getTerminator();
219
220        if (terminator->getOperands().size() != 1)
221          return $_op.emitError("only updated value must be returned");
222
223        if (terminator->getOperands().front().getType() !=
224            $_op.getRegion().getArgument(0).getType())
225          return $_op.emitError("input and yielded value must have the same type");
226
227        return mlir::success();
228      }]
229    >,
230  ];
231}
232
233def AtomicCaptureOpInterface : OpInterface<"AtomicCaptureOpInterface"> {
234  let description = [{
235    This interface is used for OpenACC/OpenMP dialect operation that performs an
236    atomic capture.
237
238    This interface requires a single region with two operations that each
239    implement one of the atomic interfaces. It can be found in one of these
240    forms:
241      `{ atomic.update, atomic.read }`
242      `{ atomic.read, atomic.update }`
243      `{ atomic.read, atomic.write }`
244  }];
245  let cppNamespace = "::mlir::accomp";
246
247  let methods = [
248    InterfaceMethod<[{
249        Returns the first operation in atomic capture region.
250      }],
251      /*retTy=*/"::mlir::Operation *",
252      /*methodName=*/"getFirstOp",
253      /*args=*/(ins),
254      /*methodBody=*/"",
255      /*defaultImplementation=*/[{
256        return &($_op.getRegion().front().getOperations().front());
257      }]
258    >,
259    InterfaceMethod<[{
260        Returns the second operation in atomic capture region.
261      }],
262      /*retTy=*/"::mlir::Operation *",
263      /*methodName=*/"getSecondOp",
264      /*args=*/(ins),
265      /*methodBody=*/"",
266      /*defaultImplementation=*/[{
267        auto &ops = $_op.getRegion().front().getOperations();
268        return ops.getNextNode(ops.front());
269      }]
270    >,
271      InterfaceMethod<[{
272        Common verifier of the required region for operation that implements
273        atomic capture interface.
274      }],
275      /*retTy=*/"::llvm::LogicalResult",
276      /*methodName=*/"verifyRegionsCommon",
277      /*args=*/(ins),
278      /*methodBody=*/"",
279      /*defaultImplementation=*/[{
280        Block::OpListType &ops = $_op.getRegion().front().getOperations();
281        if (ops.size() != 3)
282          return $_op.emitError()
283                << "expected three operations in atomic.capture region (one "
284                    "terminator, and two atomic ops)";
285        auto &firstOp = ops.front();
286        auto &secondOp = *ops.getNextNode(firstOp);
287        auto firstReadStmt = dyn_cast<AtomicReadOpInterface>(firstOp);
288        auto firstUpdateStmt = dyn_cast<AtomicUpdateOpInterface>(firstOp);
289        auto secondReadStmt = dyn_cast<AtomicReadOpInterface>(secondOp);
290        auto secondUpdateStmt = dyn_cast<AtomicUpdateOpInterface>(secondOp);
291        auto secondWriteStmt = dyn_cast<AtomicWriteOpInterface>(secondOp);
292
293        if (!((firstUpdateStmt && secondReadStmt) ||
294              (firstReadStmt && secondUpdateStmt) ||
295              (firstReadStmt && secondWriteStmt)))
296          return ops.front().emitError()
297                << "invalid sequence of operations in the capture region";
298        if (firstUpdateStmt && secondReadStmt &&
299            firstUpdateStmt.getX() != secondReadStmt.getX())
300          return firstUpdateStmt.emitError()
301                << "updated variable in atomic.update must be captured in "
302                    "second operation";
303        if (firstReadStmt && secondUpdateStmt &&
304            firstReadStmt.getX() != secondUpdateStmt.getX())
305          return firstReadStmt.emitError()
306                << "captured variable in atomic.read must be updated in second "
307                    "operation";
308        if (firstReadStmt && secondWriteStmt &&
309            firstReadStmt.getX() != secondWriteStmt.getX())
310          return firstReadStmt.emitError()
311                << "captured variable in atomic.read must be updated in "
312                    "second operation";
313
314        return mlir::success();
315      }]
316    >,
317  ];
318}
319
320#endif // OPENACC_MP_COMMON_INTERFACES_ATOMICINTERFACES
321