[polly] [Polly] Restate array sizes and subscripts when the element type shrinks (PR #221496)
Timur Baidusenov via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 15 20:52:17 PDT 2026
https://github.com/bai-tim updated https://github.com/llvm/llvm-project/pull/221496
>From 0edb8c9205085bbb5ec2118ffeb0dce24b7fead8 Mon Sep 17 00:00:00 2001
From: Timur Baidusenov <timurbaidusenov at gmail.com>
Date: Sat, 5 Sep 2026 19:41:21 +0300
Subject: [PATCH 1/3] [Polly] Restate array sizes and subscripts when the
element type shrinks
The canonical element type of a ScopArrayInfo is the smallest type any access
to it uses, so an access through a smaller type replaces the one the array was
first seen with. Dimension sizes and the innermost subscript of a delinearized
access both count canonical elements, but neither was restated in the new
element: a row of a two-dimensional array of double kept the size it had in
doubles once that type became i8, and so described eight times too few bytes.
Every access to such an array is then modelled as running past the end of its
row. The inbounds assumption that follows is infeasible, and the SCoP is
dropped without a diagnostic. A memset over an array that is otherwise accessed
two-dimensionally is enough to trigger this, and is what the test does.
Only the innermost dimension is stretched: the outer ones count rows, and a row
grows together with the innermost dimension. foldSizeConstantsToRight already
scales sizes this way.
Either order can come first, so the sizes are restated on both sides. An access
with a smaller element type can arrive once the sizes are on record, which
updateElementType handles, or the sizes of a delinearized access can arrive
after a memset has already shrunk the type, which getOrCreateScopArrayInfo
handles. The test covers both orders and a three-dimensional array, where q
stays as it is while r becomes 8r.
Nothing changes unless a base pointer is accessed through element types of
different sizes. With a single element size the canonical type never changes,
updateElementType returns before reaching any of this, and the sizes handed to
getOrCreateScopArrayInfo are already canonical. One-dimensional arrays were
unaffected even before, because their subscript is a byte offset that
updateDimensionality already divides by the canonical element size.
Assisted-by: Claude (Anthropic)
---
polly/lib/Analysis/ScopInfo.cpp | 74 ++++++-
.../ScopInfo/multidim-shrunk-element-type.ll | 204 ++++++++++++++++++
2 files changed, 277 insertions(+), 1 deletion(-)
create mode 100644 polly/test/ScopInfo/multidim-shrunk-element-type.ll
diff --git a/polly/lib/Analysis/ScopInfo.cpp b/polly/lib/Analysis/ScopInfo.cpp
index 38943f2557cf2..b36ba166f7dfe 100644
--- a/polly/lib/Analysis/ScopInfo.cpp
+++ b/polly/lib/Analysis/ScopInfo.cpp
@@ -279,6 +279,22 @@ bool ScopArrayInfo::isCompatibleWith(const ScopArrayInfo *Array) const {
return true;
}
+/// Multiply the innermost of @p Sizes by @p Factor.
+///
+/// Dimension sizes count elements of an array's canonical element type, so a
+/// row holds @p Factor times as many of them once that type becomes @p Factor
+/// times smaller. Only the innermost size changes: the outer ones count rows,
+/// and a row grows together with the innermost dimension.
+static void stretchInnermostSize(SmallVectorImpl<const SCEV *> &Sizes,
+ uint64_t Factor, ScalarEvolution &SE) {
+ if (Factor == 1 || Sizes.empty() || !Sizes.back())
+ return;
+
+ const SCEV *Innermost = Sizes.back();
+ Sizes.back() =
+ SE.getMulExpr(Innermost, SE.getConstant(Innermost->getType(), Factor));
+}
+
void ScopArrayInfo::updateElementType(Type *NewElementType) {
if (NewElementType == ElementType)
return;
@@ -295,6 +311,28 @@ void ScopArrayInfo::updateElementType(Type *NewElementType) {
auto GCD = std::gcd((uint64_t)NewElementSize, (uint64_t)OldElementSize);
ElementType = IntegerType::get(ElementType->getContext(), GCD);
}
+
+ // The sizes recorded so far count elements of the type we just replaced, so
+ // they no longer describe the same memory. Restate them in the new element.
+ // Leaving them alone would shrink every row along with the element type and
+ // model in-bounds accesses as running past its end, which makes the inbounds
+ // assumption infeasible and drops the SCoP.
+ //
+ // The canonical type is an integer of the greatest common divisor of two
+ // sizes, and rounding that up to its allocation size can leave it not
+ // dividing the type it replaces. There is no whole number of new elements
+ // per old one to restate the sizes in, so leave them as they are.
+ uint64_t FinalElementSize = DL.getTypeAllocSizeInBits(ElementType);
+ if (FinalElementSize == 0 || (uint64_t)OldElementSize % FinalElementSize != 0)
+ return;
+
+ uint64_t Factor = (uint64_t)OldElementSize / FinalElementSize;
+ if (Factor == 1)
+ return;
+
+ SmallVector<const SCEV *, 4> Stretched(DimensionSizes);
+ stretchInnermostSize(Stretched, Factor, *S.getSE());
+ updateSizes(Stretched, false /* CheckConsistency */);
}
bool ScopArrayInfo::updateSizes(ArrayRef<const SCEV *> NewSizes,
@@ -476,6 +514,26 @@ void MemoryAccess::updateDimensionality() {
if (DimsAccess == 1) {
isl::val V = isl::val(Ctx, ArrayElemSize);
AccessRelation = AccessRelation.floordiv_val(V);
+ } else if (ElemBytes > ArrayElemSize) {
+ // A delinearized access has its subscripts in elements of the type it
+ // reads or writes, which is not the canonical element type of the array
+ // when some other access forced a smaller one. Restate the innermost
+ // subscript in canonical elements too; the outer ones count rows and are
+ // already stated in the sizes this access was delinearized against.
+ assert(ElemBytes % ArrayElemSize == 0 &&
+ "Loaded element size should be multiple of canonical element size");
+ isl::map Scale = isl::map::from_domain_and_range(
+ isl::set::universe(ArraySpace), isl::set::universe(ArraySpace));
+ for (auto i : seq<unsigned>(0, DimsArray - 1))
+ Scale = Scale.equate(isl::dim::in, i, isl::dim::out, i);
+
+ isl::local_space LS(Scale.get_space());
+ isl::constraint C = isl::constraint::alloc_equality(LS);
+ C = C.set_coefficient_si(isl::dim::in, DimsArray - 1,
+ ElemBytes / ArrayElemSize);
+ C = C.set_coefficient_si(isl::dim::out, DimsArray - 1, -1);
+ Scale = Scale.add_constraint(C);
+ AccessRelation = AccessRelation.apply_range(Scale);
}
// We currently do this only if we added at least one dimension, which means
@@ -1758,9 +1816,23 @@ ScopArrayInfo *Scop::getOrCreateScopArrayInfo(Value *BasePtr, Type *ElementType,
ScopArrayInfoSet.insert(SAI.get());
} else {
SAI->updateElementType(ElementType);
+
+ // The sizes handed in count elements of ElementType, which is larger than
+ // the canonical element type of the array whenever some other access to it
+ // uses a smaller one. Restate them in the canonical element, so that they
+ // are compared against, and stored next to, sizes in the same unit.
+ auto &DL = getFunction().getParent()->getDataLayout();
+ uint64_t AccessElemSize = DL.getTypeAllocSize(ElementType);
+ uint64_t CanonicalElemSize = SAI->getElemSizeInBytes();
+
+ SmallVector<const SCEV *, 4> CanonicalSizes(Sizes);
+ if (CanonicalElemSize != 0 && AccessElemSize % CanonicalElemSize == 0)
+ stretchInnermostSize(CanonicalSizes, AccessElemSize / CanonicalElemSize,
+ *getSE());
+
// In case of mismatching array sizes, we bail out by setting the run-time
// context to false.
- if (!SAI->updateSizes(Sizes))
+ if (!SAI->updateSizes(CanonicalSizes))
invalidate(DELINEARIZATION, DebugLoc());
}
return SAI.get();
diff --git a/polly/test/ScopInfo/multidim-shrunk-element-type.ll b/polly/test/ScopInfo/multidim-shrunk-element-type.ll
new file mode 100644
index 0000000000000..91d52f206b1b9
--- /dev/null
+++ b/polly/test/ScopInfo/multidim-shrunk-element-type.ll
@@ -0,0 +1,204 @@
+; RUN: opt %loadNPMPolly -polly-only-func=shrink_after_sizes \
+; RUN: '-passes=polly-custom<scops>' -polly-print-scops -disable-output \
+; RUN: < %s 2>&1 | FileCheck %s --check-prefix=AFTER
+; RUN: opt %loadNPMPolly -polly-only-func=sizes_after_shrink \
+; RUN: '-passes=polly-custom<scops>' -polly-print-scops -disable-output \
+; RUN: < %s 2>&1 | FileCheck %s --check-prefix=BEFORE
+; RUN: opt %loadNPMPolly -polly-only-func=three_dimensions \
+; RUN: '-passes=polly-custom<scops>' -polly-print-scops -disable-output \
+; RUN: < %s 2>&1 | FileCheck %s --check-prefix=THREE
+
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
+
+; A memset shrinks the canonical element type of A from double to i8. Dimension
+; sizes and subscripts both count canonical elements, so the innermost of each
+; has to grow by the same factor. Left in double units, a row would hold m
+; bytes rather than 8m, the stores would be modelled as running past its end,
+; and the resulting inbounds assumption would be infeasible and drop the SCoP.
+
+; The memset follows the loop nest, so the sizes are recorded while double is
+; still the canonical element type and have to be restated when it shrinks.
+;
+; void shrink_after_sizes(long n, long m, double A[n][m]) {
+; for (long i = 0; i < 100; i++)
+; for (long j = 0; j < 150; j++)
+; A[i][j] = 1.0;
+; memset(A, 0, m * sizeof(double));
+; }
+
+; AFTER: Assumed Context:
+; AFTER-NEXT: [m] -> { : m >= 150 }
+
+; AFTER: Arrays {
+; AFTER-NEXT: i8 MemRef_A[*][(8 * %m)]; // Element size 1
+; AFTER-NEXT: }
+
+; AFTER: Statements {
+; AFTER-NEXT: Stmt_for_j
+; AFTER: MustWriteAccess := [Reduction Type: NONE] [Scalar: 0]
+; AFTER-NEXT: [m] -> { Stmt_for_j[i0, i1] -> MemRef_A[i0, o1] : 8i1 <= o1 <= 7 + 8i1 };
+; AFTER-NEXT: Stmt_memset_bb
+; AFTER: MustWriteAccess := [Reduction Type: NONE] [Scalar: 0]
+; AFTER-NEXT: [m] -> { Stmt_memset_bb[] -> MemRef_A[0, o1] : 0 <= o1 < 8m };
+; AFTER-NEXT: }
+
+define void @shrink_after_sizes(i64 %n, i64 %m, ptr %A) {
+entry:
+ %len = shl nsw i64 %m, 3
+ br label %for.i
+
+for.i:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %for.i.inc ]
+ %tmp = mul nsw i64 %i, %m
+ br label %for.j
+
+for.j:
+ %j = phi i64 [ 0, %for.i ], [ %j.inc, %for.j ]
+ %vlaarrayidx.sum = add i64 %j, %tmp
+ %arrayidx = getelementptr inbounds double, ptr %A, i64 %vlaarrayidx.sum
+ store double 1.0, ptr %arrayidx
+ %j.inc = add nsw i64 %j, 1
+ %j.exitcond = icmp eq i64 %j.inc, 150
+ br i1 %j.exitcond, label %for.i.inc, label %for.j
+
+for.i.inc:
+ %i.inc = add nsw i64 %i, 1
+ %i.exitcond = icmp eq i64 %i.inc, 100
+ br i1 %i.exitcond, label %memset.bb, label %for.i
+
+memset.bb:
+ call void @llvm.memset.p0.i64(ptr %A, i8 0, i64 %len, i1 false)
+ br label %end
+
+end:
+ ret void
+}
+
+; The memset comes first, so the array is created with i8 as its element type
+; and the sizes of the loop nest arrive afterwards, stated in doubles. They
+; have to be restated before they are compared against the ones on record.
+;
+; void sizes_after_shrink(long n, long m, double A[n][m]) {
+; for (long i = 0; i < 100; i++) {
+; memset(A, 0, m * sizeof(double));
+; for (long j = 0; j < 150; j++)
+; A[i][j] = 1.0;
+; }
+; }
+
+; BEFORE: Assumed Context:
+; BEFORE-NEXT: [m] -> { : m >= 150 }
+
+; BEFORE: Arrays {
+; BEFORE-NEXT: i8 MemRef_A[*][(8 * %m)]; // Element size 1
+; BEFORE-NEXT: }
+
+; BEFORE: Statements {
+; BEFORE-NEXT: Stmt_memset_bb
+; BEFORE: MustWriteAccess := [Reduction Type: NONE] [Scalar: 0]
+; BEFORE-NEXT: [m] -> { Stmt_memset_bb[i0] -> MemRef_A[0, o1] : 0 <= o1 < 8m };
+; BEFORE-NEXT: Stmt_for_j
+; BEFORE: MustWriteAccess := [Reduction Type: NONE] [Scalar: 0]
+; BEFORE-NEXT: [m] -> { Stmt_for_j[i0, i1] -> MemRef_A[i0, o1] : 8i1 <= o1 <= 7 + 8i1 };
+; BEFORE-NEXT: }
+
+define void @sizes_after_shrink(i64 %n, i64 %m, ptr %A) {
+entry:
+ %len = shl nsw i64 %m, 3
+ br label %for.i
+
+for.i:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %for.i.inc ]
+ %tmp = mul nsw i64 %i, %m
+ br label %memset.bb
+
+memset.bb:
+ call void @llvm.memset.p0.i64(ptr %A, i8 0, i64 %len, i1 false)
+ br label %for.j
+
+for.j:
+ %j = phi i64 [ 0, %memset.bb ], [ %j.inc, %for.j ]
+ %vlaarrayidx.sum = add i64 %j, %tmp
+ %arrayidx = getelementptr inbounds double, ptr %A, i64 %vlaarrayidx.sum
+ store double 1.0, ptr %arrayidx
+ %j.inc = add nsw i64 %j, 1
+ %j.exitcond = icmp eq i64 %j.inc, 150
+ br i1 %j.exitcond, label %for.i.inc, label %for.j
+
+for.i.inc:
+ %i.inc = add nsw i64 %i, 1
+ %i.exitcond = icmp eq i64 %i.inc, 100
+ br i1 %i.exitcond, label %end, label %for.i
+
+end:
+ ret void
+}
+
+; Only the innermost dimension is stretched. The outer ones count rows, and a
+; row grows together with the innermost dimension, so q stays as it is while r
+; becomes 8r.
+;
+; void three_dimensions(long q, long r, double A[100][q][r]) {
+; for (long i = 0; i < 100; i++)
+; for (long j = 0; j < 150; j++)
+; for (long k = 0; k < 200; k++)
+; A[i][j][k] = 1.0;
+; memset(A, 0, r * sizeof(double));
+; }
+
+; THREE: Assumed Context:
+; THREE-NEXT: [q, r] -> { : q >= 150 and r >= 200 }
+
+; THREE: Arrays {
+; THREE-NEXT: i8 MemRef_A[*][%q][(8 * %r)]; // Element size 1
+; THREE-NEXT: }
+
+; THREE: Statements {
+; THREE-NEXT: Stmt_for_k
+; THREE: MustWriteAccess := [Reduction Type: NONE] [Scalar: 0]
+; THREE-NEXT: [q, r] -> { Stmt_for_k[i0, i1, i2] -> MemRef_A[i0, i1, o2] : 8i2 <= o2 <= 7 + 8i2 };
+
+define void @three_dimensions(i64 %q, i64 %r, ptr %A) {
+entry:
+ %len = shl nsw i64 %r, 3
+ br label %for.i
+
+for.i:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %for.i.inc ]
+ %t1 = mul nsw i64 %i, %q
+ br label %for.j
+
+for.j:
+ %j = phi i64 [ 0, %for.i ], [ %j.inc, %for.j.inc ]
+ %t2 = add nsw i64 %t1, %j
+ %t3 = mul nsw i64 %t2, %r
+ br label %for.k
+
+for.k:
+ %k = phi i64 [ 0, %for.j ], [ %k.inc, %for.k ]
+ %idx = add nsw i64 %t3, %k
+ %arrayidx = getelementptr inbounds double, ptr %A, i64 %idx
+ store double 1.0, ptr %arrayidx
+ %k.inc = add nsw i64 %k, 1
+ %k.exitcond = icmp eq i64 %k.inc, 200
+ br i1 %k.exitcond, label %for.j.inc, label %for.k
+
+for.j.inc:
+ %j.inc = add nsw i64 %j, 1
+ %j.exitcond = icmp eq i64 %j.inc, 150
+ br i1 %j.exitcond, label %for.i.inc, label %for.j
+
+for.i.inc:
+ %i.inc = add nsw i64 %i, 1
+ %i.exitcond = icmp eq i64 %i.inc, 100
+ br i1 %i.exitcond, label %memset.bb, label %for.i
+
+memset.bb:
+ call void @llvm.memset.p0.i64(ptr %A, i8 0, i64 %len, i1 false)
+ br label %end
+
+end:
+ ret void
+}
+
+declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1)
>From 205f27b616bb15c16bc9272ed3c7f578a4ce69fc Mon Sep 17 00:00:00 2001
From: Timur Baidusenov <timurbaidusenov at gmail.com>
Date: Mon, 7 Sep 2026 19:35:14 +0300
Subject: [PATCH 2/3] [Polly] Fold the subscript scaling into the multi-element
access map
The scaling of the innermost subscript and the widening to several canonical
elements were two blocks under the same condition. Scale in the existing map
instead, by a factor of one for a non-delinearized access, whose subscript
floordiv_val() above has already stated in canonical elements.
Spell out both constraints, and close the brace in the example above them: the
map they build is what that example describes, which the widening alone was
not.
Assisted-by: Claude (Anthropic)
---
polly/lib/Analysis/ScopInfo.cpp | 36 ++++++++++++---------------------
1 file changed, 13 insertions(+), 23 deletions(-)
diff --git a/polly/lib/Analysis/ScopInfo.cpp b/polly/lib/Analysis/ScopInfo.cpp
index b36ba166f7dfe..51b646b739d6a 100644
--- a/polly/lib/Analysis/ScopInfo.cpp
+++ b/polly/lib/Analysis/ScopInfo.cpp
@@ -514,26 +514,6 @@ void MemoryAccess::updateDimensionality() {
if (DimsAccess == 1) {
isl::val V = isl::val(Ctx, ArrayElemSize);
AccessRelation = AccessRelation.floordiv_val(V);
- } else if (ElemBytes > ArrayElemSize) {
- // A delinearized access has its subscripts in elements of the type it
- // reads or writes, which is not the canonical element type of the array
- // when some other access forced a smaller one. Restate the innermost
- // subscript in canonical elements too; the outer ones count rows and are
- // already stated in the sizes this access was delinearized against.
- assert(ElemBytes % ArrayElemSize == 0 &&
- "Loaded element size should be multiple of canonical element size");
- isl::map Scale = isl::map::from_domain_and_range(
- isl::set::universe(ArraySpace), isl::set::universe(ArraySpace));
- for (auto i : seq<unsigned>(0, DimsArray - 1))
- Scale = Scale.equate(isl::dim::in, i, isl::dim::out, i);
-
- isl::local_space LS(Scale.get_space());
- isl::constraint C = isl::constraint::alloc_equality(LS);
- C = C.set_coefficient_si(isl::dim::in, DimsArray - 1,
- ElemBytes / ArrayElemSize);
- C = C.set_coefficient_si(isl::dim::out, DimsArray - 1, -1);
- Scale = Scale.add_constraint(C);
- AccessRelation = AccessRelation.apply_range(Scale);
}
// We currently do this only if we added at least one dimension, which means
@@ -551,7 +531,14 @@ void MemoryAccess::updateDimensionality() {
// access is larger than the canonical element type of the array.
//
// An access ((float *)A)[i] to an array char *A is modeled as
- // {[i] -> A[o] : 4 i <= o <= 4 i + 3
+ // {[i] -> A[o] : 4 i <= o <= 4 i + 3}
+ //
+ // The subscript of a non-delinearized access was divided by ArrayElemSize
+ // above, which already stated it in canonical elements. A delinearized one
+ // still counts elements of the type it reads or writes, so it is scaled
+ // here instead. Only the innermost subscript is scaled: the outer ones count
+ // rows and are already stated in the sizes the access was delinearized
+ // against.
if (ElemBytes > ArrayElemSize) {
assert(ElemBytes % ArrayElemSize == 0 &&
"Loaded element size should be multiple of canonical element size");
@@ -566,15 +553,18 @@ void MemoryAccess::updateDimensionality() {
LS = isl::local_space(Map.get_space());
int Num = ElemBytes / getScopArrayInfo()->getElemSizeInBytes();
+ int Scale = DimsAccess == 1 ? 1 : Num;
+ // Scale * i - o + (Num - 1) >= 0, that is o <= Scale * i + Num - 1.
C = isl::constraint::alloc_inequality(LS);
C = C.set_constant_val(isl::val(Ctx, Num - 1));
- C = C.set_coefficient_si(isl::dim::in, DimsArray - 1, 1);
+ C = C.set_coefficient_si(isl::dim::in, DimsArray - 1, Scale);
C = C.set_coefficient_si(isl::dim::out, DimsArray - 1, -1);
Map = Map.add_constraint(C);
+ // o - Scale * i >= 0, that is o >= Scale * i.
C = isl::constraint::alloc_inequality(LS);
- C = C.set_coefficient_si(isl::dim::in, DimsArray - 1, -1);
+ C = C.set_coefficient_si(isl::dim::in, DimsArray - 1, -Scale);
C = C.set_coefficient_si(isl::dim::out, DimsArray - 1, 1);
C = C.set_constant_val(isl::val(Ctx, 0));
Map = Map.add_constraint(C);
>From fea58feb3f3262e18ec9b17edc2e869295b71601 Mon Sep 17 00:00:00 2001
From: Timur Baidusenov <timurbaidusenov at gmail.com>
Date: Wed, 16 Sep 2026 03:34:45 +0000
Subject: [PATCH 3/3] [Polly] Check that the sizes can be restated before
changing the element type
The bail-out for a canonical type whose allocation size does not divide the one
it replaces ran after ElementType had already been assigned, which left the
array with a new element type and sizes still counting the old one. Compute the
canonical type into a local, decide on it there, and assign it only once the
sizes are known to follow.
Check the memset statement in the three-dimensional test as well, so that its
access relation is pinned like the ones in the other two functions.
Assisted-by: Claude (Anthropic)
---
polly/lib/Analysis/ScopInfo.cpp | 28 +++++++++++--------
.../ScopInfo/multidim-shrunk-element-type.ll | 4 +++
2 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/polly/lib/Analysis/ScopInfo.cpp b/polly/lib/Analysis/ScopInfo.cpp
index 51b646b739d6a..fcb0c6ecbf625 100644
--- a/polly/lib/Analysis/ScopInfo.cpp
+++ b/polly/lib/Analysis/ScopInfo.cpp
@@ -305,28 +305,32 @@ void ScopArrayInfo::updateElementType(Type *NewElementType) {
if (NewElementSize == OldElementSize || NewElementSize == 0)
return;
+ Type *CanonicalType;
if (NewElementSize % OldElementSize == 0 && NewElementSize < OldElementSize) {
- ElementType = NewElementType;
+ CanonicalType = NewElementType;
} else {
auto GCD = std::gcd((uint64_t)NewElementSize, (uint64_t)OldElementSize);
- ElementType = IntegerType::get(ElementType->getContext(), GCD);
+ CanonicalType = IntegerType::get(ElementType->getContext(), GCD);
}
- // The sizes recorded so far count elements of the type we just replaced, so
- // they no longer describe the same memory. Restate them in the new element.
- // Leaving them alone would shrink every row along with the element type and
- // model in-bounds accesses as running past its end, which makes the inbounds
- // assumption infeasible and drops the SCoP.
+ // The sizes on record count elements of the type being replaced, so they no
+ // longer describe the same memory once it changes. Restate them in the new
+ // element. Leaving them alone would shrink every row along with the element
+ // type and model in-bounds accesses as running past its end, which makes the
+ // inbounds assumption infeasible and drops the SCoP.
//
// The canonical type is an integer of the greatest common divisor of two
// sizes, and rounding that up to its allocation size can leave it not
- // dividing the type it replaces. There is no whole number of new elements
- // per old one to restate the sizes in, so leave them as they are.
- uint64_t FinalElementSize = DL.getTypeAllocSizeInBits(ElementType);
- if (FinalElementSize == 0 || (uint64_t)OldElementSize % FinalElementSize != 0)
+ // dividing the type it replaces. There is then no whole number of new
+ // elements per old one to restate the sizes in. Give up before touching
+ // anything rather than leave the element type and the sizes disagreeing.
+ uint64_t CanonicalSize = DL.getTypeAllocSizeInBits(CanonicalType);
+ if (CanonicalSize == 0 || (uint64_t)OldElementSize % CanonicalSize != 0)
return;
- uint64_t Factor = (uint64_t)OldElementSize / FinalElementSize;
+ ElementType = CanonicalType;
+
+ uint64_t Factor = (uint64_t)OldElementSize / CanonicalSize;
if (Factor == 1)
return;
diff --git a/polly/test/ScopInfo/multidim-shrunk-element-type.ll b/polly/test/ScopInfo/multidim-shrunk-element-type.ll
index 91d52f206b1b9..667223e53ab78 100644
--- a/polly/test/ScopInfo/multidim-shrunk-element-type.ll
+++ b/polly/test/ScopInfo/multidim-shrunk-element-type.ll
@@ -157,6 +157,10 @@ end:
; THREE-NEXT: Stmt_for_k
; THREE: MustWriteAccess := [Reduction Type: NONE] [Scalar: 0]
; THREE-NEXT: [q, r] -> { Stmt_for_k[i0, i1, i2] -> MemRef_A[i0, i1, o2] : 8i2 <= o2 <= 7 + 8i2 };
+; THREE-NEXT: Stmt_memset_bb
+; THREE: MustWriteAccess := [Reduction Type: NONE] [Scalar: 0]
+; THREE-NEXT: [q, r] -> { Stmt_memset_bb[] -> MemRef_A[0, 0, o2] : 0 <= o2 < 8r };
+; THREE-NEXT: }
define void @three_dimensions(i64 %q, i64 %r, ptr %A) {
entry:
More information about the llvm-commits
mailing list