[llvm] [Delinearization] Extract outer array dimensions using global object size (PR #175158)

Sjoerd Meijer via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 22 06:06:29 PST 2026


https://github.com/sjoerdmeijer updated https://github.com/llvm/llvm-project/pull/175158

>From a02abef24864b9bfb742b23b95019d9f12f8f140 Mon Sep 17 00:00:00 2001
From: Sjoerd Meijer <smeijer at nvidia.com>
Date: Tue, 20 Jan 2026 08:31:53 -0800
Subject: [PATCH] [Delinearization] Extract outer dimension from global object

Use the size of a statically declared array to calculate the outer array
dimension. Obtaining this size and feeding that extra information into the
subscript recognition part helps to get more accurate results.
---
 llvm/lib/Analysis/Delinearization.cpp         | 137 ++++++++++++++----
 .../Delinearization/global_array_bounds.ll    |   4 +-
 .../Delinearization/validation_large_size.ll  |   4 +-
 3 files changed, 109 insertions(+), 36 deletions(-)

diff --git a/llvm/lib/Analysis/Delinearization.cpp b/llvm/lib/Analysis/Delinearization.cpp
index b8582b09025a6..683af4b957e77 100644
--- a/llvm/lib/Analysis/Delinearization.cpp
+++ b/llvm/lib/Analysis/Delinearization.cpp
@@ -21,8 +21,10 @@
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/InstIterator.h"
 #include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
 #include "llvm/IR/PassManager.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
@@ -397,9 +399,13 @@ void llvm::computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr,
     Subscripts.push_back(R);
   }
 
-  // Also push in last position the remainder of the last division: it will be
-  // the access function of the innermost dimension.
-  Subscripts.push_back(Res);
+  if (!Res->isZero()) {
+    // This is only needed when the outermost array size is not known.  Res = 0
+    // when the outermost array dimension is known, as for example when reading
+    // array sizes from a local or global declaration.
+    Subscripts.push_back(Res);
+    LLVM_DEBUG(dbgs() << "Subscripts push_back Res: " << *Res << "\n");
+  }
 
   std::reverse(Subscripts.begin(), Subscripts.end());
 
@@ -590,6 +596,46 @@ bool llvm::findFixedSizeArrayDimensions(ScalarEvolution &SE, const SCEV *Expr,
   return true;
 }
 
+static void extractOuterDimFromGlobal(ScalarEvolution &SE, Value *BasePtr,
+                                      SmallVectorImpl<const SCEV *> &Sizes,
+                                      const SCEV *ElementSize) {
+  LLVM_DEBUG(
+    assert(Sizes.size() >= 2 && "Sizes should contain at least"
+                                " the element size and one dimension");
+    for (const SCEV *S : Sizes)
+        assert(cast<SCEVConstant>(S) && "Only SCEVConstant expected");
+
+    dbgs() << "extractArrayInfoFromGlobal called with BasePtr: "
+           << *BasePtr << "\n";
+  );
+
+  auto *GV = dyn_cast<GlobalVariable>(BasePtr);
+  if (!GV) {
+    LLVM_DEBUG(dbgs() << "Base pointer is not a global variable, "
+                      << "can't extract outer dimension.\n");
+    return;
+  }
+
+  LLVM_DEBUG(dbgs() << "Found global variable with type: "
+                    << *GV->getValueType() << "\n");
+
+  const DataLayout &DL = GV->getParent()->getDataLayout();
+  uint64_t SizeInBytes = DL.getTypeAllocSize(GV->getValueType());
+  LLVM_DEBUG(dbgs() << "Object size in bytes: " << SizeInBytes << "\n");
+
+  uint64_t MulConst = 1;
+  for (const SCEV *S : Sizes) {
+    const SCEVConstant *SC = cast<SCEVConstant>(S);
+    MulConst *= SC->getAPInt().getZExtValue();
+  }
+
+  uint64_t OuterDim = SizeInBytes / MulConst;
+  LLVM_DEBUG(dbgs() << "Outer loop dimention = " << SizeInBytes << " / "
+                    << MulConst << " = " << OuterDim << "\n");
+
+  Sizes.insert(Sizes.begin(), SE.getConstant(ElementSize->getType(), OuterDim));
+}
+
 /// Splits the SCEV into two vectors of SCEVs representing the subscripts and
 /// sizes of an array access, assuming that the array is a fixed size array.
 ///
