[Mlir-commits] [mlir] [mlir][analysis] Add interprocedural analysis to LocalAliasAnalysis (PR #177994)

lonely eagle llvmlistbot at llvm.org
Tue Feb 3 07:24:24 PST 2026


https://github.com/linuxlonelyeagle updated https://github.com/llvm/llvm-project/pull/177994

>From 5495b5d2f8eeb4260dc5cf2e9d8a24e8208a3a9b Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Tue, 3 Feb 2026 10:21:36 +0000
Subject: [PATCH 1/2] use RegionCallSiteMap.

---
 .../AliasAnalysis/LocalAliasAnalysis.cpp      | 110 +++++++++++++-----
 1 file changed, 81 insertions(+), 29 deletions(-)

diff --git a/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp b/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
index 5a4679ef31422..3830c235a2ceb 100644
--- a/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
+++ b/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
@@ -31,6 +31,9 @@ using namespace mlir;
 
 #define DEBUG_TYPE "local-alias-analysis"
 
+using RegionCallSiteMap =
+    DenseMap<Region *, DenseMap<CallOpInterface, SmallVector<Value>>>;
+
 //===----------------------------------------------------------------------===//
 // Underlying Address Computation
 //===----------------------------------------------------------------------===//
@@ -39,10 +42,22 @@ using namespace mlir;
 /// value.
 static constexpr unsigned maxUnderlyingValueSearchDepth = 10;
 
+static Region *getScope(Value value) {
+  if (BlockArgument argument = dyn_cast<BlockArgument>(value)) {
+    return argument.getParentRegion()
+        ->getParentOfType<FunctionOpInterface>()
+        .getCallableRegion();
+  }
+  return value.getDefiningOp()
+      ->getParentOfType<FunctionOpInterface>()
+      .getCallableRegion();
+}
+
 /// Given a value, collect all of the underlying values being addressed.
 static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
                                            DenseSet<Value> &visited,
-                                           SmallVectorImpl<Value> &output);
+                                           CallOpInterface callSite,
+                                           RegionCallSiteMap &output);
 
 /// Given a RegionBranchOpInterface operation  (`branch`), a Value`inputValue`
 /// which is an input for the provided successor (`initialSuccessor`), try to
@@ -50,7 +65,8 @@ static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
 static void collectUnderlyingAddressValues2(
     RegionBranchOpInterface branch, RegionSuccessor initialSuccessor,
     Value inputValue, unsigned inputIndex, unsigned maxDepth,
-    DenseSet<Value> &visited, SmallVectorImpl<Value> &output) {
+    DenseSet<Value> &visited, CallOpInterface callSite,
+    RegionCallSiteMap &output) {
   LDBG() << "collectUnderlyingAddressValues2: "
          << OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
   LDBG() << " with initialSuccessor " << initialSuccessor;
@@ -60,7 +76,7 @@ static void collectUnderlyingAddressValues2(
   ValueRange inputs = branch.getSuccessorInputs(initialSuccessor);
   if (inputs.empty()) {
     LDBG() << "  input is empty, enqueue value";
-    output.push_back(inputValue);
+    output[getScope(inputValue)][callSite].push_back(inputValue);
     return;
   }
   unsigned firstInputIndex, lastInputIndex;
@@ -75,7 +91,7 @@ static void collectUnderlyingAddressValues2(
     LDBG() << "  !! Input index " << inputIndex << " out of range "
            << firstInputIndex << " to " << lastInputIndex
            << ", adding input value to output";
-    output.push_back(inputValue);
+    output[getScope(inputValue)][callSite].push_back(inputValue);
     return;
   }
   SmallVector<Value> predecessorValues;
@@ -84,14 +100,16 @@ static void collectUnderlyingAddressValues2(
   LDBG() << "  Found " << predecessorValues.size() << " predecessor values";
   for (Value predecessorValue : predecessorValues) {
     LDBG() << "    Processing predecessor value: " << predecessorValue;
-    collectUnderlyingAddressValues(predecessorValue, maxDepth, visited, output);
+    collectUnderlyingAddressValues(predecessorValue, maxDepth, visited,
+                                   callSite, output);
   }
 }
 
 /// Given a result, collect all of the underlying values being addressed.
 static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth,
                                            DenseSet<Value> &visited,
-                                           SmallVectorImpl<Value> &output) {
+                                           CallOpInterface callSite,
+                                           RegionCallSiteMap &output) {
   LDBG() << "collectUnderlyingAddressValues (OpResult): " << result;
   LDBG() << "  maxDepth: " << maxDepth;
 
@@ -102,7 +120,7 @@ static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth,
     if (result == view.getViewDest()) {
       LDBG() << "  Unwrapping view to source: " << view.getViewSource();
       return collectUnderlyingAddressValues(view.getViewSource(), maxDepth,
-                                            visited, output);
+                                            visited, callSite, output);
     }
   }
   // Check to see if we can reason about the control flow of this op.
