blob: b4dc080411408cbba7d481c393f27bda6ef59450 [file]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -mtriple=x86_64-unknown-linux-gnu -passes=load-store-vectorizer -S -o - %s | FileCheck %s
;
; Test that LoadStoreVectorizer uses SCEV unsigned range analysis to prove
; that zext won't cause unsigned overflow when adding a constant offset,
; enabling vectorization of loads with non-nuw index arithmetic.
;
; The test uses byte GEPs (i8 element) whose outer pointer SCEV difference
; is not a compile-time constant (SCEV can't simplify zext(urem+K)-zext(urem)),
; so getConstantOffsetComplexAddrs is invoked. Inside it, safety checks 1-3
; fail, but the 4th check uses SCEV's unsigned range of urem (bounded by the
; divisor) to prove no zext overflow.
target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"
; Positive case: index bounded by urem, so SCEV knows the unsigned range is
; [0, 4294967291] (= UINT32_MAX - 4). Adding 4 can't overflow i32 through
; zext. Checks 1-3 fail (no nuw flags, KnownBits has no zeros), but check 4
; (SCEV unsigned range) proves safety -> vectorizes to <2 x i32>.
;
define void @scev_range_urem_bounded(ptr noalias %in, ptr noalias %out, i32 %idx) {
; urem bounds the index to [0, 0xFFFFFFFB]; adding 4 keeps it within i32.
; CHECK-LABEL: define void @scev_range_urem_bounded(
; CHECK-SAME: ptr noalias [[IN:%.*]], ptr noalias [[OUT:%.*]], i32 [[IDX:%.*]]) {
; CHECK-NEXT: [[BOUNDED:%.*]] = urem i32 [[IDX]], -4
; CHECK-NEXT: [[EXT0:%.*]] = zext i32 [[BOUNDED]] to i64
; CHECK-NEXT: [[GEP0:%.*]] = getelementptr i8, ptr [[IN]], i64 [[EXT0]]
; CHECK-NEXT: [[TMP1:%.*]] = load <2 x i32>, ptr [[GEP0]], align 4
; CHECK-NEXT: [[V01:%.*]] = extractelement <2 x i32> [[TMP1]], i64 0
; CHECK-NEXT: [[V42:%.*]] = extractelement <2 x i32> [[TMP1]], i64 1
; CHECK-NEXT: [[S:%.*]] = add i32 [[V01]], [[V42]]
; CHECK-NEXT: store i32 [[S]], ptr [[OUT]], align 4
; CHECK-NEXT: ret void
;
%bounded = urem i32 %idx, 4294967292
%ext0 = zext i32 %bounded to i64
%gep0 = getelementptr i8, ptr %in, i64 %ext0
%v0 = load i32, ptr %gep0, align 4
%add4 = add i32 %bounded, 4
%ext4 = zext i32 %add4 to i64
%gep4 = getelementptr i8, ptr %in, i64 %ext4
%v4 = load i32, ptr %gep4, align 4
%s = add i32 %v0, %v4
store i32 %s, ptr %out, align 4
ret void
}
; Negative case: Unbounded argument. SCEV range is full [0, UINT32_MAX],
; so UINT32_MAX + 4 overflows i32 through zext. Check 4 fails -> stays scalar.
;
define void @scev_range_unbounded(ptr noalias %in, ptr noalias %out, i32 %idx) {
; CHECK-LABEL: define void @scev_range_unbounded(
; CHECK-SAME: ptr noalias [[IN:%.*]], ptr noalias [[OUT:%.*]], i32 [[IDX:%.*]]) {
; CHECK-NEXT: [[EXT0:%.*]] = zext i32 [[IDX]] to i64
; CHECK-NEXT: [[GEP0:%.*]] = getelementptr i8, ptr [[IN]], i64 [[EXT0]]
; CHECK-NEXT: [[V0:%.*]] = load i32, ptr [[GEP0]], align 4
; CHECK-NEXT: [[ADD4:%.*]] = add i32 [[IDX]], 4
; CHECK-NEXT: [[EXT4:%.*]] = zext i32 [[ADD4]] to i64
; CHECK-NEXT: [[GEP4:%.*]] = getelementptr i8, ptr [[IN]], i64 [[EXT4]]
; CHECK-NEXT: [[V4:%.*]] = load i32, ptr [[GEP4]], align 4
; CHECK-NEXT: [[S:%.*]] = add i32 [[V0]], [[V4]]
; CHECK-NEXT: store i32 [[S]], ptr [[OUT]], align 4
; CHECK-NEXT: ret void
;
%ext0 = zext i32 %idx to i64
%gep0 = getelementptr i8, ptr %in, i64 %ext0
%v0 = load i32, ptr %gep0, align 4
%add4 = add i32 %idx, 4
%ext4 = zext i32 %add4 to i64
%gep4 = getelementptr i8, ptr %in, i64 %ext4
%v4 = load i32, ptr %gep4, align 4
%s = add i32 %v0, %v4
store i32 %s, ptr %out, align 4
ret void
}