[Mlir-commits] [mlir] [mlir][Analysis] Use explicit attribute APIs to distinguish between inherent/discardable attrs (PR #218878)

Mehdi Amini llvmlistbot at llvm.org
Wed Aug 26 03:26:31 PDT 2026


https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/218878

Migrate analysis implementations and test utilities to explicit discardable or inherent operation attribute access.

Assisted-by: Codex

>From 5c2904dc49a9b2ef5c8d5fa5bd121c4c3afb7f5c Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Thu, 20 Aug 2026 07:30:04 -0700
Subject: [PATCH] [mlir][Analysis] Use explicit attribute APIs

Migrate analysis implementations and test utilities to explicit
discardable or inherent operation attribute access.

Assisted-by: Codex
---
 mlir/lib/Analysis/CallGraph.cpp                   | 15 ++++++++++++---
 .../DataFlow/ConstantPropagationAnalysis.cpp      |  8 ++++++--
 .../Analysis/DataFlow/TestDeadCodeAnalysis.cpp    |  2 +-
 .../TestDenseBackwardDataFlowAnalysis.cpp         | 13 +++++++------
 .../Analysis/DataFlow/TestDenseDataFlowAnalysis.h |  2 +-
 .../DataFlow/TestDenseForwardDataFlowAnalysis.cpp |  5 +++--
 .../DataFlow/TestIntegerDivisibilityAnalysis.cpp  |  5 +++--
 .../Analysis/DataFlow/TestLivenessAnalysis.cpp    |  2 +-
 .../TestSparseBackwardDataFlowAnalysis.cpp        |  6 +++---
 mlir/test/lib/Analysis/TestAliasAnalysis.cpp      |  5 +++--
 mlir/test/lib/Analysis/TestDataFlowFramework.cpp  | 15 ++++++++-------
 mlir/test/lib/Analysis/TestSlice.cpp              |  4 ++--
 mlir/test/lib/Analysis/TestTopologicalSort.cpp    |  8 ++++----
 13 files changed, 54 insertions(+), 36 deletions(-)

diff --git a/mlir/lib/Analysis/CallGraph.cpp b/mlir/lib/Analysis/CallGraph.cpp
index d6fe62d8e58d6..4098c657f63e2 100644
--- a/mlir/lib/Analysis/CallGraph.cpp
+++ b/mlir/lib/Analysis/CallGraph.cpp
@@ -195,9 +195,18 @@ void CallGraph::print(raw_ostream &os) const {
     auto *parentOp = callableRegion->getParentOp();
     os << "'" << callableRegion->getParentOp()->getName() << "' - Region #"
        << callableRegion->getRegionNumber();
-    auto attrs = parentOp->getAttrDictionary();
-    if (!attrs.empty())
-      os << " : " << attrs;
+    NamedAttrList attrs(parentOp->getDiscardableAttrDictionary());
+    parentOp->getName().walkInherentAttrs(
+        parentOp,
+        [&](StringRef name, Attribute &attr) { attrs.append(name, attr); });
+    if (!attrs.empty()) {
+      os << " : { ";
+      llvm::interleaveComma(attrs, os, [&](NamedAttribute attr) {
+        os << attr.getName().getValue() << " = ";
+        attr.getValue().print(os);
+      });
+      os << " }";
+    }
   };
 
   for (auto &nodeIt : nodes) {
diff --git a/mlir/lib/Analysis/DataFlow/ConstantPropagationAnalysis.cpp b/mlir/lib/Analysis/DataFlow/ConstantPropagationAnalysis.cpp
index e6e7491b041ee..d067365a65c6c 100644
--- a/mlir/lib/Analysis/DataFlow/ConstantPropagationAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/ConstantPropagationAnalysis.cpp
@@ -70,7 +70,8 @@ LogicalResult SparseConstantPropagation::visitOperation(
   // folds in-place. The constant passed in may not correspond to the real
   // runtime value, so in-place updates are not allowed.
   SmallVector<Value, 8> originalOperands(op->getOperands());
-  DictionaryAttr originalAttrs = op->getAttrDictionary();
+  DictionaryAttr originalAttrs = op->getDiscardableAttrDictionary();
+  Attribute originalProperties = op->getPropertiesAsAttribute();
 
   // Simulate the result of folding this operation to a constant.
   SmallVector<OpFoldResult, 8> foldResults;
@@ -83,7 +84,10 @@ LogicalResult SparseConstantPropagation::visitOperation(
   // relinks use-lists even for identical values.
   if (!llvm::equal(op->getOperands(), originalOperands))
     op->setOperands(originalOperands);
-  op->setAttrs(originalAttrs);
+  op->setDiscardableAttrs(originalAttrs);
+  if (originalProperties)
+    (void)op->setPropertiesFromAttribute(originalProperties,
+                                         /*emitError=*/nullptr);
 
   // If folding failed or was in-place, mark the results as overdefined. We
   // don't allow in-place folds here: the goal is simulated execution, not
diff --git a/mlir/test/lib/Analysis/DataFlow/TestDeadCodeAnalysis.cpp b/mlir/test/lib/Analysis/DataFlow/TestDeadCodeAnalysis.cpp
index 327e807873714..4ab2a3170ef61 100644
--- a/mlir/test/lib/Analysis/DataFlow/TestDeadCodeAnalysis.cpp
+++ b/mlir/test/lib/Analysis/DataFlow/TestDeadCodeAnalysis.cpp
@@ -19,7 +19,7 @@ using namespace mlir::dataflow;
 static void printAnalysisResults(DataFlowSolver &solver, Operation *op,
                                  raw_ostream &os) {
   op->walk([&](Operation *op) {
-    auto tag = op->getAttrOfType<StringAttr>("tag");
+    auto tag = op->getDiscardableAttrOfType<StringAttr>("tag");
     if (!tag)
       return;
     os << tag.getValue() << ":\n";
diff --git a/mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp b/mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp
index e1ea28c06659f..f969869b92482 100644
--- a/mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp
+++ b/mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp
@@ -320,7 +320,7 @@ struct TestNextAccessPass
       innerAttrs.reserve(nextAcc->get().size());
       for (Operation *nextAccOp : nextAcc->get()) {
         if (auto nextAccTag =
-                nextAccOp->getAttrOfType<StringAttr>(kTagAttrName)) {
+                nextAccOp->getDiscardableAttrOfType<StringAttr>(kTagAttrName)) {
           innerAttrs.push_back(nextAccTag);
           continue;
         }
@@ -356,7 +356,7 @@ struct TestNextAccessPass
     LDBG() << "  Dataflow solver completed successfully";
     LDBG() << "  Walking operations to set next access attributes";
     op->walk([&](Operation *op) {
-      auto tag = op->getAttrOfType<StringAttr>(kTagAttrName);
+      auto tag = op->getDiscardableAttrOfType<StringAttr>(kTagAttrName);
       if (!tag)
         return;
 
@@ -364,8 +364,8 @@ struct TestNextAccessPass
              << OpWithFlags(op, OpPrintingFlags().skipRegions());
       const NextAccess *nextAccess =
           solver.lookupState<NextAccess>(solver.getProgramPointAfter(op));
-      op->setAttr(kNextAccessAttrName,
-                  makeNextAccessAttribute(op, solver, nextAccess));
+      op->setDiscardableAttr(kNextAccessAttrName,
+                             makeNextAccessAttribute(op, solver, nextAccess));
 
       auto iface = dyn_cast<RegionBranchOpInterface>(op);
       if (!iface)
@@ -383,8 +383,9 @@ struct TestNextAccessPass
         entryPointNextAccess.push_back(makeNextAccessAttribute(
             op, solver, solver.lookupState<NextAccess>(successorPoint)));
       }
-      op->setAttr(kAtEntryPointAttrName,
-                  ArrayAttr::get(op->getContext(), entryPointNextAccess));
+      op->setDiscardableAttr(
+          kAtEntryPointAttrName,
+          ArrayAttr::get(op->getContext(), entryPointNextAccess));
     });
   }
 };
diff --git a/mlir/test/lib/Analysis/DataFlow/TestDenseDataFlowAnalysis.h b/mlir/test/lib/Analysis/DataFlow/TestDenseDataFlowAnalysis.h
index 6012c90f84539..9b0508660e9cd 100644
--- a/mlir/test/lib/Analysis/DataFlow/TestDenseDataFlowAnalysis.h
+++ b/mlir/test/lib/Analysis/DataFlow/TestDenseDataFlowAnalysis.h
@@ -196,7 +196,7 @@ class UnderlyingValueAnalysis
                  ArrayRef<const UnderlyingValueLattice *> operands,
                  ArrayRef<UnderlyingValueLattice *> results) override {
     // Hook to test error propagation from visitOperation.
-    if (op->hasAttr("always_fail"))
+    if (op->hasDiscardableAttr("always_fail"))
       return op->emitError("this op is always fails");
 
     setAllToEntryStates(results);
diff --git a/mlir/test/lib/Analysis/DataFlow/TestDenseForwardDataFlowAnalysis.cpp b/mlir/test/lib/Analysis/DataFlow/TestDenseForwardDataFlowAnalysis.cpp
index f2384f32948a2..c8b67b4ba61c0 100644
--- a/mlir/test/lib/Analysis/DataFlow/TestDenseForwardDataFlowAnalysis.cpp
+++ b/mlir/test/lib/Analysis/DataFlow/TestDenseForwardDataFlowAnalysis.cpp
@@ -252,7 +252,7 @@ struct TestLastModifiedPass
     // Note that if the underlying value could not be computed or is unknown, we
     // conservatively treat the result also unknown.
     op->walk([&](Operation *op) {
-      auto tag = op->getAttrOfType<StringAttr>("tag");
+      auto tag = op->getDiscardableAttrOfType<StringAttr>("tag");
       if (!tag)
         return;
       os << "test_tag: " << tag.getValue() << ":\n";
@@ -285,7 +285,8 @@ struct TestLastModifiedPass
           } else {
             for (Operation *lastModifier : lastMod->get()) {
               if (auto tagName =
-                      lastModifier->getAttrOfType<StringAttr>("tag_name")) {
+                      lastModifier->getDiscardableAttrOfType<StringAttr>(
+                          "tag_name")) {
                 os << "  - " << tagName.getValue() << "\n";
               } else {
                 os << "  - " << lastModifier->getName() << "\n";
diff --git a/mlir/test/lib/Analysis/DataFlow/TestIntegerDivisibilityAnalysis.cpp b/mlir/test/lib/Analysis/DataFlow/TestIntegerDivisibilityAnalysis.cpp
index 626cbc0fac7aa..24d01c61e3783 100644
--- a/mlir/test/lib/Analysis/DataFlow/TestIntegerDivisibilityAnalysis.cpp
+++ b/mlir/test/lib/Analysis/DataFlow/TestIntegerDivisibilityAnalysis.cpp
@@ -71,7 +71,8 @@ struct TestIntegerDivisibilityAnalysisPass
       const auto *lattice =
           solver.lookupState<IntegerDivisibilityLattice>(value);
       if (!lattice || lattice->getValue().isUninitialized()) {
-        op->setAttr("divisibility", StringAttr::get(context, "uninitialized"));
+        op->setDiscardableAttr("divisibility",
+                               StringAttr::get(context, "uninitialized"));
         continue;
       }
 
@@ -80,7 +81,7 @@ struct TestIntegerDivisibilityAnalysisPass
       std::string result;
       llvm::raw_string_ostream os(result);
       os << "udiv = " << div.udiv() << ", sdiv = " << div.sdiv();
-      op->setAttr("divisibility", StringAttr::get(context, result));
+      op->setDiscardableAttr("divisibility", StringAttr::get(context, result));
     }
   }
 };
diff --git a/mlir/test/lib/Analysis/DataFlow/TestLivenessAnalysis.cpp b/mlir/test/lib/Analysis/DataFlow/TestLivenessAnalysis.cpp
index 2c9f46411f602..22c2a6bffd52a 100644
--- a/mlir/test/lib/Analysis/DataFlow/TestLivenessAnalysis.cpp
+++ b/mlir/test/lib/Analysis/DataFlow/TestLivenessAnalysis.cpp
@@ -38,7 +38,7 @@ struct TestLivenessAnalysisPass
     raw_ostream &os = llvm::outs();
 
     op->walk([&](Operation *op) {
-      auto tag = op->getAttrOfType<StringAttr>("tag");
+      auto tag = op->getDiscardableAttrOfType<StringAttr>("tag");
       if (!tag)
         return;
       os << "test_tag: " << tag.getValue() << ":\n";
diff --git a/mlir/test/lib/Analysis/DataFlow/TestSparseBackwardDataFlowAnalysis.cpp b/mlir/test/lib/Analysis/DataFlow/TestSparseBackwardDataFlowAnalysis.cpp
index b1978880e2bd6..18c74093e518f 100644
--- a/mlir/test/lib/Analysis/DataFlow/TestSparseBackwardDataFlowAnalysis.cpp
+++ b/mlir/test/lib/Analysis/DataFlow/TestSparseBackwardDataFlowAnalysis.cpp
@@ -104,7 +104,7 @@ WrittenToAnalysis::visitOperation(Operation *op, ArrayRef<WrittenTo *> operands,
                                   ArrayRef<const WrittenTo *> results) {
   if (auto store = dyn_cast<memref::StoreOp>(op)) {
     SetVector<StringAttr> newWrites;
-    newWrites.insert(op->getAttrOfType<StringAttr>("tag_name"));
+    newWrites.insert(op->getDiscardableAttrOfType<StringAttr>("tag_name"));
     propagateIfChanged(operands[0],
                        operands[0]->getValue().addWrites(newWrites));
     return success();
@@ -148,7 +148,7 @@ void WrittenToAnalysis::visitExternalCall(CallOpInterface call,
 
   for (WrittenTo *lattice : operands) {
     SetVector<StringAttr> newWrites;
-    StringAttr name = call->getAttrOfType<StringAttr>("tag_name");
+    StringAttr name = call->getDiscardableAttrOfType<StringAttr>("tag_name");
     if (!name) {
       name = StringAttr::get(call->getContext(),
                              call.getOperation()->getName().getStringRef());
@@ -194,7 +194,7 @@ struct TestWrittenToPass
 
     raw_ostream &os = llvm::outs();
     op->walk([&](Operation *op) {
-      auto tag = op->getAttrOfType<StringAttr>("tag");
+      auto tag = op->getDiscardableAttrOfType<StringAttr>("tag");
       if (!tag)
         return;
       os << "test_tag: " << tag.getValue() << ":\n";
diff --git a/mlir/test/lib/Analysis/TestAliasAnalysis.cpp b/mlir/test/lib/Analysis/TestAliasAnalysis.cpp
index 0125e403272a8..f5eaed98f0268 100644
--- a/mlir/test/lib/Analysis/TestAliasAnalysis.cpp
+++ b/mlir/test/lib/Analysis/TestAliasAnalysis.cpp
@@ -21,14 +21,15 @@ using namespace mlir;
 
 /// Print a value that is used as an operand of an alias query.
 static void printAliasOperand(Operation *op) {
-  llvm::errs() << op->getAttrOfType<StringAttr>("test.ptr").getValue();
+  llvm::errs()
+      << op->getDiscardableAttrOfType<StringAttr>("test.ptr").getValue();
 }
 static void printAliasOperand(Value value) {
   if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
     Region *region = arg.getParentRegion();
     unsigned parentBlockNumber = arg.getOwner()->computeBlockNumber();
     llvm::errs() << region->getParentOp()
-                        ->getAttrOfType<StringAttr>("test.ptr")
+                        ->getDiscardableAttrOfType<StringAttr>("test.ptr")
                         .getValue()
                  << ".region" << region->getRegionNumber();
     if (parentBlockNumber != 0)
diff --git a/mlir/test/lib/Analysis/TestDataFlowFramework.cpp b/mlir/test/lib/Analysis/TestDataFlowFramework.cpp
index f29136853a9cd..98fbcd8c9b12c 100644
--- a/mlir/test/lib/Analysis/TestDataFlowFramework.cpp
+++ b/mlir/test/lib/Analysis/TestDataFlowFramework.cpp
@@ -238,7 +238,7 @@ void FooAnalysis::visitOperation(Operation *op) {
   result |= state->set(*prevState);
 
   // Modify the state with the attribute, if specified.
-  if (auto attr = op->getAttrOfType<IntegerAttr>(kFooAttrName)) {
+  if (auto attr = op->getDiscardableAttrOfType<IntegerAttr>(kFooAttrName)) {
     uint64_t value = attr.getType().isUnsignedInteger()
                          ? attr.getUInt()
                          : static_cast<uint64_t>(attr.getInt());
@@ -301,7 +301,7 @@ void BarAnalysis::visitOperation(Operation *op) {
       getOrCreateFor<BarState>(point, getProgramPointBefore(op));
   result |= state->join(*prevState);
 
-  if (op->hasAttr(kTagAttrName)) {
+  if (op->hasDiscardableAttr(kTagAttrName)) {
     const FooState *fooState = getOrCreateFor<FooState>(point, point);
     if (fooState->isUninitialized())
       return;
@@ -321,7 +321,7 @@ void TestFooAnalysisPass::runOnOperation() {
   os << "function: @" << func.getSymName() << "\n";
 
   func.walk([&](Operation *op) {
-    auto tag = op->getAttrOfType<StringAttr>(kTagAttrName);
+    auto tag = op->getDiscardableAttrOfType<StringAttr>(kTagAttrName);
     if (!tag)
       return;
     const FooState *state =
@@ -344,7 +344,7 @@ void TestStagedAnalysesPass::runOnOperation() {
     return signalPassFailure();
 
   func.walk([&](Operation *op) {
-    if (!op->hasAttr(kTagAttrName))
+    if (!op->hasDiscardableAttr(kTagAttrName))
       return;
 
     ProgramPoint *point = solver.getProgramPointAfter(op);
@@ -353,9 +353,10 @@ void TestStagedAnalysesPass::runOnOperation() {
     assert(fooState && !fooState->isUninitialized());
     assert(barState && !barState->isUninitialized());
 
-    op->setAttr(kFooStateAttrName,
-                builder.getI64IntegerAttr(fooState->getValue()));
-    op->setAttr(kBarStateAttrName, builder.getBoolAttr(barState->getValue()));
+    op->setDiscardableAttr(kFooStateAttrName,
+                           builder.getI64IntegerAttr(fooState->getValue()));
+    op->setDiscardableAttr(kBarStateAttrName,
+                           builder.getBoolAttr(barState->getValue()));
   });
 }
 
diff --git a/mlir/test/lib/Analysis/TestSlice.cpp b/mlir/test/lib/Analysis/TestSlice.cpp
index 7e8320dbf3ec3..5055a7b36265c 100644
--- a/mlir/test/lib/Analysis/TestSlice.cpp
+++ b/mlir/test/lib/Analysis/TestSlice.cpp
@@ -31,14 +31,14 @@ struct TestTopologicalSortPass
   void runOnOperation() override {
     SetVector<Operation *> toSort;
     getOperation().walk([&](Operation *op) {
-      if (op->hasAttrOfType<UnitAttr>(kToSortMark))
+      if (op->hasDiscardableAttrOfType<UnitAttr>(kToSortMark))
         toSort.insert(op);
     });
 
     auto i32Type = IntegerType::get(&getContext(), 32);
     SetVector<Operation *> sortedOps = topologicalSort(toSort);
     for (auto [index, op] : llvm::enumerate(sortedOps))
-      op->setAttr(kOrderIndex, IntegerAttr::get(i32Type, index));
+      op->setDiscardableAttr(kOrderIndex, IntegerAttr::get(i32Type, index));
   }
 };
 
diff --git a/mlir/test/lib/Analysis/TestTopologicalSort.cpp b/mlir/test/lib/Analysis/TestTopologicalSort.cpp
index bd7e60f1ecc33..f219c8cf0a341 100644
--- a/mlir/test/lib/Analysis/TestTopologicalSort.cpp
+++ b/mlir/test/lib/Analysis/TestTopologicalSort.cpp
@@ -31,14 +31,14 @@ struct TestTopologicalSortAnalysisPass
     OpBuilder builder(op->getContext());
 
     WalkResult result = op->walk([&](Operation *root) {
-      if (!root->hasAttr("root"))
+      if (!root->hasDiscardableAttr("root"))
         return WalkResult::advance();
 
       SmallVector<Operation *> selectedOps;
       root->walk([&](Operation *selected) {
-        if (!selected->hasAttr("selected"))
+        if (!selected->hasDiscardableAttr("selected"))
           return WalkResult::advance();
-        if (root->hasAttr("ordered")) {
+        if (root->hasDiscardableAttr("ordered")) {
           // If the root has an "ordered" attribute, we fill the selectedOps
           // vector in a certain order.
           int64_t pos =
@@ -65,7 +65,7 @@ struct TestTopologicalSortAnalysisPass
       }
 
       for (const auto &it : llvm::enumerate(selectedOps))
-        it.value()->setAttr("pos", builder.getIndexAttr(it.index()));
+        it.value()->setDiscardableAttr("pos", builder.getIndexAttr(it.index()));
 
       return WalkResult::advance();
     });



More information about the Mlir-commits mailing list