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