[flang-commits] [flang] [flang] Stop using Operation::getAttrs (NFC) (PR #223049)

Mehdi Amini via flang-commits flang-commits at lists.llvm.org
Fri Sep 11 14:46:57 PDT 2026


https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/223049

>From 65f30d145e38203bef6376f1ad65afc7a0bcbe5b Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Thu, 10 Sep 2026 10:52:41 -0700
Subject: [PATCH] [flang] Stop using Operation::getAttrs

Migrate FIR attribute copying and printing to the explicit inherent and
discardable attribute APIs.

This is part of a general migration to use the "new" properties-based APIs
and stop mixing discardable/inherent attributes, see #155475

Assisted-by: Codex
---
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       |  38 +++--
 flang/lib/Optimizer/CodeGen/CodeGenOpenMP.cpp | 154 +++++++++---------
 flang/lib/Optimizer/Dialect/CUF/CUFOps.cpp    |   9 +-
 flang/lib/Optimizer/Dialect/FIROps.cpp        | 118 +++++++++-----
 .../HLFIR/Transforms/ConvertToFIR.cpp         |  10 +-
 .../ConstantArgumentGlobalisation.cpp         |   3 +-
 flang/lib/Optimizer/Transforms/FIRToSCF.cpp   |  12 +-
 flang/test/Fir/FirToSCF/iter-while.fir        |  10 +-
 .../test/Fir/convert-to-llvm-access-group.fir |   8 +-
 9 files changed, 208 insertions(+), 154 deletions(-)

diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 1fafee2e90714..e072416e89089 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -1504,6 +1504,15 @@ struct AllocMemOpConversion : public fir::FIROpConversion<fir::AllocMemOp> {
       size = integerCast(loc, rewriter, mallocTy, size);
 
     std::optional<uint64_t> alignment = heap.getAlignment();
+    auto getRuntimeCallBuilderAttributes = [&](int32_t numCallOperands) {
+      llvm::SmallVector<mlir::NamedAttribute> runtimeCallAttrs(
+          heap->getDiscardableAttrDictionary().getValue());
+      if (mlir::IntegerAttr alignmentAttr = heap.getAlignmentAttr())
+        runtimeCallAttrs.emplace_back(heap.getAlignmentAttrName(),
+                                      alignmentAttr);
+      return getLLVMCallBuilderAttributes(rewriter, runtimeCallAttrs,
+                                          numCallOperands);
+    };
     if (alignment && *alignment > 16) {
       auto mod = heap->getParentOfType<mlir::ModuleOp>();
       llvm::Triple triple = mod ? fir::getTargetTriple(mod) : llvm::Triple{};
@@ -1536,8 +1545,7 @@ struct AllocMemOpConversion : public fir::FIROpConversion<fir::AllocMemOp> {
           mlir::LLVM::StoreOp::create(rewriter, loc, nullPtr, memptr);
           heap->setAttr("callee", getPosixMemalign(heap, rewriter, mallocTy,
                                                    this->options));
-          auto builderAttrs =
-              getLLVMCallBuilderAttributes(rewriter, heap->getAttrs(), 3);
+          auto builderAttrs = getRuntimeCallBuilderAttributes(3);
           mlir::LLVM::CallOp::create(rewriter, loc,
                                      mlir::TypeRange{mlir::IntegerType::get(
                                          rewriter.getContext(), 32)},
@@ -1561,8 +1569,7 @@ struct AllocMemOpConversion : public fir::FIROpConversion<fir::AllocMemOp> {
             rewriter, loc, mallocTy, sizePlus, notAlignMinusOne);
         heap->setAttr("callee",
                       getAlignedAlloc(heap, rewriter, mallocTy, this->options));
-        auto builderAttrs =
-            getLLVMCallBuilderAttributes(rewriter, heap->getAttrs(), 2);
+        auto builderAttrs = getRuntimeCallBuilderAttributes(2);
         rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
             heap, mlir::TypeRange{::getLlvmPtrType(heap.getContext())},
             mlir::ValueRange{alignVal, roundedSize}, builderAttrs.properties,
@@ -1572,8 +1579,7 @@ struct AllocMemOpConversion : public fir::FIROpConversion<fir::AllocMemOp> {
     }
 
     heap->setAttr("callee", getMalloc(heap, rewriter, mallocTy, this->options));
-    auto builderAttrs =
-        getLLVMCallBuilderAttributes(rewriter, heap->getAttrs(), 1);
+    auto builderAttrs = getRuntimeCallBuilderAttributes(1);
     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
         heap, mlir::TypeRange{::getLlvmPtrType(heap.getContext())},
         mlir::ValueRange{size}, builderAttrs.properties,
@@ -1647,8 +1653,8 @@ struct FreeMemOpConversion : public fir::FIROpConversion<fir::FreeMemOp> {
                   mlir::ConversionPatternRewriter &rewriter) const override {
     mlir::Location loc = freemem.getLoc();
     freemem->setAttr("callee", getFree(freemem, rewriter, this->options));
-    auto builderAttrs =
-        getLLVMCallBuilderAttributes(rewriter, freemem->getAttrs(), 1);
+    auto builderAttrs = getLLVMCallBuilderAttributes(
+        rewriter, freemem->getDiscardableAttrDictionary().getValue(), 1);
     mlir::LLVM::CallOp::create(rewriter, loc, mlir::TypeRange{},
                                mlir::ValueRange{adaptor.getHeapref()},
                                builderAttrs.properties,
@@ -3849,11 +3855,9 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
     // Apply all non-Fir::GlobalOp attributes to the LLVM::GlobalOp, preserving
     // them; whilst taking care not to apply attributes that are lowered in
     // other ways.
-    llvm::SmallDenseSet<llvm::StringRef> elidedAttrsSet(
-        global.getAttributeNames().begin(), global.getAttributeNames().end());
-    for (auto &attr : global->getAttrs())
-      if (!elidedAttrsSet.contains(attr.getName().strref()))
-        g->setAttr(attr.getName(), attr.getValue());
+    for (mlir::NamedAttribute attr :
+         global->getDiscardableAttrDictionary().getValue())
+      g->setDiscardableAttr(attr.getName(), attr.getValue());
 
     auto &gr = g.getInitializerRegion();
     rewriter.inlineRegionBefore(global.getRegion(), gr, gr.end());
@@ -4050,13 +4054,11 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
 
       rewriter.replaceOp(load, newBoxStorage);
     } else {
-      auto builderAttrs = splitBuilderAttributes<mlir::LLVM::LoadOp>(
-          rewriter, load->getAttrs());
       mlir::LLVM::LoadOp loadOp = mlir::LLVM::LoadOp::create(
-          rewriter, load.getLoc(), mlir::TypeRange{llvmLoadTy},
-          adaptor.getOperands(), builderAttrs.properties,
-          builderAttrs.discardableAttributes);
+          rewriter, load.getLoc(), llvmLoadTy, adaptor.getOperands().front());
+      loadOp->setDiscardableAttrs(load->getDiscardableAttrDictionary());
       loadOp.setVolatile_(isVolatile);
+      loadOp.setNontemporal(load.getNontemporal());
       if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
         loadOp.setTBAATags(*optionalTag);
       else
diff --git a/flang/lib/Optimizer/CodeGen/CodeGenOpenMP.cpp b/flang/lib/Optimizer/CodeGen/CodeGenOpenMP.cpp
index 9c6ebd61f1072..35819444387e0 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGenOpenMP.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGenOpenMP.cpp
@@ -78,91 +78,91 @@ struct MapInfoOpConversion
     if (failed(converter->convertTypes(curOp->getResultTypes(), resTypes)))
       return mlir::failure();
 
-    llvm::SmallVector<mlir::NamedAttribute> newAttrs;
+    auto newProperties = curOp.getProperties();
     mlir::omp::MapBoundsOp mapBoundsOp;
-    for (mlir::NamedAttribute attr : curOp->getAttrs()) {
-      if (auto typeAttr = mlir::dyn_cast<mlir::TypeAttr>(attr.getValue())) {
-        mlir::Type newAttr;
-        if (fir::isTypeWithDescriptor(typeAttr.getValue())) {
-          newAttr = lowerTy().convertBoxTypeAsStruct(
-              mlir::cast<fir::BaseBoxType>(typeAttr.getValue()));
-        } else if (fir::isa_char_string(fir::unwrapSequenceType(
-                       fir::unwrapPassByRefType(typeAttr.getValue()))) &&
-                   !characterWithDynamicLen(
-                       fir::unwrapPassByRefType(typeAttr.getValue()))) {
-          // Characters with a LEN param are represented as strings
-          // (array of characters), the lowering to LLVM dialect
-          // doesn't generate bounds for these (and this is not
-          // done at the initial lowering either) and there is
-          // minor inconsistencies in the variable types we
-          // create for the map without this step when converting
-          // to the LLVM dialect.
-          //
-          // For example, given the types:
-          //
-          //  1) CHARACTER(LEN=16), dimension(:,:), allocatable :: char_arr
-          //  2) CHARACTER(LEN=16), dimension(10,10) :: char_arr
-          //
-          // We get the FIR types (note for 1: we already peeled off the
-          // dynamic extents from the type at this stage, but the conversion
-          // to llvm dialect does that in any case, so the final result
-          // is the same):
-          //
-          //  1) !fir.char<1,16>
-          //  2) !fir.array<10x10x!fir.char<1,16>>
-          //
-          // Which are converted to the LLVM dialect types:
-          //
-          // 1) !llvm.array<16 x i8>
-          // 2) llvm.array<10 x array<10 x array<16 x i8>>
-          //
-          // And in both cases, we are missing the innermost bounds for
-          // the !fir.char<1,16> which is expanded into a 16 x i8 array
-          // in the conversion to LLVM dialect.
-          //
-          // The problem with this is that we would like to treat these
-          // cases identically and not have to create specialised
-          // lowerings for either of these in the lowering to LLVM-IR
-          // and treat them like any other array that passes through.
-          //
-          // To do so below, we generate an extra bound for the
-          // innermost array (the char type/string) using the LEN
-          // parameter of the character type. And we "canonicalize"
-          // the type, stripping it down to the base element type,
-          // which in this case is an i8. This effectively allows
-          // the lowering to treat this as a 1-D array with multiple
-          // bounds which it is capable of handling without any special
-          // casing.
-          // TODO: Handle dynamic LEN characters.
-          if (auto ct = mlir::dyn_cast_or_null<fir::CharacterType>(
-                  fir::unwrapSequenceType(typeAttr.getValue()))) {
-            newAttr = converter->convertType(
-                fir::unwrapSequenceType(typeAttr.getValue()));
-            if (auto type = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(newAttr))
-              newAttr = type.getElementType();
-            // We do not generate MapBoundsOps for the device pass, as
-            // MapBoundsOps are not generated for the device pass, as
-            // they're unused in the device lowering.
-            auto offloadMod =
-                llvm::dyn_cast_or_null<mlir::omp::OffloadModuleInterface>(
-                    *curOp->getParentOfType<mlir::ModuleOp>());
-            if (!offloadMod.getIsTargetDevice())
-              mapBoundsOp = createBoundsForCharString(rewriter, ct.getLen(),
-                                                      curOp.getLoc());
-          } else {
-            newAttr = converter->convertType(typeAttr.getValue());
-          }
+    auto convertTypeAttr = [&](mlir::TypeAttr typeAttr) {
+      mlir::Type newAttr;
+      if (fir::isTypeWithDescriptor(typeAttr.getValue())) {
+        newAttr = lowerTy().convertBoxTypeAsStruct(
+            mlir::cast<fir::BaseBoxType>(typeAttr.getValue()));
+      } else if (fir::isa_char_string(fir::unwrapSequenceType(
+                     fir::unwrapPassByRefType(typeAttr.getValue()))) &&
+                 !characterWithDynamicLen(
+                     fir::unwrapPassByRefType(typeAttr.getValue()))) {
+        // Characters with a LEN param are represented as strings
+        // (array of characters), the lowering to LLVM dialect
+        // doesn't generate bounds for these (and this is not
+        // done at the initial lowering either) and there is
+        // minor inconsistencies in the variable types we
+        // create for the map without this step when converting
+        // to the LLVM dialect.
+        //
+        // For example, given the types:
+        //
+        //  1) CHARACTER(LEN=16), dimension(:,:), allocatable :: char_arr
+        //  2) CHARACTER(LEN=16), dimension(10,10) :: char_arr
+        //
+        // We get the FIR types (note for 1: we already peeled off the
+        // dynamic extents from the type at this stage, but the conversion
+        // to llvm dialect does that in any case, so the final result
+        // is the same):
+        //
+        //  1) !fir.char<1,16>
+        //  2) !fir.array<10x10x!fir.char<1,16>>
+        //
+        // Which are converted to the LLVM dialect types:
+        //
+        // 1) !llvm.array<16 x i8>
+        // 2) llvm.array<10 x array<10 x array<16 x i8>>
+        //
+        // And in both cases, we are missing the innermost bounds for
+        // the !fir.char<1,16> which is expanded into a 16 x i8 array
+        // in the conversion to LLVM dialect.
+        //
+        // The problem with this is that we would like to treat these
+        // cases identically and not have to create specialised
+        // lowerings for either of these in the lowering to LLVM-IR
+        // and treat them like any other array that passes through.
+        //
+        // To do so below, we generate an extra bound for the
+        // innermost array (the char type/string) using the LEN
+        // parameter of the character type. And we "canonicalize"
+        // the type, stripping it down to the base element type,
+        // which in this case is an i8. This effectively allows
+        // the lowering to treat this as a 1-D array with multiple
+        // bounds which it is capable of handling without any special
+        // casing.
+        // TODO: Handle dynamic LEN characters.
+        if (auto ct = mlir::dyn_cast_or_null<fir::CharacterType>(
+                fir::unwrapSequenceType(typeAttr.getValue()))) {
+          newAttr = converter->convertType(
+              fir::unwrapSequenceType(typeAttr.getValue()));
+          if (auto type = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(newAttr))
+            newAttr = type.getElementType();
+          // We do not generate MapBoundsOps for the device pass, as
+          // MapBoundsOps are not generated for the device pass, as
+          // they're unused in the device lowering.
+          auto offloadMod =
+              llvm::dyn_cast_or_null<mlir::omp::OffloadModuleInterface>(
+                  *curOp->getParentOfType<mlir::ModuleOp>());
+          if (!offloadMod.getIsTargetDevice())
+            mapBoundsOp = createBoundsForCharString(rewriter, ct.getLen(),
+                                                    curOp.getLoc());
         } else {
           newAttr = converter->convertType(typeAttr.getValue());
         }
-        newAttrs.emplace_back(attr.getName(), mlir::TypeAttr::get(newAttr));
       } else {
-        newAttrs.push_back(attr);
+        newAttr = converter->convertType(typeAttr.getValue());
       }
-    }
+      return mlir::TypeAttr::get(newAttr);
+    };
+    newProperties.setVarPtrType(convertTypeAttr(curOp.getVarPtrTypeAttr()));
+    if (mlir::TypeAttr varPtrPtrType = curOp.getVarPtrPtrTypeAttr())
+      newProperties.setVarPtrPtrType(convertTypeAttr(varPtrPtrType));
 
     auto newOp = rewriter.replaceOpWithNewOp<mlir::omp::MapInfoOp>(
-        curOp, resTypes, adaptor.getOperands(), newAttrs);
+        curOp, resTypes, adaptor.getOperands(), newProperties,
+        curOp->getDiscardableAttrDictionary().getValue());
     if (mapBoundsOp) {
       rewriter.startOpModification(newOp);
       newOp.getBoundsMutable().append(mlir::ValueRange{mapBoundsOp});
diff --git a/flang/lib/Optimizer/Dialect/CUF/CUFOps.cpp b/flang/lib/Optimizer/Dialect/CUF/CUFOps.cpp
index 3af5e990972f1..9782ce22f6015 100644
--- a/flang/lib/Optimizer/Dialect/CUF/CUFOps.cpp
+++ b/flang/lib/Optimizer/Dialect/CUF/CUFOps.cpp
@@ -287,6 +287,11 @@ bool cuf::KernelOp::canMoveOutOf(mlir::Operation *candidate) {
   // Operations that have !fir.ref operands cannot be moved
   // out of cuf.kernel, because this may break implicit data mapping
   // passes that may run after LICM.
+  bool hasSymbolRefAttr = false;
+  candidate->getName().walkInherentAttrs(
+      candidate, [&](llvm::StringRef, mlir::Attribute &attr) {
+        hasSymbolRefAttr |= mlir::isa_and_present<mlir::SymbolRefAttr>(attr);
+      });
   return !llvm::any_of(candidate->getOperands(),
                        [&](mlir::Value candidateOperand) {
                          return fir::isa_ref_type(candidateOperand.getType());
@@ -294,9 +299,7 @@ bool cuf::KernelOp::canMoveOutOf(mlir::Operation *candidate) {
          // Same is true for symbol operands (this has to be revisited,
          // because this may indicate an issue in ordering between
          // CUFDeviceGlobal and OffloadLiveInValueCanonicalization passes).
-         !llvm::any_of(candidate->getAttrs(), [&](mlir::NamedAttribute attr) {
-           return mlir::isa_and_present<mlir::SymbolRefAttr>(attr.getValue());
-         });
+         !hasSymbolRefAttr;
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index ede7195041076..33f8d86d5283c 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -38,6 +38,8 @@
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/Support/CommandLine.h"
 
+#include <type_traits>
+
 namespace {
 #include "flang/Optimizer/Dialect/CanonicalizationPatterns.inc"
 } // namespace
@@ -56,11 +58,27 @@ static void propagateAttributes(mlir::Operation *fromOp,
   if (!fromOp || !toOp)
     return;
 
-  for (mlir::NamedAttribute attr : fromOp->getAttrs()) {
+  for (mlir::NamedAttribute attr :
+       fromOp->getDiscardableAttrDictionary().getValue()) {
     if (attr.getName().getValue().starts_with(
             mlir::acc::OpenACCDialect::getDialectNamespace()))
-      toOp->setAttr(attr.getName(), attr.getValue());
-  }
+      toOp->setDiscardableAttr(attr.getName(), attr.getValue());
+  }
+}
+
+static llvm::SmallVector<mlir::NamedAttribute>
+collectAttrsForPrinting(mlir::Operation *op,
+                        llvm::ArrayRef<mlir::StringAttr> inherentAttrNames) {
+  llvm::SmallVector<mlir::NamedAttribute> attrs(
+      op->getDiscardableAttrDictionary().getValue());
+  for (mlir::StringAttr name : inherentAttrNames)
+    if (std::optional<mlir::Attribute> value = op->getInherentAttr(name);
+        value && *value)
+      attrs.emplace_back(name, *value);
+  llvm::sort(attrs, [](mlir::NamedAttribute lhs, mlir::NamedAttribute rhs) {
+    return lhs.getName().strref() < rhs.getName().strref();
+  });
+  return attrs;
 }
 
 /// Return true if a sequence type is of some incomplete size or a record type
@@ -174,7 +192,14 @@ static void printAllocatableOp(mlir::OpAsmPrinter &p, OP &op) {
     p << ", ";
     p.printOperand(sh);
   }
-  p.printOptionalAttrDict(op->getAttrs(), {"in_type", "operandSegmentSizes"});
+  llvm::SmallVector<mlir::StringAttr> inherentAttrNames = {
+      op.getUniqNameAttrName(), op.getBindcNameAttrName()};
+  if constexpr (std::is_same_v<OP, fir::AllocMemOp>)
+    inherentAttrNames.push_back(op.getAlignmentAttrName());
+  else
+    inherentAttrNames.push_back(op.getPinnedAttrName());
+  p.printOptionalAttrDict(collectAttrsForPrinting(op, inherentAttrNames),
+                          {"in_type", "operandSegmentSizes"});
 }
 
 bool fir::mayBeAbsentBox(mlir::Value val) {
@@ -1507,10 +1532,12 @@ void fir::CallOp::print(mlir::OpAsmPrinter &p) {
     p.printStrippedAttrOrType(fmfAttr);
   }
 
-  p.printOptionalAttrDict((*this)->getAttrs(),
-                          {fir::CallOp::getCalleeAttrNameStr(),
-                           getFastmathAttrName(), getProcedureAttrsAttrName(),
-                           getArgAttrsAttrName(), getResAttrsAttrName()});
+  p.printOptionalAttrDict(
+      collectAttrsForPrinting(
+          *this, {getInlineAttrAttrName(), getAccessGroupsAttrName()}),
+      {fir::CallOp::getCalleeAttrNameStr(), getFastmathAttrName(),
+       getProcedureAttrsAttrName(), getArgAttrsAttrName(),
+       getResAttrsAttrName()});
   p << " : ";
   mlir::call_interface_impl::printFunctionSignature(
       p, getArgs().drop_front(isDirect ? 0 : 1).getTypes(), getArgAttrsAttr(),
@@ -1685,8 +1712,9 @@ static void printCmpOp(mlir::OpAsmPrinter &p, OPTY op) {
   p.printOperand(op.getLhs());
   p << ", ";
   p.printOperand(op.getRhs());
-  p.printOptionalAttrDict(op->getAttrs(),
-                          /*elidedAttrs=*/{OPTY::getPredicateAttrName()});
+  p.printOptionalAttrDict(
+      collectAttrsForPrinting(op, {op.getFastmathAttrName()}),
+      /*elidedAttrs=*/{OPTY::getPredicateAttrName()});
   p << " : " << op.getLhs().getType();
 }
 
@@ -2253,7 +2281,7 @@ void fir::CoordinateOp::print(mlir::OpAsmPrinter &p) {
     }
   }
   p.printOptionalAttrDict(
-      (*this)->getAttrs(),
+      (*this)->getDiscardableAttrDictionary().getValue(),
       /*elideAttrs=*/{getBaseTypeAttrName(), getFieldIndicesAttrName()});
   p << " : ";
   p.printFunctionalType(getOperandTypes(), (*this)->getResultTypes());
@@ -2796,7 +2824,8 @@ mlir::ParseResult fir::TypeDescOp::parse(mlir::OpAsmParser &parser,
 
 void fir::TypeDescOp::print(mlir::OpAsmPrinter &p) {
   p << ' ' << getOperation()->getAttr("in_type");
-  p.printOptionalAttrDict(getOperation()->getAttrs(), {"in_type"});
+  p.printOptionalAttrDict(
+      getOperation()->getDiscardableAttrDictionary().getValue());
 }
 
 llvm::LogicalResult fir::TypeDescOp::verify() {
@@ -2895,10 +2924,12 @@ void fir::GlobalOp::print(mlir::OpAsmPrinter &p) {
     p << '(' << val << ')';
   // Print all other attributes that are not pretty printed here.
   p.printOptionalAttrDict(
-      (*this)->getAttrs(), /*elideAttrs=*/{
-          getSymNameAttrName(), getSymrefAttrName(), getTypeAttrName(),
-          getConstantAttrName(), getTargetAttrName(), getLinkageAttrName(),
-          getInitValAttrName(), getSymVisibilityAttrName()});
+      collectAttrsForPrinting(*this,
+                              {getDataAttrAttrName(), getAlignmentAttrName()}),
+      /*elideAttrs=*/{getSymNameAttrName(), getSymrefAttrName(),
+                      getTypeAttrName(), getConstantAttrName(),
+                      getTargetAttrName(), getLinkageAttrName(),
+                      getInitValAttrName(), getSymVisibilityAttrName()});
   if (getOperation()->getAttr(getConstantAttrName()))
     p << " " << getConstantAttrName().strref();
   if (getOperation()->getAttr(getTargetAttrName()))
@@ -3447,8 +3478,8 @@ void fir::IterWhileOp::print(mlir::OpAsmPrinter &p) {
   } else if (getFinalValue()) {
     p << " -> (" << getResultTypes() << ')';
   }
-  p.printOptionalAttrDictWithKeyword((*this)->getAttrs(),
-                                     {getFinalValueAttrNameStr()});
+  p.printOptionalAttrDictWithKeyword(
+      (*this)->getDiscardableAttrDictionary().getValue());
   p << ' ';
   p.printRegion(getRegion(), /*printEntryBlockArgs=*/false,
                 /*printBlockTerminators=*/true);
@@ -3611,7 +3642,9 @@ mlir::ParseResult fir::LoadOp::parse(mlir::OpAsmParser &parser,
 void fir::LoadOp::print(mlir::OpAsmPrinter &p) {
   p << ' ';
   p.printOperand(getMemref());
-  p.printOptionalAttrDict(getOperation()->getAttrs(), {});
+  p.printOptionalAttrDict(collectAttrsForPrinting(
+      getOperation(), {getTbaaAttrName(), getNontemporalAttrName(),
+                       getInvariantAttrName(), getAccessGroupsAttrName()}));
   p << " : " << getMemref().getType();
 }
 
@@ -3878,7 +3911,7 @@ void fir::DoLoopOp::print(mlir::OpAsmPrinter &p) {
   if (!getInductionVar().getType().isIndex())
     p << " : " << getInductionVar().getType();
   p.printOptionalAttrDictWithKeyword(
-      (*this)->getAttrs(),
+      collectAttrsForPrinting(*this, {getLoopAnnotationAttrName()}),
       {"unordered", "finalValue", "reduceAttrs", "operandSegmentSizes"});
   p << ' ';
   p.printRegion(getRegion(), /*printEntryBlockArgs=*/false,
@@ -4358,9 +4391,10 @@ static void printIntegralSwitchTerminator(OpT op, mlir::OpAsmPrinter &p) {
     op.printSuccessorAtIndex(p, i);
   }
   p << ']';
-  p.printOptionalAttrDict(
-      op->getAttrs(), {op.getCasesAttr(), getCompareOffsetAttr(),
-                       getTargetOffsetAttr(), op.getOperandSegmentSizeAttr()});
+  p.printOptionalAttrDict(op->getDiscardableAttrDictionary().getValue(),
+                          {op.getCasesAttr(), getCompareOffsetAttr(),
+                           getTargetOffsetAttr(),
+                           op.getOperandSegmentSizeAttr()});
 }
 
 //===----------------------------------------------------------------------===//
@@ -4597,9 +4631,10 @@ void fir::SelectCaseOp::print(mlir::OpAsmPrinter &p) {
     printSuccessorAtIndex(p, i);
   }
   p << ']';
-  p.printOptionalAttrDict(getOperation()->getAttrs(),
-                          {getCasesAttr(), getCompareOffsetAttr(),
-                           getTargetOffsetAttr(), getOperandSegmentSizeAttr()});
+  p.printOptionalAttrDict(
+      getOperation()->getDiscardableAttrDictionary().getValue(),
+      {getCasesAttr(), getCompareOffsetAttr(), getTargetOffsetAttr(),
+       getOperandSegmentSizeAttr()});
 }
 
 unsigned fir::SelectCaseOp::compareOffsetSize() {
@@ -4883,10 +4918,10 @@ void fir::SelectTypeOp::print(mlir::OpAsmPrinter &p) {
     printSuccessorAtIndex(p, i);
   }
   p << ']';
-  p.printOptionalAttrDict(getOperation()->getAttrs(),
-                          {getCasesAttr(), getCompareOffsetAttr(),
-                           getTargetOffsetAttr(),
-                           fir::SelectTypeOp::getOperandSegmentSizeAttr()});
+  p.printOptionalAttrDict(
+      getOperation()->getDiscardableAttrDictionary().getValue(),
+      {getCasesAttr(), getCompareOffsetAttr(), getTargetOffsetAttr(),
+       fir::SelectTypeOp::getOperandSegmentSizeAttr()});
 }
 
 llvm::LogicalResult fir::SelectTypeOp::verify() {
@@ -5169,7 +5204,9 @@ void fir::StoreOp::print(mlir::OpAsmPrinter &p) {
   p.printOperand(getValue());
   p << " to ";
   p.printOperand(getMemref());
-  p.printOptionalAttrDict(getOperation()->getAttrs(), {});
+  p.printOptionalAttrDict(collectAttrsForPrinting(
+      getOperation(), {getTbaaAttrName(), getNontemporalAttrName(),
+                       getAccessGroupsAttrName()}));
   p << " : " << getMemref().getType();
 }
 
@@ -5622,8 +5659,7 @@ void fir::IfOp::print(mlir::OpAsmPrinter &p) {
     p.printRegion(otherReg, /*printEntryBlockArgs=*/false,
                   printBlockTerminators);
   }
-  p.printOptionalAttrDict((*this)->getAttrs(),
-                          /*elideAttrs=*/{getRegionWeightsAttrName()});
+  p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
 }
 
 void fir::IfOp::resultToSourceOps(llvm::SmallVectorImpl<mlir::Value> &results,
@@ -5868,6 +5904,14 @@ valueCheckFirAttributes(mlir::Value value,
 
     return true;
   };
+  auto testOperationAttributes = [&](mlir::Operation *op) {
+    auto hasAttribute = [&](llvm::StringRef name) {
+      return op->hasDiscardableAttr(name);
+    };
+    if (checkAny)
+      return llvm::any_of(attributeNames, hasAttribute);
+    return llvm::all_of(attributeNames, hasAttribute);
+  };
   // If this is a fir.box that was loaded, the fir attributes will be on the
   // related fir.ref<fir.box> creation.
   if (mlir::isa<fir::BoxType>(value.getType()))
@@ -5892,18 +5936,18 @@ valueCheckFirAttributes(mlir::Value value,
     // If this is an allocated value, look at the allocation attributes.
     if (mlir::isa<fir::AllocMemOp>(definingOp) ||
         mlir::isa<fir::AllocaOp>(definingOp))
-      return testAttributeSets(definingOp->getAttrs(), attributeNames);
+      return testOperationAttributes(definingOp);
     // If this is an imported global, look at AddrOfOp and GlobalOp attributes.
     // Both operations are looked at because use/host associated variable (the
     // AddrOfOp) can have ASYNCHRONOUS/VOLATILE attributes even if the ultimate
     // entity (the globalOp) does not have them.
     if (auto addressOfOp = mlir::dyn_cast<fir::AddrOfOp>(definingOp)) {
-      if (testAttributeSets(addressOfOp->getAttrs(), attributeNames))
+      if (testOperationAttributes(addressOfOp))
         return true;
       if (auto module = definingOp->getParentOfType<mlir::ModuleOp>())
         if (auto globalOp =
                 module.lookupSymbol<fir::GlobalOp>(addressOfOp.getSymbol()))
-          return testAttributeSets(globalOp->getAttrs(), attributeNames);
+          return testOperationAttributes(globalOp);
     }
   }
   // TODO: Construct associated entities attributes. Decide where the fir
@@ -6711,7 +6755,7 @@ void fir::DoConcurrentLoopOp::print(mlir::OpAsmPrinter &p) {
   p << ' ';
   p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
   p.printOptionalAttrDict(
-      (*this)->getAttrs(),
+      collectAttrsForPrinting(*this, {getLoopAnnotationAttrName()}),
       /*elidedAttrs=*/{DoConcurrentLoopOp::getOperandSegmentSizeAttr(),
                        DoConcurrentLoopOp::getLocalSymsAttrName(),
                        DoConcurrentLoopOp::getReduceSymsAttrName(),
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
index 2be94d40ebd50..985e57ff70f49 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
@@ -378,13 +378,9 @@ class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {
     // Propagate other attributes from hlfir.declare to fir.declare.
     // OpenACC's acc.declare is one example. Right now, the propagation
     // is verbatim.
-    llvm::SmallSet<llvm::StringRef, 8> elidedAttrs;
-    for (const mlir::NamedAttribute &firAttr : firDeclareOp->getAttrs())
-      elidedAttrs.insert(firAttr.getName());
-    elidedAttrs.insert(declareOp.getSkipReboxAttrName());
-    for (const mlir::NamedAttribute &attr : declareOp->getAttrs())
-      if (!elidedAttrs.contains(attr.getName()))
-        firDeclareOp->setAttr(attr.getName(), attr.getValue());
+    for (mlir::NamedAttribute attr :
+         declareOp->getDiscardableAttrDictionary().getValue())
+      firDeclareOp->setDiscardableAttr(attr.getName(), attr.getValue());
 
     auto firBase = firDeclareOp.getResult();
     mlir::Value hlfirBase;
diff --git a/flang/lib/Optimizer/Transforms/ConstantArgumentGlobalisation.cpp b/flang/lib/Optimizer/Transforms/ConstantArgumentGlobalisation.cpp
index afafbd8179aff..fc2962b39cd88 100644
--- a/flang/lib/Optimizer/Transforms/ConstantArgumentGlobalisation.cpp
+++ b/flang/lib/Optimizer/Transforms/ConstantArgumentGlobalisation.cpp
@@ -131,7 +131,8 @@ class CallOpRewriter : public mlir::OpRewritePattern<fir::CallOp> {
                                                   : mlir::SymbolRefAttr{},
                                               newResultTypes, newOperands);
       // Copy all the attributes from the old to new op.
-      newOp->setAttrs(callOp->getAttrs());
+      newOp->copyProperties(callOp->getPropertiesStorage());
+      newOp->setDiscardableAttrs(callOp->getDiscardableAttrDictionary());
       rewriter.replaceOp(callOp, newOp);
 
       for (auto a : allocas) {
diff --git a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
index d6393ab53a647..ff1f69a7d0b0f 100644
--- a/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
+++ b/flang/lib/Optimizer/Transforms/FIRToSCF.cpp
@@ -19,6 +19,12 @@ namespace fir {
 } // namespace fir
 
 namespace {
+static void copyDiscardableAttrs(mlir::Operation *from, mlir::Operation *to) {
+  for (mlir::NamedAttribute attr :
+       from->getDiscardableAttrDictionary().getValue())
+    to->setDiscardableAttr(attr.getName(), attr.getValue());
+}
+
 class FIRToSCFPass : public fir::impl::FIRToSCFPassBase<FIRToSCFPass> {
   using FIRToSCFPassBase::FIRToSCFPassBase;
 
@@ -298,7 +304,7 @@ struct IterWhileConversion : public mlir::OpRewritePattern<fir::IterWhileOp> {
     rewriter.setInsertionPointToEnd(afterBody);
     rewriter.replaceOpWithNewOp<mlir::scf::YieldOp>(resultOp, results);
 
-    scfWhileOp->setAttrs(iterWhileOp->getAttrs());
+    copyDiscardableAttrs(iterWhileOp, scfWhileOp);
     rewriter.replaceOp(iterWhileOp,
                        hasFinalValue ? scfWhileOp->getResults()
                                      : scfWhileOp->getResults().drop_front());
@@ -344,7 +350,9 @@ struct IfConversion : public mlir::OpRewritePattern<fir::IfOp> {
                                   scfIfOp.getElseRegion().front());
     }
 
-    scfIfOp->setAttrs(ifOp->getAttrs());
+    copyDiscardableAttrs(ifOp, scfIfOp);
+    if (mlir::DenseI32ArrayAttr weights = ifOp.getRegionWeightsAttr())
+      scfIfOp->setDiscardableAttr(ifOp.getRegionWeightsAttrName(), weights);
     rewriter.replaceOp(ifOp, scfIfOp);
     return mlir::success();
   }
diff --git a/flang/test/Fir/FirToSCF/iter-while.fir b/flang/test/Fir/FirToSCF/iter-while.fir
index 1e9bd827338cc..0cf939270baea 100644
--- a/flang/test/Fir/FirToSCF/iter-while.fir
+++ b/flang/test/Fir/FirToSCF/iter-while.fir
@@ -25,7 +25,7 @@
 // CHECK:             %[[CONSTANT_8:.*]] = arith.constant 22 : i16
 // CHECK:             %[[CONSTANT_9:.*]] = arith.constant 33 : i32
 // CHECK:             scf.yield %[[ADDI_0]], %[[CONSTANT_7]], %[[CONSTANT_8]], %[[CONSTANT_9]] : index, i1, i16, i32
-// CHECK:           } attributes {finalValue}
+// CHECK:           }
 // CHECK:           return %[[VAL_8:.*]]#0, %[[VAL_8]]#1, %[[VAL_8]]#2, %[[VAL_8]]#3 : index, i1, i16, i32
 // CHECK:         }
 func.func @test_simple_iterate_while_1() -> (index, i1, i16, i32) {
@@ -66,7 +66,7 @@ func.func @test_simple_iterate_while_1() -> (index, i1, i16, i32) {
 // CHECK:             %[[CONSTANT_2:.*]] = arith.constant 123 : i32
 // CHECK:             %[[CONSTANT_3:.*]] = arith.constant true
 // CHECK:             scf.yield %[[ADDI_0]], %[[CONSTANT_3]], %[[CONSTANT_2]] : index, i1, i32
-// CHECK:           } attributes {finalValue}
+// CHECK:           }
 // CHECK:           return %[[VAL_6:.*]]#0, %[[VAL_6]]#1, %[[VAL_6]]#2 : index, i1, i32
 // CHECK:         }
 func.func @test_simple_iterate_while_2(%start: index, %stop: index, %cond: i1, %val: i32) -> (index, i1, i32) {
@@ -102,7 +102,7 @@ func.func @test_simple_iterate_while_2(%start: index, %stop: index, %cond: i1, %
 // CHECK:             %[[ADDI_0:.*]] = arith.addi %[[VAL_2]], %[[CONSTANT_0]] overflow<nsw> : index
 // CHECK:             %[[VAL_4:.*]] = "test.get_some_value"() : () -> i1
 // CHECK:             scf.yield %[[ADDI_0]], %[[VAL_4]] : index, i1
-// CHECK:           } attributes {finalValue}
+// CHECK:           }
 // CHECK:           return %[[VAL_5:.*]]#1 : i1
 // CHECK:         }
 func.func @loop_with_negtive_step(%lo : index, %up : index) -> i1 {
@@ -136,7 +136,7 @@ func.func @loop_with_negtive_step(%lo : index, %up : index) -> i1 {
 // CHECK:             %[[ADDI_0:.*]] = arith.addi %[[VAL_2]], %[[CONSTANT_0]] overflow<nsw> : index
 // CHECK:             %[[VAL_4:.*]] = "test.get_some_value"() : () -> i1
 // CHECK:             scf.yield %[[ADDI_0]], %[[VAL_4]] : index, i1
-// CHECK:           } attributes {finalValue}
+// CHECK:           }
 // CHECK:           return %[[VAL_5:.*]]#1 : i1
 // CHECK:         }
 func.func @loop_with_zero_step(%lo : index, %up : index) -> i1 {
@@ -170,7 +170,7 @@ func.func @loop_with_zero_step(%lo : index, %up : index) -> i1 {
 // CHECK:           ^bb0(%[[VAL_3:.*]]: index, %[[VAL_4:.*]]: i1, %[[VAL_5:.*]]: i8):
 // CHECK:             %[[ADDI_0:.*]] = arith.addi %[[VAL_3]], %[[CONSTANT_2]] overflow<nsw> : index
 // CHECK:             scf.yield %[[ADDI_0]], %[[VAL_4]], %[[VAL_5]] : index, i1, i8
-// CHECK:           } attributes {finalValue}
+// CHECK:           }
 // CHECK:           return %[[VAL_6:.*]]#0, %[[VAL_6]]#1, %[[VAL_6]]#2 : index, i1, i8
 // CHECK:         }
 func.func @test_zero_iterations() -> (index, i1, i8) {
diff --git a/flang/test/Fir/convert-to-llvm-access-group.fir b/flang/test/Fir/convert-to-llvm-access-group.fir
index e28f36ffe64ca..883a9e8bb0193 100644
--- a/flang/test/Fir/convert-to-llvm-access-group.fir
+++ b/flang/test/Fir/convert-to-llvm-access-group.fir
@@ -23,13 +23,13 @@
 // CHECK:           llvm.cond_br %[[ICMP_0]], ^bb2, ^bb3
 // CHECK:         ^bb2:
 // CHECK:           llvm.store %[[VAL_0]], %[[ALLOCA_1]] <access_groups = [#[[$ATTR_0]]]> : i32, !llvm.ptr
-// CHECK:           %[[LOAD_1:.*]] = llvm.load %[[ALLOCA_1]] <access_groups = [#[[$ATTR_0]]]> {accessGroups = [#[[$ATTR_0]]]} : !llvm.ptr -> i32
-// CHECK:           %[[LOAD_2:.*]] = llvm.load %[[ARG1]] <access_groups = [#[[$ATTR_0]]]> {accessGroups = [#[[$ATTR_0]]]} : !llvm.ptr -> i32
+// CHECK:           %[[LOAD_1:.*]] = llvm.load %[[ALLOCA_1]] <access_groups = [#[[$ATTR_0]]]> : !llvm.ptr -> i32
+// CHECK:           %[[LOAD_2:.*]] = llvm.load %[[ARG1]] <access_groups = [#[[$ATTR_0]]]> : !llvm.ptr -> i32
 // CHECK:           %[[ADD_0:.*]] = llvm.add %[[LOAD_1]], %[[LOAD_2]] : i32
 // CHECK:           %[[SITOFP_0:.*]] = llvm.sitofp %[[ADD_0]] : i32 to f32
 // CHECK:           %[[MLIR_5:.*]] = llvm.mlir.constant(72 : i32) : i32
 // CHECK:           "llvm.intr.memcpy"(%[[ALLOCA_0]], %[[ARG3]], %[[MLIR_5]]) <{access_groups = [#[[$ATTR_0]]], arg_attrs = [{llvm.align = 8 : i64}, {llvm.align = 8 : i64}, {}], isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i32) -> ()
-// CHECK:           %[[LOAD_3:.*]] = llvm.load %[[ARG0]] <access_groups = [#[[$ATTR_0]]]> {accessGroups = [#[[$ATTR_0]]]} : !llvm.ptr -> i32
+// CHECK:           %[[LOAD_3:.*]] = llvm.load %[[ARG0]] <access_groups = [#[[$ATTR_0]]]> : !llvm.ptr -> i32
 // CHECK:           %[[SEXT_1:.*]] = llvm.sext %[[LOAD_3]] : i32 to i64
 // CHECK:           %[[SEXT_2:.*]] = llvm.sext %[[LOAD_1]] : i32 to i64
 // CHECK:           %[[GETELEMENTPTR_0:.*]] = llvm.getelementptr %[[ALLOCA_0]][0, 0] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8, array<2 x array<3 x i64>>)>
@@ -60,7 +60,7 @@
 // CHECK:           %[[MUL_5:.*]] = llvm.mul %[[MUL_2]], %[[LOAD_9]] overflow<nsw, nuw> : i64
 // CHECK:           %[[GETELEMENTPTR_7:.*]] = llvm.getelementptr nusw|nuw %[[LOAD_4]]{{\[}}%[[ADD_2]]] : (!llvm.ptr, i64) -> !llvm.ptr, f32
 // CHECK:           llvm.store %[[SITOFP_0]], %[[GETELEMENTPTR_7]] <access_groups = [#[[$ATTR_0]]]> : f32, !llvm.ptr
-// CHECK:           %[[LOAD_11:.*]] = llvm.load %[[ALLOCA_1]] <access_groups = [#[[$ATTR_0]]]> {accessGroups = [#[[$ATTR_0]]]} : !llvm.ptr -> i32
+// CHECK:           %[[LOAD_11:.*]] = llvm.load %[[ALLOCA_1]] <access_groups = [#[[$ATTR_0]]]> : !llvm.ptr -> i32
 // CHECK:           %[[ADD_3:.*]] = llvm.add %[[LOAD_11]], %[[TRUNC_0]] overflow<nsw> : i32
 // CHECK:           %[[SUB_2:.*]] = llvm.sub %[[VAL_1]], %[[MLIR_3]] : i64
 // CHECK:           llvm.br ^bb1(%[[ADD_3]], %[[SUB_2]] : i32, i64) loop_annotation = #[[$ATTR_1]]



More information about the flang-commits mailing list