@@ -649,19 +695,28 @@ bool llvm::delinearizeFixedSizeArray(ScalarEvolution &SE, const SCEV *Expr,
   Subscripts.clear();
   Sizes.clear();
 
+  const SCEVUnknown *BasePointer =
+      dyn_cast<SCEVUnknown>(SE.getPointerBase(Expr));
+  const SCEV *Offset = BasePointer ? SE.getMinusSCEV(Expr, BasePointer) : Expr;
+
   // First step: find the fixed array size.
   SmallVector<uint64_t, 4> ConstSizes;
-  if (!findFixedSizeArrayDimensions(SE, Expr, ConstSizes, ElementSize)) {
+  if (!findFixedSizeArrayDimensions(SE, Offset, ConstSizes, ElementSize)) {
     Sizes.clear();
     return false;
   }
 
   // Convert the constant size to SCEV.
   for (uint64_t Size : ConstSizes)
-    Sizes.push_back(SE.getConstant(Expr->getType(), Size));
+    Sizes.push_back(SE.getConstant(Offset->getType(), Size));
+
+  // Maybe we can extract the outer dimension from the global variable
+  // declaration.
+  if (BasePointer)
+    extractOuterDimFromGlobal(SE, BasePointer->getValue(), Sizes, ElementSize);
 
   // Second step: compute the access functions for each subscript.
-  computeAccessFunctions(SE, Expr, Subscripts, Sizes);
+  computeAccessFunctions(SE, Offset, Subscripts, Sizes);
 
   return !Subscripts.empty();
 }
@@ -826,47 +881,65 @@ void printDelinearization(raw_ostream &O, Function *F, LoopInfo *LI,
     // Do not delinearize if we cannot find the base pointer.
     if (!BasePointer)
       break;
-    AccessFn = SE->getMinusSCEV(AccessFn, BasePointer);
 
+    const SCEV *Offset = SE->getMinusSCEV(AccessFn, BasePointer);
     O << "\n";
     O << "Inst:" << Inst << "\n";
-    O << "AccessFunction: " << *AccessFn << "\n";
+    O << "AccessFunction: " << *Offset << "\n";
 
     SmallVector<const SCEV *, 3> Subscripts, Sizes;
 
     auto IsDelinearizationFailed = [&]() {
-      return Subscripts.size() == 0 || Sizes.size() == 0 ||
-             Subscripts.size() != Sizes.size();
+      return Subscripts.size() == 0 || Sizes.size() == 0;
     };
 
-    delinearize(*SE, AccessFn, Subscripts, Sizes, SE->getElementSize(&Inst));
+    delinearize(*SE, Offset, Subscripts, Sizes, SE->getElementSize(&Inst));
+
+    bool DelinearizedFixedArray = false;
     if (UseFixedSizeArrayHeuristic && IsDelinearizationFailed()) {
       Subscripts.clear();
       Sizes.clear();
-      delinearizeFixedSizeArray(*SE, AccessFn, Subscripts, Sizes,
-                                SE->getElementSize(&Inst));
+      DelinearizedFixedArray = delinearizeFixedSizeArray(
+          *SE, AccessFn, Subscripts, Sizes, SE->getElementSize(&Inst));
     }
 
-      if (IsDelinearizationFailed()) {
-        O << "failed to delinearize\n";
-        continue;
-      }
+    if (IsDelinearizationFailed()) {
+      O << "failed to delinearize\n";
+      continue;
+    }
+
+    O << "Base offset: " << *BasePointer << "\n";
+    O << "ArrayDecl";
+
+    // Print [Unknown] when the outermost dimension of the array is not known.
+    // Sizes[NumSizes - 1] is the array element size.
+    int NumSubscripts = Subscripts.size();
+    int NumSizes = Sizes.size();
+    if (NumSizes == NumSubscripts)
+      O << "[UnknownSize]";
+
+    // Handle different size relationships between Subscripts and Sizes.
+    if (NumSizes > 0) {
+      // Print array dimensions (all but the last, which is element size).
+      for (const SCEV *Size : ArrayRef(Sizes).drop_back())
+        O << "[" << *Size << "]";
+
+      // Print element size (last element in Sizes array).
+      O << " with elements of " << *Sizes[NumSizes - 1] << " bytes.\n";
+    } else {
+      O << " unknown sizes.\n";
+    }
+
+    O << "ArrayRef";
+    for (int i = 0; i < NumSubscripts; i++)
+      O << "[" << *Subscripts[i] << "]";
+    O << "\n";
 
-      O << "Base offset: " << *BasePointer << "\n";
-      O << "ArrayDecl[UnknownSize]";
-      int Size = Subscripts.size();
-      for (int i = 0; i < Size - 1; i++)
-        O << "[" << *Sizes[i] << "]";
-      O << " with elements of " << *Sizes[Size - 1] << " bytes.\n";
-
-      O << "ArrayRef";
-      for (int i = 0; i < Size; i++)
-        O << "[" << *Subscripts[i] << "]";
-      O << "\n";
-
-      bool IsValid = validateDelinearizationResult(*SE, Sizes, Subscripts);
-      O << "Delinearization validation: " << (IsValid ? "Succeeded" : "Failed")
-        << "\n";
+    bool IsValid = DelinearizedFixedArray
+                       ? true
+                       : validateDelinearizationResult(*SE, Sizes, Subscripts);
+    O << "Delinearization validation: " << (IsValid ? "Succeeded" : "Failed")
+      << "\n";
   }
 }
 
