[flang-commits] [flang] 33a7f16 - [flang] Added coarse grained alias analysis for FIR.

Slava Zakharin via flang-commits flang-commits at lists.llvm.org
Wed Jan 11 10:25:39 PST 2023


Author: Slava Zakharin
Date: 2023-01-11T10:25:21-08:00
New Revision: 33a7f162d627f522a5dedc06ce2de4a30b2588bc

URL: https://github.com/llvm/llvm-project/commit/33a7f162d627f522a5dedc06ce2de4a30b2588bc
DIFF: https://github.com/llvm/llvm-project/commit/33a7f162d627f522a5dedc06ce2de4a30b2588bc.diff

LOG: [flang] Added coarse grained alias analysis for FIR.

These are experimental changes in Flang AA to provide
at least some means to disambiguate memory accesses in some
simple cases. This AA is still not used by any transformation,
so the LIT tests are the only way to trigger it currently.
I will further look into applying this AA within Flang
to address some of the known performance issues in the benchmarks.

Credits to @Renaud-K for the initial implementation.

Differential Revision: https://reviews.llvm.org/D141410

Added: 
    flang/test/Analysis/AliasAnalysis/alias-analysis-1.fir
    flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
    flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir

Modified: 
    flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
    flang/lib/Optimizer/Analysis/AliasAnalysis.cpp

Removed: 
    flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir


################################################################################
diff  --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
index a3b20b7bbfecc..8d3fa36cbed5b 100644
--- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
+++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
@@ -1,4 +1,4 @@
-//===- AliasAnalysis.h - Alias Analysis in FIR -----------------*- C++ -*-===//
+//===-- AliasAnalysis.h - Alias Analysis in FIR -----------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,10 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef FIR_ANALYSIS_ALIASANALYSIS_H_
-#define FIR_ANALYSIS_ALIASANALYSIS_H_
+#ifndef FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H
+#define FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H
 
+#include "flang/Common/enum-class.h"
+#include "flang/Common/enum-set.h"
 #include "mlir/Analysis/AliasAnalysis.h"
+#include "mlir/IR/Value.h"
+#include "llvm/ADT/PointerUnion.h"
 
 namespace fir {
 
@@ -17,13 +21,77 @@ namespace fir {
 // AliasAnalysis
 //===----------------------------------------------------------------------===//
 class AliasAnalysis {
+  // Structures to describe the memory source of a value.
+
+  /// Kind of the memory source referenced by a value.
+  ENUM_CLASS(SourceKind,
+             /// Unique memory allocated by an operation, e.g.
+             /// by fir::AllocaOp or fir::AllocMemOp.
+             Allocate,
+             /// A global object allocated statically on the module level.
+             Global,
+             /// Memory allocated outside of a function and passed
+             /// to the function as a by-ref argument.
+             Argument,
+             /// Represents memory allocated by unknown means and
+             /// with the memory address defined by a memory reading
+             /// operation (e.g. fir::LoadOp).
+             Indirect,
+             /// Represents memory allocated by unknown means.
+             Unknown);
+
+  /// Attributes of the memory source object.
+  ENUM_CLASS(Attribute, Target, Pointer, IntentIn);
+
+  struct Source {
+    using SourceUnion = llvm::PointerUnion<mlir::SymbolRefAttr, mlir::Value>;
+    using Attributes = Fortran::common::EnumSet<Attribute, Attribute_enumSize>;
+
+    /// Source definition of a value.
+    SourceUnion u;
+    /// Kind of the memory source.
+    SourceKind kind;
+    /// Value type of the source definition.
+    mlir::Type valueType;
+    /// Attributes of the memory source object, e.g. Target.
+    Attributes attributes;
+
+    /// Print information about the memory source to `os`.
+    void print(llvm::raw_ostream &os) const;
+
+    /// Return true, if Target or Pointer attribute is set.
+    bool isTargetOrPointer() const;
+
+    /// Return true, if the memory source's `valueType` is a reference type
+    /// to an object of derived type that contains a component with POINTER
+    /// attribute.
+    bool isRecordWithPointerComponent() const;
+
+    /// Return true, if `ty` is a reference type to a boxed
+    /// POINTER object or a raw fir::PointerType.
+    static bool isPointerReference(mlir::Type ty);
+  };
+
+  friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
+                                       const AliasAnalysis::Source &op);
+
 public:
   /// Given two values, return their aliasing behavior.
   mlir::AliasResult alias(mlir::Value lhs, mlir::Value rhs);
 
   /// Return the modify-reference behavior of `op` on `location`.
   mlir::ModRefResult getModRef(mlir::Operation *op, mlir::Value location);
+
+  /// Return the memory source of a value.
+  Source getSource(mlir::Value);
 };
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
+                                     const AliasAnalysis::Source &op) {
+  op.print(os);
+  return os;
+}
+
 } // namespace fir
 