@@ -110,18 +128,19 @@ static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth,
     LDBG() << "  Processing region branch operation";
     return collectUnderlyingAddressValues2(branch, RegionSuccessor::parent(),
                                            result, result.getResultNumber(),
-                                           maxDepth, visited, output);
+                                           maxDepth, visited, callSite, output);
   }
 
   LDBG() << "  Adding result to output: " << result;
-  output.push_back(result);
+  output[getScope(result)][callSite].push_back(result);
 }
 
 /// Given a block argument, collect all of the underlying values being
 /// addressed.
 static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
                                            DenseSet<Value> &visited,
-                                           SmallVectorImpl<Value> &output) {
+                                           CallOpInterface callSite,
+                                           RegionCallSiteMap &output) {
   LDBG() << "collectUnderlyingAddressValues (BlockArgument): " << arg;
   LDBG() << "  maxDepth: " << maxDepth;
   LDBG() << "  argNumber: " << arg.getArgNumber();
@@ -140,7 +159,7 @@ static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
       if (!branch) {
         LDBG() << "    Cannot analyze control flow, adding argument to output";
         // We can't analyze the control flow, so bail out early.
-        output.push_back(arg);
+        output[getScope(arg)][callSite].push_back(arg);
         return;
       }
 
@@ -150,11 +169,12 @@ static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
       if (!operand) {
         LDBG() << "    No operand found for argument, adding to output";
         // We can't analyze the control flow, so bail out early.
-        output.push_back(arg);
+        output[getScope(arg)][callSite].push_back(arg);
         return;
       }
       LDBG() << "    Processing operand from predecessor: " << operand;
-      collectUnderlyingAddressValues(operand, maxDepth, visited, output);
+      collectUnderlyingAddressValues(operand, maxDepth, visited, callSite,
+                                     output);
     }
     return;
   }
@@ -173,25 +193,27 @@ static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
     for (RegionSuccessor &successor : successors) {
       if (successor.getSuccessor() == region) {
         LDBG() << "  Found matching region successor: " << successor;
-        return collectUnderlyingAddressValues2(
-            branch, successor, arg, argNumber, maxDepth, visited, output);
+        return collectUnderlyingAddressValues2(branch, successor, arg,
+                                               argNumber, maxDepth, visited,
+                                               callSite, output);
       }
     }
     LDBG() << "  No matching region successor found, adding argument to output";
-    output.push_back(arg);
+    output[getScope(arg)][callSite].push_back(arg);
     return;
   }
 
   LDBG()
       << "  Cannot reason about underlying address, adding argument to output";
   // We can't reason about the underlying address of this argument.
-  output.push_back(arg);
+  output[getScope(arg)][callSite].push_back(arg);
 }
 
 /// Given a value, collect all of the underlying values being addressed.
 static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
                                            DenseSet<Value> &visited,
-                                           SmallVectorImpl<Value> &output) {
+                                           CallOpInterface callSite,
+                                           RegionCallSiteMap &output) {
   LDBG() << "collectUnderlyingAddressValues: " << value;
   LDBG() << "  maxDepth: " << maxDepth;
 
@@ -202,27 +224,28 @@ static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
   }
   if (maxDepth == 0) {
     LDBG() << "  Max depth reached, adding value to output";
-    output.push_back(value);
+    output[getScope(value)][callSite].push_back(value);
     return;
   }
   --maxDepth;
 
   if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
     LDBG() << "  Processing as BlockArgument";
