[SCEV] Derive element size from the access's own pointer address space (#209824)

ScalarEvolution::getElementSize always derived the element-size SCEV
using a generic address-space-0 pointer index type. On targets whose
data layout uses different pointer index widths per address space (for
example, where some address spaces use 32-bit pointers and others
64-bit), a load/store through a narrow address space produced an access
function SCEV of one width but an element-size SCEV of another.

Delinearization then called SCEVDivision::divide with numerator and
denominator of mismatched types, tripping the assertion added in
a9d295d615 once the implicit sign-extension was removed in
23a32bcedb.

Derive the index type from the memory access's actual pointer operand
address space so the element-size SCEV matches the width of the access
function's SCEV.
This commit is contained in:
Jinsong Ji 2026-07-20 11:17:38 -04:00 committed by GitHub
parent 7bbed7999d
commit b2d3db9bfb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 70 additions and 4 deletions

View file

@ -14100,14 +14100,18 @@ bool ScalarEvolution::containsErasedValue(const SCEV *S) const {
/// Return the size of an element read or written by Inst.
const SCEV *ScalarEvolution::getElementSize(Instruction *Inst) {
Type *Ty;
if (StoreInst *Store = dyn_cast<StoreInst>(Inst))
Type *PtrTy;
if (StoreInst *Store = dyn_cast<StoreInst>(Inst)) {
Ty = Store->getValueOperand()->getType();
else if (LoadInst *Load = dyn_cast<LoadInst>(Inst))
PtrTy = Store->getPointerOperandType();
} else if (LoadInst *Load = dyn_cast<LoadInst>(Inst)) {
Ty = Load->getType();
else
PtrTy = Load->getPointerOperandType();
} else {
return nullptr;
}
Type *ETy = getEffectiveSCEVType(PointerType::getUnqual(Inst->getContext()));
Type *ETy = getEffectiveSCEVType(PtrTy);
return getSizeOfExpr(ETy, Ty);
}

View file

@ -0,0 +1,62 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -passes='print<delinearization>' -disable-output 2>&1 | FileCheck %s
; The element-size SCEV is derived from the index type of the access's actual
; pointer operand. On a target whose data layout gives different pointer index
; widths per address space, an access through a narrow (32-bit) address space
; must not produce a 64-bit element size, otherwise delinearization would call
; SCEVDivision::divide with mismatched operand types and trip an assertion.
;
; Derived from the following code (with A in a 32-bit address space):
;
; void foo(int n, int m, double A[n][m]) {
; for (int i = 0; i < n; i++)
; for (int j = 0; j < m; j++)
; A[i][j] = 1.0;
; }
; Address space 3 uses 32-bit pointers; the default address space uses 64-bit.
target datalayout = "e-p:64:64-p3:32:32-i64:64-n32:64"
define void @foo(i32 %n, i32 %m, ptr addrspace(3) %A) {
; CHECK-LABEL: 'foo'
; CHECK-NEXT: Inst: %val = load double, ptr addrspace(3) %arrayidx, align 8
; CHECK-NEXT: AccessFunction: {{\{\{}}0,+,(8 * %m)}<%for.i>,+,8}<%for.j>
; CHECK-NEXT: Base offset: %A
; CHECK-NEXT: ArrayDecl[UnknownSize][%m] with elements of 8 bytes.
; CHECK-NEXT: ArrayRef[{0,+,1}<nuw><nsw><%for.i>][{0,+,1}<nuw><nsw><%for.j>]
; CHECK-NEXT: Delinearization validation: Failed
; CHECK-EMPTY:
; CHECK-NEXT: Inst: store double %val, ptr addrspace(3) %arrayidx, align 8
; CHECK-NEXT: AccessFunction: {{\{\{}}0,+,(8 * %m)}<%for.i>,+,8}<%for.j>
; CHECK-NEXT: Base offset: %A
; CHECK-NEXT: ArrayDecl[UnknownSize][%m] with elements of 8 bytes.
; CHECK-NEXT: ArrayRef[{0,+,1}<nuw><nsw><%for.i>][{0,+,1}<nuw><nsw><%for.j>]
; CHECK-NEXT: Delinearization validation: Failed
;
entry:
br label %for.i
for.i:
%i = phi i32 [ 0, %entry ], [ %i.inc, %for.i.inc ]
%tmp = mul nsw i32 %i, %m
br label %for.j
for.j:
%j = phi i32 [ 0, %for.i ], [ %j.inc, %for.j ]
%sum = add i32 %j, %tmp
%arrayidx = getelementptr inbounds double, ptr addrspace(3) %A, i32 %sum
%val = load double, ptr addrspace(3) %arrayidx
store double %val, ptr addrspace(3) %arrayidx
%j.inc = add nsw i32 %j, 1
%j.exitcond = icmp eq i32 %j.inc, %m
br i1 %j.exitcond, label %for.i.inc, label %for.j
for.i.inc:
%i.inc = add nsw i32 %i, 1
%i.exitcond = icmp eq i32 %i.inc, %n
br i1 %i.exitcond, label %end, label %for.i
end:
ret void
}