| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt < %s -passes=instsimplify -S | FileCheck %s |
| target datalayout = "p:32:32-p1:64:64" |
| |
| ; This is a collection of tests checking whether we can prove pointers |
| ; derived from two allocas as inequal *via offset checks*. Note that |
| ; instcombine has alternate approaches (one cmp rule, and compare |
| ; bases of common offset) that also handles these, but with different |
| ; logic. |
| |
| ; %a follows %b, derived equal |
| define i1 @adjacent_alloca() { |
| ; CHECK-LABEL: @adjacent_alloca( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[B:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[B_OFF:%.*]] = getelementptr i8, ptr [[B]], i64 4 |
| ; CHECK-NEXT: [[RES:%.*]] = icmp ne ptr [[A]], [[B_OFF]] |
| ; CHECK-NEXT: ret i1 [[RES]] |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %b.off = getelementptr i8, ptr %b, i64 4 |
| %res = icmp ne ptr %a, %b.off |
| ret i1 %res |
| } |
| |
| ; %b follows %a, derived equal |
| define i1 @adjacent_alloca2() { |
| ; CHECK-LABEL: @adjacent_alloca2( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[B:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[A_OFF:%.*]] = getelementptr i8, ptr [[A]], i64 4 |
| ; CHECK-NEXT: [[RES:%.*]] = icmp ne ptr [[A_OFF]], [[B]] |
| ; CHECK-NEXT: ret i1 [[RES]] |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 4 |
| %res = icmp ne ptr %a.off, %b |
| ret i1 %res |
| } |
| |
| define i1 @positive_non_equal_end() { |
| ; CHECK-LABEL: @positive_non_equal_end( |
| ; CHECK-NEXT: ret i1 true |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 4 |
| %b.off = getelementptr i8, ptr %b, i64 4 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| ; %b follows %a, derived equal |
| define i1 @positive_equal_past_end() { |
| ; CHECK-LABEL: @positive_equal_past_end( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[B:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[A_OFF:%.*]] = getelementptr i8, ptr [[A]], i64 8 |
| ; CHECK-NEXT: [[B_OFF:%.*]] = getelementptr i8, ptr [[B]], i64 12 |
| ; CHECK-NEXT: [[RES:%.*]] = icmp ne ptr [[A_OFF]], [[B_OFF]] |
| ; CHECK-NEXT: ret i1 [[RES]] |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 8 |
| %b.off = getelementptr i8, ptr %b, i64 12 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| define i1 @positive_non_equal() { |
| ; CHECK-LABEL: @positive_non_equal( |
| ; CHECK-NEXT: ret i1 true |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 3 |
| %b.off = getelementptr i8, ptr %b, i64 3 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| ; %a follows %b, derived equal |
| define i1 @one_neg_equal1() { |
| ; CHECK-LABEL: @one_neg_equal1( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[B:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[A_OFF:%.*]] = getelementptr i8, ptr [[A]], i64 -1 |
| ; CHECK-NEXT: [[B_OFF:%.*]] = getelementptr i8, ptr [[B]], i64 3 |
| ; CHECK-NEXT: [[RES:%.*]] = icmp ne ptr [[A_OFF]], [[B_OFF]] |
| ; CHECK-NEXT: ret i1 [[RES]] |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 -1 |
| %b.off = getelementptr i8, ptr %b, i64 3 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| ; %b follows %a, derived equal |
| define i1 @one_neg_equal2() { |
| ; CHECK-LABEL: @one_neg_equal2( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[B:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[A_OFF:%.*]] = getelementptr i8, ptr [[A]], i64 3 |
| ; CHECK-NEXT: [[B_OFF:%.*]] = getelementptr i8, ptr [[B]], i64 -1 |
| ; CHECK-NEXT: [[RES:%.*]] = icmp ne ptr [[A_OFF]], [[B_OFF]] |
| ; CHECK-NEXT: ret i1 [[RES]] |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 3 |
| %b.off = getelementptr i8, ptr %b, i64 -1 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| ; %b follows %a, derived equal |
| define i1 @both_neg_equal() { |
| ; CHECK-LABEL: @both_neg_equal( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[B:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[A_OFF:%.*]] = getelementptr i8, ptr [[A]], i64 -4 |
| ; CHECK-NEXT: [[B_OFF:%.*]] = getelementptr i8, ptr [[B]], i64 -8 |
| ; CHECK-NEXT: [[RES:%.*]] = icmp ne ptr [[A_OFF]], [[B_OFF]] |
| ; CHECK-NEXT: ret i1 [[RES]] |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 -4 |
| %b.off = getelementptr i8, ptr %b, i64 -8 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| define i1 @mixed_offsets1() { |
| ; CHECK-LABEL: @mixed_offsets1( |
| ; CHECK-NEXT: ret i1 true |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 -1 |
| %b.off = getelementptr i8, ptr %b, i64 2 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| define i1 @mixed_offsets2() { |
| ; CHECK-LABEL: @mixed_offsets2( |
| ; CHECK-NEXT: ret i1 true |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 1 |
| %b.off = getelementptr i8, ptr %b, i64 -2 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| define i1 @negative_in_other() { |
| ; CHECK-LABEL: @negative_in_other( |
| ; CHECK-NEXT: ret i1 true |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 -3 |
| %b.off = getelementptr i8, ptr %b, i64 -2 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| define i1 @mixed_alloca_size1() { |
| ; CHECK-LABEL: @mixed_alloca_size1( |
| ; CHECK-NEXT: ret i1 true |
| ; |
| %a = alloca i8, i32 2 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 1 |
| %b.off = getelementptr i8, ptr %b, i64 3 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| define i1 @mixed_alloca_size2() { |
| ; CHECK-LABEL: @mixed_alloca_size2( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[B:%.*]] = alloca i8, i32 2, align 1 |
| ; CHECK-NEXT: [[A_OFF:%.*]] = getelementptr i8, ptr [[A]], i64 1 |
| ; CHECK-NEXT: [[B_OFF:%.*]] = getelementptr i8, ptr [[B]], i64 3 |
| ; CHECK-NEXT: [[RES:%.*]] = icmp ne ptr [[A_OFF]], [[B_OFF]] |
| ; CHECK-NEXT: ret i1 [[RES]] |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 2 |
| %a.off = getelementptr i8, ptr %a, i64 1 |
| %b.off = getelementptr i8, ptr %b, i64 3 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| define i1 @mixed_alloca_size3() { |
| ; CHECK-LABEL: @mixed_alloca_size3( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i8, i32 2, align 1 |
| ; CHECK-NEXT: [[B:%.*]] = alloca i8, i32 4, align 1 |
| ; CHECK-NEXT: [[A_OFF:%.*]] = getelementptr i8, ptr [[A]], i64 -1 |
| ; CHECK-NEXT: [[B_OFF:%.*]] = getelementptr i8, ptr [[B]], i64 -3 |
| ; CHECK-NEXT: [[RES:%.*]] = icmp ne ptr [[A_OFF]], [[B_OFF]] |
| ; CHECK-NEXT: ret i1 [[RES]] |
| ; |
| %a = alloca i8, i32 2 |
| %b = alloca i8, i32 4 |
| %a.off = getelementptr i8, ptr %a, i64 -1 |
| %b.off = getelementptr i8, ptr %b, i64 -3 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| define i1 @mixed_alloca_size4() { |
| ; CHECK-LABEL: @mixed_alloca_size4( |
| ; CHECK-NEXT: ret i1 true |
| ; |
| %a = alloca i8, i32 4 |
| %b = alloca i8, i32 2 |
| %a.off = getelementptr i8, ptr %a, i64 -1 |
| %b.off = getelementptr i8, ptr %b, i64 -3 |
| %res = icmp ne ptr %a.off, %b.off |
| ret i1 %res |
| } |
| |
| define i1 @zst_alloca_start() { |
| ; CHECK-LABEL: @zst_alloca_start( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[A2:%.*]] = alloca {}, align 1 |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[A]], [[A2]] |
| ; CHECK-NEXT: call void @escape(ptr [[A]], ptr [[A2]]) |
| ; CHECK-NEXT: ret i1 [[CMP]] |
| ; |
| %a = alloca i64 |
| %a2 = alloca {}, align 1 |
| %gep = getelementptr i8, ptr %a, i64 0 |
| %cmp = icmp eq ptr %gep, %a2 |
| call void @escape(ptr %a, ptr %a2) |
| ret i1 %cmp |
| } |
| |
| define i1 @zst_alloca_middle() { |
| ; CHECK-LABEL: @zst_alloca_middle( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[A2:%.*]] = alloca {}, align 1 |
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 4 |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[GEP]], [[A2]] |
| ; CHECK-NEXT: call void @escape(ptr [[A]], ptr [[A2]]) |
| ; CHECK-NEXT: ret i1 [[CMP]] |
| ; |
| %a = alloca i64 |
| %a2 = alloca {}, align 1 |
| %gep = getelementptr i8, ptr %a, i64 4 |
| %cmp = icmp eq ptr %gep, %a2 |
| call void @escape(ptr %a, ptr %a2) |
| ret i1 %cmp |
| } |
| |
| define i1 @zst_alloca_end() { |
| ; CHECK-LABEL: @zst_alloca_end( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[A2:%.*]] = alloca {}, align 1 |
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 8 |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[GEP]], [[A2]] |
| ; CHECK-NEXT: call void @escape(ptr [[A]], ptr [[A2]]) |
| ; CHECK-NEXT: ret i1 [[CMP]] |
| ; |
| %a = alloca i64 |
| %a2 = alloca {}, align 1 |
| %gep = getelementptr i8, ptr %a, i64 8 |
| %cmp = icmp eq ptr %gep, %a2 |
| call void @escape(ptr %a, ptr %a2) |
| ret i1 %cmp |
| } |
| |
| @gz = external global {}, align 1 |
| |
| define i1 @zst_global_start() { |
| ; CHECK-LABEL: @zst_global_start( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[A]], @gz |
| ; CHECK-NEXT: call void @escape(ptr [[A]], ptr @gz) |
| ; CHECK-NEXT: ret i1 [[CMP]] |
| ; |
| %a = alloca i64 |
| %gep = getelementptr i8, ptr %a, i64 0 |
| %cmp = icmp eq ptr %gep, @gz |
| call void @escape(ptr %a, ptr @gz) |
| ret i1 %cmp |
| } |
| |
| define i1 @zst_global_middle() { |
| ; CHECK-LABEL: @zst_global_middle( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 4 |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[GEP]], @gz |
| ; CHECK-NEXT: call void @escape(ptr [[A]], ptr @gz) |
| ; CHECK-NEXT: ret i1 [[CMP]] |
| ; |
| %a = alloca i64 |
| %gep = getelementptr i8, ptr %a, i64 4 |
| %cmp = icmp eq ptr %gep, @gz |
| call void @escape(ptr %a, ptr @gz) |
| ret i1 %cmp |
| } |
| |
| define i1 @zst_global_end() { |
| ; CHECK-LABEL: @zst_global_end( |
| ; CHECK-NEXT: [[A:%.*]] = alloca i64, align 8 |
| ; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 8 |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[GEP]], @gz |
| ; CHECK-NEXT: call void @escape(ptr [[A]], ptr @gz) |
| ; CHECK-NEXT: ret i1 [[CMP]] |
| ; |
| %a = alloca i64 |
| %gep = getelementptr i8, ptr %a, i64 8 |
| %cmp = icmp eq ptr %gep, @gz |
| call void @escape(ptr %a, ptr @gz) |
| ret i1 %cmp |
| } |
| |
| declare void @escape(ptr, ptr) |
| |
| attributes #0 = { null_pointer_is_valid } |