-#endif // FIR_ANALYSIS_ALIASANALYSIS_H_
+#endif // FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H

diff  --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
index 85f4743d53c67..6af11d7ecc542 100644
--- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
+++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
@@ -7,7 +7,15 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Optimizer/Analysis/AliasAnalysis.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIROpsSupport.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "mlir/Analysis/AliasAnalysis.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/Value.h"
 #include "mlir/Interfaces/SideEffectInterfaces.h"
+#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/Casting.h"
 
 using namespace mlir;
 
@@ -15,13 +23,119 @@ using namespace mlir;
 // AliasAnalysis: alias
 //===----------------------------------------------------------------------===//
 
+static bool isDummyArgument(mlir::Value v) {
+  auto blockArg{v.dyn_cast<mlir::BlockArgument>()};
+  if (!blockArg)
+    return false;
+
+  return blockArg.getOwner()->isEntryBlock();
+}
+
 namespace fir {
+
+void AliasAnalysis::Source::print(llvm::raw_ostream &os) const {
+  if (auto v = llvm::dyn_cast<mlir::Value>(u))
+    os << v;
+  else if (auto gbl = llvm::dyn_cast<mlir::SymbolRefAttr>(u))
+    os << gbl;
+  os << " SourceKind: " << EnumToString(kind);
+  os << " Type: " << valueType << " ";
+  attributes.Dump(os, EnumToString);
+}
+
+bool AliasAnalysis::Source::isPointerReference(mlir::Type ty) {
+  auto eleTy = fir::dyn_cast_ptrEleTy(ty);
+  if (!eleTy)
+    return false;
+
+  return fir::isPointerType(eleTy) || eleTy.isa<fir::PointerType>();
+}
+
+bool AliasAnalysis::Source::isTargetOrPointer() const {
+  return attributes.test(Attribute::Pointer) ||
+         attributes.test(Attribute::Target);
+}
+
+bool AliasAnalysis::Source::isRecordWithPointerComponent() const {
+  auto eleTy = fir::dyn_cast_ptrEleTy(valueType);
+  if (!eleTy)
+    return false;
+  // TO DO: Look for pointer components
+  return eleTy.isa<fir::RecordType>();
+}
+
 AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
-  // This is for now a mock analysis
-  if (lhs == rhs) {
-    return AliasResult::MustAlias;
+  auto lhsSrc = getSource(lhs);
+  auto rhsSrc = getSource(rhs);
+
+  // Indirect case currently not handled. Conservatively assume
+  // it aliases with everything
+  if (lhsSrc.kind == SourceKind::Indirect ||
+      lhsSrc.kind == SourceKind::Unknown ||
+      rhsSrc.kind == SourceKind::Indirect || rhsSrc.kind == SourceKind::Unknown)
+    return AliasResult::MayAlias;
+
+  if (lhsSrc.kind == rhsSrc.kind) {
+    if (lhsSrc.u == rhsSrc.u)
+      return AliasResult::MustAlias;
+
+    // Allocate and global memory address cannot physically alias
+    if (lhsSrc.kind == SourceKind::Allocate ||
+        lhsSrc.kind == SourceKind::Global)
+      return AliasResult::NoAlias;
+
+    assert(lhsSrc.kind == SourceKind::Argument &&
+           "unexpected memory source kind");
+
+    // Dummy TARGET/POINTER arguments may alias.
+    if (lhsSrc.isTargetOrPointer() && rhsSrc.isTargetOrPointer())
+      return AliasResult::MayAlias;
+
+    // Box for POINTER component inside an object of a derived type
+    // may alias box of a POINTER object, as well as boxes for POINTER
+    // components inside two objects of derived types may alias.
+    if ((lhsSrc.isRecordWithPointerComponent() && rhsSrc.isTargetOrPointer()) ||
+        (rhsSrc.isRecordWithPointerComponent() && lhsSrc.isTargetOrPointer()) ||
+        (lhsSrc.isRecordWithPointerComponent() &&
+         rhsSrc.isRecordWithPointerComponent()))
+      return AliasResult::MayAlias;
+
+    return AliasResult::NoAlias;
   }
-  return AliasResult::MayAlias;
+
+  assert(lhsSrc.kind != rhsSrc.kind && "memory source kinds must be the same");
+
+  Source *src1, *src2;
+  if (lhsSrc.kind < rhsSrc.kind) {
+    src1 = &lhsSrc;
+    src2 = &rhsSrc;
+  } else {
+    src1 = &rhsSrc;
+    src2 = &lhsSrc;
+  }
+
+  assert(src2->kind <= SourceKind::Argument && "unexpected memory source kind");
+  if (src1->kind == SourceKind::Allocate)
+    return AliasResult::NoAlias;
+
+  assert(src1->kind == SourceKind::Global &&
+         src2->kind == SourceKind::Argument &&
+         "unexpected memory source kinds");
+
+  // Dummy TARGET/POINTER argument may alias with a global TARGET/POINTER.
+  if (src1->isTargetOrPointer() && src2->isTargetOrPointer())
+    return AliasResult::MayAlias;
+
+  // Box for POINTER component inside an object of a derived type
+  // may alias box of a POINTER object, as well as boxes for POINTER
+  // components inside two objects of derived types may alias.
+  if ((src1->isRecordWithPointerComponent() && src2->isTargetOrPointer()) ||
+      (src2->isRecordWithPointerComponent() && src1->isTargetOrPointer()) ||
+      (src1->isRecordWithPointerComponent() &&
+       src2->isRecordWithPointerComponent()))
+    return AliasResult::MayAlias;
+
+  return AliasResult::NoAlias;
 }
 
 //===----------------------------------------------------------------------===//
@@ -30,7 +144,8 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
 
 /// This is mostly inspired by MLIR::LocalAliasAnalysis with 2 notable
 /// 
diff erences 1) Regions are not handled here but will be handled by a data
-/// flow analysis to come 2) Allocate and Free effects are considered modifying
+/// flow analysis to come 2) Allocate and Free effects are considered
+/// modifying
 ModRefResult AliasAnalysis::getModRef(Operation *op, Value location) {
   MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
   if (!interface)
@@ -54,14 +169,77 @@ ModRefResult AliasAnalysis::getModRef(Operation *op, Value location) {
       continue;
 
     // Merge in the corresponding mod or ref for this effect.
-    if (isa<MemoryEffects::Read>(effect.getEffect())) {
+    if (isa<MemoryEffects::Read>(effect.getEffect()))
       result = result.merge(ModRefResult::getRef());
-    } else {
+    else
       result = result.merge(ModRefResult::getMod());
-    }
+
     if (result.isModAndRef())
       break;
   }
   return result;
 }
