| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt %s -passes=instsimplify -S | FileCheck %s |
| |
| ; Here we have add some offset to a non-null pointer, |
| ; and check that the result does not overflow and is not a null pointer. |
| ; But since the base pointer is already non-null, and we check for overflow, |
| ; that will already catch the get null pointer, |
| ; so the separate null check is redundant and can be dropped. |
| |
| define i1 @t0(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t0( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp uge i64 [[ADJUSTED]], [[BASE_INT]] |
| ; CHECK-NEXT: ret i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp ne i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp uge i64 %adjusted, %base_int |
| %res = and i1 %non_null_after_adjustment, %no_overflow_during_adjustment |
| ret i1 %res |
| } |
| define i1 @t1(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t1( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ule i64 [[BASE_INT]], [[ADJUSTED]] |
| ; CHECK-NEXT: ret i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp ne i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ule i64 %base_int, %adjusted ; swapped |
| %res = and i1 %non_null_after_adjustment, %no_overflow_during_adjustment |
| ret i1 %res |
| } |
| define i1 @t2(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t2( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp uge i64 [[ADJUSTED]], [[BASE_INT]] |
| ; CHECK-NEXT: ret i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp ne i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp uge i64 %adjusted, %base_int |
| %res = and i1 %no_overflow_during_adjustment, %non_null_after_adjustment ; swapped |
| ret i1 %res |
| } |
| define i1 @t3(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t3( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ule i64 [[BASE_INT]], [[ADJUSTED]] |
| ; CHECK-NEXT: ret i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp ne i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ule i64 %base_int, %adjusted ; swapped |
| %res = and i1 %no_overflow_during_adjustment, %non_null_after_adjustment ; swapped |
| ret i1 %res |
| } |
| |
| ; If the joining operator was 'or', i.e. we check that either we produced non-null |
| ; pointer, or no overflow happened, then the overflow check itself is redundant. |
| |
| define i1 @t4(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t4( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 |
| ; CHECK-NEXT: ret i1 [[NON_NULL_AFTER_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp ne i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp uge i64 %adjusted, %base_int |
| %res = or i1 %non_null_after_adjustment, %no_overflow_during_adjustment |
| ret i1 %res |
| } |
| define i1 @t5(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t5( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 |
| ; CHECK-NEXT: ret i1 [[NON_NULL_AFTER_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp ne i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ule i64 %base_int, %adjusted ; swapped |
| %res = or i1 %non_null_after_adjustment, %no_overflow_during_adjustment |
| ret i1 %res |
| } |
| define i1 @t6(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t6( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 |
| ; CHECK-NEXT: ret i1 [[NON_NULL_AFTER_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp ne i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp uge i64 %adjusted, %base_int |
| %res = or i1 %no_overflow_during_adjustment, %non_null_after_adjustment ; swapped |
| ret i1 %res |
| } |
| define i1 @t7(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t7( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 |
| ; CHECK-NEXT: ret i1 [[NON_NULL_AFTER_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp ne i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ule i64 %base_int, %adjusted ; swapped |
| %res = or i1 %no_overflow_during_adjustment, %non_null_after_adjustment ; swapped |
| ret i1 %res |
| } |
| |
| ; Or, we could be checking the reverse condition, that we either get null pointer, |
| ; or overflow happens, then again, the standalone null check is redundant and |
| ; can be dropped. |
| |
| define i1 @t8(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t8( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ult i64 [[ADJUSTED]], [[BASE_INT]] |
| ; CHECK-NEXT: ret i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp eq i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ult i64 %adjusted, %base_int |
| %res = or i1 %non_null_after_adjustment, %no_overflow_during_adjustment |
| ret i1 %res |
| } |
| define i1 @t9(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t9( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ugt i64 [[BASE_INT]], [[ADJUSTED]] |
| ; CHECK-NEXT: ret i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp eq i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ugt i64 %base_int, %adjusted ; swapped |
| %res = or i1 %non_null_after_adjustment, %no_overflow_during_adjustment |
| ret i1 %res |
| } |
| define i1 @t10(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t10( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ult i64 [[ADJUSTED]], [[BASE_INT]] |
| ; CHECK-NEXT: ret i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp eq i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ult i64 %adjusted, %base_int |
| %res = or i1 %no_overflow_during_adjustment, %non_null_after_adjustment ; swapped |
| ret i1 %res |
| } |
| define i1 @t11(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t11( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ugt i64 [[BASE_INT]], [[ADJUSTED]] |
| ; CHECK-NEXT: ret i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp eq i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ugt i64 %base_int, %adjusted ; swapped |
| %res = or i1 %no_overflow_during_adjustment, %non_null_after_adjustment ; swapped |
| ret i1 %res |
| } |
| |
| ; If the joining operator was 'and', i.e. we check that we both get null pointer |
| ; AND overflow happens, then the overflow check is redundant. |
| |
| define i1 @t12(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t12( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 |
| ; CHECK-NEXT: ret i1 [[NON_NULL_AFTER_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp eq i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ult i64 %adjusted, %base_int |
| %res = and i1 %non_null_after_adjustment, %no_overflow_during_adjustment |
| ret i1 %res |
| } |
| define i1 @t13(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t13( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 |
| ; CHECK-NEXT: ret i1 [[NON_NULL_AFTER_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp eq i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ugt i64 %base_int, %adjusted ; swapped |
| %res = and i1 %non_null_after_adjustment, %no_overflow_during_adjustment |
| ret i1 %res |
| } |
| define i1 @t14(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t14( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 |
| ; CHECK-NEXT: ret i1 [[NON_NULL_AFTER_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp eq i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ult i64 %adjusted, %base_int |
| %res = and i1 %no_overflow_during_adjustment, %non_null_after_adjustment ; swapped |
| ret i1 %res |
| } |
| define i1 @t15(ptr nonnull %base, i64 %offset) { |
| ; CHECK-LABEL: @t15( |
| ; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint ptr [[BASE:%.*]] to i64 |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 |
| ; CHECK-NEXT: ret i1 [[NON_NULL_AFTER_ADJUSTMENT]] |
| ; |
| %base_int = ptrtoint ptr %base to i64 |
| %adjusted = add i64 %base_int, %offset |
| %non_null_after_adjustment = icmp eq i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp ugt i64 %base_int, %adjusted ; swapped |
| %res = and i1 %no_overflow_during_adjustment, %non_null_after_adjustment ; swapped |
| ret i1 %res |
| } |
| |
| declare void @llvm.assume(i1) |
| define i1 @t16(i64 %base, i64 %offset) { |
| ; CHECK-LABEL: @t16( |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[BASE:%.*]], 0 |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) |
| ; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE]], [[OFFSET:%.*]] |
| ; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp uge i64 [[ADJUSTED]], [[BASE]] |
| ; CHECK-NEXT: ret i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]] |
| ; |
| %cmp = icmp slt i64 %base, 0 |
| call void @llvm.assume(i1 %cmp) |
| |
| %adjusted = add i64 %base, %offset |
| %non_null_after_adjustment = icmp ne i64 %adjusted, 0 |
| %no_overflow_during_adjustment = icmp uge i64 %adjusted, %base |
| %res = and i1 %non_null_after_adjustment, %no_overflow_during_adjustment |
| ret i1 %res |
| } |