case OMPC_when:
case OMPC_adjust_args:
case OMPC_append_args:
+ case OMPC_memory_order:
llvm_unreachable("Clause is not allowed in 'omp atomic'.");
}
}
CHECK_SIMPLE_CLAUSE(When, OMPC_when)
CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args)
CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args)
+CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order)
CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize)
CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks)
];
}
+def OMP_MEMORY_ORDER_SeqCst : ClauseVal<"seq_cst", 1, 1> {}
+def OMP_MEMORY_ORDER_AcqRel : ClauseVal<"acq_rel", 2, 1> {}
+def OMP_MEMORY_ORDER_Acquire : ClauseVal<"acquire", 3, 1> {}
+def OMP_MEMORY_ORDER_Release : ClauseVal<"release", 4, 1> {}
+def OMP_MEMORY_ORDER_Relaxed : ClauseVal<"relaxed", 5, 1> {}
+def OMP_MEMORY_ORDER_Default : ClauseVal<"default", 6, 0> {
+ let isDefault = 1;
+}
+def OMPC_MemoryOrder : Clause<"memory_order"> {
+ let enumClauseValue = "MemoryOrderKind";
+ let allowedClauseValues = [
+ OMP_MEMORY_ORDER_SeqCst,
+ OMP_MEMORY_ORDER_AcqRel,
+ OMP_MEMORY_ORDER_Acquire,
+ OMP_MEMORY_ORDER_Release,
+ OMP_MEMORY_ORDER_Relaxed,
+ OMP_MEMORY_ORDER_Default
+ ];
+}
+
def OMPC_Ordered : Clause<"ordered"> {
let clangClass = "OMPOrderedClause";
let flangClass = "ScalarIntConstantExpr";
}
//===----------------------------------------------------------------------===//
+// 2.17.7 atomic construct
+//===----------------------------------------------------------------------===//
+
+// In the OpenMP Specification, atomic construct has an `atomic-clause` which
+// can take the values `read`, `write`, `update` and `capture`. These four
+// kinds of atomic constructs are fundamentally independent and are handled
+// separately while lowering. Having four separate operations (one for each
+// value of the clause) here decomposes handling of this construct into a
+// two-step process.
+
+def AtomicReadOp : OpenMP_Op<"atomic.read"> {
+ let arguments = (ins OpenMP_PointerLikeType:$address,
+ DefaultValuedAttr<I64Attr, "0">:$hint,
+ OptionalAttr<MemoryOrderKind>:$memory_order);
+ let results = (outs AnyType);
+ let parser = [{ return parseAtomicReadOp(parser, result); }];
+ let printer = [{ return printAtomicReadOp(p, *this); }];
+ let verifier = [{ return verifyAtomicReadOp(*this); }];
+}
+
+def AtomicWriteOp : OpenMP_Op<"atomic.write"> {
+ let arguments = (ins OpenMP_PointerLikeType:$address,
+ AnyType:$value,
+ DefaultValuedAttr<I64Attr, "0">:$hint,
+ OptionalAttr<MemoryOrderKind>:$memory_order);
+ let parser = [{ return parseAtomicWriteOp(parser, result); }];
+ let printer = [{ return printAtomicWriteOp(p, *this); }];
+ let verifier = [{ return verifyAtomicWriteOp(*this); }];
+}
+
+//===----------------------------------------------------------------------===//
// 2.19.5.7 declare reduction Directive
//===----------------------------------------------------------------------===//
///
/// hint-clause = `hint` `(` hint-value `)`
static ParseResult parseSynchronizationHint(OpAsmParser &parser,
- IntegerAttr &hintAttr) {
- if (failed(parser.parseOptionalKeyword("hint"))) {
+ IntegerAttr &hintAttr,
+ bool parseKeyword = true) {
+ if (parseKeyword && failed(parser.parseOptionalKeyword("hint"))) {
hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), 0);
return success();
}
p << "hint(";
llvm::interleaveComma(hints, p);
- p << ")";
+ p << ") ";
}
/// Verifies a synchronization hint clause
-static LogicalResult verifySynchronizationHint(Operation *op, int32_t hint) {
+static LogicalResult verifySynchronizationHint(Operation *op, uint64_t hint) {
// Helper function to get n-th bit from the right end of `value`
auto bitn = [](int value, int n) -> bool { return value & (1 << n); };
orderClause,
orderedClause,
inclusiveClause,
+ memoryOrderClause,
+ hintClause,
COUNT
};
return failure();
auto attr = UnitAttr::get(parser.getBuilder().getContext());
result.addAttribute("inclusive", attr);
+ } else if (clauseKeyword == "memory_order") {
+ StringRef memoryOrder;
+ if (checkAllowed(memoryOrderClause) || parser.parseLParen() ||
+ parser.parseKeyword(&memoryOrder) || parser.parseRParen())
+ return failure();
+ result.addAttribute("memory_order",
+ parser.getBuilder().getStringAttr(memoryOrder));
+ } else if (clauseKeyword == "hint") {
+ IntegerAttr hint;
+ if (checkAllowed(hintClause) ||
+ parseSynchronizationHint(parser, hint, false))
+ return failure();
+ result.addAttribute("hint", hint);
} else {
return parser.emitError(parser.getNameLoc())
<< clauseKeyword << " is not a valid clause";
return success();
}
+//===----------------------------------------------------------------------===//
+// AtomicReadOp
+//===----------------------------------------------------------------------===//
+
+/// Parser for AtomicReadOp
+///
+/// operation ::= `omp.atomic.read` atomic-clause-list address `->` result-type
+/// address ::= operand `:` type
+static ParseResult parseAtomicReadOp(OpAsmParser &parser,
+ OperationState &result) {
+ OpAsmParser::OperandType address;
+ Type addressType;
+ SmallVector<ClauseType> clauses = {memoryOrderClause, hintClause};
+ SmallVector<int> segments;
+
+ if (parser.parseOperand(address) ||
+ parseClauses(parser, result, clauses, segments) ||
+ parser.parseColonType(addressType) ||
+ parser.resolveOperand(address, addressType, result.operands))
+ return failure();
+
+ SmallVector<Type> resultType;
+ if (parser.parseArrowTypeList(resultType))
+ return failure();
+ result.addTypes(resultType);
+ return success();
+}
+
+/// Printer for AtomicReadOp
+static void printAtomicReadOp(OpAsmPrinter &p, AtomicReadOp op) {
+ p << " " << op.address() << " ";
+ if (op.memory_order())
+ p << "memory_order(" << op.memory_order().getValue() << ") ";
+ if (op.hintAttr())
+ printSynchronizationHint(p << " ", op, op.hintAttr());
+ p << ": " << op.address().getType() << " -> " << op.getType();
+ return;
+}
+
+/// Verifier for AtomicReadOp
+static LogicalResult verifyAtomicReadOp(AtomicReadOp op) {
+ if (op.memory_order()) {
+ StringRef memOrder = op.memory_order().getValue();
+ if (memOrder.equals("acq_rel") || memOrder.equals("release"))
+ return op.emitError(
+ "memory-order must not be acq_rel or release for atomic reads");
+ }
+ return verifySynchronizationHint(op, op.hint());
+}
+
+//===----------------------------------------------------------------------===//
+// AtomicWriteOp
+//===----------------------------------------------------------------------===//
+
+/// Parser for AtomicWriteOp
+///
+/// operation ::= `omp.atomic.write` atomic-clause-list operands
+/// operands ::= address `,` value
+/// address ::= operand `:` type
+/// value ::= operand `:` type
+static ParseResult parseAtomicWriteOp(OpAsmParser &parser,
+ OperationState &result) {
+ OpAsmParser::OperandType address, value;
+ Type addrType, valueType;
+ SmallVector<ClauseType> clauses = {memoryOrderClause, hintClause};
+ SmallVector<int> segments;
+
+ if (parser.parseOperand(address) || parser.parseComma() ||
+ parser.parseOperand(value) ||
+ parseClauses(parser, result, clauses, segments) ||
+ parser.parseColonType(addrType) || parser.parseComma() ||
+ parser.parseType(valueType) ||
+ parser.resolveOperand(address, addrType, result.operands) ||
+ parser.resolveOperand(value, valueType, result.operands))
+ return failure();
+ return success();
+}
+
+/// Printer for AtomicWriteOp
+static void printAtomicWriteOp(OpAsmPrinter &p, AtomicWriteOp op) {
+ p << " " << op.address() << ", " << op.value() << " ";
+ if (op.memory_order())
+ p << "memory_order(" << op.memory_order() << ") ";
+ if (op.hintAttr())
+ printSynchronizationHint(p, op, op.hintAttr());
+ p << ": " << op.address().getType() << ", " << op.value().getType();
+ return;
+}
+
+/// Verifier for AtomicWriteOp
+static LogicalResult verifyAtomicWriteOp(AtomicWriteOp op) {
+ if (op.memory_order()) {
+ StringRef memoryOrder = op.memory_order().getValue();
+ if (memoryOrder.equals("acq_rel") || memoryOrder.equals("acquire"))
+ return op.emitError(
+ "memory-order must not be acq_rel or acquire for atomic writes");
+ }
+ return verifySynchronizationHint(op, op.hint());
+}
+
#define GET_OP_CLASSES
#include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"
}
return
}
+
+// -----
+
+func @omp_atomic_read1(%addr : memref<i32>) {
+ // expected-error @below {{the hints omp_sync_hint_nonspeculative and omp_sync_hint_speculative cannot be combined.}}
+ %1 = omp.atomic.read %addr hint(speculative, nonspeculative) : memref<i32> -> i32
+ return
+}
+
+// -----
+
+func @omp_atomic_read2(%addr : memref<i32>) {
+ // expected-error @below {{attribute 'memory_order' failed to satisfy constraint: MemoryOrderKind Clause}}
+ %1 = omp.atomic.read %addr memory_order(xyz) : memref<i32> -> i32
+ return
+}
+
+// -----
+
+func @omp_atomic_read3(%addr : memref<i32>) {
+ // expected-error @below {{memory-order must not be acq_rel or release for atomic reads}}
+ %1 = omp.atomic.read %addr memory_order(acq_rel) : memref<i32> -> i32
+ return
+}
+
+// -----
+
+func @omp_atomic_read4(%addr : memref<i32>) {
+ // expected-error @below {{memory-order must not be acq_rel or release for atomic reads}}
+ %1 = omp.atomic.read %addr memory_order(release) : memref<i32> -> i32
+ return
+}
+
+// -----
+
+func @omp_atomic_read5(%addr : memref<i32>) {
+ // expected-error @below {{at most one memory_order clause can appear on the omp.atomic.read operation}}
+ %1 = omp.atomic.read %addr memory_order(acquire) memory_order(relaxed) : memref<i32> -> i32
+ return
+}
+
+// -----
+
+func @omp_atomic_read6(%addr : memref<i32>) {
+ // expected-error @below {{at most one hint clause can appear on the omp.atomic.read operation}}
+ %1 = omp.atomic.read %addr hint(speculative) hint(contended) : memref<i32> -> i32
+ return
+}
+
+// -----
+
+func @omp_atomic_write1(%addr : memref<i32>, %val : i32) {
+ // expected-error @below {{the hints omp_sync_hint_uncontended and omp_sync_hint_contended cannot be combined}}
+ omp.atomic.write %addr, %val hint(contended, uncontended) : memref<i32>, i32
+ return
+}
+
+// -----
+
+func @omp_atomic_write2(%addr : memref<i32>, %val : i32) {
+ // expected-error @below {{memory-order must not be acq_rel or acquire for atomic writes}}
+ omp.atomic.write %addr, %val memory_order(acq_rel) : memref<i32>, i32
+ return
+}
+
+// -----
+
+func @omp_atomic_write3(%addr : memref<i32>, %val : i32) {
+ // expected-error @below {{memory-order must not be acq_rel or acquire for atomic writes}}
+ omp.atomic.write %addr, %val memory_order(acquire) : memref<i32>, i32
+ return
+}
+
+// -----
+
+func @omp_atomic_write4(%addr : memref<i32>, %val : i32) {
+ // expected-error @below {{at most one memory_order clause can appear on the omp.atomic.write operation}}
+ omp.atomic.write %addr, %val memory_order(release) memory_order(seq_cst) : memref<i32>, i32
+ return
+}
+
+// -----
+
+func @omp_atomic_write5(%addr : memref<i32>, %val : i32) {
+ // expected-error @below {{at most one hint clause can appear on the omp.atomic.write operation}}
+ omp.atomic.write %addr, %val hint(contended) hint(speculative) : memref<i32>, i32
+ return
+}
+
+// -----
+
+func @omp_atomic_write6(%addr : memref<i32>, %val : i32) {
+ // expected-error @below {{attribute 'memory_order' failed to satisfy constraint: MemoryOrderKind Clause}}
+ omp.atomic.write %addr, %val memory_order(xyz) : memref<i32>, i32
+ return
+}
return
}
+
+// CHECK-LABEL: omp_atomic_read
+// CHECK-SAME: (%[[ADDR:.*]]: memref<i32>)
+func @omp_atomic_read(%addr : memref<i32>) {
+ // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] : memref<i32> -> i32
+ %1 = omp.atomic.read %addr : memref<i32> -> i32
+ // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] memory_order(seq_cst) : memref<i32> -> i32
+ %2 = omp.atomic.read %addr memory_order(seq_cst) : memref<i32> -> i32
+ // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] memory_order(acquire) : memref<i32> -> i32
+ %5 = omp.atomic.read %addr memory_order(acquire) : memref<i32> -> i32
+ // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] memory_order(relaxed) : memref<i32> -> i32
+ %6 = omp.atomic.read %addr memory_order(relaxed) : memref<i32> -> i32
+ // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] hint(contended, nonspeculative) : memref<i32> -> i32
+ %7 = omp.atomic.read %addr hint(nonspeculative, contended) : memref<i32> -> i32
+ // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] memory_order(seq_cst) hint(contended, speculative) : memref<i32> -> i32
+ %8 = omp.atomic.read %addr hint(speculative, contended) memory_order(seq_cst) : memref<i32> -> i32
+ return
+}
+
+// CHECK-LABEL: omp_atomic_write
+// CHECK-SAME: (%[[ADDR:.*]]: memref<i32>, %[[VAL:.*]]: i32)
+func @omp_atomic_write(%addr : memref<i32>, %val : i32) {
+ // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] : memref<i32>, i32
+ omp.atomic.write %addr, %val : memref<i32>, i32
+ // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] memory_order(seq_cst) : memref<i32>, i32
+ omp.atomic.write %addr, %val memory_order(seq_cst) : memref<i32>, i32
+ // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] memory_order(release) : memref<i32>, i32
+ omp.atomic.write %addr, %val memory_order(release) : memref<i32>, i32
+ // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] memory_order(relaxed) : memref<i32>, i32
+ omp.atomic.write %addr, %val memory_order(relaxed) : memref<i32>, i32
+ // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] hint(uncontended, speculative) : memref<i32>, i32
+ omp.atomic.write %addr, %val hint(speculative, uncontended) : memref<i32>, i32
+ return
+}