+
+AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
+  auto *defOp = v.getDefiningOp();
+  SourceKind type{SourceKind::Unknown};
+  mlir::Type ty;
+  bool breakFromLoop{false};
+  mlir::SymbolRefAttr global;
+  Source::Attributes attributes;
+  while (defOp && !breakFromLoop) {
+    ty = defOp->getResultTypes()[0];
+    llvm::TypeSwitch<Operation *>(defOp)
+        .Case<fir::AllocaOp, fir::AllocMemOp>([&](auto op) {
+          // Unique memory allocation.
+          type = SourceKind::Allocate;
+          breakFromLoop = true;
+        })
+        .Case<fir::ConvertOp>([&](auto op) {
+          // Skip ConvertOp's and track further through the operand.
+          v = op->getOperand(0);
+          defOp = v.getDefiningOp();
+        })
+        .Case<fir::LoadOp>([&](auto op) {
+          // No further tracking for addresses loaded from memory (e.g. a box)
+          // right now.
+          type = SourceKind::Indirect;
+          breakFromLoop = true;
+        })
+        .Case<fir::AddrOfOp>([&](auto op) {
+          // Address of a global scope object.
+          type = SourceKind::Global;
+          ty = v.getType();
+          if (fir::valueHasFirAttribute(v,
+                                        fir::GlobalOp::getTargetAttrNameStr()))
+            attributes.set(Attribute::Target);
+
+          if (Source::isPointerReference(ty))
+            attributes.set(Attribute::Pointer);
+          global = llvm::cast<fir::AddrOfOp>(op).getSymbol();
+          breakFromLoop = true;
+        })
+        .Default([&](auto op) {
+          defOp = nullptr;
+          breakFromLoop = true;
+        });
+  }
+  if (!defOp && type == SourceKind::Unknown)
+    // Check if the memory source is coming through a dummy argument.
+    if (isDummyArgument(v)) {
+      type = SourceKind::Argument;
+      ty = v.getType();
+      if (fir::valueHasFirAttribute(v, fir::getTargetAttrName()))
+        attributes.set(Attribute::Target);
+
+      if (Source::isPointerReference(ty))
+        attributes.set(Attribute::Pointer);
+    }
+
+  if (type == SourceKind::Global)
+    return {global, type, ty, attributes};
+
+  return {v, type, ty, attributes};
+}
+
 } // namespace fir

