[flang-commits] [flang] 67f9f42 - [fir][AddAliasTags] allow usage of AddAliasTag pass after FirToMemref (#219493)

via flang-commits flang-commits at lists.llvm.org
Tue Sep 1 06:51:32 PDT 2026


Author: jeanPerier
Date: 2026-09-01T15:51:26+02:00
New Revision: 67f9f425e3bff87da61b041a98a5dd033e12b9d8

URL: https://github.com/llvm/llvm-project/commit/67f9f425e3bff87da61b041a98a5dd033e12b9d8
DIFF: https://github.com/llvm/llvm-project/commit/67f9f425e3bff87da61b041a98a5dd033e12b9d8.diff

LOG: [fir][AddAliasTags] allow usage of AddAliasTag pass after FirToMemref (#219493)

This is a first patch to improve mixed-dialect support in Flang. The
goal is to allow TBAA tags to be generated before codegen even
after FIRToMemRef or ExternalNameConversion has run.

Concretely:
- Treat non-FIR allocation operations as local allocations under the
  "allocated data" TBAA subtree. They cannot alias Fortran dummy
  arguments. If a conversion pass copied the Fortran variable uniq_name
  onto the new allocation, use that name so the access is distinct from other
  named allocations; otherwise use the unnamed "allocated data" tag.

- Always look for fir.internal_name when building the TBAA tree, not
  only on llvm.func. This removes the assumption that
  ExternalNameConversion has not run before AddAliasTags and
  makes it possible to place the pass later in new pipelines.

Note that this patch is not enough to generate Fortran TBAA tags in LLVM
IR after FIRToMemRef. MemRef will also need to understand the tbaa
attribute and propagate it through its LLVM lowering.

Added: 
    

Modified: 
    flang/include/flang/Optimizer/Analysis/TBAAForest.h
    flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
    flang/lib/Optimizer/Transforms/AddAliasTags.cpp
    flang/test/Fir/tbaa-codegen2.fir
    flang/test/Transforms/tbaa.fir

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/Analysis/TBAAForest.h b/flang/include/flang/Optimizer/Analysis/TBAAForest.h
index 0b70778eba3af..b4bc3dd0d2f14 100644
--- a/flang/include/flang/Optimizer/Analysis/TBAAForest.h
+++ b/flang/include/flang/Optimizer/Analysis/TBAAForest.h
@@ -137,16 +137,10 @@ class TBAAForrest {
       : separatePerFunction{separatePerFunction} {}
 
   inline const TBAATree &operator[](mlir::func::FuncOp func) {
-    return getFuncTree(func.getSymNameAttr());
+    return getFuncTree(getInternalOrSymbolName(func));
   }
   inline const TBAATree &operator[](mlir::LLVM::LLVMFuncOp func) {
-    // the external name conversion pass may rename some functions. Their old
-    // name must be used so that we add to the tbaa tree added in the FIR pass
-    mlir::Attribute attr = func->getAttr(getInternalFuncNameAttrName());
-    if (attr) {
-      return getFuncTree(mlir::cast<mlir::StringAttr>(attr));
-    }
-    return getFuncTree(func.getSymNameAttr());
+    return getFuncTree(getInternalOrSymbolName(func));
   }
   // Returns the TBAA tree associated with the scope enclosed
   // within the given function. With MLIR inlining, there may
@@ -156,7 +150,7 @@ class TBAAForrest {
   // "root" scope of the given function.
   inline TBAATree &getMutableFuncTreeWithScope(mlir::func::FuncOp func,
                                                llvm::StringRef scope) {
-    mlir::StringAttr name = func.getSymNameAttr();
+    mlir::StringAttr name = getInternalOrSymbolName(func);
     if (!scope.empty())
       name = mlir::StringAttr::get(name.getContext(),
                                    llvm::Twine(name) + " - " + scope);
@@ -169,6 +163,17 @@ class TBAAForrest {
   }
 
 private:
+  template <typename FuncOp>
+  static mlir::StringAttr getInternalOrSymbolName(FuncOp func) {
+    // External name conversion may rename a function before TBAA construction.
+    // Use its original name consistently so tags created at 
diff erent pipeline
+    // stages belong to the same tree.
+    if (auto name = func->template getAttrOfType<mlir::StringAttr>(
+            getInternalFuncNameAttrName()))
+      return name;
+    return func.getSymNameAttr();
+  }
+
   TBAATree &getFuncTree(mlir::StringAttr symName) {
     if (!separatePerFunction)
       symName = mlir::StringAttr::get(symName.getContext(), "");

diff  --git a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
index a354f7aef511b..2051b36fb9b53 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROpsSupport.h
@@ -147,6 +147,12 @@ static constexpr llvm::StringRef getAccessGroupsAttrName() {
   return "access_groups";
 }
 
+/// Attribute holding the unique name of the Fortran entity an allocation
+/// belongs to. It is an inherent attribute of the FIR allocation operations,
+/// and may also be carried by allocation operations of other dialects that
+/// FIR allocations were rewritten into.
+static constexpr llvm::StringRef getUniqNameAttrName() { return "uniq_name"; }
+
 /// Attribute to mark coarray Fortran entities with the CORANK attribute.
 constexpr llvm::StringRef getCorankAttrName() { return "fir.corank"; }
 

diff  --git a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
index db216634ac7a5..d2cd1f0072d22 100644
--- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
+++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
@@ -15,6 +15,7 @@
 #include "flang/Optimizer/Analysis/AliasAnalysis.h"
 #include "flang/Optimizer/Analysis/TBAAForest.h"
 #include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Dialect/FIROpsSupport.h"
 #include "flang/Optimizer/Dialect/FirAliasTagOpInterface.h"
 #include "flang/Optimizer/Support/DataLayout.h"
 #include "flang/Optimizer/Support/Utils.h"
@@ -817,15 +818,23 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
   } else if (enableLocalAllocs &&
              source.kind == fir::AliasAnalysis::SourceKind::Allocate) {
     std::optional<llvm::StringRef> name;
-    mlir::Operation *sourceOp =
-        llvm::cast<mlir::Value>(source.origin.u).getDefiningOp();
-    bool unknownAllocOp = false;
-    if (auto alloc = mlir::dyn_cast_or_null<fir::AllocaOp>(sourceOp))
-      name = alloc.getUniqName();
-    else if (auto alloc = mlir::dyn_cast_or_null<fir::AllocMemOp>(sourceOp))
-      name = alloc.getUniqName();
-    else
-      unknownAllocOp = true;
+    mlir::Value sourceVal = llvm::cast<mlir::Value>(source.origin.u);
+    mlir::Operation *sourceOp = sourceVal.getDefiningOp();
+    bool unknownAllocOp =
+        !fir::isNewAllocationResult(mlir::dyn_cast<mlir::OpResult>(sourceVal))
+             .value_or(false);
+    if (!unknownAllocOp) {
+      // Non-FIR allocation operations may carry the uniq_name preserved when
+      // a FIR allocation was rewritten.
+      if (auto alloc = mlir::dyn_cast<fir::AllocaOp>(sourceOp))
+        name = alloc.getUniqName();
+      else if (auto alloc = mlir::dyn_cast<fir::AllocMemOp>(sourceOp))
+        name = alloc.getUniqName();
+      else if (mlir::StringAttr nameAttr =
+                   sourceOp->getDiscardableAttrOfType<mlir::StringAttr>(
+                       fir::getUniqNameAttrName()))
+        name = nameAttr.getValue();
+    }
 
     // Check if this allocation is a local copy of a VALUE dummy argument.
     // A VALUE dummy arg is lowered as a local alloca declared with the
@@ -837,7 +846,6 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
     // which then incorrectly eliminates the copy stores.
     bool isValueDummyCopy = false;
     if (!unknownAllocOp) {
-      mlir::Value sourceVal = llvm::cast<mlir::Value>(source.origin.u);
       if (fir::DeclareOp declareOp = getDeclareOp(sourceVal)) {
         auto varIf = mlir::cast<fir::FortranVariableOpInterface>(
             declareOp.getOperation());
@@ -886,6 +894,8 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
       tag = state.getFuncTreeWithScope(func, scopeOp)
                 .allocatedDataTree.getTag(*name);
     } else if (state.attachLocalAllocTag()) {
+      // Recognized anonymous allocations, including allocations from other
+      // dialects, use the generic "allocated data" tag.
       LLVM_DEBUG(llvm::dbgs().indent(2)
                  << "WARN: couldn't find a name for allocation " << *op
                  << "\n");

diff  --git a/flang/test/Fir/tbaa-codegen2.fir b/flang/test/Fir/tbaa-codegen2.fir
index 192903f09f662..49a0b903ce0ae 100644
--- a/flang/test/Fir/tbaa-codegen2.fir
+++ b/flang/test/Fir/tbaa-codegen2.fir
@@ -113,4 +113,4 @@ module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.targ
 // CHECK: ![[DATA_ACCESS_TYPE]] = !{!"any data access", ![[ANY_ACCESS_TYPE]], i64 0}
 // CHECK: ![[TMP_DATA_ACCESS_TAG]] = !{![[TMP_DATA_ACCESS_TYPE:.*]], ![[TMP_DATA_ACCESS_TYPE]], i64 0}
 // CHECK: ![[TMP_DATA_ACCESS_TYPE]] = !{!"allocated data/", ![[TMP_ACCESS_TYPE:.*]], i64 0}
-// CHECK: ![[TMP_ACCESS_TYPE]] = !{!"allocated data", ![[TARGET_ACCESS_TAG:.*]], i64 0}
+// CHECK: ![[TMP_ACCESS_TYPE]] = !{!"allocated data", ![[DATA_ACCESS_TYPE]], i64 0}

diff  --git a/flang/test/Transforms/tbaa.fir b/flang/test/Transforms/tbaa.fir
index bbc0d235bef50..6ee23d6495e1d 100644
--- a/flang/test/Transforms/tbaa.fir
+++ b/flang/test/Transforms/tbaa.fir
@@ -229,3 +229,109 @@ fir.global internal @_QFEi : i32 {
   fir.has_value %c0_i32 : i32
 }
 }
+
+// -----
+
+// Verify that alias tags created after external name conversion use the
+// original function name, matching tags added later during code generation.
+module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr = dense<64> : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} {
+  func.func @renamed_(%arg0: !fir.ref<i32>) attributes {fir.internal_name = "_QPrenamed"} {
+    %scope = fir.dummy_scope : !fir.dscope
+    %0 = fir.declare %arg0 dummy_scope %scope {uniq_name = "_QFrenamedEa"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32>
+    %1 = fir.load %0 : !fir.ref<i32>
+    fir.store %1 to %0 : !fir.ref<i32>
+    return
+  }
+
+// CHECK: #[[RENAMED_ROOT:.+]] = #llvm.tbaa_root<id = "Flang function root _QPrenamed">
+// CHECK-NOT: #llvm.tbaa_root<id = "Flang function root renamed_">
+// CHECK: #[[RENAMED_TAG:.+]] = #llvm.tbaa_tag
+// CHECK-LABEL: func.func @renamed_
+// CHECK: fir.load {{.*}} {tbaa = [#[[RENAMED_TAG]]]}
+// CHECK: fir.store {{.*}} {tbaa = [#[[RENAMED_TAG]]]}
+}
+
+// -----
+
+// uniq_name on a non-fir allocation must be read from persistent StringAttr
+// storage (not a temporary std::string).
+module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr = dense<64> : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} {
+  func.func @_QPnonfiralloc() {
+    %c1 = arith.constant 1 : i64
+    %0 = llvm.alloca %c1 x i32 {uniq_name = "_QFnonfirallocEi"} : (i64) -> !llvm.ptr
+    %1 = fir.convert %0 : (!llvm.ptr) -> !fir.ref<i32>
+    %2 = fir.declare %1 {uniq_name = "_QFnonfirallocEi"} : (!fir.ref<i32>) -> !fir.ref<i32>
+    %3 = fir.load %2 : !fir.ref<i32>
+    fir.store %3 to %2 : !fir.ref<i32>
+    return
+  }
+
+// CHECK: #[[ALLOC_DATA:.+]] = #llvm.tbaa_type_desc<id = "allocated data/_QFnonfirallocEi", members = {{{.*}}}>
+// CHECK: #[[ALLOC_TAG:.+]] = #llvm.tbaa_tag<base_type = #[[ALLOC_DATA]], access_type = #[[ALLOC_DATA]], offset = 0>
+// CHECK-LABEL: func.func @_QPnonfiralloc(
+// CHECK: fir.load {{.*}} {tbaa = [#[[ALLOC_TAG]]]}
+// CHECK: fir.store {{.*}} {tbaa = [#[[ALLOC_TAG]]]}
+}
+
+// -----
+
+// uniq_name on memref.alloca is used to distinguish named allocations.
+module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr = dense<64> : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} {
+  func.func @_QPmemrefalloc() {
+    %0 = memref.alloca() {uniq_name = "_QFmemrefallocEi"} : memref<i32>
+    %1 = fir.convert %0 : (memref<i32>) -> !fir.ref<i32>
+    %2 = fir.load %1 : !fir.ref<i32>
+    fir.store %2 to %1 : !fir.ref<i32>
+    return
+  }
+
+// CHECK: #[[MEMREF_ALLOC_DATA:.+]] = #llvm.tbaa_type_desc<id = "allocated data/_QFmemrefallocEi", members = {{{.*}}}>
+// CHECK: #[[MEMREF_ALLOC_TAG:.+]] = #llvm.tbaa_tag<base_type = #[[MEMREF_ALLOC_DATA]], access_type = #[[MEMREF_ALLOC_DATA]], offset = 0>
+// CHECK-LABEL: func.func @_QPmemrefalloc(
+// CHECK: fir.load {{.*}} {tbaa = [#[[MEMREF_ALLOC_TAG]]]}
+// CHECK: fir.store {{.*}} {tbaa = [#[[MEMREF_ALLOC_TAG]]]}
+}
+
+// -----
+
+// An explicit empty uniq_name gets its own subtree, distinguishing it from an
+// allocation with no uniq_name.
+module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr = dense<64> : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} {
+  func.func @_QPemptyname() {
+    %0 = memref.alloca() {uniq_name = ""} : memref<i32>
+    %1 = fir.convert %0 : (memref<i32>) -> !fir.ref<i32>
+    %2 = fir.load %1 : !fir.ref<i32>
+    fir.store %2 to %1 : !fir.ref<i32>
+    return
+  }
+
+// CHECK: #[[EMPTY_ALLOC_PARENT:.+]] = #llvm.tbaa_type_desc<id = "allocated data", members = {{{.*}}}>
+// CHECK: #[[EMPTY_ALLOC_DATA:.+]] = #llvm.tbaa_type_desc<id = "allocated data/", members = {<#[[EMPTY_ALLOC_PARENT]], 0>}>
+// CHECK: #[[EMPTY_ALLOC_TAG:.+]] = #llvm.tbaa_tag<base_type = #[[EMPTY_ALLOC_DATA]], access_type = #[[EMPTY_ALLOC_DATA]], offset = 0>
+// CHECK-LABEL: func.func @_QPemptyname(
+// CHECK: fir.load {{.*}} {tbaa = [#[[EMPTY_ALLOC_TAG]]]}
+// CHECK: fir.store {{.*}} {tbaa = [#[[EMPTY_ALLOC_TAG]]]}
+}
+
+// -----
+
+// An anonymous allocation of another dialect is still recognized as a local
+// allocation through its memory effects, so accesses get the unnamed
+// "allocated data" tag instead of no tag at all.
+module attributes {dlti.dl_spec = #dlti.dl_spec<!llvm.ptr = dense<64> : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} {
+  func.func @_QPanonalloc(%arg0: index) {
+    %0 = memref.alloca(%arg0) {bindc_name = ".tmp.array"} : memref<?xf32>
+    %1 = fir.convert %0 : (memref<?xf32>) -> !fir.ref<f32>
+    %2 = fir.load %1 : !fir.ref<f32>
+    fir.store %2 to %1 : !fir.ref<f32>
+    return
+  }
+
+// CHECK: #[[ANY_DATA:.+]] = #llvm.tbaa_type_desc<id = "any data access", members = {{{.*}}}>
+// CHECK: #[[ANON_ALLOC_DATA:.+]] = #llvm.tbaa_type_desc<id = "allocated data", members = {<#[[ANY_DATA]], 0>}>
+// CHECK-NOT: #llvm.tbaa_type_desc<id = "allocated data/
+// CHECK: #[[ANON_ALLOC_TAG:.+]] = #llvm.tbaa_tag<base_type = #[[ANON_ALLOC_DATA]], access_type = #[[ANON_ALLOC_DATA]], offset = 0>
+// CHECK-LABEL: func.func @_QPanonalloc(
+// CHECK: fir.load {{.*}} {tbaa = [#[[ANON_ALLOC_TAG]]]}
+// CHECK: fir.store {{.*}} {tbaa = [#[[ANON_ALLOC_TAG]]]}
+}


        


More information about the flang-commits mailing list