From: Roman Lebedev Date: Sun, 8 Sep 2019 17:50:40 +0000 (+0000) Subject: [NFC][InstSimplify] Some tests for dropping null check after uadd.with.overflow of... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=aeb864ee776312ca3496021587b57d8483047305;p=llvm [NFC][InstSimplify] Some tests for dropping null check after uadd.with.overflow of non-null (PR43246) https://rise4fun.com/Alive/WRzq Name: C <= Y && Y != 0 --> C <= Y iff C != 0 Pre: C != 0 %y_is_nonnull = icmp ne i64 %y, 0 %no_overflow = icmp ule i64 C, %y %r = and i1 %y_is_nonnull, %no_overflow => %r = %no_overflow Name: C <= Y || Y != 0 --> Y != 0 iff C != 0 Pre: C != 0 %y_is_nonnull = icmp ne i64 %y, 0 %no_overflow = icmp ule i64 C, %y %r = or i1 %y_is_nonnull, %no_overflow => %r = %y_is_nonnull Name: C > Y || Y == 0 --> C > Y iff C != 0 Pre: C != 0 %y_is_null = icmp eq i64 %y, 0 %overflow = icmp ugt i64 C, %y %r = or i1 %y_is_null, %overflow => %r = %overflow Name: C > Y && Y == 0 --> Y == 0 iff C != 0 Pre: C != 0 %y_is_null = icmp eq i64 %y, 0 %overflow = icmp ugt i64 C, %y %r = and i1 %y_is_null, %overflow => %r = %y_is_null https://bugs.llvm.org/show_bug.cgi?id=43246 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@371339 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/test/Transforms/InstSimplify/redundant-null-check-in-uadd_with_overflow-of-nonnull-ptr.ll b/test/Transforms/InstSimplify/redundant-null-check-in-uadd_with_overflow-of-nonnull-ptr.ll new file mode 100644 index 00000000000..87f15c3b0de --- /dev/null +++ b/test/Transforms/InstSimplify/redundant-null-check-in-uadd_with_overflow-of-nonnull-ptr.ll @@ -0,0 +1,278 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt %s -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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t0( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp uge i64 [[ADJUSTED]], [[BASE_INT]] +; CHECK-NEXT: [[RES:%.*]] = and i1 [[NON_NULL_AFTER_ADJUSTMENT]], [[NO_OVERFLOW_DURING_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t1( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ule i64 [[BASE_INT]], [[ADJUSTED]] +; CHECK-NEXT: [[RES:%.*]] = and i1 [[NON_NULL_AFTER_ADJUSTMENT]], [[NO_OVERFLOW_DURING_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t2( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp uge i64 [[ADJUSTED]], [[BASE_INT]] +; CHECK-NEXT: [[RES:%.*]] = and i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]], [[NON_NULL_AFTER_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t3( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ule i64 [[BASE_INT]], [[ADJUSTED]] +; CHECK-NEXT: [[RES:%.*]] = and i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]], [[NON_NULL_AFTER_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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 null +; pointer, or no overflow happened, then the overflow check itself is redundant. + +define i1 @t4(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t4( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp uge i64 [[ADJUSTED]], [[BASE_INT]] +; CHECK-NEXT: [[RES:%.*]] = or i1 [[NON_NULL_AFTER_ADJUSTMENT]], [[NO_OVERFLOW_DURING_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t5( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ule i64 [[BASE_INT]], [[ADJUSTED]] +; CHECK-NEXT: [[RES:%.*]] = or i1 [[NON_NULL_AFTER_ADJUSTMENT]], [[NO_OVERFLOW_DURING_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t6( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp uge i64 [[ADJUSTED]], [[BASE_INT]] +; CHECK-NEXT: [[RES:%.*]] = or i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]], [[NON_NULL_AFTER_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t7( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp ne i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ule i64 [[BASE_INT]], [[ADJUSTED]] +; CHECK-NEXT: [[RES:%.*]] = or i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]], [[NON_NULL_AFTER_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t8( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ult i64 [[ADJUSTED]], [[BASE_INT]] +; CHECK-NEXT: [[RES:%.*]] = or i1 [[NON_NULL_AFTER_ADJUSTMENT]], [[NO_OVERFLOW_DURING_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t9( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ugt i64 [[BASE_INT]], [[ADJUSTED]] +; CHECK-NEXT: [[RES:%.*]] = or i1 [[NON_NULL_AFTER_ADJUSTMENT]], [[NO_OVERFLOW_DURING_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t10( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ult i64 [[ADJUSTED]], [[BASE_INT]] +; CHECK-NEXT: [[RES:%.*]] = or i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]], [[NON_NULL_AFTER_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t11( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ugt i64 [[BASE_INT]], [[ADJUSTED]] +; CHECK-NEXT: [[RES:%.*]] = or i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]], [[NON_NULL_AFTER_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t12( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ult i64 [[ADJUSTED]], [[BASE_INT]] +; CHECK-NEXT: [[RES:%.*]] = and i1 [[NON_NULL_AFTER_ADJUSTMENT]], [[NO_OVERFLOW_DURING_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t13( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ugt i64 [[BASE_INT]], [[ADJUSTED]] +; CHECK-NEXT: [[RES:%.*]] = and i1 [[NON_NULL_AFTER_ADJUSTMENT]], [[NO_OVERFLOW_DURING_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t14( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ult i64 [[ADJUSTED]], [[BASE_INT]] +; CHECK-NEXT: [[RES:%.*]] = and i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]], [[NON_NULL_AFTER_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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(i8* nonnull %base, i64 %offset) { +; CHECK-LABEL: @t15( +; CHECK-NEXT: [[BASE_INT:%.*]] = ptrtoint i8* [[BASE:%.*]] to i64 +; CHECK-NEXT: [[ADJUSTED:%.*]] = add i64 [[BASE_INT]], [[OFFSET:%.*]] +; CHECK-NEXT: [[NON_NULL_AFTER_ADJUSTMENT:%.*]] = icmp eq i64 [[ADJUSTED]], 0 +; CHECK-NEXT: [[NO_OVERFLOW_DURING_ADJUSTMENT:%.*]] = icmp ugt i64 [[BASE_INT]], [[ADJUSTED]] +; CHECK-NEXT: [[RES:%.*]] = and i1 [[NO_OVERFLOW_DURING_ADJUSTMENT]], [[NON_NULL_AFTER_ADJUSTMENT]] +; CHECK-NEXT: ret i1 [[RES]] +; + %base_int = ptrtoint i8* %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 +}