diff  --git a/flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-1.fir
similarity index 72%
rename from flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir
rename to flang/test/Analysis/AliasAnalysis/alias-analysis-1.fir
index 4ed492ebae2e4..4ccde65741f95 100644
--- a/flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-1.fir
@@ -1,7 +1,9 @@
-// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file 2>&1 | FileCheck %s
+// Use --mlir-disable-threading so that the AA queries are serialized
+// as well as its diagnostic output.
+// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s
 
 // CHECK-LABEL: Testing : "_QPtest"
-// CHECK-DAG: alloca_1#0 <-> address_of#0: MayAlias
+// CHECK-DAG: alloca_1#0 <-> address_of#0: NoAlias
 func.func @_QPtest(%arg1: !fir.ref<i32>) {
   %c1_i32 = arith.constant 1 : i32
   %0 = fir.alloca () -> () {test.ptr = "alloca_1"}
@@ -14,8 +16,5 @@ func.func @_QPtest(%arg1: !fir.ref<i32>) {
   return
 }
 
-// -----
 func.func private @_QPs(%arg0: () -> ()) 
-
-// -----
 func.func private @_QPf() -> i32

diff  --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
new file mode 100644
index 0000000000000..6c43e8d7cde9e
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir
@@ -0,0 +1,173 @@
+// Use --mlir-disable-threading so that the AA queries are serialized
+// as well as its diagnostic output.
+// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s
+
+// CHECK-LABEL: Testing : "_QFPtest"
+
+// p1.addr and p2.addr result from 2 
diff erent allocas
+// They cannot physically alias
+// CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias
+
+// p1.addr and p2.addr could both be wrapped inside boxes
+// CHECK-DAG: p1.addr#0 <-> boxp1.addr#0: MayAlias
+// CHECK-DAG: p2.addr#0 <-> boxp1.addr#0: MayAlias
+// CHECK-DAG: p1.addr#0 <-> arg2.addr#0: MayAlias
+// CHECK-DAG: p2.addr#0 <-> arg2.addr#0: MayAlias
+
+// p1.addr and p2.addr are the result of an allocation
+// They cannot physically alias with an argument
+// CHECK-DAG: p1.addr#0 <-> func.region0#0: NoAlias
+// CHECK-DAG: p2.addr#0 <-> func.region0#0: NoAlias
+// CHECK-DAG: p1.addr#0 <-> func.region0#1: NoAlias
+// CHECK-DAG: p2.addr#0 <-> func.region0#1: NoAlias
+// CHECK-DAG: p1.addr#0 <-> func.region0#2: NoAlias
+// CHECK-DAG: p2.addr#0 <-> func.region0#2: NoAlias
+
+// All arguments are either pointers or targets
+// A pointer in a box may alias with both
+// CHECK-DAG: boxp1.addr#0 <-> func.region0#0: MayAlias
+// CHECK-DAG: boxp1.addr#0 <-> func.region0#1: MayAlias
+// CHECK-DAG: boxp1.addr#0 <-> func.region0#2: MayAlias
+
+// A target dummy may alias with another target
+// CHECK-DAG: func.region0#0 <-> func.region0#1: MayAlias
+
+// arg2 is a reference to a pointer. Modifying arg2 could
+// modify a target with a pointer component
+// CHECK-DAG: func.region0#0 <-> func.region0#2: MayAlias
+// CHECK-DAG: func.region0#1 <-> func.region0#2: MayAlias
+
+// However, the address wrapped by arg2, can alias with any target or
+// pointer arguments
+// CHECK-DAG: arg2.addr#0 <-> func.region0#0: MayAlias
+// CHECK-DAG: arg2.addr#0 <-> func.region0#1: MayAlias
+// CHECK-DAG: arg2.addr#0 <-> func.region0#2: MayAlias
+// CHECK-DAG: boxp1.addr#0 <-> arg2.addr#0: MayAlias
+
+func.func @_QFPtest(%arg0: !fir.ref<f32> {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref<f32> {fir.bindc_name = "v2", fir.target}, %arg2: !fir.ref<!fir.box<!fir.ptr<f32>>> ) attributes {test.ptr = "func"} {
+
+  %1 = fir.alloca !fir.ptr<f32> {test.ptr = "p1.addr"}
+  %2 = fir.zero_bits !fir.ptr<f32>
+  fir.store %2 to %1 : !fir.ref<!fir.ptr<f32>>
+
+  %4 = fir.alloca !fir.ptr<f32> {test.ptr = "p2.addr"}
+  fir.store %2 to %4 : !fir.ref<!fir.ptr<f32>>
+
+  %5 = fir.convert %arg0 : (!fir.ref<f32>) -> !fir.ptr<f32>
+  fir.store %5 to %1 : !fir.ref<!fir.ptr<f32>>
+
+  %6 = fir.convert %arg1 : (!fir.ref<f32>) -> !fir.ptr<f32>
+  fir.store %6 to %4 : !fir.ref<!fir.ptr<f32>>
+
+  %0 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p1", uniq_name = "_QFtestEp1"}
+  %7 = fir.load %1 : !fir.ref<!fir.ptr<f32>>
+  %8 = fir.embox %7 : (!fir.ptr<f32>) -> !fir.box<!fir.ptr<f32>>
+  fir.store %8 to %0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+
+  %3 = fir.alloca !fir.box<!fir.ptr<f32>> {bindc_name = "p2", uniq_name = "_QFtestEp2"}
+  %9 = fir.load %4 : !fir.ref<!fir.ptr<f32>>
+  %10 = fir.embox %9 : (!fir.ptr<f32>) -> !fir.box<!fir.ptr<f32>>
+  fir.store %10 to %3 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+
+  %11 = fir.load %0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %12 = fir.box_addr %11 {test.ptr = "boxp1.addr"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  fir.store %12 to %1 : !fir.ref<!fir.ptr<f32>>
+
+  %13 = fir.load %3 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %14 = fir.box_addr %13 : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  fir.store %14 to %4 : !fir.ref<!fir.ptr<f32>>
+
+  %15 = fir.load %arg2 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %16 = fir.box_addr %15 {test.ptr = "arg2.addr"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QFPtest2"
+
+// subroutine test2(v1,p1,p2)
+//   real, target :: v1
+//   real, pointer :: p1, p2
+//   ...
+// end subroutine
+
+// Direct access to dummy POINTER references can modify other dummy POINTER references
+// CHECK-DAG: func.region0#1 <-> func.region0#2: MayAlias
+
+// They can also modify targets that have pointer components
+// CHECK-DAG: func.region0#0 <-> func.region0#1: MayAlias
+// CHECK-DAG: func.region0#0 <-> func.region0#2: MayAlias
+
+func.func @_QFPtest2(%arg0: !fir.ref<f32> {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref<!fir.box<!fir.ptr<f32>>>, %arg2: !fir.ref<!fir.ptr<f32>> ) attributes {test.ptr = "func"} {
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "_QFPtest3"
+
+// module pointers
+//   real, pointer :: p
+// end module
+//
+// program main
+//   use pointers
+//   real, target :: var1 = 1, var2 =2
+//   p => var1
+//
+//   call test3(p)
+//
+// contains
+//   subroutine test3(p1)
+//     real, pointer :: p1
+//     p1 => var2
+//     print *, p
+//   end subroutine
+// end
+
+// The global pointer p may alias with the dummy argument p1
+// but not with the dummy arg1 which is just a regular dummy
+// CHECK-DAG: p#0 <-> func.region0#0: MayAlias
+// CHECK-DAG: p#0 <-> func.region0#1: NoAlias
+
+// FIXME: p and p1 are pointers, they cannot alias with a wrapped address.
+//        Only the addresses they wrap could alias with the address wrapped by the box
+// CHECK-DAG: p#0 <-> box.addr#0: MayAlias
+// CHECK-DAG: box.addr#0 <-> func.region0#0: MayAlias
+
+// var2, although it is a target, cannot alias with p
+// A modification of p would only make them point to a new target but not modify it
+// CHECK-DAG: var2#0 <-> p#0: NoAlias
+// It can alias with p1, if p1 is a pointer component
+// CHECK-DAG: var2#0 <-> func.region0#0: MayAlias
+// It can alias with a box.addr
+// CHECK-DAG: var2#0 <-> box.addr#0: MayAlias
+
+// A global may not alias with a dummy
+// CHECK-DAG: var2#0 <-> func.region0#1: NoAlias
+
+// FIXME: a pointer may only alias with a target but arg1 is a regular dummy
+// CHECK-DAG: box.addr#0 <-> func.region0#1: MayAlias
+
+// Dummy argument do not alias
+// CHECK-DAG: func.region0#0 <-> func.region0#1: NoAlias
+
+fir.global @_QMpointersEp : !fir.box<!fir.ptr<f32>> {
+  %0 = fir.zero_bits !fir.ptr<f32>
+  %1 = fir.embox %0 : (!fir.ptr<f32>) -> !fir.box<!fir.ptr<f32>>
+  fir.has_value %1 : !fir.box<!fir.ptr<f32>>
+}
+
+fir.global internal @_QFEvar2 target : f32 {
+  %cst = arith.constant 2.000000e+00 : f32
+  fir.has_value %cst : f32
+}
+
+func.func @_QFPtest3(%arg0: !fir.ref<!fir.box<!fir.ptr<f32>>> {fir.bindc_name = "p1"}, %arg1: !fir.ref<f32>) attributes {test.ptr = "func"} {
+  %4 = fir.address_of(@_QFEvar2) {test.ptr = "var2"} : !fir.ref<f32>
+  %5 = fir.address_of(@_QMpointersEp) {test.ptr = "p"} : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %6 = fir.embox %4 : (!fir.ref<f32>) -> !fir.box<!fir.ptr<f32>>
+  %13 = fir.box_addr %6 {test.ptr = "box.addr"} : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  return
+}

diff  --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir
new file mode 100644
index 0000000000000..d7bd970b51907
--- /dev/null
+++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir
@@ -0,0 +1,157 @@
+// Use --mlir-disable-threading so that the AA queries are serialized
+// as well as its diagnostic output.
+// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s
+
+// module m
+//   type t
+//      real, pointer :: pointer_component
+//   end type t
+//   type(t) :: a
+// contains
+//   subroutine test(pointer_dummy, x)
+//     real, pointer :: pointer_dummy
+//     real, target :: x
+//     pointer_dummy => x
+//     call test2(a%pointer_component)
+//   end subroutine test
+// end module m
+
+// A composite with a pointer component may alias with a dummy pointer
+// CHECK-LABEL: Testing : "_QMmPtest
+// CHECK: a#0 <-> func.region0#0: MayAlias
+
+// FIXME: a's box cannot alias with raw reference to f32 (x), so MayAlias below must be NoAlias:
+// CHECK: a#0 <-> func.region0#1: MayAlias
+
+// FIXME: pointer_dummy's box cannot alias with raw reference to f32 (x), so MayAlias below must be NoAlias:
+// CHECK: func.region0#0 <-> func.region0#1: MayAlias
+
+fir.global @_QMmEa : !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}> {
+  %0 = fir.undefined !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
+  fir.has_value %0 : !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
+}
+func.func @_QMmPtest(%arg0: !fir.ref<!fir.box<!fir.ptr<f32>>> {fir.bindc_name = "pointer_dummy"}, %arg1: !fir.ref<f32> {fir.bindc_name = "x", fir.target}) attributes {test.ptr = "func"} {
+  %0 = fir.address_of(@_QMmEa) {test.ptr = "a"} : !fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>>
+  %1 = fir.embox %arg1 : (!fir.ref<f32>) -> !fir.box<!fir.ptr<f32>>
+  fir.store %1 to %arg0 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %2 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
+  %3 = fir.coordinate_of %0, %2 : (!fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %4 = fir.load %3 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %5 = fir.box_addr %4 : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  %6 = fir.convert %5 : (!fir.ptr<f32>) -> !fir.ref<f32>
+  fir.call @_QPtest2(%6) fastmath<contract> : (!fir.ref<f32>) -> ()
+  return
+}
+func.func private @_QPtest2(!fir.ref<f32>)
+
+// -----
+
+// A composite with a pointer component may alias with a dummy
+// argument of composite type with a pointer component:
+// module m
+//   type t
+//      real, pointer :: pointer_component
+//   end type t
+//   type(t) :: a
+// contains
+//   subroutine test(b, x)
+//     type(t) :: b
+//     real, target :: x
+//     a%pointer_component => x
+//     call test2(b%pointer_component)
+//   end subroutine test
+// end module m
+
+// CHECK-LABEL: Testing : "_QMmPtest"
+// CHECK: a#0 <-> func.region0#0: MayAlias
+
+fir.global @_QMmEa : !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}> {
+  %0 = fir.undefined !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
+  fir.has_value %0 : !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
+}
+func.func @_QMmPtest(%arg0: !fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>> {fir.bindc_name = "b"}, %arg1: !fir.ref<f32> {fir.bindc_name = "x", fir.target}) attributes {test.ptr = "func"} {
+  %0 = fir.address_of(@_QMmEa) {test.ptr = "a"} : !fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>>
+  %1 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
+  %2 = fir.coordinate_of %0, %1 : (!fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %3 = fir.embox %arg1 : (!fir.ref<f32>) -> !fir.box<!fir.ptr<f32>>
+  fir.store %3 to %2 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %4 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
+  %5 = fir.coordinate_of %arg0, %4 : (!fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %6 = fir.load %5 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %7 = fir.box_addr %6 : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  %8 = fir.convert %7 : (!fir.ptr<f32>) -> !fir.ref<f32>
+  fir.call @_QPtest2(%8) fastmath<contract> : (!fir.ref<f32>) -> ()
+  return
+}
+func.func private @_QPtest2(!fir.ref<f32>)
+
+// -----
+
+// Two dummy arguments of composite type with a pointer component
+// may alias each other:
+// module m
+//   type t
+//      real, pointer :: pointer_component
+//   end type t
+// contains
+//   subroutine test(a, b, x)
+//     type(t) :: a, b
+//     real, target :: x
+//     a%pointer_component => x
+//     call test2(b%pointer_component)
+//   end subroutine test
+// end module m
+
+// CHECK-LABEL: Testing : "_QMmPtest"
+// CHECK: func.region0#0 <-> func.region0#1: MayAlias
+
+func.func @_QMmPtest(%arg0: !fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>> {fir.bindc_name = "a"}, %arg1: !fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>> {fir.bindc_name = "b"}, %arg2: !fir.ref<f32> {fir.bindc_name = "x", fir.target}) attributes {test.ptr = "func"} {
+  %0 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
+  %1 = fir.coordinate_of %arg0, %0 : (!fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %2 = fir.embox %arg2 : (!fir.ref<f32>) -> !fir.box<!fir.ptr<f32>>
+  fir.store %2 to %1 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %3 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
+  %4 = fir.coordinate_of %arg1, %3 : (!fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %5 = fir.load %4 : !fir.ref<!fir.box<!fir.ptr<f32>>>
+  %6 = fir.box_addr %5 : (!fir.box<!fir.ptr<f32>>) -> !fir.ptr<f32>
+  %7 = fir.convert %6 : (!fir.ptr<f32>) -> !fir.ref<f32>
+  fir.call @_QPtest2(%7) fastmath<contract> : (!fir.ref<f32>) -> ()
+  return
+}
+func.func private @_QPtest2(!fir.ref<f32>)
+
+// -----
+
+// Two dummy arguments of composite type consisting of an allocatable
+// component cannot alias:
+// module m
+//   type t
+//      real, allocatable :: allocatable_component
+//   end type t
+// contains
+//   subroutine test(a, b)
+//     type(t) :: a, b
+//     allocate(a%allocatable_component)
+//     call test2(b%allocatable_component)
+//   end subroutine test
+// end module m
+
+// CHECK-LABEL: Testing : "_QMmPtest"
+// FIXME: MayAlias must be NoAlias
+// CHECK: func.region0#0 <-> func.region0#1: MayAlias
+
+func.func @_QMmPtest(%arg0: !fir.ref<!fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>> {fir.bindc_name = "a"}, %arg1: !fir.ref<!fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>> {fir.bindc_name = "b"}) attributes {test.ptr = "func"} {
+  %0 = fir.field_index allocatable_component, !fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>
+  %1 = fir.coordinate_of %arg0, %0 : (!fir.ref<!fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+  %2 = fir.allocmem f32 {uniq_name = "_QMmEallocatable_component.alloc"}
+  %3 = fir.embox %2 : (!fir.heap<f32>) -> !fir.box<!fir.heap<f32>>
+  fir.store %3 to %1 : !fir.ref<!fir.box<!fir.heap<f32>>>
+  %4 = fir.field_index allocatable_component, !fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>
+  %5 = fir.coordinate_of %arg1, %4 : (!fir.ref<!fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>>, !fir.field) -> !fir.ref<!fir.box<!fir.heap<f32>>>
+  %6 = fir.load %5 : !fir.ref<!fir.box<!fir.heap<f32>>>
+  %7 = fir.box_addr %6 : (!fir.box<!fir.heap<f32>>) -> !fir.heap<f32>
+  %8 = fir.convert %7 : (!fir.heap<f32>) -> !fir.ref<f32>
+  fir.call @_QPtest2(%8) fastmath<contract> : (!fir.ref<f32>) -> ()
+  return
+}
+func.func private @_QPtest2(!fir.ref<f32>)


        


More information about the flang-commits mailing list