[mlir][llvm] Add extra attributes to the atomic ops.
authorTobias Gysi <tobias.gysi@nextsilicon.com>
Thu, 9 Feb 2023 08:15:56 +0000 (09:15 +0100)
committerTobias Gysi <tobias.gysi@nextsilicon.com>
Thu, 9 Feb 2023 09:23:06 +0000 (10:23 +0100)
The revision adds a number of extra arguments to the
atomic read modify write and compare and exchange
operations. The extra arguments include the volatile,
weak, syncscope, and alignment attributes.

The implementation also adapts the fence operation to use
a assembly format and generalizes the helper used
to obtain the syncscope name.

Reviewed By: Dinistro

Differential Revision: https://reviews.llvm.org/D143554

mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
mlir/lib/Conversion/MemRefToLLVM/MemRefToLLVM.cpp
mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
mlir/lib/Target/LLVMIR/ModuleImport.cpp
mlir/test/Dialect/LLVMIR/roundtrip.mlir
mlir/test/Target/LLVMIR/Import/instructions.ll
mlir/test/Target/LLVMIR/llvmir.mlir

index 5ff3690..39b79f1 100644 (file)
@@ -169,9 +169,8 @@ def LLVM_FRemOp : LLVM_FloatArithmeticOp<"frem", "FRem">;
 def LLVM_FNegOp : LLVM_UnaryFloatArithmeticOp<
   LLVM_ScalarOrVectorOf<LLVM_AnyFloat>, "fneg", "FNeg">;
 