-    return collectUnderlyingAddressValues(arg, maxDepth, visited, output);
+    return collectUnderlyingAddressValues(arg, maxDepth, visited, callSite,
+                                          output);
   }
   LDBG() << "  Processing as OpResult";
   collectUnderlyingAddressValues(cast<OpResult>(value), maxDepth, visited,
-                                 output);
+                                 callSite, output);
 }
 
 /// Given a value, collect all of the underlying values being addressed.
 static void collectUnderlyingAddressValues(Value value,
-                                           SmallVectorImpl<Value> &output) {
+                                           RegionCallSiteMap &output) {
   LDBG() << "collectUnderlyingAddressValues: " << value;
   DenseSet<Value> visited;
   collectUnderlyingAddressValues(value, maxUnderlyingValueSearchDepth, visited,
-                                 output);
+                                 CallOpInterface(), output);
   LDBG() << "  Collected " << output.size() << " underlying values";
 }
 
@@ -438,7 +461,7 @@ AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) {
   }
 
   // Get the underlying values being addressed.
-  SmallVector<Value, 8> lhsValues, rhsValues;
+  RegionCallSiteMap lhsValues, rhsValues;
   collectUnderlyingAddressValues(lhs, lhsValues);
   collectUnderlyingAddressValues(rhs, rhsValues);
 
