[flang-commits] [flang] [flang] Add initial support for RegionBranchOpInterface to AA (PR #196132)

via flang-commits flang-commits at lists.llvm.org
Wed May 6 10:39:25 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Razvan Lupusoru (razvanlupusoru)

<details>
<summary>Changes</summary>

This PR introduces initial support for being able to disambiguate values through operations that implement RegionBranchOpInterface (eg fir.if). The initial approach implements a localized approach which instead of an invasive update to the current algorithm so that a single underlying origin to be replaced with multiple, it still leaves the single origin concept. It does so through a conservative merge of all input sources.

The main initial goal is to allow disambiguating OPTIONAL pattern where we have an allocate on one branch and an absent on the other. Additionally, it is able to properly merge sources if the sources have same properties.

---

Patch is 20.50 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/196132.diff


2 Files Affected:

- (modified) flang/lib/Optimizer/Analysis/AliasAnalysis.cpp (+183) 
- (added) flang/test/Analysis/AliasAnalysis/alias-analysis-regionbranch.mlir (+262) 


``````````diff
diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 2d19e07d9d7ce..98478a49477a9 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -22,6 +22,7 @@
 #include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/Value.h"
+#include "mlir/Interfaces/ControlFlowInterfaces.h"
 #include "mlir/Interfaces/SideEffectInterfaces.h"
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/Support/Casting.h"
@@ -141,6 +142,156 @@ static fir::AliasAnalysis::Source getSourceForACCMappedValue(
   return source;
 }
 
+/// Predecessor SSA values that may define a result of \p branch when control
+/// continues in the parent region (same mapping as
+/// `LocalAliasAnalysis::collectUnderlyingAddressValues2` for
+/// `RegionSuccessor::parent()`).
+static void getRegionBranchPredecessorValuesForParentResult(
+    mlir::RegionBranchOpInterface branch, mlir::OpResult result,
+    llvm::SmallVectorImpl<mlir::Value> &out) {
+  mlir::RegionSuccessor parentSucc = mlir::RegionSuccessor::parent();
+  mlir::Value inputValue = result;
+  unsigned inputIndex = result.getResultNumber();
+  mlir::ValueRange inputs = branch.getSuccessorInputs(parentSucc);
+  if (inputs.empty()) {
+    out.push_back(inputValue);
+    return;
+  }
+  unsigned firstInputIndex, lastInputIndex;
+  if (mlir::isa<mlir::BlockArgument>(inputs[0])) {
+    firstInputIndex = mlir::cast<mlir::BlockArgument>(inputs[0]).getArgNumber();
+    lastInputIndex =
+        mlir::cast<mlir::BlockArgument>(inputs.back()).getArgNumber();
+  } else {
+    firstInputIndex = mlir::cast<mlir::OpResult>(inputs[0]).getResultNumber();
+    lastInputIndex =
+        mlir::cast<mlir::OpResult>(inputs.back()).getResultNumber();
+  }
+  if (firstInputIndex > inputIndex || lastInputIndex < inputIndex) {
+    out.push_back(inputValue);
+    return;
+  }
+  branch.getPredecessorValues(parentSucc, inputIndex - firstInputIndex, out);
+}
+
+/// True when \p src's tracked origin value is an SSA result of an operation
+/// nested under \p branch's regions.
+static bool originIsInsideRegionBranch(mlir::RegionBranchOpInterface branch,
+                                       const fir::AliasAnalysis::Source &src) {
+  const fir::AliasAnalysis::Source::SourceOrigin &origin = src.origin;
+  if (llvm::isa<mlir::SymbolRefAttr>(origin.u))
+    return false;
+  mlir::Value originVal = llvm::cast<mlir::Value>(origin.u);
+  if (mlir::isa<mlir::BlockArgument>(originVal))
+    return false;
+  mlir::Operation *defOp = originVal.getDefiningOp();
+  if (!defOp)
+    return false;
+  return branch.getOperation()->isProperAncestor(defOp);
+}
+
+/// Conservative join of memory sources from region-branch predecessors.
+static fir::AliasAnalysis::Source mergeRegionBranchPredecessorSources(
+    llvm::ArrayRef<fir::AliasAnalysis::Source> sources,
+    mlir::Value fallbackValue, mlir::Type fallbackType, bool followingData) {
+  assert(!sources.empty() && "expected at least one predecessor source");
+
+  // For kind, origin, attributes, isApproximate/accessPath, valueType, we
+  // capture if all of the sources have exactly the same value.
+  bool allKindsSame =
+      llvm::all_of(sources, [&](const fir::AliasAnalysis::Source &s) {
+        return s.kind == sources[0].kind;
+      });
+  bool allOriginsSame =
+      llvm::all_of(sources, [&](const fir::AliasAnalysis::Source &s) {
+        return s.origin == sources[0].origin;
+      });
+  bool allAttrsSame =
+      llvm::all_of(sources, [&](const fir::AliasAnalysis::Source &s) {
+        return s.attributes == sources[0].attributes;
+      });
+  bool allPathsSame =
+      llvm::all_of(sources, [&](const fir::AliasAnalysis::Source &s) {
+        return s.accessPath.isApproximate ==
+                   sources[0].accessPath.isApproximate &&
+               s.accessPath.steps == sources[0].accessPath.steps;
+      });
+  bool allTypesSame =
+      llvm::all_of(sources, [&](const fir::AliasAnalysis::Source &s) {
+        return s.valueType == sources[0].valueType;
+      });
+
+  // For approximateSource and isCapturedInInternalProcedure, we mark them
+  // as true if any of the sources are true.
+  bool mergedApprox =
+      llvm::any_of(sources, [](const fir::AliasAnalysis::Source &s) {
+        return s.approximateSource;
+      });
+  bool mergedCaptured =
+      llvm::any_of(sources, [](const fir::AliasAnalysis::Source &s) {
+        return s.isCapturedInInternalProcedure;
+      });
+
+  fir::AliasAnalysis::SourceKind mergedKind;
+  fir::AliasAnalysis::Source::Attributes mergedAttrs;
+  if (!allKindsSame) {
+    mergedKind = fir::AliasAnalysis::SourceKind::Unknown;
+    mergedAttrs = {};
+  } else if (!allAttrsSame) {
+    mergedKind = fir::AliasAnalysis::SourceKind::Unknown;
+    mergedAttrs = {};
+  } else if (!allOriginsSame) {
+    // Same kind and attributes on every path, but different concrete origins.
+    // Since origins are different, for most cases fall back to Indirect here.
+    // However, for Allocate, we want to keep the information about this being
+    // an Allocate as long as all are defined inside the region's branches
+    // because then they are all unique and thus cannot alias anything outside
+    // the region (this is key here - because this only holds when comparing
+    // region's result only with outside values not the origins themselves).
+    // TODO: An origin list would be better to preserve this information
+    // more accurately instead of a single origin.
+    auto branchOp = mlir::dyn_cast<mlir::RegionBranchOpInterface>(
+        mlir::cast<mlir::OpResult>(fallbackValue).getOwner());
+    assert(branchOp && "merge region-branch sources expects branch op result");
+    unsigned originsOutsideBranch =
+        llvm::count_if(sources, [&](const fir::AliasAnalysis::Source &s) {
+          return !originIsInsideRegionBranch(branchOp, s);
+        });
+    bool keepAllocate =
+        sources[0].kind == fir::AliasAnalysis::SourceKind::Allocate &&
+        originsOutsideBranch == 0u;
+    mergedKind = keepAllocate ? fir::AliasAnalysis::SourceKind::Allocate
+                              : fir::AliasAnalysis::SourceKind::Indirect;
+    mergedAttrs = sources[0].attributes;
+  } else {
+    mergedKind = sources[0].kind;
+    mergedAttrs = sources[0].attributes;
+  }
+
+  fir::AliasAnalysis::Source::SourceOrigin mergedOrigin;
+  if (allOriginsSame) {
+    mergedOrigin = sources[0].origin;
+  } else {
+    // Set the origin as the fallbackValue provided - which should be the
+    // region-branch result.
+    mergedOrigin = {fallbackValue, nullptr, followingData};
+  }
+
+  fir::AliasAnalysis::Source::AccessPath mergedPath;
+  if (allPathsSame) {
+    mergedPath = sources[0].accessPath;
+    mergedPath.isApproximate |= mergedApprox;
+  } else {
+    mergedPath = {};
+    mergedPath.isApproximate = true;
+  }
+
+  mlir::Type mergedTy = allTypesSame ? sources[0].valueType : fallbackType;
+
+  return {mergedOrigin, mergedKind, mergedTy,      mergedAttrs,
+          mergedApprox, mergedPath, mergedCaptured};
+}
+
 namespace fir {
 
 void AliasAnalysis::Source::AccessPath::print(llvm::raw_ostream &os) const {
@@ -861,6 +1012,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
     }
     ty = opResult.getType();
     std::optional<AliasAnalysis::Source> accSourceReturn;
+    std::optional<AliasAnalysis::Source> regionBranchReturn;
     llvm::TypeSwitch<Operation *>(defOp)
         .Case([&](hlfir::AsExprOp op) {
           // TODO: we should probably always report hlfir.as_expr
@@ -1225,10 +1377,41 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
               followingData, attributes);
           breakFromLoop = true;
         })
+        .Case([&](mlir::RegionBranchOpInterface branch) {
+          llvm::SmallVector<mlir::Value, 4> predecessors;
+          getRegionBranchPredecessorValuesForParentResult(branch, opResult,
+                                                          predecessors);
+          if (predecessors.empty() ||
+              llvm::all_of(predecessors,
+                           [&](mlir::Value pred) { return pred == v; })) {
+            regionBranchReturn = {{{v, instantiationPoint, followingData},
+                                   SourceKind::Unknown,
+                                   ty,
+                                   attributes,
+                                   /*approximateSource=*/true,
+                                   /*accessPath=*/{},
+                                   isCapturedInInternalProcedure}};
+            breakFromLoop = true;
+            return;
+          }
+          llvm::SmallVector<AliasAnalysis::Source, 4> predSources;
+          predSources.reserve(predecessors.size());
+          for (mlir::Value pred : predecessors)
+            predSources.push_back(getSource(pred, getLastInstantiationPoint));
+          regionBranchReturn = mergeRegionBranchPredecessorSources(
+              predSources, v, ty, followingData);
+          regionBranchReturn->attributes |= attributes;
+          regionBranchReturn->approximateSource |= approximateSource;
+          regionBranchReturn->isCapturedInInternalProcedure |=
+              isCapturedInInternalProcedure;
+          breakFromLoop = true;
+        })
         .Default([&](auto op) {
           defOp = nullptr;
           breakFromLoop = true;
         });
+    if (regionBranchReturn)
+      return *regionBranchReturn;
     if (accSourceReturn)
       return *accSourceReturn;
   }
diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-regionbranch.mlir b/flang/test/Analysis/AliasAnalysis/alias-analysis-regionbranch.mlir
new file mode 100644
index 0000000000000..7b0ed3aee84f4
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-regionbranch.mlir
@@ -0,0 +1,262 @@
+// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' \
+// RUN:   -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s
+
+// -----
+
+// Distinct fir.alloca in each branch; unrelated fir.alloca outside the if.
+// CHECK-LABEL: Testing : "test_rb_both_alloc_distinct"
+// CHECK-DAG: outside_alloc#0 <-> join_alloc#0: NoAlias
+
+func.func @test_rb_both_alloc_distinct() {
+  %cond = arith.constant true
+  %a_out = fir.alloca f32 {uniq_name = "_QFEa_out"}
+  %d_out = fir.declare %a_out {uniq_name = "_QFEa_out", test.ptr = "outside_alloc"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %jf = fir.if %cond -> !fir.ref<f32> {
+    %a1 = fir.alloca f32 {uniq_name = "_QFEa1"}
+    %d1 = fir.declare %a1 {uniq_name = "_QFEa1"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    fir.result %d1 : !fir.ref<f32>
+  } else {
+    %a2 = fir.alloca f32 {uniq_name = "_QFEa2"}
+    %d2 = fir.declare %a2 {uniq_name = "_QFEa2"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    fir.result %d2 : !fir.ref<f32>
+  }
+  %join = fir.convert %jf {test.ptr = "join_alloc"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  return
+}
+
+// -----
+
+// Each branch yields different fir.declare which means that the
+// origin is from either - this case is handled as Indirect.
+// CHECK-LABEL: Testing : "test_rb_both_alloc_distinct_outer_scope"
+// CHECK-DAG: d1#0 <-> join_outer#0: MayAlias
+// CHECK-DAG: d2#0 <-> join_outer#0: MayAlias
+// CHECK-DAG: outside_outer#0 <-> join_outer#0: MayAlias
+
+func.func @test_rb_both_alloc_distinct_outer_scope() {
+  %cond = arith.constant true
+  %a1 = fir.alloca f32 {uniq_name = "_QFEa1o"}
+  %a2 = fir.alloca f32 {uniq_name = "_QFEa2o"}
+  %d1 = fir.declare %a1 {uniq_name = "_QFEa1o", test.ptr = "d1"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %d2 = fir.declare %a2 {uniq_name = "_QFEa2o", test.ptr = "d2"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %a_ext = fir.alloca f32 {uniq_name = "_QFEa_ext_o"}
+  %d_ext = fir.declare %a_ext {uniq_name = "_QFEa_ext_o", test.ptr = "outside_outer"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %jf = fir.if %cond -> !fir.ref<f32> {
+    fir.result %d1 : !fir.ref<f32>
+  } else {
+    fir.result %d2 : !fir.ref<f32>
+  }
+  %join = fir.convert %jf {test.ptr = "join_outer"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  return
+}
+
+// -----
+
+// Both branches yield the same fir.declare from outside.
+// CHECK-LABEL: Testing : "test_rb_both_alloc_same_decl"
+// CHECK-DAG: shared_decl#0 <-> join_same#0: MustAlias
+
+func.func @test_rb_both_alloc_same_decl() {
+  %cond = arith.constant true
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa", test.ptr = "shared_decl"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %jf = fir.if %cond -> !fir.ref<f32> {
+    fir.result %da : !fir.ref<f32>
+  } else {
+    fir.result %da : !fir.ref<f32>
+  }
+  %join = fir.convert %jf {test.ptr = "join_same"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  return
+}
+
+// -----
+
+// An example of conservative behavior where one branch return value comes from
+// outside. Because at the merge we do not find a common origin and since there's
+// currently no way to record multiple origins, we fallback to MayAlias.
+// TODO: Ideally this should be NoAlias
+// CHECK-LABEL: Testing : "test_rb_alloc_outer_vs_inner"
+// CHECK-DAG: outside_neither#0 <-> join_oi#0: MayAlias
+
+func.func @test_rb_alloc_outer_vs_inner() {
+  %cond = arith.constant true
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %a_neither = fir.alloca f32 {uniq_name = "_QFEa_neither"}
+  %d_neither = fir.declare %a_neither {uniq_name = "_QFEa_neither", test.ptr = "outside_neither"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %jf = fir.if %cond -> !fir.ref<f32> {
+    fir.result %da : !fir.ref<f32>
+  } else {
+    %a_else = fir.alloca f32 {uniq_name = "_QFEa_else"}
+    %d_else = fir.declare %a_else {uniq_name = "_QFEa_else"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    fir.result %d_else : !fir.ref<f32>
+  }
+  %join = fir.convert %jf {test.ptr = "join_oi"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  return
+}
+
+// -----
+
+fir.global @rb_merge_g : f32 {
+  %0 = arith.constant 0.0 : f32
+  fir.has_value %0 : f32
+}
+
+// Both branches yield the same global.
+// CHECK-LABEL: Testing : "test_rb_both_same_global"
+// CHECK-DAG: global_decl#0 <-> join_g#0: MustAlias
+
+func.func @test_rb_both_same_global() {
+  %cond = arith.constant true
+  %addr = fir.address_of(@rb_merge_g) : !fir.ref<f32>
+  %dg = fir.declare %addr {uniq_name = "_QErb_merge_g", test.ptr = "global_decl"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %jf = fir.if %cond -> !fir.ref<f32> {
+    fir.result %dg : !fir.ref<f32>
+  } else {
+    fir.result %dg : !fir.ref<f32>
+  }
+  %join = fir.convert %jf {test.ptr = "join_g"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  return
+}
+
+// -----
+
+// Both branches yield the same dummy argument.
+// CHECK-LABEL: Testing : "test_rb_both_same_argument"
+// CHECK-DAG: decl_arg#0 <-> join_arg#0: MustAlias
+
+func.func @test_rb_both_same_argument(%arg0: !fir.ref<f32> {fir.bindc_name = "x"}) {
+  %cond = arith.constant true
+  %ds = fir.dummy_scope : !fir.dscope
+  %dx = fir.declare %arg0 dummy_scope %ds arg 1 {uniq_name = "_QFEex", test.ptr = "decl_arg"} : (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32>
+  %jf = fir.if %cond -> !fir.ref<f32> {
+    fir.result %dx : !fir.ref<f32>
+  } else {
+    fir.result %dx : !fir.ref<f32>
+  }
+  %join = fir.convert %jf {test.ptr = "join_arg"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  return
+}
+
+// -----
+
+fir.global @rb_side_g : f32 {
+  %0 = arith.constant 0.0 : f32
+  fir.has_value %0 : f32
+}
+
+// Merged kinds differ (Allocate vs Global) thus conservative MayAlias
+// (and this would be the case even if we tracked multiple origins).
+// CHECK-LABEL: Testing : "test_rb_merged_unknown_global_vs_alloc"
+// CHECK-DAG: outside_mixed#0 <-> join_mixed#0: MayAlias
+
+func.func @test_rb_merged_unknown_global_vs_alloc() {
+  %cond = arith.constant true
+  %addr = fir.address_of(@rb_side_g) : !fir.ref<f32>
+  %dg = fir.declare %addr {uniq_name = "_QErb_side_g"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %a = fir.alloca f32 {uniq_name = "_QFEa"}
+  %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %a_by = fir.alloca f32 {uniq_name = "_QFEa_by"}
+  %d_by = fir.declare %a_by {uniq_name = "_QFEa_by", test.ptr = "outside_mixed"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %jf = fir.if %cond -> !fir.ref<f32> {
+    fir.result %dg : !fir.ref<f32>
+  } else {
+    fir.result %da : !fir.ref<f32>
+  }
+  %join = fir.convert %jf {test.ptr = "join_mixed"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  return
+}
+
+// -----
+
+// Distinct fir.alloca in each branch with mismatched Fortran attrs on the declares;
+// despite being different allocations inside the branches, we give conservative
+// response. This is primarily to test that the attribute merge is working but
+// this pattern is unlikely to be generated from real Fortran code.
+// CHECK-LABEL: Testing : "test_rb_merged_unknown_attr_mismatch"
+// CHECK-DAG: outside_attr#0 <-> join_attr#0: MayAlias
+
+func.func @test_rb_merged_unknown_attr_mismatch() {
+  %cond = arith.constant true
+  %a_ext = fir.alloca f32 {uniq_name = "_QFEa_attr_ext"}
+  %d_ext = fir.declare %a_ext {uniq_name = "_QFEa_attr_ext", test.ptr = "outside_attr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  %jf = fir.if %cond -> !fir.ref<f32> {
+    %a1 = fir.alloca f32 {uniq_name = "_QFEa1"}
+    %dt = fir.declare %a1 {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFEa1"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    fir.result %dt : !fir.ref<f32>
+  } else {
+    %a2 = fir.alloca f32 {uniq_name = "_QFEa2"}
+    %dp = fir.declare %a2 {uniq_name = "_QFEa2"} : (!fir.ref<f32>) -> !fir.ref<f32>
+    fir.result %dp : !fir.ref<f32>
+  }
+  %join = fir.convert %jf {test.ptr = "join_attr"} : (!fir.ref<f32>) -> !fir.ref<f32>
+  return
+}
+
+// -----
+
+// One branch yields the dummy box, the other yields fir.pack_array of the same
+// box (approximateSource on that predecessor). Join is disambiguated from a
+// different dummy argument's data.
+// CHECK-LABEL: Testing : "test_rb_merge_one_branch_pack_array"
+// CHECK-DAG: join_pack#0 <-> other_arg_box#0: NoAlias
+
+func.func @test_rb_merge_one_branch_pack_array(
+    %arg0: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "x"},
+    %arg1: !fir.box<!fir.array<?xf32>> {fir.bindc_name = "y"}) {
+  %c = arith.constant true
+  %packed = fir.pack_array %arg0 heap whole : (!fir.box<!fir.array<?xf32>>) -> !fir.box<!fir.array<?xf32>>
+  %jf = fir.if %c -> !fir.box<!fir.array<?xf32>> {
+    fir.result %arg0 : !fir.box<!fir.array<?xf32>>
+  } else {
+    fir.result %packed : !fir.box<!fir.array<?xf32>>
+  }
+  %join = fir.convert %jf {test.ptr = "join_pack"} : (!fir.box<!fir.array<?xf32>>) -> !fir.box<!fir.array<?xf32>>
+  %other = fir.convert %arg1 {test.ptr = "other_arg_box"} : (!fir.box<!fir.array<?xf32>>) -> !fir.box<!fir.array<?xf32>>
+  return
+}
+
+// -----
+
+// OPTIONAL pattern tested against an unrelated fir.alloca.
+// CHECK-LABEL: Testing : "test_rb_optional_ref_present_absent"
+// CHECK-DAG: outside_opt#0 <-> opt_join#0: NoAlias
+
+func.func @test_rb_optional_ref_present_absent(%arg0: !fir.ref<i32> {fir.bindc_name = "x", fir.optional}) {
+  %present = fir.is_present %arg0 : (!fir.ref<i32>) -> i1
+  %a_ext = fir.alloca i32 {uniq_name = "_QFEopt_ext"}
+  %d_ext = fir.declare %a_ext {uniq_name = "_QFEopt_ext", test.ptr = "outside_opt"} : (!fir.ref<i32>) -> !fir.ref<i32>
+  %slot = fir.if %present -> !fir.ref<i32> {
+    %a = fir.alloca i32 {uniq_name = "_QFEopt"}
+    %d = fir.declare %a {uniq_name = "_QFEopt"} : (!fir.ref<i32>) -> !fir.ref<i32>
+    fir.result %d : !fir.ref<i32>
+  } else {
+    %abs = fir.absent !fir.ref<i32>
+    fir.result %abs : !fir.ref<i32>
+  }
+  %join = fir.convert %slot {test.ptr = "opt_join"} : (!fir.ref<i32>) -> !fir.ref<i32>
+  return
+}
+
+// -----
+
+// Same OPTIONAL idea as above, but the unrelated fir.alloca outside the if is
+// declared with TARGET: join should still not alias that unrelated storage.
+// CHECK-LABEL: Testing : "test_rb_optional_ref_outside_target"
+// CHECK-DAG: outside_opt_tgt#0 <-> opt_join_tgt#0: NoAlias
+
+func.func @test_rb_optional_ref_outside_target(%arg0: !fir.ref<i32> {fir.bindc_name = "x", fir.optional}) {
+  %present = fir.is_present %arg0 : (!fir.ref<i32>) -> i1
+  %a_ext = fir.alloca i32 {uniq_name = "_QFEopt_ext_t"}
+  %d_ext = fir.declare %a_ext {fortran_attrs = #fir.var_attrs<...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/196132


More information about the flang-commits mailing list