-// Common code definition that is used to verify and set the alignment attribute
-// of LLVM ops that accept such an attribute.
-class MemoryOpWithAlignmentBase {
+// Common code definitions used to set memory operation attributes and flags.
+class MemoryOpBase {
   code setAlignmentCode = [{
     if ($alignment.has_value()) {
       auto align = *$alignment;
@@ -179,10 +178,15 @@ class MemoryOpWithAlignmentBase {
         inst->setAlignment(llvm::Align(align));
     }
   }];
-}
-
-// Code definition that is used for nontemporal metadata creation.
-class MemoryOpWithAlignmentAndAttributes : MemoryOpWithAlignmentBase {
+  code setVolatileCode = [{
+    inst->setVolatile($volatile_);
+  }];
+  code setSyncScopeCode = [{
+    if ($syncscope.has_value()) {
+      llvm::LLVMContext &llvmContext = builder.getContext();
+      inst->setSyncScopeID(llvmContext.getOrInsertSyncScopeID(*$syncscope));
+    }
+  }];
   code setNonTemporalMetadataCode = [{
     if ($nontemporal) {
       llvm::Module *module = builder.GetInsertBlock()->getModule();
@@ -192,22 +196,19 @@ class MemoryOpWithAlignmentAndAttributes : MemoryOpWithAlignmentBase {
       inst->setMetadata(module->getMDKindID("nontemporal"), metadata);
     }
   }];
-
   code setAccessGroupsMetadataCode = [{
     moduleTranslation.setAccessGroupsMetadata(op, inst);
   }];
-
   code setAliasScopeMetadataCode = [{
     moduleTranslation.setAliasScopeMetadata(op, inst);
   }];
-
   code setTBAAMetadataCode = [{
     moduleTranslation.setTBAAMetadata(op, inst);
   }];
 }
 
 // Memory-related operations.
-def LLVM_AllocaOp : LLVM_Op<"alloca">, MemoryOpWithAlignmentBase {
+def LLVM_AllocaOp : LLVM_Op<"alloca">, MemoryOpBase {
   let arguments = (ins AnyInteger:$arraySize,
                    OptionalAttr<I64Attr>:$alignment,
                    OptionalAttr<TypeAttr>:$elem_type);
@@ -344,7 +345,7 @@ def LLVM_GEPOp : LLVM_Op<"getelementptr", [Pure]> {
   let hasVerifier = 1;
 }
 
-def LLVM_LoadOp : LLVM_Op<"load">, MemoryOpWithAlignmentAndAttributes {
+def LLVM_LoadOp : LLVM_Op<"load">, MemoryOpBase {
   let arguments = (ins Arg<LLVM_PointerTo<LLVM_LoadableType>, "", [MemRead]>:$addr,
                    OptionalAttr<SymbolRefArrayAttr>:$access_groups,
                    OptionalAttr<SymbolRefArrayAttr>:$alias_scopes,
@@ -383,7 +384,7 @@ def LLVM_LoadOp : LLVM_Op<"load">, MemoryOpWithAlignmentAndAttributes {
   let hasVerifier = 1;
 }
 
-def LLVM_StoreOp : LLVM_Op<"store">, MemoryOpWithAlignmentAndAttributes {
+def LLVM_StoreOp : LLVM_Op<"store">, MemoryOpBase {
   let arguments = (ins LLVM_LoadableType:$value,
                    Arg<LLVM_PointerTo<LLVM_LoadableType>,"",[MemWrite]>:$addr,
                    OptionalAttr<SymbolRefArrayAttr>:$access_groups,
@@ -1655,79 +1656,111 @@ def LLVM_ConstantOp
 
 def LLVM_AtomicRMWType : AnyTypeOf<[LLVM_AnyFloat, AnyInteger]>;
 
-// FIXME: Need to add alignment and syncscope attribute to MLIR atomicrmw
-// operation.
 def LLVM_AtomicRMWOp : LLVM_Op<"atomicrmw", [
       TypesMatchWith<"result #0 and operand #1 have the same type",
-                     "val", "res", "$_self">]> {
+                     "val", "res", "$_self">]>, MemoryOpBase {
   let arguments = (ins AtomicBinOp:$bin_op,
                    LLVM_PointerTo<LLVM_AtomicRMWType>:$ptr,
-                   LLVM_AtomicRMWType:$val, AtomicOrdering:$ordering);
+                   LLVM_AtomicRMWType:$val, AtomicOrdering:$ordering,
+                   OptionalAttr<StrAttr>:$syncscope,
+                   OptionalAttr<I64Attr>:$alignment,
+                   UnitAttr:$volatile_);
   let results = (outs LLVM_AtomicRMWType:$res);
-  let hasVerifier = 1;
   let assemblyFormat = [{
-    $bin_op $ptr `,` $val $ordering
-    attr-dict `:` qualified(type($ptr)) `,` type($val)
+    (`volatile` $volatile_^)? $bin_op $ptr `,` $val
+    (`syncscope` `(` $syncscope^ `)`)? $ordering attr-dict `:`
+    qualified(type($ptr)) `,` type($val)
   }];
   string llvmInstName = "AtomicRMW";
   string llvmBuilder = [{
-    $res = builder.CreateAtomicRMW(
+    auto *inst = builder.CreateAtomicRMW(
         convertAtomicBinOpToLLVM($bin_op), $ptr, $val, llvm::MaybeAlign(),
         convertAtomicOrderingToLLVM($ordering));
-  }];
+    $res = inst;
+  }] # setVolatileCode
+     # setSyncScopeCode
+     # setAlignmentCode;
   string mlirBuilder = [{
     auto *atomicInst = cast<llvm::AtomicRMWInst>(inst);
-    $res = $_builder.create<LLVM::AtomicRMWOp>($_location, $_resultType,
+    unsigned alignment = atomicInst->getAlign().value();
+    $res = $_builder.create<LLVM::AtomicRMWOp>($_location,
         convertAtomicBinOpFromLLVM(atomicInst->getOperation()), $ptr, $val,
-        convertAtomicOrderingFromLLVM(atomicInst->getOrdering()));
+        convertAtomicOrderingFromLLVM(atomicInst->getOrdering()),
+        getLLVMSyncScope(atomicInst), alignment, atomicInst->isVolatile());
   }];
-  // Only $ptr and $val are llvm instruction operands.
-  list<int> llvmArgIndices = [-1, 0, 1, -1];
+  list<int> llvmArgIndices = [-1, 0, 1, -1, -1, -1, -1];
+  let builders = [
+    OpBuilder<(ins "LLVM::AtomicBinOp":$binOp, "Value":$ptr, "Value":$val,
+      "LLVM::AtomicOrdering":$ordering,
+      CArg<"StringRef", "StringRef()">:$syncscope,
+      CArg<"unsigned", "0">:$alignment, CArg<"bool", "false">:$isVolatile
+    )>
+  ];
+  let hasVerifier = 1;
 }
 
 def LLVM_AtomicCmpXchgType : AnyTypeOf<[AnyInteger, LLVM_AnyPointer]>;
 
-// FIXME: Need to add alignment attribute to MLIR cmpxchg operation.
 def LLVM_AtomicCmpXchgOp : LLVM_Op<"cmpxchg", [
       TypesMatchWith<"operand #1 and operand #2 have the same type",
                      "val", "cmp", "$_self">,
       TypesMatchWith<"result #0 has an LLVM struct type consisting of "
-                     "the type of operand #2 and a bool",
-                     "val", "res", "getValAndBoolStructType($_self)">]> {
+                     "the type of operand #2 and a bool", "val", "res",
+                     "getValAndBoolStructType($_self)">]>, MemoryOpBase {
   let arguments = (ins LLVM_PointerTo<LLVM_AtomicCmpXchgType>:$ptr,
                    LLVM_AtomicCmpXchgType:$cmp, LLVM_AtomicCmpXchgType:$val,
                    AtomicOrdering:$success_ordering,
-                   AtomicOrdering:$failure_ordering);
+                   AtomicOrdering:$failure_ordering,
+                   OptionalAttr<StrAttr>:$syncscope,
+                   OptionalAttr<I64Attr>:$alignment,
+                   UnitAttr:$weak,
+                   UnitAttr:$volatile_);
   let results = (outs LLVM_AnyStruct:$res);
-  let hasVerifier = 1;
   let assemblyFormat = [{
-    $ptr `,` $cmp `,` $val $success_ordering $failure_ordering
+    (`weak` $weak^)? (`volatile` $volatile_^)? $ptr `,` $cmp `,` $val
+    (`syncscope` `(` $syncscope^ `)`)? $success_ordering $failure_ordering
     attr-dict `:` qualified(type($ptr)) `,` type($val)
   }];
   string llvmInstName = "AtomicCmpXchg";
   string llvmBuilder = [{
-    $res = builder.CreateAtomicCmpXchg($ptr, $cmp, $val, llvm::MaybeAlign(),
-                   convertAtomicOrderingToLLVM($success_ordering),
-                   convertAtomicOrderingToLLVM($failure_ordering));
-  }];
+    auto *inst = builder.CreateAtomicCmpXchg($ptr, $cmp, $val,
+        llvm::MaybeAlign(), convertAtomicOrderingToLLVM($success_ordering),
+        convertAtomicOrderingToLLVM($failure_ordering));
+    $res = inst;
+    inst->setWeak($weak);
+  }] # setVolatileCode
+     # setSyncScopeCode
+     # setAlignmentCode;
   string mlirBuilder = [{
     auto *cmpXchgInst = cast<llvm::AtomicCmpXchgInst>(inst);
+    unsigned alignment = cmpXchgInst->getAlign().value();
     $res = $_builder.create<LLVM::AtomicCmpXchgOp>(
-      $_location, $_resultType, $ptr, $cmp, $val,
+      $_location, $ptr, $cmp, $val,
       convertAtomicOrderingFromLLVM(cmpXchgInst->getSuccessOrdering()),
-      convertAtomicOrderingFromLLVM(cmpXchgInst->getFailureOrdering()));
+      convertAtomicOrderingFromLLVM(cmpXchgInst->getFailureOrdering()),
+      getLLVMSyncScope(cmpXchgInst), alignment, cmpXchgInst->isWeak(),
+      cmpXchgInst->isVolatile());
   }];
+  let builders = [
+    OpBuilder<(ins "Value":$ptr, "Value":$cmp, "Value":$val,
+      "LLVM::AtomicOrdering":$successOrdering,
+      "LLVM::AtomicOrdering":$failureOrdering,
+      CArg<"StringRef", "StringRef()">:$syncscope,
+      CArg<"unsigned", "0">:$alignment, CArg<"bool", "false">:$isWeak,
+      CArg<"bool", "false">:$isVolatile
+    )>
+  ];
+  let hasVerifier = 1;
 }
 
-def LLVM_FenceOp : LLVM_Op<"fence"> {
-  let arguments = (ins AtomicOrdering:$ordering, StrAttr:$syncscope);
-  let builders = [LLVM_VoidResultTypeOpBuilder, LLVM_ZeroResultOpBuilder];
+def LLVM_FenceOp : LLVM_Op<"fence">, MemoryOpBase {
+  let arguments = (ins AtomicOrdering:$ordering,
+                   OptionalAttr<StrAttr>:$syncscope);
+  let assemblyFormat = "(`syncscope` `(` $syncscope^ `)`)? $ordering attr-dict";
   string llvmInstName = "Fence";
   let llvmBuilder = [{
-    llvm::LLVMContext &llvmContext = builder.getContext();
-    builder.CreateFence(convertAtomicOrderingToLLVM($ordering),
-      llvmContext.getOrInsertSyncScopeID($syncscope));
-  }];
+    auto *inst = builder.CreateFence(convertAtomicOrderingToLLVM($ordering));
+  }] # setSyncScopeCode;
   string mlirBuilder = [{
     llvm::FenceInst *fenceInst = cast<llvm::FenceInst>(inst);
     $_op = $_builder.create<LLVM::FenceOp>(
@@ -1735,7 +1768,12 @@ def LLVM_FenceOp : LLVM_Op<"fence"> {
       convertAtomicOrderingFromLLVM(fenceInst->getOrdering()),
       getLLVMSyncScope(fenceInst));
   }];
-  let hasCustomAssemblyFormat = 1;
+  let builders = [
+    LLVM_VoidResultTypeOpBuilder,
+    LLVM_ZeroResultOpBuilder,
+    OpBuilder<(ins "LLVM::AtomicOrdering":$ordering,
+      CArg<"StringRef", "StringRef()">:$syncscope)>
+  ];
   let hasVerifier = 1;
 }
 
index 685691d..d6097f0 100644 (file)
@@ -588,12 +588,8 @@ struct GenericAtomicRMWOpLowering
     // Append the cmpxchg op to the end of the loop block.
     auto successOrdering = LLVM::AtomicOrdering::acq_rel;
     auto failureOrdering = LLVM::AtomicOrdering::monotonic;
-    auto boolType = IntegerType::get(rewriter.getContext(), 1);
-    auto pairType = LLVM::LLVMStructType::getLiteral(rewriter.getContext(),
-                                                     {valueType, boolType});
     auto cmpxchg = rewriter.create<LLVM::AtomicCmpXchgOp>(
-        loc, pairType, dataPtr, loopArgument, result, successOrdering,
-        failureOrdering);
+        loc, dataPtr, loopArgument, result, successOrdering, failureOrdering);
     // Extract the %new_loaded and %ok values from the pair.
     Value newLoaded = rewriter.create<LLVM::ExtractValueOp>(loc, cmpxchg, 0);
     Value ok = rewriter.create<LLVM::ExtractValueOp>(loc, cmpxchg, 1);
@@ -1671,13 +1667,12 @@ struct AtomicRMWOpLowering : public LoadStoreOpLowering<memref::AtomicRMWOp> {
     auto maybeKind = matchSimpleAtomicOp(atomicOp);
     if (!maybeKind)
       return failure();
-    auto resultType = adaptor.getValue().getType();
     auto memRefType = atomicOp.getMemRefType();
     auto dataPtr =
         getStridedElementPtr(atomicOp.getLoc(), memRefType, adaptor.getMemref(),
                              adaptor.getIndices(), rewriter);
     rewriter.replaceOpWithNewOp<LLVM::AtomicRMWOp>(
-        atomicOp, resultType, *maybeKind, dataPtr, adaptor.getValue(),
+        atomicOp, *maybeKind, dataPtr, adaptor.getValue(),
         LLVM::AtomicOrdering::acq_rel);
     return success();
   }
index 1c3e35d..a4acf04 100644 (file)
@@ -229,7 +229,7 @@ static omp::ReductionDeclareOp addAtomicRMW(OpBuilder &builder,
   builder.setInsertionPointToEnd(atomicBlock);
   Value loaded = builder.create<LLVM::LoadOp>(reduce.getLoc(),
                                               atomicBlock->getArgument(1));
-  builder.create<LLVM::AtomicRMWOp>(reduce.getLoc(), type, atomicKind,
+  builder.create<LLVM::AtomicRMWOp>(reduce.getLoc(), atomicKind,
                                     atomicBlock->getArgument(0), loaded,
                                     LLVM::AtomicOrdering::monotonic);
   builder.create<omp::YieldOp>(reduce.getLoc(), ArrayRef<Value>());
index 79e39e1..9d7ce48 100644 (file)
@@ -2227,40 +2227,18 @@ LogicalResult LLVM::ConstantOp::verify() {
 OpFoldResult LLVM::ConstantOp::fold(FoldAdaptor) { return getValue(); }
 
 //===----------------------------------------------------------------------===//
-// Utility functions for parsing atomic ops
-//===----------------------------------------------------------------------===//
-
-// Helper function to parse a keyword into the specified attribute named by
-// `attrName`. The keyword must match one of the string values defined by the
-// AtomicOrdering enum. The resulting I64 attribute is added to the `result`
-// state.
-static ParseResult parseAtomicOrdering(OpAsmParser &parser,
-                                       OperationState &result,
-                                       StringRef attrName) {
-  SMLoc loc;
-  StringRef ordering;
-  if (parser.getCurrentLocation(&loc) || parser.parseKeyword(&ordering))
-    return failure();
-
-  // 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 value = static_cast<int64_t>(*kind);
-  auto attr = parser.getBuilder().getI64IntegerAttr(value);
-  result.addAttribute(attrName, attr);
+// AtomicRMWOp
+//===----------------------------------------------------------------------===//
 
-  return success();
+void AtomicRMWOp::build(OpBuilder &builder, OperationState &state,
+                        AtomicBinOp binOp, Value ptr, Value val,
+                        AtomicOrdering ordering, StringRef syncscope,
+                        unsigned alignment, bool isVolatile) {
+  build(builder, state, val.getType(), binOp, ptr, val, ordering,
+        !syncscope.empty() ? builder.getStringAttr(syncscope) : nullptr,
+        alignment ? builder.getI64IntegerAttr(alignment) : nullptr, isVolatile);
 }
 
-//===----------------------------------------------------------------------===//
-// Verifier for LLVM::AtomicRMWOp.
-//===----------------------------------------------------------------------===//
-
 LogicalResult AtomicRMWOp::verify() {
   auto ptrType = getPtr().getType().cast<LLVM::LLVMPointerType>();
   auto valType = getVal().getType();
@@ -2297,7 +2275,7 @@ LogicalResult AtomicRMWOp::verify() {
 }
 
 //===----------------------------------------------------------------------===//
-// Verifier for LLVM::AtomicCmpXchgOp.
+// AtomicCmpXchgOp
 //===----------------------------------------------------------------------===//
 
 /// Returns an LLVM struct type that contains a value type and a boolean type.
@@ -2306,6 +2284,18 @@ static LLVMStructType getValAndBoolStructType(Type valType) {
   return LLVMStructType::getLiteral(valType.getContext(), {valType, boolType});
 }
 
+void AtomicCmpXchgOp::build(OpBuilder &builder, OperationState &state,
+                            Value ptr, Value cmp, Value val,
+                            AtomicOrdering successOrdering,
+                            AtomicOrdering failureOrdering, StringRef syncscope,
+                            unsigned alignment, bool isWeak, bool isVolatile) {
+  build(builder, state, getValAndBoolStructType(val.getType()), ptr, cmp, val,
+        successOrdering, failureOrdering,
+        !syncscope.empty() ? builder.getStringAttr(syncscope) : nullptr,
+        alignment ? builder.getI64IntegerAttr(alignment) : nullptr, isWeak,
+        isVolatile);
+}
+
 LogicalResult AtomicCmpXchgOp::verify() {
   auto ptrType = getPtr().getType().cast<LLVM::LLVMPointerType>();
   if (!ptrType)
@@ -2331,35 +2321,13 @@ LogicalResult AtomicCmpXchgOp::verify() {
 }
 
 //===----------------------------------------------------------------------===//
-// Printer, parser and verifier for LLVM::FenceOp.
+// FenceOp
 //===----------------------------------------------------------------------===//
 
-// <operation> ::= `llvm.fence` (`syncscope(`strAttr`)`)? keyword
-// attribute-dict?
-ParseResult FenceOp::parse(OpAsmParser &parser, OperationState &result) {
-  StringAttr sScope;
-  StringRef syncscopeKeyword = "syncscope";
-  if (!failed(parser.parseOptionalKeyword(syncscopeKeyword))) {
-    if (parser.parseLParen() ||
-        parser.parseAttribute(sScope, syncscopeKeyword, result.attributes) ||
-        parser.parseRParen())
-      return failure();
-  } else {
-    result.addAttribute(syncscopeKeyword,
-                        parser.getBuilder().getStringAttr(""));
-  }
-  if (parseAtomicOrdering(parser, result, "ordering") ||
-      parser.parseOptionalAttrDict(result.attributes))
-    return failure();
-  return success();
-}
-
-void FenceOp::print(OpAsmPrinter &p) {
-  StringRef syncscopeKeyword = "syncscope";
-  p << ' ';
-  if (!(*this)->getAttr(syncscopeKeyword).cast<StringAttr>().getValue().empty())
-    p << "syncscope(" << (*this)->getAttr(syncscopeKeyword) << ") ";
-  p << stringifyAtomicOrdering(getOrdering());
+void FenceOp::build(OpBuilder &builder, OperationState &state,
+                    AtomicOrdering ordering, StringRef syncscope) {
+  build(builder, state, ordering,
+        syncscope.empty() ? nullptr : builder.getStringAttr(syncscope));
 }
 
 LogicalResult FenceOp::verify() {
index 9d7c82b..a5142f9 100644 (file)
@@ -98,15 +98,27 @@ static FloatType getDLFloatType(MLIRContext &ctx, int32_t bitwidth) {
   }
 }
 
-/// Converts the sync scope identifier of `fenceInst` to the string
-/// representation necessary to build the LLVM dialect fence operation.
-static StringRef getLLVMSyncScope(llvm::FenceInst *fenceInst) {
-  llvm::LLVMContext &llvmContext = fenceInst->getContext();
-  SmallVector<StringRef> syncScopeNames;
-  llvmContext.getSyncScopeNames(syncScopeNames);
-  for (StringRef name : syncScopeNames)
-    if (fenceInst->getSyncScopeID() == llvmContext.getOrInsertSyncScopeID(name))
-      return name;
+/// Converts the sync scope identifier of `inst` to the string representation
+/// necessary to build an atomic LLVM dialect operation. Returns the empty
+/// string if the operation has either no sync scope or the default system-level
+/// sync scope attached. The atomic operations only set their sync scope
+/// attribute if they have a non-default sync scope attached.
+static StringRef getLLVMSyncScope(llvm::Instruction *inst) {
+  std::optional<llvm::SyncScope::ID> syncScopeID =
+      llvm::getAtomicSyncScopeID(inst);
+  if (!syncScopeID)
+    return "";
+
+  // Search the sync scope name for the given identifier. The default
+  // system-level sync scope thereby maps to the empty string.
+  SmallVector<StringRef> syncScopeName;
+  llvm::LLVMContext &llvmContext = inst->getContext();
+  llvmContext.getSyncScopeNames(syncScopeName);
+  auto *it = llvm::find_if(syncScopeName, [&](StringRef name) {
+    return *syncScopeID == llvmContext.getOrInsertSyncScopeID(name);
+  });
+  if (it != syncScopeName.end())
+    return *it;
   llvm_unreachable("incorrect sync scope identifier");
 }
 
index 5627d2f..e789978 100644 (file)
@@ -343,6 +343,8 @@ func.func @null() {
 func.func @atomicrmw(%ptr : !llvm.ptr, %val : f32) {
   // CHECK: llvm.atomicrmw fadd %{{.*}}, %{{.*}} monotonic : !llvm.ptr, f32
   %0 = llvm.atomicrmw fadd %ptr, %val monotonic : !llvm.ptr, f32
+  // CHECK: llvm.atomicrmw volatile fsub %{{.*}}, %{{.*}} syncscope("singlethread") monotonic {alignment = 16 : i64} : !llvm.ptr, f32
+  %1 = llvm.atomicrmw volatile fsub %ptr, %val syncscope("singlethread") monotonic {alignment = 16 : i64} : !llvm.ptr, f32
   llvm.return
 }
 
@@ -350,6 +352,8 @@ func.func @atomicrmw(%ptr : !llvm.ptr, %val : f32) {
 func.func @cmpxchg(%ptr : !llvm.ptr, %cmp : i32, %new : i32) {
   // CHECK: llvm.cmpxchg %{{.*}}, %{{.*}}, %{{.*}} acq_rel monotonic : !llvm.ptr, i32
   %0 = llvm.cmpxchg %ptr, %cmp, %new acq_rel monotonic : !llvm.ptr, i32
+  // CHECK: llvm.cmpxchg weak volatile %{{.*}}, %{{.*}}, %{{.*}} syncscope("singlethread") acq_rel monotonic {alignment = 16 : i64} : !llvm.ptr, i32
+  %1 = llvm.cmpxchg weak volatile %ptr, %cmp, %new syncscope("singlethread") acq_rel monotonic {alignment = 16 : i64} : !llvm.ptr, i32
   llvm.return
 }
 
index 15abc3d..14dbf07 100644 (file)
@@ -401,6 +401,11 @@ define void @atomic_rmw(ptr %ptr1, i32 %val1, ptr %ptr2, float %val2) {
   %16 = atomicrmw uinc_wrap ptr %ptr1, i32 %val1 acquire
   ; CHECK:  llvm.atomicrmw udec_wrap %[[PTR1]], %[[VAL1]] acquire
   %17 = atomicrmw udec_wrap ptr %ptr1, i32 %val1 acquire
+
+  ; CHECK:  llvm.atomicrmw volatile
+  ; CHECK-SAME:  syncscope("singlethread")
+  ; CHECK-SAME:  {alignment = 8 : i64}
+  %18 = atomicrmw volatile udec_wrap ptr %ptr1, i32 %val1 syncscope("singlethread") acquire, align 8
   ret void
 }
 
@@ -415,6 +420,11 @@ define void @atomic_cmpxchg(ptr %ptr1, i32 %val1, i32 %val2) {
   %1 = cmpxchg ptr %ptr1, i32 %val1, i32 %val2 seq_cst seq_cst
   ; CHECK:  llvm.cmpxchg %[[PTR1]], %[[VAL1]], %[[VAL2]] monotonic seq_cst
   %2 = cmpxchg ptr %ptr1, i32 %val1, i32 %val2 monotonic seq_cst
+
+  ; CHECK:  llvm.cmpxchg weak volatile
+  ; CHECK-SAME:  syncscope("singlethread")
+  ; CHECK-SAME:  {alignment = 8 : i64}
+  %3 = cmpxchg weak volatile ptr %ptr1, i32 %val1, i32 %val2 syncscope("singlethread") monotonic seq_cst, align 8
   ret void
 }
 
index bbaa857..840b1f2 100644 (file)
@@ -1436,6 +1436,11 @@ llvm.func @atomicrmw(
   %15 = llvm.atomicrmw uinc_wrap %i32_ptr, %i32 monotonic : !llvm.ptr<i32>, i32
   // CHECK: atomicrmw udec_wrap ptr %{{.*}}, i32 %{{.*}} monotonic
   %16 = llvm.atomicrmw udec_wrap %i32_ptr, %i32 monotonic : !llvm.ptr<i32>, i32
+
+  // CHECK: atomicrmw volatile
+  // CHECK-SAME:  syncscope("singlethread")
+  // CHECK-SAME:  align 8
+  %17 = llvm.atomicrmw volatile udec_wrap %i32_ptr, %i32 syncscope("singlethread") monotonic {alignment = 8 : i64} : !llvm.ptr<i32>, i32
   llvm.return
 }
 
@@ -1447,6 +1452,11 @@ llvm.func @cmpxchg(%ptr : !llvm.ptr<i32>, %cmp : i32, %val: i32) {
   %1 = llvm.extractvalue %0[0] : !llvm.struct<(i32, i1)>
   // CHECK: %{{[0-9]+}} = extractvalue { i32, i1 } %{{[0-9]+}}, 1
   %2 = llvm.extractvalue %0[1] : !llvm.struct<(i32, i1)>
+
+  // CHECK:  cmpxchg weak volatile
+  // CHECK-SAME:  syncscope("singlethread")
+  // CHECK-SAME:  align 8
+  %3 = llvm.cmpxchg weak volatile %ptr, %cmp, %val syncscope("singlethread") acq_rel monotonic {alignment = 8 : i64} : !llvm.ptr<i32>, i32
   llvm.return
 }