[Mlir-commits] [mlir] 8fe5093 - [mlir][Linalg] Split explicit inherent/discardable attribute APIs access (#218916)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Aug 26 15:55:46 PDT 2026


Author: Mehdi Amini
Date: 2026-08-26T22:55:41Z
New Revision: 8fe50937417b74c835fa9fcfb12aae9fe46af3f0

URL: https://github.com/llvm/llvm-project/commit/8fe50937417b74c835fa9fcfb12aae9fe46af3f0
DIFF: https://github.com/llvm/llvm-project/commit/8fe50937417b74c835fa9fcfb12aae9fe46af3f0.diff

LOG: [mlir][Linalg] Split explicit inherent/discardable attribute APIs access (#218916)

Migrate Linalg, Bufferization, and MemRef users to explicit discardable
or operation-specific attribute access, including the Linalg generator
and C API.

Assisted-by: Codex

Added: 
    

Modified: 
    mlir/include/mlir/IR/OpImplementation.h
    mlir/include/mlir/IR/Operation.h
    mlir/lib/CAPI/Dialect/Linalg.cpp
    mlir/lib/Conversion/LinalgToStandard/LinalgToStandard.cpp
    mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp
    mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
    mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp
    mlir/lib/Dialect/Bufferization/Transforms/OwnershipBasedBufferDeallocation.cpp
    mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
    mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
    mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
    mlir/lib/Dialect/Linalg/Transforms/BufferizableOpInterfaceImpl.cpp
    mlir/lib/Dialect/Linalg/Transforms/DropUnitDims.cpp
    mlir/lib/Dialect/Linalg/Transforms/ElementwiseToLinalg.cpp
    mlir/lib/Dialect/Linalg/Transforms/EraseUnusedOperandsAndResults.cpp
    mlir/lib/Dialect/Linalg/Transforms/FoldIntoElementwise.cpp
    mlir/lib/Dialect/Linalg/Transforms/SimplifyDepthwiseConv.cpp
    mlir/lib/Dialect/Linalg/Transforms/TilingInterfaceImpl.cpp
    mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp
    mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
    mlir/lib/Dialect/MemRef/Transforms/AllocationOpInterfaceImpl.cpp
    mlir/lib/Dialect/MemRef/Transforms/MultiBuffer.cpp
    mlir/lib/Dialect/MemRef/Transforms/NormalizeMemRefs.cpp
    mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize.mlir
    mlir/test/Dialect/Linalg/library-calls.mlir
    mlir/test/Dialect/Linalg/rank-reduce-contraction-ops.mlir
    mlir/test/Dialect/MemRef/multibuffer.mlir
    mlir/test/lib/Dialect/Bufferization/TestTensorLikeAndBufferLike.cpp
    mlir/test/mlir-linalg-ods-gen/test-linalg-ods-yaml-gen.yaml
    mlir/tools/mlir-linalg-ods-gen/mlir-linalg-ods-yaml-gen.cpp
    mlir/unittests/Dialect/Linalg/InferConvolutionDimsTest.cpp
    mlir/unittests/IR/OperationSupportTest.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/IR/OpImplementation.h b/mlir/include/mlir/IR/OpImplementation.h
index 6e0f001f83ba9..ce26c4b4c9cbf 100644
--- a/mlir/include/mlir/IR/OpImplementation.h
+++ b/mlir/include/mlir/IR/OpImplementation.h
@@ -502,6 +502,11 @@ class OpAsmPrinter : public AsmPrinter {
   virtual void printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,
                                      ArrayRef<StringRef> elidedAttrs = {}) = 0;
 
+  void printOptionalAttrDict(DictionaryAttr attrs,
+                             ArrayRef<StringRef> elidedAttrs = {}) {
+    printOptionalAttrDict(attrs.getValue(), elidedAttrs);
+  }
+
   /// If the specified operation has attributes, print out an attribute
   /// dictionary prefixed with 'attributes'.
   virtual void

diff  --git a/mlir/include/mlir/IR/Operation.h b/mlir/include/mlir/IR/Operation.h
index 92a716e8b0c23..3ea9ad8ca0cfc 100644
--- a/mlir/include/mlir/IR/Operation.h
+++ b/mlir/include/mlir/IR/Operation.h
@@ -467,6 +467,13 @@ class alignas(8) Operation final
   /// to use Properties instead.
   std::optional<Attribute> getInherentAttr(StringRef name);
 
+  /// Access an inherent attribute by name and cast it to `AttrClass`.
+  template <typename AttrClass>
+  AttrClass getInherentAttrOfType(StringRef name) {
+    return llvm::dyn_cast_or_null<AttrClass>(
+        getInherentAttr(name).value_or(Attribute{}));
+  }
+
   /// Set an inherent attribute by name.
   ///
   /// This method is available as a transient facility in the migration process

diff  --git a/mlir/lib/CAPI/Dialect/Linalg.cpp b/mlir/lib/CAPI/Dialect/Linalg.cpp
index 92ead3eed9a95..7a66f368459c6 100644
--- a/mlir/lib/CAPI/Dialect/Linalg.cpp
+++ b/mlir/lib/CAPI/Dialect/Linalg.cpp
@@ -39,7 +39,10 @@ void mlirLinalgFillBuiltinNamedOpRegion(MlirOperation mlirOp) {
   Region &region = op->getRegion(0);
   Block *body = b.createBlock(&region, /*insertPt=*/{}, argTypes, argLocs);
   b.setInsertionPointToStart(body);
-  fun(b, *body, op->getAttrs(), /*emitError=*/{});
+  NamedAttrList attrs;
+  op->getName().walkInherentAttrs(
+      op, [&](StringRef name, Attribute &attr) { attrs.append(name, attr); });
+  fun(b, *body, attrs, /*emitError=*/{});
 }
 
 MLIR_CAPI_EXPORTED bool mlirLinalgIsAContractionOp(MlirOperation op) {

diff  --git a/mlir/lib/Conversion/LinalgToStandard/LinalgToStandard.cpp b/mlir/lib/Conversion/LinalgToStandard/LinalgToStandard.cpp
index 54c554eb6bd93..b5b2b0ba2c277 100644
--- a/mlir/lib/Conversion/LinalgToStandard/LinalgToStandard.cpp
+++ b/mlir/lib/Conversion/LinalgToStandard/LinalgToStandard.cpp
@@ -83,8 +83,8 @@ getLibraryCallSymbolRef(Operation *op, PatternRewriter &rewriter) {
   // Insert a function attribute that will trigger the emission of the
   // corresponding `_mlir_ciface_xxx` interface so that external libraries see
   // a normalized ABI. This interface is added during std to llvm conversion.
-  funcOp->setAttr(LLVM::LLVMDialect::getEmitCWrapperAttrName(),
-                  UnitAttr::get(op->getContext()));
+  funcOp->setDiscardableAttr(LLVM::LLVMDialect::getEmitCWrapperAttrName(),
+                             UnitAttr::get(op->getContext()));
   funcOp.setPrivate();
   return fnNameAttr;
 }

diff  --git a/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp
index 8ca968367b026..e76b51aa8015d 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp
@@ -332,10 +332,10 @@ struct CallOpInterface
     }
 
     // 3. Create the new CallOp.
-    Operation *newCallOp =
-        func::CallOp::create(rewriter, callOp.getLoc(), funcOp.getSymName(),
-                             resultTypes, newOperands);
-    newCallOp->setAttrs(callOp->getAttrs());
+    func::CallOp newCallOp =
+        func::CallOp::create(rewriter, callOp.getLoc(), resultTypes,
+                             newOperands, callOp.getProperties(),
+                             callOp->getDiscardableAttrDictionary().getValue());
 
     // 4. Replace the old op with the new op.
     replaceOpWithBufferizedValues(rewriter, callOp, newCallOp->getResults());

diff  --git a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
index 57ef3b88b291c..937a765d49ad8 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
@@ -89,7 +89,7 @@ constexpr StringLiteral kBbArgAliasSetAttrName = "__bbarg_alias_set_attr__";
 static void setInPlaceOpOperand(OpOperand &opOperand, bool inPlace) {
   Operation *op = opOperand.getOwner();
   SmallVector<StringRef> inPlaceVector;
-  if (auto attr = op->getAttr(kInPlaceOperandsAttrName)) {
+  if (auto attr = op->getDiscardableAttr(kInPlaceOperandsAttrName)) {
     inPlaceVector = SmallVector<StringRef>(llvm::to_vector<4>(
         cast<ArrayAttr>(attr).getAsValueRange<StringAttr>()));
     // The existing attribute may have fewer entries than the current operand
@@ -104,8 +104,8 @@ static void setInPlaceOpOperand(OpOperand &opOperand, bool inPlace) {
         inPlaceVector[opOperand.getOperandNumber()] = "false";
   }
   inPlaceVector[opOperand.getOperandNumber()] = inPlace ? "true" : "false";
-  op->setAttr(kInPlaceOperandsAttrName,
-              OpBuilder(op).getStrArrayAttr(inPlaceVector));
+  op->setDiscardableAttr(kInPlaceOperandsAttrName,
+                         OpBuilder(op).getStrArrayAttr(inPlaceVector));
 }
 
 //===----------------------------------------------------------------------===//
@@ -446,21 +446,23 @@ static void annotateConflict(OpOperand *uRead, OpOperand *uConflictingWrite,
       id +
       "[CONFL-WRITE: " + std::to_string(uConflictingWrite->getOperandNumber()) +
       "]";
-  conflictingWritingOp->setAttr(conflictingWriteAttr, b.getUnitAttr());
+  conflictingWritingOp->setDiscardableAttr(conflictingWriteAttr,
+                                           b.getUnitAttr());
 
   std::string readAttr =
       id + "[READ: " + std::to_string(uRead->getOperandNumber()) + "]";
-  readingOp->setAttr(readAttr, b.getUnitAttr());
+  readingOp->setDiscardableAttr(readAttr, b.getUnitAttr());
 
   if (auto opResult = dyn_cast<OpResult>(definition)) {
     std::string defAttr =
         id + "[DEF: result " + std::to_string(opResult.getResultNumber()) + "]";
-    opResult.getDefiningOp()->setAttr(defAttr, b.getUnitAttr());
+    opResult.getDefiningOp()->setDiscardableAttr(defAttr, b.getUnitAttr());
   } else {
     auto bbArg = cast<BlockArgument>(definition);
     std::string defAttr =
         id + "[DEF: bbArg " + std::to_string(bbArg.getArgNumber()) + "]";
-    bbArg.getOwner()->getParentOp()->setAttr(defAttr, b.getUnitAttr());
+    bbArg.getOwner()->getParentOp()->setDiscardableAttr(defAttr,
+                                                        b.getUnitAttr());
   }
 }
 
@@ -908,12 +910,12 @@ static void annotateNonWritableTensor(Value value) {
   if (auto opResult = dyn_cast<OpResult>(value)) {
     std::string attr = id + "[NOT-WRITABLE: result " +
                        std::to_string(opResult.getResultNumber()) + "]";
-    opResult.getDefiningOp()->setAttr(attr, b.getUnitAttr());
+    opResult.getDefiningOp()->setDiscardableAttr(attr, b.getUnitAttr());
   } else {
     auto bbArg = cast<BlockArgument>(value);
     std::string attr = id + "[NOT-WRITABLE: bbArg " +
                        std::to_string(bbArg.getArgNumber()) + "]";
-    bbArg.getOwner()->getParentOp()->setAttr(attr, b.getUnitAttr());
+    bbArg.getOwner()->getParentOp()->setDiscardableAttr(attr, b.getUnitAttr());
   }
 }
 
@@ -1299,7 +1301,8 @@ static void annotateOpsWithAliasSets(Operation *op,
       }
     }
     if (!opResultAliasSets.empty())
-      op->setAttr(kOpResultAliasSetAttrName, b.getArrayAttr(opResultAliasSets));
+      op->setDiscardableAttr(kOpResultAliasSetAttrName,
+                             b.getArrayAttr(opResultAliasSets));
 
     // Build alias set array for every BlockArgument.
     SmallVector<Attribute> regionAliasSets;
@@ -1319,7 +1322,8 @@ static void annotateOpsWithAliasSets(Operation *op,
       regionAliasSets.push_back(b.getArrayAttr(blockAliasSets));
     }
     if (hasTensorBbArg)
-      op->setAttr(kBbArgAliasSetAttrName, b.getArrayAttr(regionAliasSets));
+      op->setDiscardableAttr(kBbArgAliasSetAttrName,
+                             b.getArrayAttr(regionAliasSets));
   });
 }
 

