diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td index 2441583c570bb..ede50b264d06f 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td @@ -317,14 +317,12 @@ def WsLoopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments, def YieldOp : OpenMP_Op<"yield", [NoSideEffect, ReturnLike, Terminator, - ParentOneOf<["WsLoopOp", "ReductionDeclareOp"]>]> { + ParentOneOf<["WsLoopOp", "ReductionDeclareOp", "AtomicUpdateOp"]>]> { let summary = "loop yield and termination operation"; let description = [{ "omp.yield" yields SSA values from the OpenMP dialect op region and terminates the region. The semantics of how the values are yielded is defined by the parent operation. - If "omp.yield" has any operands, the operands must match the parent - operation's results. }]; let arguments = (ins Variadic:$results); @@ -559,11 +557,11 @@ def TaskwaitOp : OpenMP_Op<"taskwait"> { // 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, +def AtomicReadOp : OpenMP_Op<"atomic.read", [AllTypesMatch<["x", "v"]>]> { + let arguments = (ins OpenMP_PointerLikeType:$x, + OpenMP_PointerLikeType:$v, DefaultValuedAttr:$hint, OptionalAttr:$memory_order); - let results = (outs AnyType); let parser = [{ return parseAtomicReadOp(parser, result); }]; let printer = [{ return printAtomicReadOp(p, *this); }]; let verifier = [{ return verifyAtomicReadOp(*this); }]; @@ -606,18 +604,25 @@ def AtomicBinOpKindAttr : I64EnumAttr< let symbolToStringFnName = "AtomicBinOpKindToString"; } -def AtomicUpdateOp : OpenMP_Op<"atomic.update"> { +def AtomicUpdateOp : OpenMP_Op<"atomic.update", [SingleBlockImplicitTerminator<"YieldOp">]> { let arguments = (ins OpenMP_PointerLikeType:$x, - AnyType:$expr, - UnitAttr:$isXBinopExpr, - AtomicBinOpKindAttr:$binop, DefaultValuedAttr:$hint, OptionalAttr:$memory_order); + let regions = (region SizedRegion<1>:$region); let parser = [{ return parseAtomicUpdateOp(parser, result); }]; let printer = [{ return printAtomicUpdateOp(p, *this); }]; let verifier = [{ return verifyAtomicUpdateOp(*this); }]; } +def AtomicCaptureOp : OpenMP_Op<"atomic.capture", [SingleBlockImplicitTerminator<"TerminatorOp">]>{ + let arguments = (ins DefaultValuedAttr:$hint, + OptionalAttr:$memory_order); + let regions = (region SizedRegion<1>:$region); + let parser = [{ return parseAtomicCaptureOp(parser, result); }]; + let printer = [{ return printAtomicCaptureOp(p, *this); }]; + let verifier = [{ return verifyAtomicCaptureOp(*this); }]; +} + //===----------------------------------------------------------------------===// // 2.19.5.7 declare reduction Directive //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp index 2f1e850d39595..045ccb8fecdc2 100644 --- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp +++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp @@ -1227,32 +1227,28 @@ static LogicalResult verifyOrderedRegionOp(OrderedRegionOp op) { /// address ::= operand `:` type static ParseResult parseAtomicReadOp(OpAsmParser &parser, OperationState &result) { - OpAsmParser::OperandType address; + OpAsmParser::OperandType x, v; Type addressType; SmallVector clauses = {memoryOrderClause, hintClause}; SmallVector segments; - if (parser.parseOperand(address) || + if (parser.parseOperand(v) || parser.parseEqual() || parser.parseOperand(x) || parseClauses(parser, result, clauses, segments) || parser.parseColonType(addressType) || - parser.resolveOperand(address, addressType, result.operands)) + parser.resolveOperand(x, addressType, result.operands) || + parser.resolveOperand(v, addressType, result.operands)) return failure(); - - SmallVector 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() << " "; + p << " " << op.v() << " = " << op.x() << " "; 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(); + p << ": " << op.x().getType(); return; } @@ -1264,6 +1260,9 @@ static LogicalResult verifyAtomicReadOp(AtomicReadOp op) { return op.emitError( "memory-order must not be acq_rel or release for atomic reads"); } + if (op.x() == op.v()) + return op.emitError( + "read and write must not be to the same location for atomic reads"); return verifySynchronizationHint(op, op.hint()); } @@ -1284,7 +1283,7 @@ static ParseResult parseAtomicWriteOp(OpAsmParser &parser, SmallVector clauses = {memoryOrderClause, hintClause}; SmallVector segments; - if (parser.parseOperand(address) || parser.parseComma() || + if (parser.parseOperand(address) || parser.parseEqual() || parser.parseOperand(value) || parseClauses(parser, result, clauses, segments) || parser.parseColonType(addrType) || parser.parseComma() || @@ -1297,7 +1296,7 @@ static ParseResult parseAtomicWriteOp(OpAsmParser &parser, /// Printer for AtomicWriteOp static void printAtomicWriteOp(OpAsmPrinter &p, AtomicWriteOp op) { - p << " " << op.address() << ", " << op.value() << " "; + p << " " << op.address() << " = " << op.value() << " "; if (op.memory_order()) p << "memory_order(" << op.memory_order() << ") "; if (op.hintAttr()) @@ -1328,61 +1327,28 @@ static ParseResult parseAtomicUpdateOp(OpAsmParser &parser, OperationState &result) { SmallVector clauses = {memoryOrderClause, hintClause}; SmallVector segments; - OpAsmParser::OperandType x, y, z; - Type xType, exprType; - StringRef binOp; - - // x = y `op` z : xtype, exprtype - if (parser.parseOperand(x) || parser.parseEqual() || parser.parseOperand(y) || - parser.parseKeyword(&binOp) || parser.parseOperand(z) || - parseClauses(parser, result, clauses, segments) || parser.parseColon() || - parser.parseType(xType) || parser.parseComma() || - parser.parseType(exprType) || - parser.resolveOperand(x, xType, result.operands)) { + OpAsmParser::OperandType x, expr; + Type xType; + + if (parseClauses(parser, result, clauses, segments) || + parser.parseOperand(x) || parser.parseColon() || + parser.parseType(xType) || + parser.resolveOperand(x, xType, result.operands) || + parser.parseRegion(*result.addRegion())) { return failure(); } - - auto binOpEnum = AtomicBinOpKindToEnum(binOp.upper()); - if (!binOpEnum) - return parser.emitError(parser.getNameLoc()) - << "invalid atomic bin op in atomic update\n"; - auto attr = - parser.getBuilder().getI64IntegerAttr((int64_t)binOpEnum.getValue()); - result.addAttribute("binop", attr); - - OpAsmParser::OperandType expr; - if (x.name == y.name && x.number == y.number) { - expr = z; - result.addAttribute("isXBinopExpr", parser.getBuilder().getUnitAttr()); - } else if (x.name == z.name && x.number == z.number) { - expr = y; - } else { - return parser.emitError(parser.getNameLoc()) - << "atomic update variable " << x.name - << " not found in the RHS of the assignment statement in an" - " atomic.update operation"; - } - return parser.resolveOperand(expr, exprType, result.operands); + return success(); } /// Printer for AtomicUpdateOp static void printAtomicUpdateOp(OpAsmPrinter &p, AtomicUpdateOp op) { - p << " " << op.x() << " = "; - Value y, z; - if (op.isXBinopExpr()) { - y = op.x(); - z = op.expr(); - } else { - y = op.expr(); - z = op.x(); - } - p << y << " " << AtomicBinOpKindToString(op.binop()).lower() << " " << z - << " "; + p << " "; if (op.memory_order()) p << "memory_order(" << op.memory_order() << ") "; if (op.hintAttr()) printSynchronizationHint(p, op, op.hintAttr()); - p << ": " << op.x().getType() << ", " << op.expr().getType(); + p << op.x() << " : " << op.x().getType(); + p.printRegion(op.region()); } /// Verifier for AtomicUpdateOp @@ -1393,6 +1359,84 @@ static LogicalResult verifyAtomicUpdateOp(AtomicUpdateOp op) { return op.emitError( "memory-order must not be acq_rel or acquire for atomic updates"); } + if (op.region().getNumArguments() != 1) + return op.emitError("the region must accept exactly one argument"); + + if (op.x().getType().cast().getElementType() != + op.region().getArgument(0).getType()) { + return op.emitError( + "the type of the operand must be a pointer type whose " + "element type is the same as that of the region argument"); + } + + YieldOp yieldOp = *op.region().getOps().begin(); + if (yieldOp.results().size() != 1) + return op.emitError("only updated value must be returned"); + if (yieldOp.results().front().getType() != + op.region().getArgument(0).getType()) + return op.emitError("input and yielded value must have the same type"); + return success(); +} + +//===----------------------------------------------------------------------===// +// AtomicCaptureOp +//===----------------------------------------------------------------------===// + +/// Parser for AtomicCaptureOp +static LogicalResult parseAtomicCaptureOp(OpAsmParser &parser, + OperationState &result) { + SmallVector clauses = {memoryOrderClause, hintClause}; + SmallVector segments; + if (parseClauses(parser, result, clauses, segments) || + parser.parseRegion(*result.addRegion())) + return failure(); + return success(); +} + +/// Printer for AtomicCaptureOp +static void printAtomicCaptureOp(OpAsmPrinter &p, AtomicCaptureOp op) { + if (op.memory_order()) + p << "memory_order(" << op.memory_order() << ") "; + if (op.hintAttr()) + printSynchronizationHint(p, op, op.hintAttr()); + p.printRegion(op.region()); +} + +/// Verifier for AtomicCaptureOp +static LogicalResult verifyAtomicCaptureOp(AtomicCaptureOp op) { + Block::OpListType &ops = op.region().front().getOperations(); + if (ops.size() != 3) + return emitError(op.getLoc()) + << "expected three operations in omp.atomic.capture region (one" + " terminator, and two atomic ops"; + auto &firstOp = ops.front(); + auto &secondOp = *ops.getNextNode(firstOp); + auto firstReadStmt = dyn_cast(firstOp); + auto firstUpdateStmt = dyn_cast(firstOp); + auto secondReadStmt = dyn_cast(secondOp); + auto secondUpdateStmt = dyn_cast(secondOp); + auto secondWriteStmt = dyn_cast(secondOp); + + if (!((firstUpdateStmt && secondReadStmt) || + (firstReadStmt && secondUpdateStmt) || + (firstReadStmt && secondWriteStmt))) + return emitError(ops.front().getLoc()) + << "invalid sequence of operations in the capture region"; + if (firstUpdateStmt && secondReadStmt && + firstUpdateStmt.x() != secondReadStmt.x()) + return emitError(firstUpdateStmt.getLoc()) + << "updated variable in omp.atomic.update must be captured in " + "second operation"; + if (firstReadStmt && secondUpdateStmt && + firstReadStmt.x() != secondUpdateStmt.x()) + return emitError(firstReadStmt.getLoc()) + << "captured variable in omp.atomic.read must be updated in " + "second operation"; + if (firstReadStmt && secondWriteStmt && + firstReadStmt.x() != secondWriteStmt.address()) + return emitError(firstReadStmt.getLoc()) + << "captured variable in omp.atomic.read must be updated in " + "second operation"; return success(); } diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir index 77854266ecf9c..a437caf37982c 100644 --- a/mlir/test/Dialect/OpenMP/invalid.mlir +++ b/mlir/test/Dialect/OpenMP/invalid.mlir @@ -505,49 +505,57 @@ func @omp_ordered5(%arg1 : i32, %arg2 : i32, %arg3 : i32, %vec0 : i64, %vec1 : i // ----- -func @omp_atomic_read1(%addr : memref) { +func @omp_atomic_read1(%x : memref, %v: memref) { // 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 + omp.atomic.read %v = %x hint(speculative, nonspeculative) : memref return } // ----- -func @omp_atomic_read2(%addr : memref) { +func @omp_atomic_read2(%x : memref, %v: memref) { // expected-error @below {{attribute 'memory_order' failed to satisfy constraint: MemoryOrderKind Clause}} - %1 = omp.atomic.read %addr memory_order(xyz) : memref -> i32 + omp.atomic.read %v = %x memory_order(xyz) : memref return } // ----- -func @omp_atomic_read3(%addr : memref) { +func @omp_atomic_read3(%x : memref, %v: memref) { // 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 + omp.atomic.read %v = %x memory_order(acq_rel) : memref return } // ----- -func @omp_atomic_read4(%addr : memref) { +func @omp_atomic_read4(%x : memref, %v: memref) { // 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 + omp.atomic.read %v = %x memory_order(release) : memref return } // ----- -func @omp_atomic_read5(%addr : memref) { +func @omp_atomic_read5(%x : memref, %v: memref) { // 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 + omp.atomic.read %v = %x memory_order(acquire) memory_order(relaxed) : memref return } // ----- -func @omp_atomic_read6(%addr : memref) { +func @omp_atomic_read6(%x : memref, %v: memref) { // 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 + omp.atomic.read %v = %x hint(speculative) hint(contended) : memref + return +} + +// ----- + +func @omp_atomic_read7(%x : memref, %v: memref) { + // expected-error @below {{read and write must not be to the same location for atomic reads}} + omp.atomic.read %x = %x hint(speculative) : memref return } @@ -555,7 +563,7 @@ func @omp_atomic_read6(%addr : memref) { func @omp_atomic_write1(%addr : memref, %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 + omp.atomic.write %addr = %val hint(contended, uncontended) : memref, i32 return } @@ -563,7 +571,7 @@ func @omp_atomic_write1(%addr : memref, %val : i32) { func @omp_atomic_write2(%addr : memref, %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 + omp.atomic.write %addr = %val memory_order(acq_rel) : memref, i32 return } @@ -571,7 +579,7 @@ func @omp_atomic_write2(%addr : memref, %val : i32) { func @omp_atomic_write3(%addr : memref, %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 + omp.atomic.write %addr = %val memory_order(acquire) : memref, i32 return } @@ -579,7 +587,7 @@ func @omp_atomic_write3(%addr : memref, %val : i32) { func @omp_atomic_write4(%addr : memref, %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 + omp.atomic.write %addr = %val memory_order(release) memory_order(seq_cst) : memref, i32 return } @@ -587,7 +595,7 @@ func @omp_atomic_write4(%addr : memref, %val : i32) { func @omp_atomic_write5(%addr : memref, %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 + omp.atomic.write %addr = %val hint(contended) hint(speculative) : memref, i32 return } @@ -595,23 +603,56 @@ func @omp_atomic_write5(%addr : memref, %val : i32) { func @omp_atomic_write6(%addr : memref, %val : i32) { // expected-error @below {{attribute 'memory_order' failed to satisfy constraint: MemoryOrderKind Clause}} - omp.atomic.write %addr, %val memory_order(xyz) : memref, i32 + omp.atomic.write %addr = %val memory_order(xyz) : memref, i32 + return +} + +// ----- + + + +// ----- + +func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var3 : memref) -> () { + // expected-error @below {{operand used in both private and firstprivate clauses}} + omp.sections private(%data_var1 : memref) firstprivate(%data_var1 : memref) { + omp.terminator + } return } // ----- -func @omp_atomic_update1(%x: memref, %expr: i32, %foo: memref) { - // expected-error @below {{atomic update variable %x not found in the RHS of the assignment statement in an atomic.update operation}} - omp.atomic.update %x = %foo add %expr : memref, i32 +func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var3 : memref) -> () { + // expected-error @below {{operand used in both private and lastprivate clauses}} + omp.sections private(%data_var1 : memref) lastprivate(%data_var1 : memref) { + omp.terminator + } + return +} + +// ----- + +func @omp_atomic_update1(%x: memref, %expr: f32) { + // expected-error @below {{the type of the operand must be a pointer type whose element type is the same as that of the region argument}} + omp.atomic.update %x : memref { + ^bb0(%xval: f32): + %newval = llvm.fadd %xval, %expr : f32 + omp.yield (%newval : f32) + } return } // ----- func @omp_atomic_update2(%x: memref, %expr: i32) { - // expected-error @below {{invalid atomic bin op in atomic update}} - omp.atomic.update %x = %x invalid %expr : memref, i32 + // expected-error @+2 {{op expects regions to end with 'omp.yield', found 'omp.terminator'}} + // expected-note @below {{in custom textual format, the absence of terminator implies 'omp.yield'}} + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.terminator + } return } @@ -619,7 +660,11 @@ func @omp_atomic_update2(%x: memref, %expr: i32) { func @omp_atomic_update3(%x: memref, %expr: i32) { // expected-error @below {{memory-order must not be acq_rel or acquire for atomic updates}} - omp.atomic.update %x = %x add %expr memory_order(acq_rel) : memref, i32 + omp.atomic.update memory_order(acq_rel) %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } return } @@ -627,7 +672,11 @@ func @omp_atomic_update3(%x: memref, %expr: i32) { func @omp_atomic_update4(%x: memref, %expr: i32) { // expected-error @below {{memory-order must not be acq_rel or acquire for atomic updates}} - omp.atomic.update %x = %x add %expr memory_order(acquire) : memref, i32 + omp.atomic.update memory_order(acquire) %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } return } @@ -636,15 +685,56 @@ func @omp_atomic_update4(%x: memref, %expr: i32) { // expected-note @below {{prior use here}} func @omp_atomic_update5(%x: memref, %expr: i32) { // expected-error @below {{use of value '%x' expects different type than prior uses: 'i32' vs 'memref'}} - omp.atomic.update %x = %x add %expr : i32, memref + omp.atomic.update %x : i32 { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } return } // ----- -func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var3 : memref) -> () { - // expected-error @below {{operand used in both private and firstprivate clauses}} - omp.sections private(%data_var1 : memref) firstprivate(%data_var1 : memref) { +func @omp_atomic_update6(%x: memref, %expr: i32) { + // expected-error @below {{only updated value must be returned}} + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval, %expr : i32, i32) + } + return +} + +// ----- + +func @omp_atomic_update7(%x: memref, %expr: i32, %y: f32) { + // expected-error @below {{input and yielded value must have the same type}} + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%y: f32) + } + return +} + +// ----- + +func @omp_atomic_update8(%x: memref, %expr: i32) { + // expected-error @below {{the region must accept exactly one argument}} + omp.atomic.update %x : memref { + ^bb0(%xval: i32, %tmp: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } + return +} + +// ----- + +func @omp_atomic_capture(%x: memref, %v: memref, %expr: i32) { + // expected-error @below {{expected three operations in omp.atomic.capture region}} + omp.atomic.capture { + omp.atomic.read %v = %x : memref omp.terminator } return @@ -652,9 +742,11 @@ func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var // ----- -func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var3 : memref) -> () { - // expected-error @below {{operand used in both private and lastprivate clauses}} - omp.sections private(%data_var1 : memref) lastprivate(%data_var1 : memref) { +func @omp_atomic_capture(%x: memref, %v: memref, %expr: i32) { + omp.atomic.capture { + // expected-error @below {{invalid sequence of operations in the capture region}} + omp.atomic.read %v = %x : memref + omp.atomic.read %v = %x : memref omp.terminator } return @@ -662,6 +754,123 @@ func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var // ----- +func @omp_atomic_capture(%x: memref, %v: memref, %expr: i32) { + omp.atomic.capture { + // expected-error @below {{invalid sequence of operations in the capture region}} + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } + omp.terminator + } + return +} + +// ----- + +func @omp_atomic_capture(%x: memref, %v: memref, %expr: i32) { + omp.atomic.capture { + // expected-error @below {{invalid sequence of operations in the capture region}} + omp.atomic.write %x = %expr : memref, i32 + omp.atomic.write %x = %expr : memref, i32 + omp.terminator + } + return +} + +// ----- + +func @omp_atomic_capture(%x: memref, %v: memref, %expr: i32) { + omp.atomic.capture { + // expected-error @below {{invalid sequence of operations in the capture region}} + omp.atomic.write %x = %expr : memref, i32 + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } + omp.terminator + } + return +} + +// ----- + +func @omp_atomic_capture(%x: memref, %v: memref, %expr: i32) { + omp.atomic.capture { + // expected-error @below {{invalid sequence of operations in the capture region}} + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } + omp.atomic.write %x = %expr : memref, i32 + omp.terminator + } + return +} + +// ----- + +func @omp_atomic_capture(%x: memref, %v: memref, %expr: i32) { + omp.atomic.capture { + // expected-error @below {{invalid sequence of operations in the capture region}} + omp.atomic.write %x = %expr : memref, i32 + omp.atomic.read %v = %x : memref + omp.terminator + } + return +} + +// ----- + +func @omp_atomic_capture(%x: memref, %y: memref, %v: memref, %expr: i32) { + omp.atomic.capture { + // expected-error @below {{updated variable in omp.atomic.update must be captured in second operation}} + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } + omp.atomic.read %v = %y : memref + omp.terminator + } +} + +// ----- + +func @omp_atomic_capture(%x: memref, %y: memref, %v: memref, %expr: i32) { + omp.atomic.capture { + // expected-error @below {{captured variable in omp.atomic.read must be updated in second operation}} + omp.atomic.read %v = %y : memref + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield (%newval : i32) + } + omp.terminator + } +} + +// ----- + +func @omp_atomic_capture(%x: memref, %y: memref, %v: memref, %expr: i32) { + omp.atomic.capture { + // expected-error @below {{captured variable in omp.atomic.read must be updated in second operation}} + omp.atomic.read %v = %x : memref + omp.atomic.write %y = %expr : memref, i32 + omp.terminator + } +} + +// ----- + func @omp_sections(%data_var1 : memref, %data_var2 : memref, %data_var3 : memref) -> () { // expected-error @below {{operand used in both private and lastprivate clauses}} omp.sections private(%data_var1 : memref, %data_var2 : memref) lastprivate(%data_var3 : memref, %data_var2 : memref) { diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir index 28f5bba6d5201..1dc3be785850e 100644 --- a/mlir/test/Dialect/OpenMP/ops.mlir +++ b/mlir/test/Dialect/OpenMP/ops.mlir @@ -500,97 +500,145 @@ func @omp_ordered(%arg1 : i32, %arg2 : i32, %arg3 : i32, } // CHECK-LABEL: omp_atomic_read -// CHECK-SAME: (%[[ADDR:.*]]: memref) -func @omp_atomic_read(%addr : memref) { - // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] : memref -> i32 - %1 = omp.atomic.read %addr : memref -> i32 - // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] memory_order(seq_cst) : memref -> i32 - %2 = omp.atomic.read %addr memory_order(seq_cst) : memref -> i32 - // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] memory_order(acquire) : memref -> i32 - %5 = omp.atomic.read %addr memory_order(acquire) : memref -> i32 - // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] memory_order(relaxed) : memref -> i32 - %6 = omp.atomic.read %addr memory_order(relaxed) : memref -> i32 - // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] hint(contended, nonspeculative) : memref -> i32 - %7 = omp.atomic.read %addr hint(nonspeculative, contended) : memref -> i32 - // CHECK: %{{.*}} = omp.atomic.read %[[ADDR]] memory_order(seq_cst) hint(contended, speculative) : memref -> i32 - %8 = omp.atomic.read %addr hint(speculative, contended) memory_order(seq_cst) : memref -> i32 +// CHECK-SAME: (%[[v:.*]]: memref, %[[x:.*]]: memref) +func @omp_atomic_read(%v: memref, %x: memref) { + // CHECK: omp.atomic.read %[[v]] = %[[x]] : memref + omp.atomic.read %v = %x : memref + // CHECK: omp.atomic.read %[[v]] = %[[x]] memory_order(seq_cst) : memref + omp.atomic.read %v = %x memory_order(seq_cst) : memref + // CHECK: omp.atomic.read %[[v]] = %[[x]] memory_order(acquire) : memref + omp.atomic.read %v = %x memory_order(acquire) : memref + // CHECK: omp.atomic.read %[[v]] = %[[x]] memory_order(relaxed) : memref + omp.atomic.read %v = %x memory_order(relaxed) : memref + // CHECK: omp.atomic.read %[[v]] = %[[x]] hint(contended, nonspeculative) : memref + omp.atomic.read %v = %x hint(nonspeculative, contended) : memref + // CHECK: omp.atomic.read %[[v]] = %[[x]] memory_order(seq_cst) hint(contended, speculative) : memref + omp.atomic.read %v = %x hint(speculative, contended) memory_order(seq_cst) : memref return } // CHECK-LABEL: omp_atomic_write // CHECK-SAME: (%[[ADDR:.*]]: memref, %[[VAL:.*]]: i32) func @omp_atomic_write(%addr : memref, %val : i32) { - // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] : memref, i32 - omp.atomic.write %addr, %val : memref, i32 - // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] memory_order(seq_cst) : memref, i32 - omp.atomic.write %addr, %val memory_order(seq_cst) : memref, i32 - // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] memory_order(release) : memref, i32 - omp.atomic.write %addr, %val memory_order(release) : memref, i32 - // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] memory_order(relaxed) : memref, i32 - omp.atomic.write %addr, %val memory_order(relaxed) : memref, i32 - // CHECK: omp.atomic.write %[[ADDR]], %[[VAL]] hint(uncontended, speculative) : memref, i32 - omp.atomic.write %addr, %val hint(speculative, uncontended) : memref, i32 + // CHECK: omp.atomic.write %[[ADDR]] = %[[VAL]] : memref, i32 + omp.atomic.write %addr = %val : memref, i32 + // CHECK: omp.atomic.write %[[ADDR]] = %[[VAL]] memory_order(seq_cst) : memref, i32 + omp.atomic.write %addr = %val memory_order(seq_cst) : memref, i32 + // CHECK: omp.atomic.write %[[ADDR]] = %[[VAL]] memory_order(release) : memref, i32 + omp.atomic.write %addr = %val memory_order(release) : memref, i32 + // CHECK: omp.atomic.write %[[ADDR]] = %[[VAL]] memory_order(relaxed) : memref, i32 + omp.atomic.write %addr = %val memory_order(relaxed) : memref, i32 + // CHECK: omp.atomic.write %[[ADDR]] = %[[VAL]] hint(uncontended, speculative) : memref, i32 + omp.atomic.write %addr = %val hint(speculative, uncontended) : memref, i32 return } // CHECK-LABEL: omp_atomic_update // CHECK-SAME: (%[[X:.*]]: memref, %[[EXPR:.*]]: i32, %[[XBOOL:.*]]: memref, %[[EXPRBOOL:.*]]: i1) func @omp_atomic_update(%x : memref, %expr : i32, %xBool : memref, %exprBool : i1) { - // CHECK: omp.atomic.update %[[X]] = %[[X]] add %[[EXPR]] : memref, i32 - omp.atomic.update %x = %x add %expr : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[X]] sub %[[EXPR]] : memref, i32 - omp.atomic.update %x = %x sub %expr : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[X]] mul %[[EXPR]] : memref, i32 - omp.atomic.update %x = %x mul %expr : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[X]] div %[[EXPR]] : memref, i32 - omp.atomic.update %x = %x div %expr : memref, i32 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[XBOOL]] and %[[EXPRBOOL]] : memref, i1 - omp.atomic.update %xBool = %xBool and %exprBool : memref, i1 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[XBOOL]] or %[[EXPRBOOL]] : memref, i1 - omp.atomic.update %xBool = %xBool or %exprBool : memref, i1 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[XBOOL]] xor %[[EXPRBOOL]] : memref, i1 - omp.atomic.update %xBool = %xBool xor %exprBool : memref, i1 - // CHECK: omp.atomic.update %[[X]] = %[[X]] shiftr %[[EXPR]] : memref, i32 - omp.atomic.update %x = %x shiftr %expr : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[X]] shiftl %[[EXPR]] : memref, i32 - omp.atomic.update %x = %x shiftl %expr : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[X]] max %[[EXPR]] : memref, i32 - omp.atomic.update %x = %x max %expr : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[X]] min %[[EXPR]] : memref, i32 - omp.atomic.update %x = %x min %expr : memref, i32 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[XBOOL]] eqv %[[EXPRBOOL]] : memref, i1 - omp.atomic.update %xBool = %xBool eqv %exprBool : memref, i1 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[XBOOL]] neqv %[[EXPRBOOL]] : memref, i1 - omp.atomic.update %xBool = %xBool neqv %exprBool : memref, i1 - - // CHECK: omp.atomic.update %[[X]] = %[[EXPR]] add %[[X]] : memref, i32 - omp.atomic.update %x = %expr add %x : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[EXPR]] sub %[[X]] : memref, i32 - omp.atomic.update %x = %expr sub %x : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[EXPR]] mul %[[X]] : memref, i32 - omp.atomic.update %x = %expr mul %x : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[EXPR]] div %[[X]] : memref, i32 - omp.atomic.update %x = %expr div %x : memref, i32 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[EXPRBOOL]] and %[[XBOOL]] : memref, i1 - omp.atomic.update %xBool = %exprBool and %xBool : memref, i1 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[EXPRBOOL]] or %[[XBOOL]] : memref, i1 - omp.atomic.update %xBool = %exprBool or %xBool : memref, i1 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[EXPRBOOL]] xor %[[XBOOL]] : memref, i1 - omp.atomic.update %xBool = %exprBool xor %xBool : memref, i1 - // CHECK: omp.atomic.update %[[X]] = %[[EXPR]] shiftr %[[X]] : memref, i32 - omp.atomic.update %x = %expr shiftr %x : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[EXPR]] shiftl %[[X]] : memref, i32 - omp.atomic.update %x = %expr shiftl %x : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[EXPR]] max %[[X]] : memref, i32 - omp.atomic.update %x = %expr max %x : memref, i32 - // CHECK: omp.atomic.update %[[X]] = %[[EXPR]] min %[[X]] : memref, i32 - omp.atomic.update %x = %expr min %x : memref, i32 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[EXPRBOOL]] eqv %[[XBOOL]] : memref, i1 - omp.atomic.update %xBool = %exprBool eqv %xBool : memref, i1 - // CHECK: omp.atomic.update %[[XBOOL]] = %[[EXPRBOOL]] neqv %[[XBOOL]] : memref, i1 - omp.atomic.update %xBool = %exprBool neqv %xBool : memref, i1 - // CHECK: omp.atomic.update %[[X]] = %[[EXPR]] add %[[X]] memory_order(seq_cst) hint(speculative) : memref, i32 - omp.atomic.update %x = %expr add %x hint(speculative) memory_order(seq_cst) : memref, i32 + // CHECK: omp.atomic.update %[[X]] : memref + // CHECK-NEXT: (%[[XVAL:.*]]: i32): + // CHECK-NEXT: %[[NEWVAL:.*]] = llvm.add %[[XVAL]], %[[EXPR]] : i32 + // CHECK-NEXT: omp.yield(%[[NEWVAL]] : i32) + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield(%newval : i32) + } + // CHECK: omp.atomic.update %[[XBOOL]] : memref + // CHECK-NEXT: (%[[XVAL:.*]]: i1): + // CHECK-NEXT: %[[NEWVAL:.*]] = llvm.and %[[XVAL]], %[[EXPRBOOL]] : i1 + // CHECK-NEXT: omp.yield(%[[NEWVAL]] : i1) + omp.atomic.update %xBool : memref { + ^bb0(%xval: i1): + %newval = llvm.and %xval, %exprBool : i1 + omp.yield(%newval : i1) + } + // CHECK: omp.atomic.update %[[X]] : memref + // CHECK-NEXT: (%[[XVAL:.*]]: i32): + // CHECK-NEXT: %[[NEWVAL:.*]] = llvm.shl %[[XVAL]], %[[EXPR]] : i32 + // CHECK-NEXT: omp.yield(%[[NEWVAL]] : i32) + // CHECK-NEXT: } + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.shl %xval, %expr : i32 + omp.yield(%newval : i32) + } + // CHECK: omp.atomic.update %[[X]] : memref + // CHECK-NEXT: (%[[XVAL:.*]]: i32): + // CHECK-NEXT: %[[NEWVAL:.*]] = "llvm.intr.smax"(%[[XVAL]], %[[EXPR]]) : (i32, i32) -> i32 + // CHECK-NEXT: omp.yield(%[[NEWVAL]] : i32) + // CHECK-NEXT: } + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = "llvm.intr.smax"(%xval, %expr) : (i32, i32) -> i32 + omp.yield(%newval : i32) + } + + // CHECK: omp.atomic.update %[[XBOOL]] : memref + // CHECK-NEXT: (%[[XVAL:.*]]: i1): + // CHECK-NEXT: %[[NEWVAL:.*]] = llvm.icmp "eq" %[[XVAL]], %[[EXPRBOOL]] : i1 + // CHECK-NEXT: omp.yield(%[[NEWVAL]] : i1) + // } + omp.atomic.update %xBool : memref { + ^bb0(%xval: i1): + %newval = llvm.icmp "eq" %xval, %exprBool : i1 + omp.yield(%newval : i1) + } + return +} + +// CHECK-LABEL: omp_atomic_capture +// CHECK-SAME: (%[[v:.*]]: memref, %[[x:.*]]: memref, %[[expr:.*]]: i32) +func @omp_atomic_capture(%v: memref, %x: memref, %expr: i32) { + // CHECK: omp.atomic.capture { + // CHECK-NEXT: omp.atomic.read %[[v]] = %[[x]] : memref + // CHECK-NEXT: omp.atomic.write %[[x]] = %[[expr]] : memref, i32 + // CHECK-NEXT: omp.terminator + // CHECK-NEXT: } + omp.atomic.capture{ + omp.atomic.read %v = %x : memref + omp.atomic.write %x = %expr : memref, i32 + omp.terminator + } + + // CHECK: omp.atomic.capture { + // CHECK: omp.atomic.update %[[x]] : memref + // CHECK-NEXT: ^bb0(%[[XVAL:.*]]: i32): + // CHECK-NEXT: %[[NEWVAL:.*]] = llvm.add %[[XVAL]], %[[expr]] : i32 + // CHECK-NEXT: omp.yield(%[[NEWVAL]] : i32) + // CHECK-NEXT: } + // CHECK-NEXT: omp.atomic.read %[[v]] = %[[x]] : memref + // CHECK-NEXT: omp.terminator + // CHECK-NEXT: } + omp.atomic.capture{ + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield(%newval : i32) + } + omp.atomic.read %v = %x : memref + omp.terminator + } + + // CHECK: omp.atomic.capture { + // CHECK-NEXT: omp.atomic.read %[[v]] = %[[x]] : memref + // CHECK: omp.atomic.update %[[x]] : memref + // CHECK-NEXT: ^bb0(%[[XVAL:.*]]: i32): + // CHECK-NEXT: %[[NEWVAL:.*]] = llvm.add %[[XVAL]], %[[expr]] : i32 + // CHECK-NEXT: omp.yield(%[[NEWVAL]] : i32) + // CHECK-NEXT: } + // CHECK-NEXT: omp.terminator + // CHECK-NEXT: } + omp.atomic.capture{ + omp.atomic.read %v = %x : memref + omp.atomic.update %x : memref { + ^bb0(%xval: i32): + %newval = llvm.add %xval, %expr : i32 + omp.yield(%newval : i32) + } + omp.terminator + } return }