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

via flang-commits flang-commits at lists.llvm.org
Fri Aug 28 07:45:16 PDT 2026


https://github.com/jeanPerier created https://github.com/llvm/llvm-project/pull/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.

>From ec24148bac0552d6f17bda2fc7e339b90cd59b24 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 28 Aug 2026 07:25:03 -0700
Subject: [PATCH] [fir][AddAliasTags] allow usage of AddAliasTag pass after
 FirToMemref

---
 .../flang/Optimizer/Analysis/TBAAForest.h     | 23 ++++---
 .../lib/Optimizer/Transforms/AddAliasTags.cpp | 20 ++++--
 flang/test/Transforms/tbaa.fir                | 66 +++++++++++++++++++
 3 files changed, 96 insertions(+), 13 deletions(-)

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 different 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/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
index db216634ac7a5..6abb92854cf21 100644
--- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
+++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp
@@ -817,15 +817,28 @@ 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();
+    mlir::Value sourceVal = llvm::cast<mlir::Value>(source.origin.u);
+    mlir::Operation *sourceOp = sourceVal.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
+    else if (mlir::StringAttr nameAttr =
+                 sourceOp ? sourceOp->getAttrOfType<mlir::StringAttr>(
+                                fir::AllocaOp::getUniqNameAttrName())
+                          : mlir::StringAttr{}) {
+      // Keep a view into the StringAttr storage; str() returns a temporary.
+      name = nameAttr.getValue();
+    } else if (!fir::isNewAllocationResult(
+                    mlir::dyn_cast<mlir::OpResult>(sourceVal))
+                    .value_or(false)) {
+      // Anonymous allocations of other dialects (e.g. memref.alloca for
+      // a compiler generated temporary) are still recognizable as allocations
+      // through their memory effects, and can use the unnamed
+      // "allocated data" tag below.
       unknownAllocOp = true;
+    }
 
     // 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 +850,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());
diff --git a/flang/test/Transforms/tbaa.fir b/flang/test/Transforms/tbaa.fir
index bbc0d235bef50..b3f0990768f1e 100644
--- a/flang/test/Transforms/tbaa.fir
+++ b/flang/test/Transforms/tbaa.fir
@@ -229,3 +229,69 @@ 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]]]}
+}
+
+// -----
+
+// 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