diff --git a/llvm/test/Analysis/Delinearization/global_array_bounds.ll b/llvm/test/Analysis/Delinearization/global_array_bounds.ll
index 2e13fd66eb930..bb7d544216c93 100644
--- a/llvm/test/Analysis/Delinearization/global_array_bounds.ll
+++ b/llvm/test/Analysis/Delinearization/global_array_bounds.ll
@@ -18,9 +18,9 @@ define void @test_2d_array(i64 %i, i64 %j, i64 %N, i64 %M) {
 ; CHECK-NEXT:  Inst: %val = load i32, ptr %gepij, align 4
 ; CHECK-NEXT:  AccessFunction: {{\{\{}}0,+,80}<%for.i>,+,4}<%for.j>
 ; CHECK-NEXT:  Base offset: @test_array_10x20
-; CHECK-NEXT:  ArrayDecl[UnknownSize][20] with elements of 4 bytes.
+; CHECK-NEXT:  ArrayDecl[10][20] with elements of 4 bytes.
 ; CHECK-NEXT:  ArrayRef[{0,+,1}<nuw><%for.i>][{0,+,1}<nuw><nsw><%for.j>]
-; CHECK-NEXT:  Delinearization validation: Failed
+; CHECK-NEXT:  Delinearization validation: Succeeded
 ;
 entry:
   br label %for.i
diff --git a/llvm/test/Analysis/Delinearization/validation_large_size.ll b/llvm/test/Analysis/Delinearization/validation_large_size.ll
index b798813c302b2..a7ad7678f07fc 100644
--- a/llvm/test/Analysis/Delinearization/validation_large_size.ll
+++ b/llvm/test/Analysis/Delinearization/validation_large_size.ll
@@ -26,7 +26,7 @@ define void @large_size_fixed(ptr %A) {
 ; CHECK-NEXT:  Base offset: %A
 ; CHECK-NEXT:  ArrayDecl[UnknownSize][256] with elements of 1 bytes.
 ; CHECK-NEXT:  ArrayRef[{0,+,1}<nuw><nsw><%for.i.header>][{0,+,1}<nuw><nsw><%for.j>]
-; CHECK-NEXT:  Delinearization validation: Failed
+; CHECK-NEXT:  Delinearization validation: Succeeded
 ;
 entry:
   br label %for.i.header
@@ -151,7 +151,7 @@ define void @elementsize_cause_ovfl(ptr %A) {
 ; CHECK-NEXT:  Base offset: %A
 ; CHECK-NEXT:  ArrayDecl[UnknownSize][256] with elements of 8 bytes.
 ; CHECK-NEXT:  ArrayRef[{0,+,1}<nuw><nsw><%for.i.header>][{0,+,1}<nuw><nsw><%for.j>]
-; CHECK-NEXT:  Delinearization validation: Failed
+; CHECK-NEXT:  Delinearization validation: Succeeded
 ;
 entry:
   br label %for.i.header



More information about the llvm-commits mailing list