blob: 5ea89bdaf979e8addf4d9298944531514daee06c [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt < %s -passes=slsr,gvn -S | FileCheck %s
target datalayout = "e-i64:64-v16:16-v32:32-n16:32:64-p:64:64:64"
; The GEP index is sign-extended from i32 to the i64 pointer-index width. SLSR
; must not factor the narrow stride, fold a Stride-delta in i32, and then
; sign-extend that constant: sext does not distribute over a wrapping i32
; subtraction. ScalarEvolution models `xor X, signmask` as a non-nsw
; `add X, signmask`, so the two strides differ by a constant in i32 even though
; sext(stride2) - sext(stride1) is not that constant. The candidates must stay
; independent instead of being rewritten to g1 + 4 * sext(0x80000000).
define void @slsr_gep_xor_signmask_no_rewrite(ptr %p, i32 %n, float %r) {
; CHECK-LABEL: define void @slsr_gep_xor_signmask_no_rewrite(
; CHECK-SAME: ptr [[P:%.*]], i32 [[N:%.*]], float [[R:%.*]]) {
; CHECK-NEXT: [[I1:%.*]] = sext i32 [[N]] to i64
; CHECK-NEXT: [[G1:%.*]] = getelementptr float, ptr [[P]], i64 [[I1]]
; CHECK-NEXT: store float [[R]], ptr [[G1]], align 4
; CHECK-NEXT: [[X:%.*]] = xor i32 [[N]], -2147483648
; CHECK-NEXT: [[I2:%.*]] = sext i32 [[X]] to i64
; CHECK-NEXT: [[G2:%.*]] = getelementptr float, ptr [[P]], i64 [[I2]]
; CHECK-NEXT: store float [[R]], ptr [[G2]], align 4
; CHECK-NEXT: ret void
;
%i1 = sext i32 %n to i64
%g1 = getelementptr float, ptr %p, i64 %i1
store float %r, ptr %g1, align 4
%x = xor i32 %n, -2147483648
%i2 = sext i32 %x to i64
%g2 = getelementptr float, ptr %p, i64 %i2
store float %r, ptr %g2, align 4
ret void
}
; The candidate stride `%x = add %m, %n` has no nsw flag, so %m + %n may
; signed-overflow and sext(%x) != sext(%m) + sext(%n). The narrow stride must
; not be factored, otherwise g2 is wrongly rewritten to g1 + 4 * sext(%m).
define void @slsr_gep_add_no_rewrite(ptr %p, i32 %m, i32 %n, float %r) {
; CHECK-LABEL: define void @slsr_gep_add_no_rewrite(
; CHECK-SAME: ptr [[P:%.*]], i32 [[M:%.*]], i32 [[N:%.*]], float [[R:%.*]]) {
; CHECK-NEXT: [[I1:%.*]] = sext i32 [[N]] to i64
; CHECK-NEXT: [[G1:%.*]] = getelementptr float, ptr [[P]], i64 [[I1]]
; CHECK-NEXT: store float [[R]], ptr [[G1]], align 4
; CHECK-NEXT: [[X:%.*]] = add i32 [[M]], [[N]]
; CHECK-NEXT: [[I2:%.*]] = sext i32 [[X]] to i64
; CHECK-NEXT: [[G2:%.*]] = getelementptr float, ptr [[P]], i64 [[I2]]
; CHECK-NEXT: store float [[R]], ptr [[G2]], align 4
; CHECK-NEXT: ret void
;
%i1 = sext i32 %n to i64
%g1 = getelementptr float, ptr %p, i64 %i1
store float %r, ptr %g1, align 4
%x = add i32 %m, %n
%i2 = sext i32 %x to i64
%g2 = getelementptr float, ptr %p, i64 %i2
store float %r, ptr %g2, align 4
ret void
}
; Sanity: when the narrow stride difference provably does not wrap (nsw add),
; sext distributes and the Stride-delta rewrite is still performed.
define void @slsr_gep_nsw_still_rewrites(ptr %p, i32 %n, float %r) {
; CHECK-LABEL: define void @slsr_gep_nsw_still_rewrites(
; CHECK-SAME: ptr [[P:%.*]], i32 [[N:%.*]], float [[R:%.*]]) {
; CHECK-NEXT: [[I1:%.*]] = sext i32 [[N]] to i64
; CHECK-NEXT: [[G1:%.*]] = getelementptr float, ptr [[P]], i64 [[I1]]
; CHECK-NEXT: store float [[R]], ptr [[G1]], align 4
; CHECK-NEXT: [[G2:%.*]] = getelementptr i8, ptr [[G1]], i64 64
; CHECK-NEXT: store float [[R]], ptr [[G2]], align 4
; CHECK-NEXT: ret void
;
%i1 = sext i32 %n to i64
%g1 = getelementptr float, ptr %p, i64 %i1
store float %r, ptr %g1, align 4
%x = add nsw i32 %n, 16
%i2 = sext i32 %x to i64
%g2 = getelementptr float, ptr %p, i64 %i2
store float %r, ptr %g2, align 4
ret void
}
; Same miscompile with the roles swapped: the `xor X, signmask` stride is the
; basis (g1, dominates) and the safe stride %n is the candidate (g2). The stride
; difference still sign-extends non-linearly, so neither stride being the basis
; nor the candidate makes the Stride-delta rewrite valid. g2 must stay
; independent instead of being rewritten to g1 + 4 * sext(0x80000000).
define void @slsr_gep_xor_signmask_as_basis(ptr %p, i32 %n, float %r) {
; CHECK-LABEL: define void @slsr_gep_xor_signmask_as_basis(
; CHECK-SAME: ptr [[P:%.*]], i32 [[N:%.*]], float [[R:%.*]]) {
; CHECK-NEXT: [[X:%.*]] = xor i32 [[N]], -2147483648
; CHECK-NEXT: [[I1:%.*]] = sext i32 [[X]] to i64
; CHECK-NEXT: [[G1:%.*]] = getelementptr float, ptr [[P]], i64 [[I1]]
; CHECK-NEXT: store float [[R]], ptr [[G1]], align 4
; CHECK-NEXT: [[I2:%.*]] = sext i32 [[N]] to i64
; CHECK-NEXT: [[G2:%.*]] = getelementptr float, ptr [[P]], i64 [[I2]]
; CHECK-NEXT: store float [[R]], ptr [[G2]], align 4
; CHECK-NEXT: ret void
;
%x = xor i32 %n, -2147483648
%i1 = sext i32 %x to i64
%g1 = getelementptr float, ptr %p, i64 %i1
store float %r, ptr %g1, align 4
%i2 = sext i32 %n to i64
%g2 = getelementptr float, ptr %p, i64 %i2
store float %r, ptr %g2, align 4
ret void
}
; A non-nsw `add X, C` basis with a constant stride delta is unsound too: %n+100
; may signed-overflow, so sext(%n+100) != sext(%n)+100 and rewriting g2 to
; g1 - 400 is wrong. The constant delta makes the basis stride participate, so
; it must be screened even though the candidate stride %n cannot wrap.
define void @slsr_gep_add_const_as_basis(ptr %p, i32 %n, float %r) {
; CHECK-LABEL: define void @slsr_gep_add_const_as_basis(
; CHECK-SAME: ptr [[P:%.*]], i32 [[N:%.*]], float [[R:%.*]]) {
; CHECK-NEXT: [[X:%.*]] = add i32 [[N]], 100
; CHECK-NEXT: [[I1:%.*]] = sext i32 [[X]] to i64
; CHECK-NEXT: [[G1:%.*]] = getelementptr float, ptr [[P]], i64 [[I1]]
; CHECK-NEXT: store float [[R]], ptr [[G1]], align 4
; CHECK-NEXT: [[I2:%.*]] = sext i32 [[N]] to i64
; CHECK-NEXT: [[G2:%.*]] = getelementptr float, ptr [[P]], i64 [[I2]]
; CHECK-NEXT: store float [[R]], ptr [[G2]], align 4
; CHECK-NEXT: ret void
;
%x = add i32 %n, 100
%i1 = sext i32 %x to i64
%g1 = getelementptr float, ptr %p, i64 %i1
store float %r, ptr %g1, align 4
%i2 = sext i32 %n to i64
%g2 = getelementptr float, ptr %p, i64 %i2
store float %r, ptr %g2, align 4
ret void
}