diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -775,4 +775,19 @@ let verifier = "return ::verify(*this);"; } +def LLVM_AtomicCmpXchgOp : LLVM_Op<"cmpxchg">, + Arguments<(ins LLVM_Type:$ptr, LLVM_Type:$cmp, LLVM_Type:$val, + AtomicOrdering:$success_ordering, + AtomicOrdering:$failure_ordering)>, + Results<(outs LLVM_Type:$res)> { + let llvmBuilder = [{ + $res = builder.CreateAtomicCmpXchg($ptr, $cmp, $val, + getLLVMAtomicOrdering($success_ordering), + getLLVMAtomicOrdering($failure_ordering)); + }]; + let parser = [{ return parseAtomicCmpXchgOp(parser, result); }]; + let printer = [{ printAtomicCmpXchgOp(p, *this); }]; + let verifier = "return ::verify(*this);"; +} + #endif // LLVMIR_OPS diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -1344,74 +1344,82 @@ } //===----------------------------------------------------------------------===// -// Printer, parser and verifier for LLVM::AtomicRMWOp. +// Utility functions for parsing atomic ops //===----------------------------------------------------------------------===// -static void printAtomicRMWOp(OpAsmPrinter &p, AtomicRMWOp &op) { - p << op.getOperationName() << " "; - p << '"' << stringifyAtomicBinOp(op.bin_op()) << "\" "; - p << '"' << stringifyAtomicOrdering(op.ordering()) << "\" "; - p << op.ptr() << ", " << op.val(); - p.printOptionalAttrDict(op.getAttrs(), {"bin_op", "ordering"}); - p << " : (" << op.ptr().getType() << ", " << op.val().getType() << ") -> " - << op.res().getType(); -} - -// ::= `llvm.atomicrmw` string-literal string-literal -// ssa-use `,` ssa-use attribute-dict? `:` type -static ParseResult parseAtomicRMWOp(OpAsmParser &parser, - OperationState &result) { - Type type; - StringAttr binOp, ordering; - llvm::SMLoc binOpLoc, orderingLoc, trailingTypeLoc; - OpAsmParser::OperandType ptr, val; - if (parser.getCurrentLocation(&binOpLoc) || - parser.parseAttribute(binOp, "bin_op", result.attributes) || - parser.getCurrentLocation(&orderingLoc) || - parser.parseAttribute(ordering, "ordering", result.attributes) || - parser.parseOperand(ptr) || parser.parseComma() || - parser.parseOperand(val) || - parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() || - parser.getCurrentLocation(&trailingTypeLoc) || parser.parseType(type)) +static ParseResult parseAtomicBinOp(OpAsmParser &parser, OperationState &result, + StringRef attrName) { + llvm::SMLoc loc; + StringRef binOp; + if (parser.getCurrentLocation(&loc) || parser.parseKeyword(&binOp)) return failure(); - // Extract the result type from the trailing function type. - auto funcType = type.dyn_cast(); - if (!funcType || funcType.getNumInputs() != 2 || - funcType.getNumResults() != 1) - return parser.emitError( - trailingTypeLoc, - "expected trailing function type with two arguments and one result"); + // Replace the keyword `binOp` with an integer attribute. + auto kind = symbolizeAtomicBinOp(binOp); + if (!kind) { + return parser.emitError(loc) + << "'" << binOp << "' is an incorrect value of the '" << attrName + << "' attribute"; + } + + auto value = static_cast(kind.getValue()); + auto attr = parser.getBuilder().getI64IntegerAttr(value); + result.addAttribute(attrName, attr); + + return success(); +} - if (parser.resolveOperand(ptr, funcType.getInput(0), result.operands) || - parser.resolveOperand(val, funcType.getInput(1), result.operands)) +static ParseResult parseAtomicOrdering(OpAsmParser &parser, + OperationState &result, + StringRef attrName) { + llvm::SMLoc loc; + StringRef ordering; + if (parser.getCurrentLocation(&loc) || parser.parseKeyword(&ordering)) return failure(); - // Replace the string attribute `bin_op` with an integer attribute. - auto binOpKind = symbolizeAtomicBinOp(binOp.getValue()); - if (!binOpKind) { - return parser.emitError(binOpLoc) - << "'" << binOp.getValue() - << "' is an incorrect value of the 'bin_op' attribute"; + // Replace the keyword `ordering` with an integer attribute. + auto kind = symbolizeAtomicOrdering(ordering); + if (!kind) { + return parser.emitError(loc) + << "'" << ordering << "' is an incorrect value of the '" << attrName + << "' attribute"; } - auto binOpValue = static_cast(binOpKind.getValue()); - auto binOpAttr = parser.getBuilder().getI64IntegerAttr(binOpValue); - result.attributes[0].second = binOpAttr; + auto value = static_cast(kind.getValue()); + auto attr = parser.getBuilder().getI64IntegerAttr(value); + result.addAttribute(attrName, attr); - // Replace the string attribute `ordering` with an integer attribute. - auto orderingKind = symbolizeAtomicOrdering(ordering.getValue()); - if (!orderingKind) { - return parser.emitError(orderingLoc) - << "'" << ordering.getValue() - << "' is an incorrect value of the 'ordering' attribute"; - } + return success(); +} - auto orderingValue = static_cast(orderingKind.getValue()); - auto orderingAttr = parser.getBuilder().getI64IntegerAttr(orderingValue); - result.attributes[1].second = orderingAttr; +//===----------------------------------------------------------------------===// +// Printer, parser and verifier for LLVM::AtomicRMWOp. +//===----------------------------------------------------------------------===// - result.addTypes(funcType.getResults()); +static void printAtomicRMWOp(OpAsmPrinter &p, AtomicRMWOp &op) { + p << op.getOperationName() << ' ' << stringifyAtomicBinOp(op.bin_op()) << ' ' + << op.ptr() << ", " << op.val() << ' ' + << stringifyAtomicOrdering(op.ordering()) << ' '; + p.printOptionalAttrDict(op.getAttrs(), {"bin_op", "ordering"}); + p << " : " << op.res().getType(); +} + +// ::= `llvm.atomicrmw` keyword ssa-use `,` ssa-use keyword +// attribute-dict? `:` type +static ParseResult parseAtomicRMWOp(OpAsmParser &parser, + OperationState &result) { + LLVMType type; + OpAsmParser::OperandType ptr, val; + if (parseAtomicBinOp(parser, result, "bin_op") || parser.parseOperand(ptr) || + parser.parseComma() || parser.parseOperand(val) || + parseAtomicOrdering(parser, result, "ordering") || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.resolveOperand(ptr, type.getPointerTo(), result.operands) || + parser.resolveOperand(val, type, result.operands)) + return failure(); + + result.addTypes(type); return success(); } @@ -1444,6 +1452,72 @@ return success(); } +//===----------------------------------------------------------------------===// +// Printer, parser and verifier for LLVM::AtomicCmpXchgOp. +//===----------------------------------------------------------------------===// + +static void printAtomicCmpXchgOp(OpAsmPrinter &p, AtomicCmpXchgOp &op) { + p << op.getOperationName() << ' ' << op.ptr() << ", " << op.cmp() << ", " + << op.val() << ' ' << stringifyAtomicOrdering(op.success_ordering()) << ' ' + << stringifyAtomicOrdering(op.failure_ordering()); + p.printOptionalAttrDict(op.getAttrs(), + {"success_ordering", "failure_ordering"}); + p << " : " << op.val().getType(); +} + +// ::= `llvm.cmpxchg` ssa-use `,` ssa-use `,` ssa-use +// keyword keyword attribute-dict? `:` type +static ParseResult parseAtomicCmpXchgOp(OpAsmParser &parser, + OperationState &result) { + auto &builder = parser.getBuilder(); + LLVMType type; + OpAsmParser::OperandType ptr, cmp, val; + if (parser.parseOperand(ptr) || parser.parseComma() || + parser.parseOperand(cmp) || parser.parseComma() || + parser.parseOperand(val) || + parseAtomicOrdering(parser, result, "success_ordering") || + parseAtomicOrdering(parser, result, "failure_ordering") || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.resolveOperand(ptr, type.getPointerTo(), result.operands) || + parser.resolveOperand(cmp, type, result.operands) || + parser.resolveOperand(val, type, result.operands)) + return failure(); + + auto *dialect = builder.getContext()->getRegisteredDialect(); + auto boolType = LLVMType::getInt1Ty(dialect); + auto resultType = LLVMType::getStructTy(type, boolType); + result.addTypes(resultType); + + return success(); +} + +static LogicalResult verify(AtomicCmpXchgOp op) { + auto ptrType = op.ptr().getType().cast(); + if (!ptrType.isPointerTy()) + return op.emitOpError("expected LLVM IR pointer type for operand #0"); + auto cmpType = op.cmp().getType().cast(); + auto valType = op.val().getType().cast(); + if (cmpType != ptrType.getPointerElementTy() || cmpType != valType) + return op.emitOpError("expected LLVM IR element type for operand #0 to " + "match type for all other operands"); + if (!valType.isPointerTy() && !valType.isIntegerTy(8) && + !valType.isIntegerTy(16) && !valType.isIntegerTy(32) && + !valType.isIntegerTy(64) && !valType.getUnderlyingType()->isHalfTy() && + !valType.isFloatTy() && !valType.isDoubleTy()) + return op.emitOpError("unexpected LLVM IR type"); + if (op.success_ordering() < AtomicOrdering::monotonic || + op.failure_ordering() < AtomicOrdering::monotonic) + return op.emitOpError("ordering must be at least 'monotonic'"); + if (op.success_ordering() < op.failure_ordering()) + return op.emitOpError( + "failure ordering must be no stronger than success ordering"); + if (op.failure_ordering() == AtomicOrdering::release || + op.failure_ordering() == AtomicOrdering::acq_rel) + return op.emitOpError("failure ordering cannot be 'release' or 'acq_rel'"); + return success(); +} + //===----------------------------------------------------------------------===// // LLVMDialect initialization, type parsing, and registration. //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir --- a/mlir/test/Dialect/LLVMIR/invalid.mlir +++ b/mlir/test/Dialect/LLVMIR/invalid.mlir @@ -398,46 +398,123 @@ // CHECK-LABEL: @atomicrmw_expected_ptr func @atomicrmw_expected_ptr(%f32 : !llvm.float) { // expected-error@+1 {{expected LLVM IR pointer type for operand #0}} - %0 = llvm.atomicrmw "fadd" "unordered" %f32, %f32 : (!llvm.float, !llvm.float) -> !llvm.float + %0 = "llvm.atomicrmw"(%f32, %f32) {bin_op=11, ordering=1} : (!llvm.float, !llvm.float) -> !llvm.float llvm.return } // ----- + // CHECK-LABEL: @atomicrmw_mismatched_operands func @atomicrmw_mismatched_operands(%f32_ptr : !llvm<"float*">, %i32 : !llvm.i32) { // expected-error@+1 {{expected LLVM IR element type for operand #0 to match type for operand #1}} - %0 = llvm.atomicrmw "fadd" "unordered" %f32_ptr, %i32 : (!llvm<"float*">, !llvm.i32) -> !llvm.float + %0 = "llvm.atomicrmw"(%f32_ptr, %i32) {bin_op=11, ordering=1} : (!llvm<"float*">, !llvm.i32) -> !llvm.float llvm.return } // ----- + // CHECK-LABEL: @atomicrmw_mismatched_result func @atomicrmw_mismatched_operands(%f32_ptr : !llvm<"float*">, %f32 : !llvm.float) { // expected-error@+1 {{expected LLVM IR result type to match type for operand #1}} - %0 = llvm.atomicrmw "fadd" "unordered" %f32_ptr, %f32 : (!llvm<"float*">, !llvm.float) -> !llvm.i32 + %0 = "llvm.atomicrmw"(%f32_ptr, %f32) {bin_op=11, ordering=1} : (!llvm<"float*">, !llvm.float) -> !llvm.i32 llvm.return } // ----- + // CHECK-LABEL: @atomicrmw_expected_float func @atomicrmw_expected_float(%i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) { // expected-error@+1 {{expected LLVM IR floating point type}} - %0 = llvm.atomicrmw "fadd" "unordered" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %0 = llvm.atomicrmw fadd %i32_ptr, %i32 unordered : !llvm.i32 llvm.return } // ----- + // CHECK-LABEL: @atomicrmw_unexpected_xchg_type -func @atomicrmw_xchg_type(%i1_ptr : !llvm<"i1*">, %i1 : !llvm.i1) { +func @atomicrmw_unexpected_xchg_type(%i1_ptr : !llvm<"i1*">, %i1 : !llvm.i1) { // expected-error@+1 {{unexpected LLVM IR type for 'xchg' bin_op}} - %0 = llvm.atomicrmw "xchg" "unordered" %i1_ptr, %i1 : (!llvm<"i1*">, !llvm.i1) -> !llvm.i1 + %0 = llvm.atomicrmw xchg %i1_ptr, %i1 unordered : !llvm.i1 llvm.return } // ----- + // CHECK-LABEL: @atomicrmw_expected_int func @atomicrmw_expected_int(%f32_ptr : !llvm<"float*">, %f32 : !llvm.float) { // expected-error@+1 {{expected LLVM IR integer type}} - %0 = llvm.atomicrmw "max" "unordered" %f32_ptr, %f32 : (!llvm<"float*">, !llvm.float) -> !llvm.float + %0 = llvm.atomicrmw max %f32_ptr, %f32 unordered : !llvm.float + llvm.return +} + +// ----- + +// CHECK-LABEL: @cmpxchg_expected_ptr +func @cmpxchg_expected_ptr(%f32_ptr : !llvm<"float*">, %f32 : !llvm.float) { + // expected-error@+1 {{expected LLVM IR pointer type for operand #0}} + %0 = "llvm.cmpxchg"(%f32, %f32, %f32) {success_ordering=2,failure_ordering=2} : (!llvm.float, !llvm.float, !llvm.float) -> !llvm<"{ float, i1 }"> + llvm.return +} + +// ----- + +// CHECK-LABEL: @cmpxchg_mismatched_operands +func @cmpxchg_mismatched_operands(%f32_ptr : !llvm<"float*">, %i32 : !llvm.i32) { + // expected-error@+1 {{expected LLVM IR element type for operand #0 to match type for all other operands}} + %0 = "llvm.cmpxchg"(%f32_ptr, %i32, %i32) {success_ordering=2,failure_ordering=2} : (!llvm<"float*">, !llvm.i32, !llvm.i32) -> !llvm<"{ i32, i1 }"> + llvm.return +} + +// ----- + +// CHECK-LABEL: @cmpxchg_unexpected_type +func @cmpxchg_unexpected_type(%i1_ptr : !llvm<"i1*">, %i1 : !llvm.i1) { + // expected-error@+1 {{unexpected LLVM IR type}} + %0 = llvm.cmpxchg %i1_ptr, %i1, %i1 monotonic monotonic : !llvm.i1 + llvm.return +} + +// ----- + +// CHECK-LABEL: @cmpxchg_at_least_monotonic_success +func @cmpxchg_at_least_monotonic_success(%i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) { + // expected-error@+1 {{ordering must be at least 'monotonic'}} + %0 = llvm.cmpxchg %i32_ptr, %i32, %i32 unordered monotonic : !llvm.i32 + llvm.return +} + +// ----- + +// CHECK-LABEL: @cmpxchg_at_least_monotonic_failure +func @cmpxchg_at_least_monotonic_failure(%i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) { + // expected-error@+1 {{ordering must be at least 'monotonic'}} + %0 = llvm.cmpxchg %i32_ptr, %i32, %i32 monotonic unordered : !llvm.i32 + llvm.return +} + +// ----- + +// CHECK-LABEL: @cmpxchg_success_must_be_stronger +func @cmpxchg_success_must_be_stronger(%i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) { + // expected-error@+1 {{failure ordering must be no stronger than success ordering}} + %0 = llvm.cmpxchg %i32_ptr, %i32, %i32 monotonic acquire : !llvm.i32 + llvm.return +} + +// ----- + +// CHECK-LABEL: @cmpxchg_failure_release +func @cmpxchg_failure_release(%i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) { + // expected-error@+1 {{failure ordering cannot be 'release' or 'acq_rel'}} + %0 = llvm.cmpxchg %i32_ptr, %i32, %i32 acq_rel release : !llvm.i32 + llvm.return +} + +// ----- + +// CHECK-LABEL: @cmpxchg_failure_acq_rel +func @cmpxchg_failure_acq_rel(%i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) { + // expected-error@+1 {{failure ordering cannot be 'release' or 'acq_rel'}} + %0 = llvm.cmpxchg %i32_ptr, %i32, %i32 acq_rel acq_rel : !llvm.i32 llvm.return } diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -219,9 +219,16 @@ llvm.return } -// CHECK-LABEL: @atomics -func @atomics(%arg0 : !llvm<"float*">, %arg1 : !llvm.float) { - // CHECK: llvm.atomicrmw "fadd" "unordered" %{{.*}}, %{{.*}} : (!llvm<"float*">, !llvm.float) -> !llvm.float - %0 = llvm.atomicrmw "fadd" "unordered" %arg0, %arg1 : (!llvm<"float*">, !llvm.float) -> !llvm.float +// CHECK-LABEL: @atomicrmw +func @atomicrmw(%ptr : !llvm<"float*">, %val : !llvm.float) { + // CHECK: llvm.atomicrmw fadd %{{.*}}, %{{.*}} unordered : !llvm.float + %0 = llvm.atomicrmw fadd %ptr, %val unordered : !llvm.float + llvm.return +} + +// CHECK-LABEL: @cmpxchg +func @cmpxchg(%ptr : !llvm<"float*">, %cmp : !llvm.float, %new : !llvm.float) { + // CHECK: llvm.cmpxchg %{{.*}}, %{{.*}}, %{{.*}} acq_rel monotonic : !llvm.float + %0 = llvm.cmpxchg %ptr, %cmp, %new acq_rel monotonic : !llvm.float llvm.return } diff --git a/mlir/test/Target/llvmir.mlir b/mlir/test/Target/llvmir.mlir --- a/mlir/test/Target/llvmir.mlir +++ b/mlir/test/Target/llvmir.mlir @@ -1087,35 +1087,46 @@ llvm.return %0 : !llvm<"[2 x [2 x [2 x i32]]]"> } -// CHECK-LABEL: @atomics -llvm.func @atomics( +// CHECK-LABEL: @atomicrmw +llvm.func @atomicrmw( %f32_ptr : !llvm<"float*">, %f32 : !llvm.float, - %i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) -> !llvm.float { + %i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) { // CHECK: atomicrmw fadd float* %{{.*}}, float %{{.*}} unordered - %0 = llvm.atomicrmw "fadd" "unordered" %f32_ptr, %f32 : (!llvm<"float*">, !llvm.float) -> !llvm.float + %0 = llvm.atomicrmw fadd %f32_ptr, %f32 unordered : !llvm.float // CHECK: atomicrmw fsub float* %{{.*}}, float %{{.*}} unordered - %1 = llvm.atomicrmw "fsub" "unordered" %f32_ptr, %f32 : (!llvm<"float*">, !llvm.float) -> !llvm.float + %1 = llvm.atomicrmw fsub %f32_ptr, %f32 unordered : !llvm.float // CHECK: atomicrmw xchg float* %{{.*}}, float %{{.*}} monotonic - %2 = llvm.atomicrmw "xchg" "monotonic" %f32_ptr, %f32 : (!llvm<"float*">, !llvm.float) -> !llvm.float + %2 = llvm.atomicrmw xchg %f32_ptr, %f32 monotonic : !llvm.float // CHECK: atomicrmw add i32* %{{.*}}, i32 %{{.*}} acquire - %3 = llvm.atomicrmw "add" "acquire" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %3 = llvm.atomicrmw add %i32_ptr, %i32 acquire : !llvm.i32 // CHECK: atomicrmw sub i32* %{{.*}}, i32 %{{.*}} release - %4 = llvm.atomicrmw "sub" "release" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %4 = llvm.atomicrmw sub %i32_ptr, %i32 release : !llvm.i32 // CHECK: atomicrmw and i32* %{{.*}}, i32 %{{.*}} acq_rel - %5 = llvm.atomicrmw "_and" "acq_rel" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %5 = llvm.atomicrmw _and %i32_ptr, %i32 acq_rel : !llvm.i32 // CHECK: atomicrmw nand i32* %{{.*}}, i32 %{{.*}} seq_cst - %6 = llvm.atomicrmw "nand" "seq_cst" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %6 = llvm.atomicrmw nand %i32_ptr, %i32 seq_cst : !llvm.i32 // CHECK: atomicrmw or i32* %{{.*}}, i32 %{{.*}} unordered - %7 = llvm.atomicrmw "_or" "unordered" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %7 = llvm.atomicrmw _or %i32_ptr, %i32 unordered : !llvm.i32 // CHECK: atomicrmw xor i32* %{{.*}}, i32 %{{.*}} unordered - %8 = llvm.atomicrmw "_xor" "unordered" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %8 = llvm.atomicrmw _xor %i32_ptr, %i32 unordered : !llvm.i32 // CHECK: atomicrmw max i32* %{{.*}}, i32 %{{.*}} unordered - %9 = llvm.atomicrmw "max" "unordered" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %9 = llvm.atomicrmw max %i32_ptr, %i32 unordered : !llvm.i32 // CHECK: atomicrmw min i32* %{{.*}}, i32 %{{.*}} unordered - %10 = llvm.atomicrmw "min" "unordered" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %10 = llvm.atomicrmw min %i32_ptr, %i32 unordered : !llvm.i32 // CHECK: atomicrmw umax i32* %{{.*}}, i32 %{{.*}} unordered - %11 = llvm.atomicrmw "umax" "unordered" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 + %11 = llvm.atomicrmw umax %i32_ptr, %i32 unordered : !llvm.i32 // CHECK: atomicrmw umin i32* %{{.*}}, i32 %{{.*}} unordered - %12 = llvm.atomicrmw "umin" "unordered" %i32_ptr, %i32 : (!llvm<"i32*">, !llvm.i32) -> !llvm.i32 - llvm.return %0 : !llvm.float + %12 = llvm.atomicrmw umin %i32_ptr, %i32 unordered : !llvm.i32 + llvm.return +} + +// CHECK-LABEL: @cmpxchg +llvm.func @cmpxchg(%ptr : !llvm<"float*">, %cmp : !llvm.float, %val: !llvm.float) { + // CHECK: cmpxchg float* %{{.*}}, float %{{.*}}, float %{{.*}} acq_rel monotonic + %0 = llvm.cmpxchg %ptr, %cmp, %val acq_rel monotonic : !llvm.float + // CHECK: %{{[0-9]+}} = extractvalue { float, i1 } %{{[0-9]+}}, 0 + %1 = llvm.extractvalue %0[0] : !llvm<"{ float, i1 }"> + // CHECK: %{{[0-9]+}} = extractvalue { float, i1 } %{{[0-9]+}}, 1 + %2 = llvm.extractvalue %0[1] : !llvm<"{ float, i1 }"> + llvm.return }