[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