[Mlir-commits] [mlir] [mlir][ptr] Add load and store ops. (PR #156093)

Fabian Mora llvmlistbot at llvm.org
Mon Sep 1 04:05:46 PDT 2025


https://github.com/fabianmcg updated https://github.com/llvm/llvm-project/pull/156093

>From 7f3579388165eeff7b92e88c5034f1414666a93b Mon Sep 17 00:00:00 2001
From: Fabian Mora <6982088+fabianmcg at users.noreply.github.com>
Date: Fri, 29 Aug 2025 20:00:03 +0000
Subject: [PATCH 1/3] add load store ops

---
 .../Dialect/Ptr/IR/MemorySpaceInterfaces.h    |   4 +-
 .../Dialect/Ptr/IR/MemorySpaceInterfaces.td   |   8 +-
 mlir/include/mlir/Dialect/Ptr/IR/PtrEnums.td  |  54 ++++----
 mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td    | 118 ++++++++++++++++++
 mlir/include/mlir/IR/Properties.td            |   7 +-
 mlir/lib/Dialect/Ptr/IR/PtrAttrs.cpp          |   9 +-
 mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp        | 116 +++++++++++++++++
 mlir/test/Dialect/Ptr/invalid.mlir            |  24 ++++
 mlir/test/Dialect/Ptr/ops.mlir                |  33 +++--
 mlir/test/lib/Dialect/Test/TestAttributes.cpp |  11 +-
 10 files changed, 332 insertions(+), 52 deletions(-)

diff --git a/mlir/include/mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h b/mlir/include/mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h
index a0467550c4623..3e6754c6bec99 100644
--- a/mlir/include/mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h
+++ b/mlir/include/mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h
@@ -20,8 +20,8 @@
 namespace mlir {
 class Operation;
 namespace ptr {
-enum class AtomicBinOp : uint64_t;
-enum class AtomicOrdering : uint64_t;
+enum class AtomicBinOp : uint32_t;
+enum class AtomicOrdering : uint32_t;
 } // namespace ptr
 } // namespace mlir
 
diff --git a/mlir/include/mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.td b/mlir/include/mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.td
index 54efeb0bc0f9e..0171b9ca2e5dc 100644
--- a/mlir/include/mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.td
+++ b/mlir/include/mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.td
@@ -42,7 +42,7 @@ def MemorySpaceAttrInterface : AttrInterface<"MemorySpaceAttrInterface"> {
       /*methodName=*/  "isValidLoad",
       /*args=*/        (ins "::mlir::Type":$type,
                             "::mlir::ptr::AtomicOrdering":$ordering,
-                            "::mlir::IntegerAttr":$alignment,
+                            "std::optional<int64_t>":$alignment,
                             "::llvm::function_ref<::mlir::InFlightDiagnostic()>":$emitError)
     >,
     InterfaceMethod<
@@ -57,7 +57,7 @@ def MemorySpaceAttrInterface : AttrInterface<"MemorySpaceAttrInterface"> {
       /*methodName=*/  "isValidStore",
       /*args=*/        (ins "::mlir::Type":$type,
                             "::mlir::ptr::AtomicOrdering":$ordering,
-                            "::mlir::IntegerAttr":$alignment,
+                            "std::optional<int64_t>":$alignment,
                             "::llvm::function_ref<::mlir::InFlightDiagnostic()>":$emitError)
     >,
     InterfaceMethod<
@@ -73,7 +73,7 @@ def MemorySpaceAttrInterface : AttrInterface<"MemorySpaceAttrInterface"> {
       /*args=*/        (ins "::mlir::ptr::AtomicBinOp":$op,
                             "::mlir::Type":$type,
                             "::mlir::ptr::AtomicOrdering":$ordering,
-                            "::mlir::IntegerAttr":$alignment,
+                            "std::optional<int64_t>":$alignment,
                             "::llvm::function_ref<::mlir::InFlightDiagnostic()>":$emitError)
     >,
     InterfaceMethod<
@@ -90,7 +90,7 @@ def MemorySpaceAttrInterface : AttrInterface<"MemorySpaceAttrInterface"> {
       /*args=*/        (ins "::mlir::Type":$type,
                             "::mlir::ptr::AtomicOrdering":$successOrdering,
                             "::mlir::ptr::AtomicOrdering":$failureOrdering,
-                            "::mlir::IntegerAttr":$alignment,
+                            "std::optional<int64_t>":$alignment,
                             "::llvm::function_ref<::mlir::InFlightDiagnostic()>":$emitError)
     >,
     InterfaceMethod<
diff --git a/mlir/include/mlir/Dialect/Ptr/IR/PtrEnums.td b/mlir/include/mlir/Dialect/Ptr/IR/PtrEnums.td
index cc556c61e6416..c169f48e573d0 100644
--- a/mlir/include/mlir/Dialect/Ptr/IR/PtrEnums.td
+++ b/mlir/include/mlir/Dialect/Ptr/IR/PtrEnums.td
@@ -15,25 +15,25 @@ include "mlir/IR/EnumAttr.td"
 // Atomic binary op enum attribute.
 //===----------------------------------------------------------------------===//
 
-def AtomicBinOpXchg : I64EnumAttrCase<"xchg", 0, "xchg">;
-def AtomicBinOpAdd  : I64EnumAttrCase<"add", 1, "add">;
-def AtomicBinOpSub  : I64EnumAttrCase<"sub", 2, "sub">;
-def AtomicBinOpAnd  : I64EnumAttrCase<"_and", 3, "_and">;
-def AtomicBinOpNand : I64EnumAttrCase<"nand", 4, "nand">;
-def AtomicBinOpOr   : I64EnumAttrCase<"_or", 5, "_or">;
-def AtomicBinOpXor  : I64EnumAttrCase<"_xor", 6, "_xor">;
-def AtomicBinOpMax  : I64EnumAttrCase<"max", 7, "max">;
-def AtomicBinOpMin  : I64EnumAttrCase<"min", 8, "min">;
-def AtomicBinOpUMax : I64EnumAttrCase<"umax", 9, "umax">;
-def AtomicBinOpUMin : I64EnumAttrCase<"umin", 10, "umin">;
-def AtomicBinOpFAdd : I64EnumAttrCase<"fadd", 11, "fadd">;
-def AtomicBinOpFSub : I64EnumAttrCase<"fsub", 12, "fsub">;
-def AtomicBinOpFMax : I64EnumAttrCase<"fmax", 13, "fmax">;
-def AtomicBinOpFMin : I64EnumAttrCase<"fmin", 14, "fmin">;
-def AtomicBinOpUIncWrap : I64EnumAttrCase<"uinc_wrap", 15, "uinc_wrap">;
-def AtomicBinOpUDecWrap : I64EnumAttrCase<"udec_wrap", 16, "udec_wrap">;
+def AtomicBinOpXchg : I32EnumCase<"xchg", 0, "xchg">;
+def AtomicBinOpAdd  : I32EnumCase<"add", 1, "add">;
+def AtomicBinOpSub  : I32EnumCase<"sub", 2, "sub">;
+def AtomicBinOpAnd  : I32EnumCase<"_and", 3, "_and">;
+def AtomicBinOpNand : I32EnumCase<"nand", 4, "nand">;
+def AtomicBinOpOr   : I32EnumCase<"_or", 5, "_or">;
+def AtomicBinOpXor  : I32EnumCase<"_xor", 6, "_xor">;
+def AtomicBinOpMax  : I32EnumCase<"max", 7, "max">;
+def AtomicBinOpMin  : I32EnumCase<"min", 8, "min">;
+def AtomicBinOpUMax : I32EnumCase<"umax", 9, "umax">;
+def AtomicBinOpUMin : I32EnumCase<"umin", 10, "umin">;
+def AtomicBinOpFAdd : I32EnumCase<"fadd", 11, "fadd">;
+def AtomicBinOpFSub : I32EnumCase<"fsub", 12, "fsub">;
+def AtomicBinOpFMax : I32EnumCase<"fmax", 13, "fmax">;
+def AtomicBinOpFMin : I32EnumCase<"fmin", 14, "fmin">;
+def AtomicBinOpUIncWrap : I32EnumCase<"uinc_wrap", 15, "uinc_wrap">;
+def AtomicBinOpUDecWrap : I32EnumCase<"udec_wrap", 16, "udec_wrap">;
 
-def AtomicBinOp : I64EnumAttr<
+def AtomicBinOp : I32Enum<
     "AtomicBinOp",
     "ptr.atomicrmw binary operations",
     [AtomicBinOpXchg, AtomicBinOpAdd, AtomicBinOpSub, AtomicBinOpAnd,
@@ -48,15 +48,15 @@ def AtomicBinOp : I64EnumAttr<
 // Atomic ordering enum attribute.
 //===----------------------------------------------------------------------===//
 
-def AtomicOrderingNotAtomic : I64EnumAttrCase<"not_atomic", 0, "not_atomic">;
-def AtomicOrderingUnordered : I64EnumAttrCase<"unordered", 1, "unordered">;
-def AtomicOrderingMonotonic : I64EnumAttrCase<"monotonic", 2, "monotonic">;
-def AtomicOrderingAcquire   : I64EnumAttrCase<"acquire", 3, "acquire">;
-def AtomicOrderingRelease   : I64EnumAttrCase<"release", 4, "release">;
-def AtomicOrderingAcqRel    : I64EnumAttrCase<"acq_rel", 5, "acq_rel">;
-def AtomicOrderingSeqCst    : I64EnumAttrCase<"seq_cst", 6, "seq_cst">;
+def AtomicOrderingNotAtomic : I32EnumCase<"not_atomic", 0, "not_atomic">;
+def AtomicOrderingUnordered : I32EnumCase<"unordered", 1, "unordered">;
+def AtomicOrderingMonotonic : I32EnumCase<"monotonic", 2, "monotonic">;
+def AtomicOrderingAcquire   : I32EnumCase<"acquire", 3, "acquire">;
+def AtomicOrderingRelease   : I32EnumCase<"release", 4, "release">;
+def AtomicOrderingAcqRel    : I32EnumCase<"acq_rel", 5, "acq_rel">;
+def AtomicOrderingSeqCst    : I32EnumCase<"seq_cst", 6, "seq_cst">;
 
-def AtomicOrdering : I64EnumAttr<
+def AtomicOrdering : I32Enum<
     "AtomicOrdering",
     "Atomic ordering for LLVM's memory model",
     [AtomicOrderingNotAtomic, AtomicOrderingUnordered, AtomicOrderingMonotonic,
@@ -66,6 +66,8 @@ def AtomicOrdering : I64EnumAttr<
   let cppNamespace = "::mlir::ptr";
 }
 
+def AtomicOrderingProp : EnumProp<AtomicOrdering>;
+
 //===----------------------------------------------------------------------===//
 // Ptr add flags enum properties.
 //===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td b/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
index 440f6e571eec5..edf1788d40e10 100644
--- a/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
+++ b/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
@@ -118,6 +118,124 @@ def Ptr_PtrAddOp : Pointer_Op<"ptr_add", [
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// LoadOp
+//===----------------------------------------------------------------------===//
+
+def AlignmentProp : OptionalProp<I64Prop>;
+
+def Ptr_LoadOp : Pointer_Op<"load", [
+    DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
+  ]> {
+  let description = [{
+    The `load` operation is used to read from memory. A load may be marked as
+    atomic, volatile, and/or nontemporal.
+
+    An atomic load only supports a limited set of pointer, integer, and
+    floating point types, and requires an explicit alignment.
+
+    Examples:
+    ```mlir
+    // A volatile load of a float variable.
+    %0 = ptr.load volatile %ptr : !ptr.ptr -> f32
+
+    // A nontemporal load of a float variable.
+    %0 = ptr.load %ptr nontemporal : !ptr.ptr -> f32
+
+    // An atomic load of an integer variable.
+    %0 = ptr.load %ptr atomic monotonic alignment = 8 : !ptr.ptr -> i64
+    ```
+  }];
+  let arguments = (ins Ptr_PtrType:$ptr,
+                       AlignmentProp:$alignment,
+                       UnitProp:$volatile_,
+                       UnitProp:$nontemporal,
+                       UnitProp:$invariant,
+                       UnitProp:$invariantGroup,
+                       DefaultValuedProp<
+                         AtomicOrderingProp,
+                         "AtomicOrdering::not_atomic">:$ordering,
+                       OptionalAttr<StrAttr>:$syncscope);
+  let results = (outs AnyType:$value);
+  let assemblyFormat = [{
+    (`volatile` $volatile_^)? $ptr
+    (`atomic` (`syncscope` `(` $syncscope^ `)`)? $ordering^)?
+    oilist(
+      `nontemporal` $nontemporal |
+      `invariant` $invariant |
+      `invariant_group` $invariantGroup |
+      `alignment` `=` $alignment
+    )
+    attr-dict `:` qualified(type($ptr)) `->` type($value)
+  }];
+  let builders = [
+    OpBuilder<(ins "Type":$type, "Value":$ptr,
+      CArg<"unsigned", "0">:$alignment, CArg<"bool", "false">:$isVolatile,
+      CArg<"bool", "false">:$isNonTemporal, CArg<"bool", "false">:$isInvariant,
+      CArg<"bool", "false">:$isInvariantGroup,
+      CArg<"AtomicOrdering", "AtomicOrdering::not_atomic">:$ordering,
+      CArg<"StringRef", "StringRef()">:$syncscope)>
+  ];
+  let hasVerifier = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// StoreOp
+//===----------------------------------------------------------------------===//
+
+def Ptr_StoreOp : Pointer_Op<"store", [
+    DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
+  ]> {
+  let arguments = (ins AnyType:$value,
+                       Ptr_PtrType:$ptr,
+                       AlignmentProp:$alignment,
+                       UnitProp:$volatile_,
+                       UnitProp:$nontemporal,
+                       UnitProp:$invariantGroup,
+                       DefaultValuedProp<
+                         AtomicOrderingProp,
+                         "AtomicOrdering::not_atomic">:$ordering,
+                       OptionalAttr<StrAttr>:$syncscope);
+  let description = [{
+    The `store` operation is used to write to memory. A store may be marked as
+    atomic, volatile, and/or nontemporal.
+
+    An atomic store only supports a limited set of pointer, integer, and
+    floating point types, and requires an explicit alignment.
+
+    Examples:
+    ```mlir
+    // A volatile store of a float variable.
+    ptr.store volatile %val, %ptr : f32, !ptr.ptr
+
+    // A nontemporal store of a float variable.
+    ptr.store %val, %ptr nontemporal : f32, !ptr.ptr
+
+    // An atomic store of an integer variable.
+    ptr.store %val, %ptr atomic monotonic alignment = 8: i64, !ptr.ptr
+    ```
+  }];
+  let assemblyFormat = [{
+    (`volatile` $volatile_^)? $value `,` $ptr
+    (`atomic` (`syncscope` `(` $syncscope^ `)`)? $ordering^)?
+    oilist(
+      `nontemporal` $nontemporal |
+      `invariant_group` $invariantGroup |
+      `alignment` `=` $alignment
+    )
+    attr-dict `:` type($value) `,` qualified(type($ptr))
+  }];
+  let builders = [
+    OpBuilder<(ins "Value":$value, "Value":$ptr,
+      CArg<"unsigned", "0">:$alignment, CArg<"bool", "false">:$isVolatile,
+      CArg<"bool", "false">:$isNonTemporal,
+      CArg<"bool", "false">:$isInvariantGroup,
+      CArg<"AtomicOrdering", "AtomicOrdering::not_atomic">:$ordering,
+      CArg<"StringRef", "StringRef()">:$syncscope)>
+  ];
+  let hasVerifier = 1;
+}
+
 //===----------------------------------------------------------------------===//
 // ToPtrOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/IR/Properties.td b/mlir/include/mlir/IR/Properties.td
index a6221f9aaaef9..a7ade0675b9bb 100644
--- a/mlir/include/mlir/IR/Properties.td
+++ b/mlir/include/mlir/IR/Properties.td
@@ -773,9 +773,10 @@ class OptionalProp<Property p, bit canDelegateParsing = 1>
   }];
   let writeToMlirBytecode = [{
     $_writer.writeOwnedBool($_storage.has_value());
-    if (!$_storage.has_value())
-      return;
-  }] # !subst("$_storage", "(*($_storage))", p.writeToMlirBytecode);
+    if ($_storage.has_value()) {
+      }] # !subst("$_storage", "(*($_storage))", p.writeToMlirBytecode) # [{
+    }
+  }];
 
   let hashProperty = !if(!empty(p.hashProperty), p.hashProperty,
     [{ hash_value($_storage.has_value() ? std::optional<::llvm::hash_code>{}] #
diff --git a/mlir/lib/Dialect/Ptr/IR/PtrAttrs.cpp b/mlir/lib/Dialect/Ptr/IR/PtrAttrs.cpp
index 772d25d8a31ee..dd4e906536cfc 100644
--- a/mlir/lib/Dialect/Ptr/IR/PtrAttrs.cpp
+++ b/mlir/lib/Dialect/Ptr/IR/PtrAttrs.cpp
@@ -22,26 +22,27 @@ constexpr const static unsigned kBitsInByte = 8;
 //===----------------------------------------------------------------------===//
 
 bool GenericSpaceAttr::isValidLoad(
-    Type type, ptr::AtomicOrdering ordering, IntegerAttr alignment,
+    Type type, ptr::AtomicOrdering ordering, std::optional<int64_t> alignment,
     function_ref<InFlightDiagnostic()> emitError) const {
   return true;
 }
 
 bool GenericSpaceAttr::isValidStore(
-    Type type, ptr::AtomicOrdering ordering, IntegerAttr alignment,
+    Type type, ptr::AtomicOrdering ordering, std::optional<int64_t> alignment,
     function_ref<InFlightDiagnostic()> emitError) const {
   return true;
 }
 
 bool GenericSpaceAttr::isValidAtomicOp(
     ptr::AtomicBinOp op, Type type, ptr::AtomicOrdering ordering,
-    IntegerAttr alignment, function_ref<InFlightDiagnostic()> emitError) const {
+    std::optional<int64_t> alignment,
+    function_ref<InFlightDiagnostic()> emitError) const {
   return true;
 }
 
 bool GenericSpaceAttr::isValidAtomicXchg(
     Type type, ptr::AtomicOrdering successOrdering,
-    ptr::AtomicOrdering failureOrdering, IntegerAttr alignment,
+    ptr::AtomicOrdering failureOrdering, std::optional<int64_t> alignment,
     function_ref<InFlightDiagnostic()> emitError) const {
   return true;
 }
diff --git a/mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp b/mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp
index c5ec0cad941c5..bf87f83afc273 100644
--- a/mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp
+++ b/mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp
@@ -84,6 +84,122 @@ LogicalResult FromPtrOp::verify() {
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// LoadOp
+//===----------------------------------------------------------------------===//
+
+/// Verifies the attributes and the type of atomic memory access operations.
+template <typename OpTy>
+static LogicalResult
+verifyAtomicMemOp(OpTy memOp, ArrayRef<AtomicOrdering> unsupportedOrderings) {
+  if (memOp.getOrdering() != AtomicOrdering::not_atomic) {
+    if (llvm::is_contained(unsupportedOrderings, memOp.getOrdering()))
+      return memOp.emitOpError("unsupported ordering '")
+             << stringifyAtomicOrdering(memOp.getOrdering()) << "'";
+    if (!memOp.getAlignment())
+      return memOp.emitOpError("expected alignment for atomic access");
+    return success();
+  }
+  if (memOp.getSyncscope()) {
+    return memOp.emitOpError(
+        "expected syncscope to be null for non-atomic access");
+  }
+  return success();
+}
+
+/// Verifies that the alignment attribute is a power of 2 if present.
+static LogicalResult
+verifyAlignment(std::optional<int64_t> alignment,
+                function_ref<InFlightDiagnostic()> emitError) {
+  if (!alignment)
+    return success();
+  if (alignment.value() <= 0)
+    return emitError() << "alignment must be positive";
+  if (!llvm::isPowerOf2_64(alignment.value()))
+    return emitError() << "alignment must be a power of 2";
+  return success();
+}
+
+void LoadOp::getEffects(
+    SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
+        &effects) {
+  effects.emplace_back(MemoryEffects::Read::get(), &getPtrMutable());
+  // Volatile operations can have target-specific read-write effects on
+  // memory besides the one referred to by the pointer operand.
+  // Similarly, atomic operations that are monotonic or stricter cause
+  // synchronization that from a language point-of-view, are arbitrary
+  // read-writes into memory.
+  if (getVolatile_() || (getOrdering() != AtomicOrdering::not_atomic &&
+                         getOrdering() != AtomicOrdering::unordered)) {
+    effects.emplace_back(MemoryEffects::Write::get());
+    effects.emplace_back(MemoryEffects::Read::get());
+  }
+}
+
+LogicalResult LoadOp::verify() {
+  auto emitDiag = [&]() -> InFlightDiagnostic { return emitError(); };
+  MemorySpaceAttrInterface ms = getPtr().getType().getMemorySpace();
+  if (!ms.isValidLoad(getResult().getType(), getOrdering(), getAlignment(),
+                      emitDiag))
+    return failure();
+  if (failed(verifyAlignment(getAlignment(), emitDiag)))
+    return failure();
+  return verifyAtomicMemOp(*this,
+                           {AtomicOrdering::release, AtomicOrdering::acq_rel});
+}
+
+void LoadOp::build(OpBuilder &builder, OperationState &state, Type type,
+                   Value addr, unsigned alignment, bool isVolatile,
+                   bool isNonTemporal, bool isInvariant, bool isInvariantGroup,
+                   AtomicOrdering ordering, StringRef syncscope) {
+  build(builder, state, type, addr,
+        alignment ? std::optional<int64_t>(alignment) : std::nullopt,
+        isVolatile, isNonTemporal, isInvariant, isInvariantGroup, ordering,
+        syncscope.empty() ? nullptr : builder.getStringAttr(syncscope));
+}
+
+//===----------------------------------------------------------------------===//
+// StoreOp
+//===----------------------------------------------------------------------===//
+
+void StoreOp::getEffects(
+    SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
+        &effects) {
+  effects.emplace_back(MemoryEffects::Write::get(), &getPtrMutable());
+  // Volatile operations can have target-specific read-write effects on
+  // memory besides the one referred to by the pointer operand.
+  // Similarly, atomic operations that are monotonic or stricter cause
+  // synchronization that from a language point-of-view, are arbitrary
+  // read-writes into memory.
+  if (getVolatile_() || (getOrdering() != AtomicOrdering::not_atomic &&
+                         getOrdering() != AtomicOrdering::unordered)) {
+    effects.emplace_back(MemoryEffects::Write::get());
+    effects.emplace_back(MemoryEffects::Read::get());
+  }
+}
+
+LogicalResult StoreOp::verify() {
+  auto emitDiag = [&]() -> InFlightDiagnostic { return emitError(); };
+  MemorySpaceAttrInterface ms = getPtr().getType().getMemorySpace();
+  if (!ms.isValidStore(getValue().getType(), getOrdering(), getAlignment(),
+                       emitDiag))
+    return failure();
+  if (failed(verifyAlignment(getAlignment(), emitDiag)))
+    return failure();
+  return verifyAtomicMemOp(*this,
+                           {AtomicOrdering::acquire, AtomicOrdering::acq_rel});
+}
+
+void StoreOp::build(OpBuilder &builder, OperationState &state, Value value,
+                    Value addr, unsigned alignment, bool isVolatile,
+                    bool isNonTemporal, bool isInvariantGroup,
+                    AtomicOrdering ordering, StringRef syncscope) {
+  build(builder, state, value, addr,
+        alignment ? std::optional<int64_t>(alignment) : std::nullopt,
+        isVolatile, isNonTemporal, isInvariantGroup, ordering,
+        syncscope.empty() ? nullptr : builder.getStringAttr(syncscope));
+}
+
 //===----------------------------------------------------------------------===//
 // PtrAddOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/Ptr/invalid.mlir b/mlir/test/Dialect/Ptr/invalid.mlir
index 19fd715e5bba6..5702097567fc7 100644
--- a/mlir/test/Dialect/Ptr/invalid.mlir
+++ b/mlir/test/Dialect/Ptr/invalid.mlir
@@ -14,3 +14,27 @@ func.func @invalid_to_ptr(%v: !ptr.ptr<#ptr.generic_space>) {
   %r = ptr.to_ptr %v : !ptr.ptr<#ptr.generic_space> -> !ptr.ptr<#ptr.generic_space>
   return
 }
+
+// -----
+
+func.func @invalid_load_alignment(%arg0: !ptr.ptr<#ptr.generic_space>) -> i64 {
+  // expected-error at +1 {{alignment must be a power of 2}}
+  %r = ptr.load %arg0 alignment = 3 : !ptr.ptr<#ptr.generic_space> -> i64
+  return %r : i64
+}
+
+// -----
+
+func.func @invalid_store_alignment(%arg0: !ptr.ptr<#ptr.generic_space>, %arg1: i64) {
+  // expected-error at +1 {{alignment must be a power of 2}}
+  ptr.store %arg1, %arg0 alignment = 3 : i64, !ptr.ptr<#ptr.generic_space>
+  return
+}
+
+// -----
+
+func.func @store_const(%arg0: !ptr.ptr<#test.const_memory_space>, %arg1: i64) {
+  // expected-error at +1 {{memory space is read-only}}
+  ptr.store %arg1, %arg0 atomic monotonic alignment = 8 : i64, !ptr.ptr<#test.const_memory_space>
+  return
+}
diff --git a/mlir/test/Dialect/Ptr/ops.mlir b/mlir/test/Dialect/Ptr/ops.mlir
index eed3272d98da9..dc89489be9efc 100644
--- a/mlir/test/Dialect/Ptr/ops.mlir
+++ b/mlir/test/Dialect/Ptr/ops.mlir
@@ -1,14 +1,7 @@
-// RUN: mlir-opt %s --verify-roundtrip | FileCheck %s
+// RUN: mlir-opt %s --verify-roundtrip
 
 /// Check op assembly.
-// CHECK-LABEL: @ptr_add_type_offset
 func.func @ptr_add_type_offset(%ptr: !ptr.ptr<#ptr.generic_space>) -> !ptr.ptr<#ptr.generic_space> {
-  // CHECK: ptr.type_offset f32 : index
-  // CHECK-NEXT: ptr.ptr_add %{{.*}}, %{{.*}} : <#ptr.generic_space>, index
-  // CHECK-NEXT: ptr.ptr_add %{{.*}}, %{{.*}} : <#ptr.generic_space>, index
-  // CHECK-NEXT: ptr.ptr_add nusw %{{.*}}, %{{.*}} : <#ptr.generic_space>, index
-  // CHECK-NEXT: ptr.ptr_add nuw %{{.*}}, %{{.*}} : <#ptr.generic_space>, index
-  // CHECK-NEXT: ptr.ptr_add inbounds %{{.*}}, %{{.*}} : <#ptr.generic_space>, index
   %off = ptr.type_offset f32 : index
   %res = ptr.ptr_add %ptr, %off : !ptr.ptr<#ptr.generic_space>, index
   %res0 = ptr.ptr_add none %ptr, %off : !ptr.ptr<#ptr.generic_space>, index
@@ -19,7 +12,6 @@ func.func @ptr_add_type_offset(%ptr: !ptr.ptr<#ptr.generic_space>) -> !ptr.ptr<#
 }
 
 /// Check cast ops assembly.
-// CHECK-LABEL: @cast_ops
 func.func @cast_ops(%mr: memref<f32, #ptr.generic_space>) -> memref<f32, #ptr.generic_space> {
   %ptr = ptr.to_ptr %mr : memref<f32, #ptr.generic_space> -> !ptr.ptr<#ptr.generic_space>
   %mda = ptr.get_metadata %mr : memref<f32, #ptr.generic_space>
@@ -27,3 +19,26 @@ func.func @cast_ops(%mr: memref<f32, #ptr.generic_space>) -> memref<f32, #ptr.ge
   %mr0 = ptr.from_ptr %ptr : !ptr.ptr<#ptr.generic_space> -> memref<f32, #ptr.generic_space>
   return %res : memref<f32, #ptr.generic_space>
 }
+
+/// Check load ops assembly.
+func.func @load_ops(%arg0: !ptr.ptr<#ptr.generic_space>) -> (f32, f32, f32, f32, f32, i64, i32) {
+  %0 = ptr.load %arg0 : !ptr.ptr<#ptr.generic_space> -> f32
+  %1 = ptr.load volatile %arg0 : !ptr.ptr<#ptr.generic_space> -> f32
+  %2 = ptr.load %arg0 nontemporal : !ptr.ptr<#ptr.generic_space> -> f32
+  %3 = ptr.load %arg0 invariant : !ptr.ptr<#ptr.generic_space> -> f32
+  %4 = ptr.load %arg0 invariant_group : !ptr.ptr<#ptr.generic_space> -> f32
+  %5 = ptr.load %arg0 atomic monotonic alignment = 8 : !ptr.ptr<#ptr.generic_space> -> i64
+  %6 = ptr.load volatile %arg0 atomic syncscope("workgroup") acquire nontemporal alignment = 4 : !ptr.ptr<#ptr.generic_space> -> i32
+  return %0, %1, %2, %3, %4, %5, %6 : f32, f32, f32, f32, f32, i64, i32
+}
+
+/// Check store ops assembly.
+func.func @store_ops(%arg0: !ptr.ptr<#ptr.generic_space>, %arg1: f32, %arg2: i64, %arg3: i32) {
+  ptr.store %arg1, %arg0 : f32, !ptr.ptr<#ptr.generic_space>
+  ptr.store volatile %arg1, %arg0 : f32, !ptr.ptr<#ptr.generic_space>
+  ptr.store %arg1, %arg0 nontemporal : f32, !ptr.ptr<#ptr.generic_space>
+  ptr.store %arg1, %arg0 invariant_group : f32, !ptr.ptr<#ptr.generic_space>
+  ptr.store %arg2, %arg0 atomic monotonic alignment = 8 : i64, !ptr.ptr<#ptr.generic_space>
+  ptr.store volatile %arg3, %arg0 atomic syncscope("workgroup") release nontemporal alignment = 4 : i32, !ptr.ptr<#ptr.generic_space>
+  return
+}
diff --git a/mlir/test/lib/Dialect/Test/TestAttributes.cpp b/mlir/test/lib/Dialect/Test/TestAttributes.cpp
index 58909131e50a3..af5f1a3a699a0 100644
--- a/mlir/test/lib/Dialect/Test/TestAttributes.cpp
+++ b/mlir/test/lib/Dialect/Test/TestAttributes.cpp
@@ -385,13 +385,15 @@ TestOpAsmAttrInterfaceAttr::getAlias(::llvm::raw_ostream &os) const {
 //===----------------------------------------------------------------------===//
 
 bool TestConstMemorySpaceAttr::isValidLoad(
-    Type type, mlir::ptr::AtomicOrdering ordering, IntegerAttr alignment,
+    Type type, mlir::ptr::AtomicOrdering ordering,
+    std::optional<int64_t> alignment,
     function_ref<InFlightDiagnostic()> emitError) const {
   return true;
 }
 
 bool TestConstMemorySpaceAttr::isValidStore(
-    Type type, mlir::ptr::AtomicOrdering ordering, IntegerAttr alignment,
+    Type type, mlir::ptr::AtomicOrdering ordering,
+    std::optional<int64_t> alignment,
     function_ref<InFlightDiagnostic()> emitError) const {
   if (emitError)
     emitError() << "memory space is read-only";
@@ -400,7 +402,8 @@ bool TestConstMemorySpaceAttr::isValidStore(
 
 bool TestConstMemorySpaceAttr::isValidAtomicOp(
     mlir::ptr::AtomicBinOp binOp, Type type, mlir::ptr::AtomicOrdering ordering,
-    IntegerAttr alignment, function_ref<InFlightDiagnostic()> emitError) const {
+    std::optional<int64_t> alignment,
+    function_ref<InFlightDiagnostic()> emitError) const {
   if (emitError)
     emitError() << "memory space is read-only";
   return false;
@@ -408,7 +411,7 @@ bool TestConstMemorySpaceAttr::isValidAtomicOp(
 
 bool TestConstMemorySpaceAttr::isValidAtomicXchg(
     Type type, mlir::ptr::AtomicOrdering successOrdering,
-    mlir::ptr::AtomicOrdering failureOrdering, IntegerAttr alignment,
+    mlir::ptr::AtomicOrdering failureOrdering, std::optional<int64_t> alignment,
     function_ref<InFlightDiagnostic()> emitError) const {
   if (emitError)
     emitError() << "memory space is read-only";

>From 84d4d49d127a316cfca80c5c24dd2306479c0923 Mon Sep 17 00:00:00 2001
From: Fabian Mora <6982088+fabianmcg at users.noreply.github.com>
Date: Sun, 31 Aug 2025 12:04:34 +0000
Subject: [PATCH 2/3] address reviewer comments

---
 mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td | 34 +++++++++++++---------
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td b/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
index edf1788d40e10..efe447954b948 100644
--- a/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
+++ b/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
@@ -131,8 +131,8 @@ def Ptr_LoadOp : Pointer_Op<"load", [
     The `load` operation is used to read from memory. A load may be marked as
     atomic, volatile, and/or nontemporal.
 
-    An atomic load only supports a limited set of pointer, integer, and
-    floating point types, and requires an explicit alignment.
+    An atomic load only supports a limited set of value types, and requires
+    an explicit alignment.
 
     Examples:
     ```mlir
@@ -145,6 +145,9 @@ def Ptr_LoadOp : Pointer_Op<"load", [
     // An atomic load of an integer variable.
     %0 = ptr.load %ptr atomic monotonic alignment = 8 : !ptr.ptr -> i64
     ```
+
+    See the following link for more details on the load op:
+    https://llvm.org/docs/LangRef.html#load-instruction
   }];
   let arguments = (ins Ptr_PtrType:$ptr,
                        AlignmentProp:$alignment,
@@ -186,22 +189,12 @@ def Ptr_LoadOp : Pointer_Op<"load", [
 def Ptr_StoreOp : Pointer_Op<"store", [
     DeclareOpInterfaceMethods<MemoryEffectsOpInterface>
   ]> {
-  let arguments = (ins AnyType:$value,
-                       Ptr_PtrType:$ptr,
-                       AlignmentProp:$alignment,
-                       UnitProp:$volatile_,
-                       UnitProp:$nontemporal,
-                       UnitProp:$invariantGroup,
-                       DefaultValuedProp<
-                         AtomicOrderingProp,
-                         "AtomicOrdering::not_atomic">:$ordering,
-                       OptionalAttr<StrAttr>:$syncscope);
   let description = [{
     The `store` operation is used to write to memory. A store may be marked as
     atomic, volatile, and/or nontemporal.
 
-    An atomic store only supports a limited set of pointer, integer, and
-    floating point types, and requires an explicit alignment.
+    An atomic store only supports a limited set of value types, and requires
+    an explicit alignment.
 
     Examples:
     ```mlir
@@ -214,7 +207,20 @@ def Ptr_StoreOp : Pointer_Op<"store", [
     // An atomic store of an integer variable.
     ptr.store %val, %ptr atomic monotonic alignment = 8: i64, !ptr.ptr
     ```
+
+    See the following link for more details on the store op:
+    https://llvm.org/docs/LangRef.html#store-instruction
   }];
+  let arguments = (ins AnyType:$value,
+                       Ptr_PtrType:$ptr,
+                       AlignmentProp:$alignment,
+                       UnitProp:$volatile_,
+                       UnitProp:$nontemporal,
+                       UnitProp:$invariantGroup,
+                       DefaultValuedProp<
+                         AtomicOrderingProp,
+                         "AtomicOrdering::not_atomic">:$ordering,
+                       OptionalAttr<StrAttr>:$syncscope);
   let assemblyFormat = [{
     (`volatile` $volatile_^)? $value `,` $ptr
     (`atomic` (`syncscope` `(` $syncscope^ `)`)? $ordering^)?

>From 1adcfb98d377d48d649c01e2d8619ca809f54aaf Mon Sep 17 00:00:00 2001
From: Fabian Mora <6982088+fabianmcg at users.noreply.github.com>
Date: Mon, 1 Sep 2025 11:04:13 +0000
Subject: [PATCH 3/3] improve comment referring users to LLVM lang ref

---
 mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td b/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
index efe447954b948..1c88efced950e 100644
--- a/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
+++ b/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
@@ -146,7 +146,9 @@ def Ptr_LoadOp : Pointer_Op<"load", [
     %0 = ptr.load %ptr atomic monotonic alignment = 8 : !ptr.ptr -> i64
     ```
 
-    See the following link for more details on the load op:
+    See the following link for more details on the meaning of `alignment`,
+    `volatile_`, `nontemporal`, `invariant`, `invariant_group`, `ordering`,
+    and `syncscope`:
     https://llvm.org/docs/LangRef.html#load-instruction
   }];
   let arguments = (ins Ptr_PtrType:$ptr,
@@ -208,7 +210,8 @@ def Ptr_StoreOp : Pointer_Op<"store", [
     ptr.store %val, %ptr atomic monotonic alignment = 8: i64, !ptr.ptr
     ```
 
-    See the following link for more details on the store op:
+    See the following link for more details on the meaning of `alignment`,
+    `volatile_`, `nontemporal`, `invariant_group`, `ordering`, and `syncscope`:
     https://llvm.org/docs/LangRef.html#store-instruction
   }];
   let arguments = (ins AnyType:$value,



More information about the Mlir-commits mailing list