| ; 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 |
| } |