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