diff  --git a/mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp b/mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp
index 5552246427188..41d9a37844c9a 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp
@@ -99,8 +99,8 @@ static void annotateEquivalentReturnBbArg(OpOperand &returnVal,
   Operation *op = returnVal.getOwner();
 
   SmallVector<int64_t> equivBbArgs;
-  if (op->hasAttr(kEquivalentArgsAttr)) {
-    auto attr = cast<ArrayAttr>(op->getAttr(kEquivalentArgsAttr));
+  if (op->hasDiscardableAttr(kEquivalentArgsAttr)) {
+    auto attr = cast<ArrayAttr>(op->getDiscardableAttr(kEquivalentArgsAttr));
     equivBbArgs = llvm::map_to_vector<4>(attr, [](Attribute a) {
       return cast<IntegerAttr>(a).getValue().getSExtValue();
     });
@@ -110,7 +110,7 @@ static void annotateEquivalentReturnBbArg(OpOperand &returnVal,
   equivBbArgs[returnVal.getOperandNumber()] = bbArg.getArgNumber();
 
   OpBuilder b(op->getContext());
-  op->setAttr(kEquivalentArgsAttr, b.getI64ArrayAttr(equivBbArgs));
+  op->setDiscardableAttr(kEquivalentArgsAttr, b.getI64ArrayAttr(equivBbArgs));
 }
 
 /// Store function BlockArguments that are equivalent to/aliasing a returned

diff  --git a/mlir/lib/Dialect/Bufferization/Transforms/OwnershipBasedBufferDeallocation.cpp b/mlir/lib/Dialect/Bufferization/Transforms/OwnershipBasedBufferDeallocation.cpp
index 7b8340e363e39..d7aba40313a47 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/OwnershipBasedBufferDeallocation.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/OwnershipBasedBufferDeallocation.cpp
@@ -674,10 +674,10 @@ Operation *BufferDeallocation::appendOpResults(Operation *op,
   SmallVector<Value> oldResults(op->getResults());
 
   newTypes.append(types.begin(), types.end());
-  auto *newOp = Operation::create(op->getLoc(), op->getName(), newTypes,
-                                  op->getOperands(), op->getAttrDictionary(),
-                                  op->getPropertiesStorage(),
-                                  op->getSuccessors(), op->getNumRegions());
+  auto *newOp = Operation::create(
+      op->getLoc(), op->getName(), newTypes, op->getOperands(),
+      op->getDiscardableAttrDictionary(), op->getPropertiesStorage(),
+      op->getSuccessors(), op->getNumRegions());
   for (auto [oldRegion, newRegion] :
        llvm::zip(op->getRegions(), newOp->getRegions()))
     newRegion.takeBody(oldRegion);
@@ -875,7 +875,7 @@ BufferDeallocation::handleInterface(MemoryEffectOpInterface op) {
       // usually forbidden in the input IR (not supported by the buffer
       // deallocation pass). However, if they are under manual deallocation,
       // they can be safely ignored by the buffer deallocation pass.
-      if (!op->hasAttr(BufferizationDialect::kManualDeallocation))
+      if (!op->hasDiscardableAttr(BufferizationDialect::kManualDeallocation))
         return op->emitError(
             "memory free side-effect on MemRef value not supported!");
 
@@ -913,7 +913,7 @@ BufferDeallocation::handleInterface(MemoryEffectOpInterface op) {
         continue;
       }
 
-      if (op->hasAttr(BufferizationDialect::kManualDeallocation)) {
+      if (op->hasDiscardableAttr(BufferizationDialect::kManualDeallocation)) {
         // This allocation will be deallocated manually. Assign an ownership of
         // "false", so that it will never be deallocated by the buffer
         // deallocation pass.

diff  --git a/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
index e3f4988b3ab76..3045e65630cf2 100644
--- a/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
@@ -854,6 +854,12 @@ static FailureOr<ConvolutionDimensions> inferConvolutionDimsImpl(
   return dimensions;
 }
 
+static DenseIntElementsAttr getInherentConvolutionAttr(LinalgOp linalgOp,
+                                                       StringRef name) {
+  return dyn_cast_or_null<DenseIntElementsAttr>(
+      linalgOp->getInherentAttr(name).value_or(Attribute{}));
+}
+
 /// Find at least 1 parallel (output_image) and reduction (filter_loop)
 /// dimension candidates that form a convolution subcomputation within
 /// `linalgOp`. The LHS is assumed to be the convolution input while the
@@ -898,8 +904,8 @@ mlir::linalg::inferConvolutionDims(LinalgOp linalgOp) {
   return inferConvolutionDimsImpl(
       indexingMaps, linalgOp.getIteratorTypesArray(), inputExprWalker,
       /*allowEmptyConvolvedDims=*/false,
-      linalgOp->getAttrOfType<DenseIntElementsAttr>("strides"),
-      linalgOp->getAttrOfType<DenseIntElementsAttr>("dilations"));
+      getInherentConvolutionAttr(linalgOp, "strides"),
+      getInherentConvolutionAttr(linalgOp, "dilations"));
 }
 
 FailureOr<ConvolutionDimensions>
@@ -1067,8 +1073,8 @@ mlir::linalg::detail::isConvolutionInterfaceImpl(
   if (dimensions) {
     FailureOr<ConvolutionDimensions> res = inferConvolutionDimsImpl(
         indexingMaps, iteratorTypes, inputExprWalker, allowEmptyConvolvedDims,
-        linalgOp->getAttrOfType<DenseIntElementsAttr>("strides"),
-        linalgOp->getAttrOfType<DenseIntElementsAttr>("dilations"));
+        getInherentConvolutionAttr(linalgOp, "strides"),
+        getInherentConvolutionAttr(linalgOp, "dilations"));
     assert(succeeded(res) && "unexpected failure to infer convolution dims");
     *dimensions = *res;
   }

diff  --git a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
index af670b8d4fae7..9ff7f1f07a425 100644
--- a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
@@ -424,7 +424,10 @@ static void printNamedStructuredOpResults(OpAsmPrinter &p,
 static void printNamedStructuredOp(OpAsmPrinter &p, Operation *op,
                                    ValueRange inputs, ValueRange outputs,
                                    ArrayRef<StringRef> elidedAttrs = {}) {
-  p.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
+  NamedAttrList attrs(op->getDiscardableAttrDictionary());
+  op->getName().walkInherentAttrs(
+      op, [&](StringRef name, Attribute &attr) { attrs.append(name, attr); });
+  p.printOptionalAttrDict(attrs, elidedAttrs);
 
   // Printing is shared with generic ops, except for the region and
   // attributes.
@@ -1224,7 +1227,11 @@ void GenericOp::print(OpAsmPrinter &p) {
   llvm::StringSet<> genericAttrNamesSet;
   genericAttrNamesSet.insert_range(genericAttrNames);
   SmallVector<NamedAttribute, 8> genericAttrs;
-  for (auto attr : (*this)->getAttrs()) {
+  for (StringRef attrName : genericAttrNames) {
+    std::optional<Attribute> value = (*this)->getInherentAttr(attrName);
+    if (!value || !*value)
+      continue;
+    NamedAttribute attr{StringAttr::get(getContext(), attrName), *value};
     if (attr.getName() == getIteratorTypesAttrName()) {
       auto iteratorTypes =
           llvm::cast<ArrayAttr>(attr.getValue())
@@ -1257,13 +1264,13 @@ void GenericOp::print(OpAsmPrinter &p) {
   genericAttrNamesSet.insert(genericAttrNames.back());
 
   bool hasExtraAttrs = false;
-  for (NamedAttribute n : (*this)->getAttrs()) {
+  for (NamedAttribute n : (*this)->getDiscardableAttrDictionary()) {
     if ((hasExtraAttrs = !genericAttrNamesSet.contains(n.getName().strref())))
       break;
   }
   if (hasExtraAttrs) {
     p << " attrs = ";
-    p.printOptionalAttrDict((*this)->getAttrs(),
+    p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary(),
                             /*elidedAttrs=*/genericAttrNames);
   }
 
@@ -1673,8 +1680,12 @@ static bool canUseShortForm(Block *body, bool initFirst = false,
 static void printShortForm(OpAsmPrinter &p, Operation *payloadOp) {
   SmallVector<StringRef> elidedAttrs;
   std::string attrToElide;
+  NamedAttrList attrs(payloadOp->getDiscardableAttrDictionary());
+  payloadOp->getName().walkInherentAttrs(
+      payloadOp,
+      [&](StringRef name, Attribute &attr) { attrs.append(name, attr); });
   p << " { " << payloadOp->getName().getStringRef();
-  for (const auto &attr : payloadOp->getAttrs()) {
+  for (const auto &attr : attrs) {
     auto fastAttr =
         llvm::dyn_cast<mlir::arith::FastMathFlagsAttr>(attr.getValue());
     if (fastAttr && fastAttr.getValue() == mlir::arith::FastMathFlags::none) {
@@ -1683,7 +1694,7 @@ static void printShortForm(OpAsmPrinter &p, Operation *payloadOp) {
       break;
     }
   }
-  p.printOptionalAttrDict(payloadOp->getAttrs(), elidedAttrs);
+  p.printOptionalAttrDict(attrs, elidedAttrs);
   p << " }";
 }
 
@@ -1696,7 +1707,7 @@ void MapOp::print(OpAsmPrinter &p) {
   }
 
   printCommonStructuredOpParts(p, getDpsInputs(), getDpsInits());
-  p.printOptionalAttrDict((*this)->getAttrs());
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary());
 
   if (!useShortForm) {
     // Print region if the payload op was not detected.
@@ -1906,7 +1917,8 @@ void ReduceOp::print(OpAsmPrinter &p) {
 
   printCommonStructuredOpParts(p, getDpsInputs(), getDpsInits());
   printDenseI64ArrayAttr(p, getDimensionsAttrName(), getDimensions());
-  p.printOptionalAttrDict((*this)->getAttrs(), {getDimensionsAttrName()});
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary(),
+                          {getDimensionsAttrName()});
   if (!useShortForm) {
     // Print region if the payload op was not detected.
     p.increaseIndent();
@@ -2085,7 +2097,8 @@ void TransposeOp::getAsmResultNames(
 void TransposeOp::print(OpAsmPrinter &p) {
   printCommonStructuredOpParts(p, getDpsInputs(), getDpsInits());
   printDenseI64ArrayAttr(p, getPermutationAttrName(), getPermutation());
-  p.printOptionalAttrDict((*this)->getAttrs(), {getPermutationAttrName()});
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary(),
+                          {getPermutationAttrName()});
 }
 
 LogicalResult TransposeOp::verify() {
@@ -2333,7 +2346,8 @@ void BroadcastOp::getAsmResultNames(
 void BroadcastOp::print(OpAsmPrinter &p) {
   printCommonStructuredOpParts(p, getDpsInputs(), getDpsInits());
   printDenseI64ArrayAttr(p, getDimensionsAttrName(), getDimensions());
-  p.printOptionalAttrDict((*this)->getAttrs(), {getDimensionsAttrName()});
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary(),
+                          {getDimensionsAttrName()});
 }
 
 LogicalResult BroadcastOp::verify() {
@@ -2481,7 +2495,7 @@ void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
 void linalg::YieldOp::print(OpAsmPrinter &p) {
   if (getNumOperands() > 0)
     p << ' ' << getOperands();
-  p.printOptionalAttrDict((*this)->getAttrs());
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary());
   if (getNumOperands() > 0)
     p << " : " << getOperandTypes();
 }
@@ -2642,13 +2656,13 @@ std::string mlir::linalg::generateLibraryCallName(Operation *op) {
   assert(isa<LinalgOp>(op));
   std::string name(op->getName().getStringRef().str());
   std::string fun = "";
-  for (NamedAttribute kv : op->getAttrs()) {
-    if (UnaryFnAttr ufa = llvm::dyn_cast<UnaryFnAttr>(kv.getValue())) {
+  op->getName().walkInherentAttrs(op, [&](StringRef, Attribute &attr) {
+    if (UnaryFnAttr ufa = llvm::dyn_cast<UnaryFnAttr>(attr)) {
       fun = stringifyEnum(ufa.getValue()).str() + "_";
-    } else if (BinaryFnAttr bfa = llvm::dyn_cast<BinaryFnAttr>(kv.getValue())) {
+    } else if (BinaryFnAttr bfa = llvm::dyn_cast<BinaryFnAttr>(attr)) {
       fun = stringifyEnum(bfa.getValue()).str() + "_";
     }
-  }
+  });
   name.reserve(128);
   llvm::replace(name, '.', '_');
   llvm::raw_string_ostream ss(name);
@@ -4217,9 +4231,9 @@ MatmulTransposeAOp::create(OpBuilder &builder, Location location,
 }
 
 bool MatmulTransposeAOp::classof(Operation *op) {
-  return dyn_cast_or_null<linalg::MatmulOp>(op) &&
-         MatmulTransposeAOp::isDefaultIndexingMaps(
-             op->getAttr("indexing_maps"));
+  auto matmulOp = dyn_cast_or_null<linalg::MatmulOp>(op);
+  return matmulOp && MatmulTransposeAOp::isDefaultIndexingMaps(
+                         matmulOp.getIndexingMapsAttr());
 }
 
 SmallVector<AffineMap>
@@ -4311,9 +4325,9 @@ MatmulTransposeBOp::create(OpBuilder &builder, Location location,
 }
 
 bool MatmulTransposeBOp::classof(Operation *op) {
-  return dyn_cast_or_null<linalg::MatmulOp>(op) &&
-         MatmulTransposeBOp::isDefaultIndexingMaps(
-             op->getAttr("indexing_maps"));
+  auto matmulOp = dyn_cast_or_null<linalg::MatmulOp>(op);
+  return matmulOp && MatmulTransposeBOp::isDefaultIndexingMaps(
+                         matmulOp.getIndexingMapsAttr());
 }
 
 SmallVector<AffineMap>
@@ -4404,9 +4418,9 @@ BatchMatmulTransposeAOp::create(OpBuilder &builder, Location location,
 }
 
 bool BatchMatmulTransposeAOp::classof(Operation *op) {
-  return dyn_cast_or_null<linalg::BatchMatmulOp>(op) &&
-         BatchMatmulTransposeAOp::isDefaultIndexingMaps(
-             op->getAttr("indexing_maps"));
+  auto matmulOp = dyn_cast_or_null<linalg::BatchMatmulOp>(op);
+  return matmulOp && BatchMatmulTransposeAOp::isDefaultIndexingMaps(
+                         matmulOp.getIndexingMapsAttr());
 }
 
 SmallVector<AffineMap>
@@ -4497,9 +4511,9 @@ BatchMatmulTransposeBOp::create(OpBuilder &builder, Location location,
 }
 
 bool BatchMatmulTransposeBOp::classof(Operation *op) {
-  return dyn_cast_or_null<linalg::BatchMatmulOp>(op) &&
-         BatchMatmulTransposeBOp::isDefaultIndexingMaps(
-             op->getAttr("indexing_maps"));
+  auto matmulOp = dyn_cast_or_null<linalg::BatchMatmulOp>(op);
+  return matmulOp && BatchMatmulTransposeBOp::isDefaultIndexingMaps(
+                         matmulOp.getIndexingMapsAttr());
 }
 
 //===----------------------------------------------------------------------===//
@@ -5553,7 +5567,7 @@ void PackOp::print(OpAsmPrinter &p) {
 
   p << " into " << getDest();
 
-  p.printOptionalAttrDict((*this)->getAttrs(),
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary(),
                           {"static_inner_tiles", "inner_dims_pos",
                            "outer_dims_perm", "operandSegmentSizes"});
 
@@ -6302,7 +6316,7 @@ void UnPackOp::print(OpAsmPrinter &p) {
 
   p << " into " << getDest();
 
-  p.printOptionalAttrDict((*this)->getAttrs(),
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary(),
                           {"static_inner_tiles", "inner_dims_pos",
                            "outer_dims_perm", "operandSegmentSizes"});
 

diff  --git a/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp b/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
index af50aa79bd491..8299e1c2edb41 100644
--- a/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
+++ b/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
@@ -1680,9 +1680,13 @@ transform::MatchOp::apply(transform::TransformRewriter &rewriter,
         if (attr.getName() == getInterfaceAttrName() ||
             attr.getName() == getOpsAttrName())
           continue;
-        if (!op->hasAttr(attr.getName()))
+        std::optional<Attribute> inherent = op->getInherentAttr(attr.getName());
+        Attribute actual = inherent.has_value()
+                               ? *inherent
+                               : op->getDiscardableAttr(attr.getName());
+        if (!actual)
           return;
-        if (op->getAttr(attr.getName()) != attr.getValue())
+        if (actual != attr.getValue())
           return;
       }
     }
@@ -3118,8 +3122,11 @@ void SplitOp::print(OpAsmPrinter &printer) {
   else
     printer << getDynamicChunkSizes();
   printer << " ";
-  printer.printOptionalAttrDict(getOperation()->getAttrs(),
-                                {getStaticChunkSizesAttrName()});
+  NamedAttrList attrs(getOperation()->getDiscardableAttrDictionary());
+  attrs.append(getDimensionAttrName(), getDimensionAttr());
+  if (UnitAttr multiway = getMultiwayAttr())
+    attrs.append(getMultiwayAttrName(), multiway);
+  printer.printOptionalAttrDict(attrs, {getStaticChunkSizesAttrName()});
   printer << " : " << getTarget().getType();
   if (staticChunkSize == ShapedType::kDynamic)
     printer << ", " << getDynamicChunkSizes().getType();

diff  --git a/mlir/lib/Dialect/Linalg/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Linalg/Transforms/BufferizableOpInterfaceImpl.cpp
index ca5ee62e5ffeb..28945642da099 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/BufferizableOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/BufferizableOpInterfaceImpl.cpp
@@ -74,10 +74,10 @@ static LogicalResult bufferizeDestinationStyleOpInterface(
   // new op. Since the new op does not have any tensor results, it does not
   // return anything.
   assert(op->getNumRegions() == 1 && "expected that op has 1 region");
-  OperationState opState(op->getLoc(), op->getName(), newOperands, TypeRange{},
-                         op->getAttrs());
-  opState.addRegion();
-  Operation *newOp = Operation::create(opState);
+  Operation *newOp = Operation::create(
+      op->getLoc(), op->getName(), TypeRange{}, newOperands,
+      op->getDiscardableAttrDictionary(), op->getPropertiesStorage(),
+      /*successors=*/{}, /*numRegions=*/1);
   newOp->getRegion(0).getBlocks().splice(newOp->getRegion(0).begin(),
                                          op->getRegion(0).getBlocks());
 
@@ -226,7 +226,8 @@ struct PackOpInterface
     llvm::append_range(operands, packOp.getInnerTiles());
 
     linalg::PackOp::create(rewriter, packOp.getLoc(), TypeRange{}, operands,
-                           op->getAttrs());
+                           packOp.getProperties(),
+                           packOp->getDiscardableAttrDictionary().getValue());
     replaceOpWithBufferizedValues(rewriter, op, *destBuffer);
     return success();
   }
@@ -263,8 +264,10 @@ struct UnPackOpInterface
     operands.push_back(*destBuffer);
     llvm::append_range(operands, unPackOp.getInnerTiles());
 
-    linalg::UnPackOp::create(rewriter, unPackOp.getLoc(), TypeRange{}, operands,
-                             op->getAttrs());
+    linalg::UnPackOp::create(
+        rewriter, unPackOp.getLoc(), TypeRange{}, operands,
+        unPackOp.getProperties(),
+        unPackOp->getDiscardableAttrDictionary().getValue());
     replaceOpWithBufferizedValues(rewriter, op, *destBuffer);
     return success();
   }

diff  --git a/mlir/lib/Dialect/Linalg/Transforms/DropUnitDims.cpp b/mlir/lib/Dialect/Linalg/Transforms/DropUnitDims.cpp
index c02413bd05d08..dac2891ce65e5 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/DropUnitDims.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/DropUnitDims.cpp
@@ -988,14 +988,28 @@ struct RankReduceContractionOps : OpRewritePattern<FromOpTy> {
     SmallVector<Type, 1> collapsedResultTy;
     if (isa<RankedTensorType>(collapsedInit.getType()))
       collapsedResultTy.push_back(collapsedInit.getType());
-    auto collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
-                                      ValueRange{collapsedLhs, collapsedRhs},
-                                      ValueRange{collapsedInit});
-    for (auto attr : contractionOp->getAttrs()) {
+    ToOpTy collapsedOp;
+    if constexpr (std::is_same_v<FromOpTy, BatchMatmulOp> &&
+                  std::is_same_v<ToOpTy, MatmulOp>) {
+      if (TypeFnAttr castAttr = contractionOp.getCastAttr()) {
+        collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
+                                     ValueRange{collapsedLhs, collapsedRhs},
+                                     ValueRange{collapsedInit}, castAttr);
+      } else {
+        collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
+                                     ValueRange{collapsedLhs, collapsedRhs},
+                                     ValueRange{collapsedInit});
+      }
+    } else {
+      collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
+                                   ValueRange{collapsedLhs, collapsedRhs},
+                                   ValueRange{collapsedInit});
+    }
+    for (auto attr : contractionOp->getDiscardableAttrDictionary()) {
       if (attr.getName() == LinalgDialect::kMemoizedIndexingMapsAttrName ||
           attr.getName() == "indexing_maps")
         continue;
-      collapsedOp->setAttr(attr.getName(), attr.getValue());
+      collapsedOp->setDiscardableAttr(attr.getName(), attr.getValue());
     }
 
     auto results = contractionOp.getResults();

diff  --git a/mlir/lib/Dialect/Linalg/Transforms/ElementwiseToLinalg.cpp b/mlir/lib/Dialect/Linalg/Transforms/ElementwiseToLinalg.cpp
index aea17d8de4a76..1338ca0aeb79b 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/ElementwiseToLinalg.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/ElementwiseToLinalg.cpp
@@ -142,10 +142,11 @@ struct ConvertAnyElementwiseMappableOpOnRankedTensors : public RewritePattern {
               llvm::map_to_vector<6>(op->getResultTypes(), [](Type type) {
                 return cast<TensorType>(type).getElementType();
               });
-          Operation *scalarOp =
-              builder.create(loc, op->getName().getIdentifier(),
-                             regionArgs.take_front(op->getNumOperands()),
-                             resultEltTys, op->getAttrs());
+          Operation *scalarOp = builder.insert(Operation::create(
+              loc, op->getName(), resultEltTys,
+              regionArgs.take_front(op->getNumOperands()),
+              op->getDiscardableAttrDictionary(), op->getPropertiesStorage(),
+              /*successors=*/{}, /*numRegions=*/0));
           linalg::YieldOp::create(builder, loc, scalarOp->getResults());
         });
     return success();

diff  --git a/mlir/lib/Dialect/Linalg/Transforms/EraseUnusedOperandsAndResults.cpp b/mlir/lib/Dialect/Linalg/Transforms/EraseUnusedOperandsAndResults.cpp
index 6800b3042d524..506e20ab436f8 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/EraseUnusedOperandsAndResults.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/EraseUnusedOperandsAndResults.cpp
@@ -271,9 +271,9 @@ mlir::linalg::deduplicateOperandsAndRemoveDeadResults(
       });
   // Copy over unknown attributes. They might be load bearing for some flow.
   ArrayRef<StringRef> odsAttrs = genericOp.getAttributeNames();
-  for (NamedAttribute kv : genericOp->getAttrs())
+  for (NamedAttribute kv : genericOp->getDiscardableAttrDictionary())
     if (!llvm::is_contained(odsAttrs, kv.getName().getValue()))
-      newOp->setAttr(kv.getName(), kv.getValue());
+      newOp->setDiscardableAttr(kv.getName(), kv.getValue());
 
   // Fix up the payload of the canonicalized operation.
   populateOpPayload(genericOp, newOp, origInsToNewInsPos, origOutsToNewOutsPos,

diff  --git a/mlir/lib/Dialect/Linalg/Transforms/FoldIntoElementwise.cpp b/mlir/lib/Dialect/Linalg/Transforms/FoldIntoElementwise.cpp
index 94db259d662d4..bbc6d3968b00c 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/FoldIntoElementwise.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/FoldIntoElementwise.cpp
@@ -93,7 +93,8 @@ struct FoldIntoElementwisePattern : public OpInterfaceRewritePattern<LinalgOp> {
     rewriter.modifyOpInPlace(op, [&] {
       for (auto [index, operand] : llvm::enumerate(op.getDpsInputOperands()))
         op->setOperand(operand->getOperandNumber(), newIns[index]);
-      op->setAttr("indexing_maps", rewriter.getAffineMapArrayAttr(newMaps));
+      op->setInherentAttr(rewriter.getStringAttr("indexing_maps"),
+                          rewriter.getAffineMapArrayAttr(newMaps));
     });
     return success();
   }

diff  --git a/mlir/lib/Dialect/Linalg/Transforms/SimplifyDepthwiseConv.cpp b/mlir/lib/Dialect/Linalg/Transforms/SimplifyDepthwiseConv.cpp
index c55a0bf7ef9a3..c6a305d539c10 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/SimplifyDepthwiseConv.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/SimplifyDepthwiseConv.cpp
@@ -93,7 +93,7 @@ matchAndReplaceDepthwiseConv(Operation *operation, Value input, Value kernel,
   if (!newConv)
     return failure();
   for (auto attr : preservedAttrs)
-    newConv->setAttr(attr.getName(), attr.getValue());
+    newConv->setDiscardableAttr(attr.getName(), attr.getValue());
 
   // Expand dimensions back out to
   rewriter.replaceOpWithNewOp<tensor::ExpandShapeOp>(

diff  --git a/mlir/lib/Dialect/Linalg/Transforms/TilingInterfaceImpl.cpp b/mlir/lib/Dialect/Linalg/Transforms/TilingInterfaceImpl.cpp
index b00ab8a7d6ee7..c84e5e50cb0e2 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/TilingInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/TilingInterfaceImpl.cpp
@@ -1149,8 +1149,10 @@ struct PackOpTiling
     for (auto tile : packOp.getInnerTiles())
       tiledOperands.push_back(tile);
 
-    Operation *tiledPackOp = PackOp::create(
-        b, loc, TypeRange{outSlice.getType()}, tiledOperands, op->getAttrs());
+    PackOp tiledPackOp =
+        PackOp::create(b, loc, TypeRange{outSlice.getType()}, tiledOperands,
+                       packOp.getProperties(),
+                       packOp->getDiscardableAttrDictionary().getValue());
 
     return TilingResult{
         {tiledPackOp},
@@ -1486,8 +1488,10 @@ struct PackOpTiling
     for (auto tile : packOp.getInnerTiles())
       tiledOperands.push_back(tile);
 
-    Operation *tiledPackOp = PackOp::create(
-        b, loc, TypeRange{outSlice.getType()}, tiledOperands, op->getAttrs());
+    PackOp tiledPackOp =
+        PackOp::create(b, loc, TypeRange{outSlice.getType()}, tiledOperands,
+                       packOp.getProperties(),
+                       packOp->getDiscardableAttrDictionary().getValue());
 
     return TilingResult{
         {tiledPackOp},
@@ -1727,8 +1731,10 @@ struct UnPackOpTiling
     for (auto tile : unpackOp.getInnerTiles())
       tiledOperands.push_back(tile);
 
-    Operation *tiledUnpackOp = UnPackOp::create(
-        b, loc, TypeRange{sliceDest.getType()}, tiledOperands, op->getAttrs());
+    UnPackOp tiledUnpackOp =
+        UnPackOp::create(b, loc, TypeRange{sliceDest.getType()}, tiledOperands,
+                         unpackOp.getProperties(),
+                         unpackOp->getDiscardableAttrDictionary().getValue());
 
     if (isPerfectTilingCase)
       return TilingResult{{tiledUnpackOp},
@@ -1986,9 +1992,10 @@ struct UnPackOpTiling
       tiledOperands.push_back(tile);
 
     // Create tiled unpack op.
-    Operation *tiledUnPackOp =
+    UnPackOp tiledUnPackOp =
         UnPackOp::create(b, loc, TypeRange{extractDestSlice.getType()},
-                         tiledOperands, op->getAttrs());
+                         tiledOperands, unPackOp.getProperties(),
+                         unPackOp->getDiscardableAttrDictionary().getValue());
 
     return TilingResult{{tiledUnPackOp},
                         SmallVector<Value>(tiledUnPackOp->getResults()),

diff  --git a/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp b/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp
index 21ca3108efcd6..e8f8d737e86de 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp
@@ -1442,10 +1442,12 @@ vectorizeOneOp(RewriterBase &rewriter, VectorizationState &state,
             : resultType);
   }
   //   d. Build and return the new op.
-  return VectorizationHookResult{
-      VectorizationHookStatus::NewOp,
-      rewriter.create(op->getLoc(), op->getName().getIdentifier(), vecOperands,
-                      resultTypes, op->getAttrs())};
+  Operation *newOp = Operation::create(
+      op->getLoc(), op->getName(), resultTypes, vecOperands,
+      op->getDiscardableAttrDictionary(), op->getPropertiesStorage(),
+      /*successors=*/{}, /*numRegions=*/0);
+  return VectorizationHookResult{VectorizationHookStatus::NewOp,
+                                 rewriter.insert(newOp)};
 }
 
 /// Generic vectorization function that rewrites the body of a `linalgOp` into
@@ -2733,8 +2735,8 @@ struct PadOpVectorizationWithTransferReadPattern
 
     rewriter.modifyOpInPlace(xferOp, [&]() {
       SmallVector<bool> inBounds(xferOp.getVectorType().getRank(), false);
-      xferOp->setAttr(xferOp.getInBoundsAttrName(),
-                      rewriter.getBoolArrayAttr(inBounds));
+      xferOp->setInherentAttr(xferOp.getInBoundsAttrName(),
+                              rewriter.getBoolArrayAttr(inBounds));
       xferOp.getBaseMutable().assign(padOp.getSource());
       xferOp.getPaddingMutable().assign(padValue);
     });
@@ -3794,8 +3796,9 @@ struct Conv1DGenerator
 
       SmallVector<bool> inBounds(maskShape.size(), true);
       auto xferOp = cast<VectorTransferOpInterface>(opToMask);
-      xferOp->setAttr(xferOp.getInBoundsAttrName(),
-                      rewriter.getBoolArrayAttr(inBounds));
+      xferOp->setInherentAttr(
+          rewriter.getStringAttr(xferOp.getInBoundsAttrName()),
+          rewriter.getBoolArrayAttr(inBounds));
 
       SmallVector<OpFoldResult> mixedDims = vector::getMixedSizesXfer(
           cast<LinalgOp>(op).hasPureTensorSemantics(), opToMask, rewriter);

diff  --git a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
index c8bba20fc7edf..c6a2d1e97f644 100644
--- a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
+++ b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
@@ -162,7 +162,7 @@ bubbleDownCastsPassthroughOpImpl(ConcreteOpTy op, OpBuilder &builder,
   // Create the new op and results.
   auto newOp = ConcreteOpTy::create(
       builder, op.getLoc(), TypeRange(resTy), operands, op.getProperties(),
-      llvm::to_vector_of<NamedAttribute>(op->getDiscardableAttrs()));
+      op->getDiscardableAttrDictionary().getValue());
 
   // Insert a memory-space cast to the original memory space of the op.
   MemorySpaceCastOpInterface result = castOp.cloneMemorySpaceCastOp(
@@ -377,7 +377,7 @@ void AllocaScopeOp::print(OpAsmPrinter &p) {
   p.printRegion(getBodyRegion(),
                 /*printEntryBlockArgs=*/false,
                 /*printBlockTerminators=*/printBlockTerminators);
-  p.printOptionalAttrDict((*this)->getAttrs());
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary());
 }
 
 ParseResult AllocaScopeOp::parse(OpAsmParser &parser, OperationState &result) {
@@ -1262,7 +1262,7 @@ void DmaStartOp::print(OpAsmPrinter &p) {
   if (isStrided())
     p << ", " << getStride() << ", " << getNumElementsPerStride();
 
-  p.printOptionalAttrDict((*this)->getAttrs());
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary());
   p << " : " << getSrcMemRef().getType() << ", " << getDstMemRef().getType()
     << ", " << getTagMemRef().getType();
 }
@@ -1652,7 +1652,7 @@ void GenericAtomicRMWOp::print(OpAsmPrinter &p) {
   p << ' ' << getMemref() << "[" << getIndices()
     << "] : " << getMemref().getType() << ' ';
   p.printRegion(getRegion());
-  p.printOptionalAttrDict((*this)->getAttrs());
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary());
 }
 
 TypedValue<MemRefType> GenericAtomicRMWOp::getAccessedMemref() {
@@ -1941,7 +1941,7 @@ void PrefetchOp::print(OpAsmPrinter &p) {
   p << ", locality<" << getLocalityHint();
   p << ">, " << (getIsDataCache() ? "data" : "instr");
   p.printOptionalAttrDict(
-      (*this)->getAttrs(),
+      (*this)->getDiscardableAttrDictionary(),
       /*elidedAttrs=*/{"localityHint", "isWrite", "isDataCache"});
   p << " : " << getMemRefType();
 }
@@ -3837,7 +3837,8 @@ void TransposeOp::build(OpBuilder &b, OperationState &result, Value in,
 // transpose $in $permutation attr-dict : type($in) `to` type(results)
 void TransposeOp::print(OpAsmPrinter &p) {
   p << " " << getIn() << " " << getPermutation();
-  p.printOptionalAttrDict((*this)->getAttrs(), {getPermutationAttrStrName()});
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary(),
+                          {getPermutationAttrStrName()});
   p << " : " << getIn().getType() << " to " << getType();
 }
 

diff  --git a/mlir/lib/Dialect/MemRef/Transforms/AllocationOpInterfaceImpl.cpp b/mlir/lib/Dialect/MemRef/Transforms/AllocationOpInterfaceImpl.cpp
index 75cc39e61656a..b53e3bdf1cf69 100644
--- a/mlir/lib/Dialect/MemRef/Transforms/AllocationOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/MemRef/Transforms/AllocationOpInterfaceImpl.cpp
@@ -34,11 +34,12 @@ struct DefaultAllocationInterface
   }
   static ::std::optional<::mlir::Operation *>
   buildPromotedAlloc(OpBuilder &builder, Value alloc) {
-    Operation *definingOp = alloc.getDefiningOp();
-    return memref::AllocaOp::create(
-        builder, definingOp->getLoc(),
-        cast<MemRefType>(definingOp->getResultTypes()[0]),
-        definingOp->getOperands(), definingOp->getAttrs());
+    auto allocOp = cast<memref::AllocOp>(alloc.getDefiningOp());
+    memref::AllocaOp allocaOp = memref::AllocaOp::create(
+        builder, allocOp.getLoc(), allocOp.getType(), allocOp.getDynamicSizes(),
+        allocOp.getSymbolOperands(), allocOp.getAlignmentAttr());
+    allocaOp->setDiscardableAttrs(allocOp->getDiscardableAttrDictionary());
+    return allocaOp.getOperation();
   }
 };
 

diff  --git a/mlir/lib/Dialect/MemRef/Transforms/MultiBuffer.cpp b/mlir/lib/Dialect/MemRef/Transforms/MultiBuffer.cpp
index ce45f847ccaed..69adf0611b623 100644
--- a/mlir/lib/Dialect/MemRef/Transforms/MultiBuffer.cpp
+++ b/mlir/lib/Dialect/MemRef/Transforms/MultiBuffer.cpp
@@ -224,8 +224,9 @@ mlir::memref::multiBuffer(RewriterBase &rewriter, memref::AllocOp allocOp,
   Location loc = allocOp->getLoc();
   OpBuilder::InsertionGuard g(rewriter);
   rewriter.setInsertionPoint(allocOp);
-  auto mbAlloc = memref::AllocOp::create(rewriter, loc, mbMemRefType,
-                                         ValueRange{}, allocOp->getAttrs());
+  auto mbAlloc = memref::AllocOp::create(
+      rewriter, loc, mbMemRefType, ValueRange{}, allocOp.getAlignmentAttr());
+  mbAlloc->setDiscardableAttrs(allocOp->getDiscardableAttrDictionary());
   LLVM_DEBUG(DBGS() << "--multi-buffered alloc: " << mbAlloc << "\n");
 
   // 3. Within the loop, build the modular leading index (i.e. each loop

diff  --git a/mlir/lib/Dialect/MemRef/Transforms/NormalizeMemRefs.cpp b/mlir/lib/Dialect/MemRef/Transforms/NormalizeMemRefs.cpp
index d5e0dace3c775..d1ca18e7661e5 100644
--- a/mlir/lib/Dialect/MemRef/Transforms/NormalizeMemRefs.cpp
+++ b/mlir/lib/Dialect/MemRef/Transforms/NormalizeMemRefs.cpp
@@ -462,6 +462,9 @@ void NormalizeMemRefs::normalizeFuncOpMemRefs(func::FuncOp funcOp,
           }
         }
         if (!replacingMemRefUsesFailed) {
+          for (auto [oldRegion, newRegion] :
+               llvm::zip(op->getRegions(), newOp->getRegions()))
+            newRegion.takeBody(oldRegion);
           // Replace other ops with new op and delete the old op when the
           // replacement succeeded.
           op->replaceAllUsesWith(newOp);
@@ -510,12 +513,7 @@ void NormalizeMemRefs::normalizeFuncOpMemRefs(func::FuncOp funcOp,
 /// without affine map, `oldOp` is returned without modification.
 Operation *NormalizeMemRefs::createOpResultsNormalized(func::FuncOp funcOp,
                                                        Operation *oldOp) {
-  // Prepare OperationState to create newOp containing normalized memref in
-  // the operation results.
-  OperationState result(oldOp->getLoc(), oldOp->getName());
-  result.addOperands(oldOp->getOperands());
-  result.addAttributes(oldOp->getAttrs());
-  // Add normalized MemRefType to the OperationState.
+  // Compute the normalized result types for the new operation.
   SmallVector<Type, 4> resultTypes;
   OpBuilder b(funcOp);
   bool resultTypeNormalized = false;
@@ -539,16 +537,15 @@ Operation *NormalizeMemRefs::createOpResultsNormalized(func::FuncOp funcOp,
     resultTypes.push_back(newMemRefType);
     resultTypeNormalized = true;
   }
-  result.addTypes(resultTypes);
   // When all of the results of `oldOp` have no memrefs or memrefs without
   // affine map, `oldOp` is returned without modification.
   if (resultTypeNormalized) {
     OpBuilder bb(oldOp);
-    for (auto &oldRegion : oldOp->getRegions()) {
-      Region *newRegion = result.addRegion();
-      newRegion->takeBody(oldRegion);
-    }
-    return bb.create(result);
+    Operation *newOp = Operation::create(
+        oldOp->getLoc(), oldOp->getName(), resultTypes, oldOp->getOperands(),
+        oldOp->getDiscardableAttrDictionary(), oldOp->getPropertiesStorage(),
+        /*successors=*/{}, oldOp->getNumRegions());
+    return bb.insert(newOp);
   }
   return oldOp;
 }

diff  --git a/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize.mlir b/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize.mlir
index 325a00566845f..d20f06fd3278a 100644
--- a/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize.mlir
+++ b/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize.mlir
@@ -1,5 +1,6 @@
 // Note: Default is function-boundary-type-conversion=infer-layout-map
 // RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1" -canonicalize -drop-equivalent-buffer-results -split-input-file | FileCheck %s
+// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1" -split-input-file | FileCheck %s --check-prefix=INHERENT
 
 // Run fuzzer with 
diff erent seeds.
 // RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1 test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=23" -split-input-file -o /dev/null
@@ -590,6 +591,7 @@ func.func private @inner_func(%t: tensor<?xf32>) -> tensor<?xf32> {
 
 // CHECK-LABEL: func @equivalent_func_arg(
 //  CHECK-SAME:     %[[arg0:.*]]: memref<?xf32
+// INHERENT-LABEL: func @equivalent_func_arg(
 func.func @equivalent_func_arg(%t0: tensor<?xf32> {bufferization.writable = true},
                                %c0: index, %c10: index, %c1: index) -> tensor<?xf32> {
   // CHECK-NOT: alloc
@@ -597,7 +599,8 @@ func.func @equivalent_func_arg(%t0: tensor<?xf32> {bufferization.writable = true
   // CHECK: scf.for {{.*}} iter_args(%[[t1:.*]] = %[[arg0]])
   %1 = scf.for %iv = %c0 to %c10 step %c1 iter_args(%t1 = %t0) -> (tensor<?xf32>) {
     // CHECK: call @inner_func(%[[t1]])
-    %3 = func.call @inner_func(%t1) : (tensor<?xf32>) -> tensor<?xf32>
+    // INHERENT: call @inner_func({{.*}}) {no_inline}
+    %3 = func.call @inner_func(%t1) {no_inline} : (tensor<?xf32>) -> tensor<?xf32>
     // CHECK: scf.yield %[[t1]]
     scf.yield %3 : tensor<?xf32>
   }

diff  --git a/mlir/test/Dialect/Linalg/library-calls.mlir b/mlir/test/Dialect/Linalg/library-calls.mlir
index 77c9d4a911447..e7ae43ed1bdc8 100644
--- a/mlir/test/Dialect/Linalg/library-calls.mlir
+++ b/mlir/test/Dialect/Linalg/library-calls.mlir
@@ -17,7 +17,8 @@ func.func @matmul(%A: memref<?x?xf32>, %B: memref<?x?xf32>) -> (memref<?x?xf32>)
   linalg.fill ins(%f0 : f32) outs(%C : memref<?x?xf32>)
 
   // CHECK:  call @linalg_matmul_viewsxsxf32_viewsxsxf32_viewsxsxf32({{.*}}) : (memref<?x?xf32, {{.*}}>, memref<?x?xf32, {{.*}}>, memref<?x?xf32, {{.*}}>) -> ()
-  linalg.matmul ins(%A, %B: memref<?x?xf32>, memref<?x?xf32>)
+  linalg.matmul {metadata = #linalg.binary_fn<add>}
+                ins(%A, %B: memref<?x?xf32>, memref<?x?xf32>)
                 outs(%C: memref<?x?xf32>)
   return %C : memref<?x?xf32>
 }

diff  --git a/mlir/test/Dialect/Linalg/rank-reduce-contraction-ops.mlir b/mlir/test/Dialect/Linalg/rank-reduce-contraction-ops.mlir
index 704576de41960..b7c095ca96b2d 100644
--- a/mlir/test/Dialect/Linalg/rank-reduce-contraction-ops.mlir
+++ b/mlir/test/Dialect/Linalg/rank-reduce-contraction-ops.mlir
@@ -18,6 +18,19 @@ func.func @singleton_batch_matmul_tensor(%arg0 : tensor<1x128x512xf32>, %arg1 :
 
 // -----
 
+func.func @singleton_batch_matmul_unsigned_cast(
+    %arg0: tensor<1x128x512xi16>, %arg1: tensor<1x512x256xi64>,
+    %arg2: tensor<1x128x256xi32>) -> tensor<1x128x256xi32> {
+  // CHECK-LABEL: @singleton_batch_matmul_unsigned_cast
+  // CHECK: linalg.matmul {cast = #linalg.type_fn<cast_unsigned>}
+  %0 = linalg.batch_matmul {cast = #linalg.type_fn<cast_unsigned>}
+      ins(%arg0, %arg1 : tensor<1x128x512xi16>, tensor<1x512x256xi64>)
+      outs(%arg2 : tensor<1x128x256xi32>) -> tensor<1x128x256xi32>
+  return %0 : tensor<1x128x256xi32>
+}
+
+// -----
+
 func.func @singleton_batch_matmul_memref(%arg0 : memref<1x?x?xf32>, %arg1 : memref<1x?x?xf32>, %arg2: memref<1x?x?xf32>) {
   // CHECK-LABEL: @singleton_batch_matmul_memref
   //  CHECK-SAME:     %[[LHS:[a-zA-Z0-9]+]]: memref<1x?x?xf32>

diff  --git a/mlir/test/Dialect/MemRef/multibuffer.mlir b/mlir/test/Dialect/MemRef/multibuffer.mlir
index b004ebfa1abd0..948a60aaac25d 100644
--- a/mlir/test/Dialect/MemRef/multibuffer.mlir
+++ b/mlir/test/Dialect/MemRef/multibuffer.mlir
@@ -4,10 +4,10 @@
 
 // CHECK-LABEL: func @multi_buffer
 func.func @multi_buffer(%a: memref<1024x1024xf32>) {
-// CHECK-DAG: %[[A:.*]] = memref.alloc() {someAttribute} : memref<5x4x128xf32>
+// CHECK-DAG: %[[A:.*]] = memref.alloc() alignment = 64 {someAttribute} : memref<5x4x128xf32>
 // CHECK-DAG: %[[C1:.*]] = arith.constant 1 : index
 // CHECK-DAG: %[[C3:.*]] = arith.constant 3 : index
-  %0 = memref.alloc() {someAttribute} : memref<4x128xf32>
+  %0 = memref.alloc() alignment = 64 {someAttribute} : memref<4x128xf32>
   %c1024 = arith.constant 1024 : index
   %c1 = arith.constant 1 : index
   %c3 = arith.constant 3 : index

diff  --git a/mlir/test/lib/Dialect/Bufferization/TestTensorLikeAndBufferLike.cpp b/mlir/test/lib/Dialect/Bufferization/TestTensorLikeAndBufferLike.cpp
index 60e60849f3e6c..3e71175a34474 100644
--- a/mlir/test/lib/Dialect/Bufferization/TestTensorLikeAndBufferLike.cpp
+++ b/mlir/test/lib/Dialect/Bufferization/TestTensorLikeAndBufferLike.cpp
@@ -85,7 +85,7 @@ struct TestTensorLikeAndBufferLikePass
     op.walk([](func::FuncOp funcOp) {
       const auto dict = findAllImplementeesOfTensorOrBufferLike(funcOp);
       if (!dict.empty()) {
-        funcOp->setAttr("found", dict);
+        funcOp->setDiscardableAttr("found", dict);
       }
     });
   }

diff  --git a/mlir/test/mlir-linalg-ods-gen/test-linalg-ods-yaml-gen.yaml b/mlir/test/mlir-linalg-ods-gen/test-linalg-ods-yaml-gen.yaml
index 00c70705cbb35..e2c811d523da2 100644
--- a/mlir/test/mlir-linalg-ods-gen/test-linalg-ods-yaml-gen.yaml
+++ b/mlir/test/mlir-linalg-ods-gen/test-linalg-ods-yaml-gen.yaml
@@ -183,7 +183,7 @@ structured_op: !LinalgStructuredOpConfig
 
 #       IMPL:  Test2Op::hasDynamicIndexingMaps() { return true; }
 #       IMPL:  Test2Op::verifyIndexingMapRequiredAttributes()
-#       IMPL:  auto attr = op->getAttrOfType<DenseElementsAttr>("strides")
+#       IMPL:  op->getInherentAttrOfType<DenseElementsAttr>("strides")
 #       IMPL:  "incorrect element type for index attribute 'strides'"
 #       IMPL:  "incorrect shape for index attribute 'strides'"
 #       IMPL:  void Test2Op::regionBuilder(ImplicitLocOpBuilder &b,

diff  --git a/mlir/tools/mlir-linalg-ods-gen/mlir-linalg-ods-yaml-gen.cpp b/mlir/tools/mlir-linalg-ods-gen/mlir-linalg-ods-yaml-gen.cpp
index 67dbab8ca79f9..c84efc50caa68 100644
--- a/mlir/tools/mlir-linalg-ods-gen/mlir-linalg-ods-yaml-gen.cpp
+++ b/mlir/tools/mlir-linalg-ods-gen/mlir-linalg-ods-yaml-gen.cpp
@@ -623,7 +623,8 @@ SmallVector<utils::IteratorType> {0}::getIteratorTypesArray() {{
 static const char structuredOpIndexingMapsFormat[] = R"FMT(
 ArrayAttr {0}::getIndexingMaps() {{
   static const char memoizeAttr[] = "linalg.memoized_indexing_maps";
-  ArrayAttr cached = getOperation()->getAttrOfType<ArrayAttr>(memoizeAttr);
+  ArrayAttr cached =
+      getOperation()->getDiscardableAttrOfType<ArrayAttr>(memoizeAttr);
   if (cached)
     return cached;
 
@@ -632,7 +633,7 @@ ArrayAttr {0}::getIndexingMaps() {{
   SmallVector<AffineMap> maps;
   {1}
   cached = Builder(context).getAffineMapArrayAttr(maps);
-  getOperation()->setAttr(memoizeAttr, cached);
+  getOperation()->setDiscardableAttr(memoizeAttr, cached);
   return cached;
 }
 )FMT";
@@ -976,7 +977,7 @@ std::string {0}::getLibraryCallName() {{
       // {0}: Attribute name
       // {1}: Attribute size
       static const char attrFmt[] = R"FMT(
-if (auto attr = op->getAttrOfType<DenseElementsAttr>("{0}")) {{
+if (auto attr = op->getInherentAttrOfType<DenseElementsAttr>("{0}")) {{
   if (!attr.getType().getElementType().isInteger(64))
     return op->emitError("incorrect element type for index attribute '{0}'");
   if (attr.getType().getShape() != ArrayRef<int64_t>{{ {1} })

diff  --git a/mlir/unittests/Dialect/Linalg/InferConvolutionDimsTest.cpp b/mlir/unittests/Dialect/Linalg/InferConvolutionDimsTest.cpp
index cab17f2a6e9c1..558becad8f04c 100644
--- a/mlir/unittests/Dialect/Linalg/InferConvolutionDimsTest.cpp
+++ b/mlir/unittests/Dialect/Linalg/InferConvolutionDimsTest.cpp
@@ -156,6 +156,8 @@ TEST_F(InferConvolutionDimsTest, Conv2DPairing) {
   // Create equivalent generic with swapped filter loop order: (oh, ow, kw, kh)
   linalg::GenericOp swappedOp =
       createConv2DWithSwappedFilterLoops(builder, conv2DOp);
+  swappedOp->setDiscardableAttr("strides", builder.getI64TensorAttr({7, 8}));
+  swappedOp->setDiscardableAttr("dilations", builder.getI64TensorAttr({9, 10}));
   FailureOr<ConvolutionDimensions> swappedDims =
       inferConvolutionDims(swappedOp);
   ASSERT_TRUE(succeeded(swappedDims));
@@ -174,6 +176,8 @@ TEST_F(InferConvolutionDimsTest, Conv2DPairing) {
       << "outputImage[0]=0 should pair with filterLoop[0]=3 (oh <-> kh)";
   EXPECT_EQ(swappedDims->filterLoop[1], 2u)
       << "outputImage[1]=1 should pair with filterLoop[1]=2 (ow <-> kw)";
+  EXPECT_EQ(swappedDims->strides, (SmallVector<int64_t, 2>{1, 1}));
+  EXPECT_EQ(swappedDims->dilations, (SmallVector<int64_t, 2>{1, 1}));
 }
 
 /// Asserts that two ConvolutionDimensions are equal across every populated
@@ -228,6 +232,10 @@ TEST_F(InferConvolutionDimsTest, MapsOverloadMatchesOpOverload) {
     FailureOr<ConvolutionDimensions> fromOp = inferConvolutionDims(linalgOp);
     ASSERT_TRUE(succeeded(fromOp))
         << "op overload failed for " << op->getName().getStringRef().str();
+    if (op == convs.front()) {
+      EXPECT_EQ(fromOp->strides, SmallVector<int64_t>({2, 1}));
+      EXPECT_EQ(fromOp->dilations, SmallVector<int64_t>({3, 1}));
+    }
     FailureOr<ConvolutionDimensions> fromMaps =
         inferConvolutionDims(linalgOp.getIndexingMapsArray());
     ASSERT_TRUE(succeeded(fromMaps))

diff  --git a/mlir/unittests/IR/OperationSupportTest.cpp b/mlir/unittests/IR/OperationSupportTest.cpp
index fa5fdfe16fde0..33f69a6e8f21a 100644
--- a/mlir/unittests/IR/OperationSupportTest.cpp
+++ b/mlir/unittests/IR/OperationSupportTest.cpp
@@ -307,6 +307,11 @@ TEST(OperandStorageTest, PopulateDefaultAttrs) {
                                              nullptr, nullptr, req2);
   auto opt = op->getInherentAttr("default_valued_attr");
   EXPECT_NE(opt, nullptr) << *op;
+  auto typed = op->getInherentAttrOfType<IntegerAttr>("default_valued_attr");
+  ASSERT_TRUE(typed) << *op;
+  EXPECT_EQ(typed.getInt(), 42);
+  EXPECT_FALSE(op->getInherentAttrOfType<StringAttr>("default_valued_attr"));
+  EXPECT_FALSE(op->getInherentAttrOfType<IntegerAttr>("unknown_attr"));
 
   op->destroy();
 }


        


More information about the Mlir-commits mailing list