@@ -454,6 +477,7 @@ AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) {
 
   // Check the alias results against each of the underlying values.
   std::optional<AliasResult> result;
+  /*
   for (Value lhsVal : lhsValues) {
     for (Value rhsVal : rhsValues) {
       LDBG() << "  Checking underlying values: " << lhsVal << " vs " << rhsVal;
@@ -464,8 +488,36 @@ AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) {
                                                       : "MayAlias");
       result = result ? result->merge(nextResult) : nextResult;
     }
+  }*/
+  for (Region *lhsRegion : lhsValues.keys()) {
+    for (auto &&lhsRegionMap : lhsValues[lhsRegion]) {
+      for (Region *rhsRegion : rhsValues.keys()) {
+        if (lhsRegion != rhsRegion)
+          continue;
+        for (auto &&rhsRegionMap : rhsValues[rhsRegion]) {
+          bool callSityNull = !lhsRegionMap.first || !rhsRegionMap.first;
+          bool callSityEqual = lhsRegionMap.first == rhsRegionMap.first;
+          if (!callSityNull && !callSityEqual)
+            continue;
+          for (Value lhsVal : lhsRegionMap.second) {
+            for (Value rhsVal : rhsRegionMap.second) {
+              LDBG() << "  Checking underlying values: " << lhsVal << " vs "
+                     << rhsVal;
+              AliasResult nextResult = aliasImpl(lhsVal, rhsVal);
+              LDBG() << "  Result: "
+                     << (nextResult == AliasResult::MustAlias ? "MustAlias"
+                         : nextResult == AliasResult::NoAlias ? "NoAlias"
+                                                              : "MayAlias");
+              result = result ? result->merge(nextResult) : nextResult;
+            }
+          }
+        }
+      }
+    }
+  }
+  if (!result.has_value()) {
+    return AliasResult::MustAlias;
   }
-
   // We should always have a valid result here.
   LDBG() << "  Final result: "
          << (result->isMust() ? "MustAlias"
@@ -486,10 +538,10 @@ ModRefResult LocalAliasAnalysis::getModRef(Operation *op, Value location) {
   if (op->hasTrait<OpTrait::HasRecursiveMemoryEffects>()) {
     LDBG() << "  Operation has recursive memory effects, returning ModAndRef";
     // TODO: To check recursive operations we need to check all of the nested
-    // operations, which can result in a quadratic number of queries. We should
-    // introduce some caching of some kind to help alleviate this, especially as
-    // this caching could be used in other areas of the codebase (e.g. when
-    // checking `wouldOpBeTriviallyDead`).
+    // operations, which can result in a quadratic number of queries. We
+    // should introduce some caching of some kind to help alleviate this,
+    // especially as this caching could be used in other areas of the codebase
+    // (e.g. when checking `wouldOpBeTriviallyDead`).
     return ModRefResult::getModAndRef();
   }
 

>From 138269522ad33bb6a2e5c95cec565d452966f53b Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Tue, 3 Feb 2026 15:22:33 +0000
Subject: [PATCH 2/2] add compare logic.

---
 .../AliasAnalysis/LocalAliasAnalysis.cpp      | 44 +++++++-----
 mlir/test/Analysis/test-alias-analysis.mlir   | 72 +++++++++++++++++++
 2 files changed, 98 insertions(+), 18 deletions(-)

diff --git a/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp b/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
index 3830c235a2ceb..ba9a95e30910e 100644
--- a/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
+++ b/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
@@ -31,6 +31,10 @@ using namespace mlir;
 
 #define DEBUG_TYPE "local-alias-analysis"
 
+/// The `RegionCallSiteMap` uses both the Region and the
+/// CallSite(CallOpInterface) to locate values along a Value's definition and
+/// use chain. The Region represents the scope, and the indexed Values are the
+/// operands passed into the CallSite.
 using RegionCallSiteMap =
     DenseMap<Region *, DenseMap<CallOpInterface, SmallVector<Value>>>;
 
@@ -201,6 +205,19 @@ static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
     LDBG() << "  No matching region successor found, adding argument to output";
     output[getScope(arg)][callSite].push_back(arg);
     return;
+  } else if (auto func = dyn_cast<FunctionOpInterface>(op)) {
+    if (func.isPrivate()) {
+      std::optional<SymbolTable::UseRange> uses =
+          func.getSymbolUses(func->getParentOfType<ModuleOp>());
+      if (uses && !(*uses).empty()) {
+        for (SymbolTable::SymbolUse use : *uses) {
+          CallOpInterface callSite = cast<CallOpInterface>(use.getUser());
+          collectUnderlyingAddressValues(callSite->getOperand(argNumber),
+                                         maxDepth, visited, callSite, output);
+        }
+        return;
+      }
+    }
   }
 
   LDBG()
@@ -477,27 +494,18 @@ AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) {
 
   // Check the alias results against each of the underlying values.
   std::optional<AliasResult> result;
-  /*
-  for (Value lhsVal : lhsValues) {
-    for (Value rhsVal : rhsValues) {
-      LDBG() << "  Checking underlying values: " << lhsVal << " vs " << rhsVal;
-      AliasResult nextResult = aliasImpl(lhsVal, rhsVal);
-      LDBG() << "  Result: "
-             << (nextResult == AliasResult::MustAlias ? "MustAlias"
-                 : nextResult == AliasResult::NoAlias ? "NoAlias"
-                                                      : "MayAlias");
-      result = result ? result->merge(nextResult) : nextResult;
-    }
-  }*/
   for (Region *lhsRegion : lhsValues.keys()) {
     for (auto &&lhsRegionMap : lhsValues[lhsRegion]) {
       for (Region *rhsRegion : rhsValues.keys()) {
+        // Since the scopes differ, we do not perform a comparison.
         if (lhsRegion != rhsRegion)
           continue;
         for (auto &&rhsRegionMap : rhsValues[rhsRegion]) {
-          bool callSityNull = !lhsRegionMap.first || !rhsRegionMap.first;
-          bool callSityEqual = lhsRegionMap.first == rhsRegionMap.first;
-          if (!callSityNull && !callSityEqual)
+          bool callSityNull = !lhsRegionMap.first && !rhsRegionMap.first;
+          bool callSityNotEqual = lhsRegionMap.first != rhsRegionMap.first;
+          // If the call sites differ, we skip the comparison because they are
+          // not passed to the call site at the same time.
+          if (callSityNull && callSityNotEqual)
             continue;
           for (Value lhsVal : lhsRegionMap.second) {
             for (Value rhsVal : rhsRegionMap.second) {
@@ -515,9 +523,9 @@ AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) {
       }
     }
   }
-  if (!result.has_value()) {
-    return AliasResult::MustAlias;
-  }
+  if (!result.has_value())
+    return AliasResult::MayAlias;
+
   // We should always have a valid result here.
   LDBG() << "  Final result: "
          << (result->isMust() ? "MustAlias"
diff --git a/mlir/test/Analysis/test-alias-analysis.mlir b/mlir/test/Analysis/test-alias-analysis.mlir
index d71adee05c7a3..ad1d12f68f3aa 100644
--- a/mlir/test/Analysis/test-alias-analysis.mlir
+++ b/mlir/test/Analysis/test-alias-analysis.mlir
@@ -1,4 +1,5 @@
 // RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(test-alias-analysis))' -split-input-file -allow-unregistered-dialect 2>&1 | FileCheck %s
+// RUN: mlir-opt %s -pass-pipeline='builtin.module(test-alias-analysis)' -split-input-file -allow-unregistered-dialect 2>&1 | FileCheck %s -check-prefix=CHECK-INTERPROCEDURAL
 
 // CHECK-LABEL: Testing : "simple"
 // CHECK-DAG: func.region0#0 <-> func.region0#1: MayAlias
@@ -272,3 +273,74 @@ func.func @distinct_objects(%arg: memref<?xf32>, %arg1: memref<?xf32>) attribute
   %0, %1 = memref.distinct_objects %arg, %arg1 {test.ptr = "distinct"} : memref<?xf32>, memref<?xf32>
   return
 }
+
+// -----
+
+// CHECK-INTERPROCEDURAL-LABEL: Testing : "m_may_alias"
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#0 <-> func_foo.region0#1: MayAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#0 <-> alloc_1#0: MayAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#1 <-> alloc_1#0: NoAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#0 <-> alloc_2#0: MayAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#1 <-> alloc_2#0: MustAlias
+// CHECK-INTERPROCEDURAL-DAG: alloc_1#0 <-> alloc_2#0: NoAlias
+module @m_may_alias{
+  func.func private @foo(%arg: memref<8x64xf32>, %arg1:memref<8x64xf32>) attributes {test.ptr = "func_foo"} {
+    return
+  }
+
+  func.func @main(%arg: memref<2xf32>, %arg1: memref<2xf32>) {
+    %2 = memref.alloc() {test.ptr = "alloc_1"} : memref<8x64xf32>
+    %3 = memref.alloc() {test.ptr = "alloc_2"} : memref<8x64xf32>
+    func.call @foo(%3, %3) : (memref<8x64xf32>, memref<8x64xf32>) -> ()
+    func.call @foo(%2, %3) : (memref<8x64xf32>, memref<8x64xf32>) -> ()
+    return
+  }
+}
+
+// -----
+
+// CHECK-INTERPROCEDURAL-LABEL: Testing : "m_must_alias"
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#0 <-> func_foo.region0#1: MustAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#0 <-> alloc_1#0: NoAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#1 <-> alloc_1#0: NoAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#0 <-> alloc_2#0: MustAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#1 <-> alloc_2#0: MustAlias
+// CHECK-INTERPROCEDURAL-DAG: alloc_1#0 <-> alloc_2#0: NoAlias
+
+module @m_must_alias{
+  func.func private @foo(%arg: memref<8x64xf32>, %arg1:memref<8x64xf32>) attributes {test.ptr = "func_foo"} {
+    return
+  }
+
+  func.func @main(%arg: memref<2xf32>, %arg1: memref<2xf32>) {
+    %2 = memref.alloc() {test.ptr = "alloc_1"} : memref<8x64xf32>
+    %3 = memref.alloc() {test.ptr = "alloc_2"} : memref<8x64xf32>
+    func.call @foo(%3, %3) : (memref<8x64xf32>, memref<8x64xf32>) -> ()
+    func.call @foo(%3, %3) : (memref<8x64xf32>, memref<8x64xf32>) -> ()
+    return
+  }
+}
+
+// -----
+
+// CHECK-INTERPROCEDURAL-LABEL: Testing : "m_no_alias"
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#0 <-> func_foo.region0#1: NoAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#0 <-> alloc_1#0: MustAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#1 <-> alloc_1#0: NoAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#0 <-> alloc_2#0: NoAlias
+// CHECK-INTERPROCEDURAL-DAG: func_foo.region0#1 <-> alloc_2#0: MustAlias
+// CHECK-INTERPROCEDURAL-DAG: alloc_1#0 <-> alloc_2#0: NoAlias
+
+module @m_no_alias{
+  func.func private @foo(%arg: memref<8x64xf32>, %arg1:memref<8x64xf32>) attributes {test.ptr = "func_foo"} {
+    return
+  }
+
+  func.func @main(%arg: memref<2xf32>, %arg1: memref<2xf32>) {
+    %2 = memref.alloc() {test.ptr = "alloc_1"} : memref<8x64xf32>
+    %3 = memref.alloc() {test.ptr = "alloc_2"} : memref<8x64xf32>
+    func.call @foo(%2, %3) : (memref<8x64xf32>, memref<8x64xf32>) -> ()
+    func.call @foo(%2, %3) : (memref<8x64xf32>, memref<8x64xf32>) -> ()
+    return
+  }
+}
\ No newline at end of file



More information about the Mlir-commits mailing list