| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s |
| |
| ; Tests for using inbounds information from GEPs where the GEP only causes UB in the use blocks. |
| |
| declare void @noundef(ptr noundef) willreturn nounwind |
| declare void @noundef2(ptr noundef) |
| |
| declare void @use(i1) |
| |
| ; %start + %n.ext is guaranteed to not overflow (due to inbounds). |
| ; %start + %idx.ext does not overflow if %idx.ext <= %n.ext. |
| define i1 @inbounds_poison_is_ub_in_use_block_1(ptr %src, i32 %n, i32 %idx) { |
| ; CHECK-LABEL: @inbounds_poison_is_ub_in_use_block_1( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64 |
| ; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]] |
| ; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]] |
| ; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 |
| ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] |
| ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] |
| ; CHECK: then: |
| ; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) |
| ; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] |
| ; CHECK: else: |
| ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] |
| ; |
| entry: |
| %n.ext = zext i32 %n to i64 |
| %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext |
| %cmp.idx = icmp ult i32 %idx, %n |
| %idx.ext = zext i32 %idx to i64 |
| %src.idx = getelementptr i32, ptr %src, i64 %idx.ext |
| br i1 %cmp.idx, label %then, label %else |
| |
| then: |
| call void @noundef(ptr %upper) |
| %cmp.upper.1 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.1 |
| |
| else: |
| %cmp.upper.2 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.2 |
| } |
| |
| define i1 @inbounds_poison_is_ub_in_use_block_2(ptr %src, i32 %n, i32 %idx, i1 %c) { |
| ; CHECK-LABEL: @inbounds_poison_is_ub_in_use_block_2( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64 |
| ; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]] |
| ; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]] |
| ; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 |
| ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] |
| ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] |
| ; CHECK: then: |
| ; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] |
| ; CHECK: else: |
| ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] |
| ; |
| entry: |
| %n.ext = zext i32 %n to i64 |
| %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext |
| %cmp.idx = icmp ult i32 %idx, %n |
| %idx.ext = zext i32 %idx to i64 |
| %src.idx = getelementptr i32, ptr %src, i64 %idx.ext |
| br i1 %cmp.idx, label %then, label %else |
| |
| then: |
| %cmp.upper.1 = icmp ule ptr %src.idx, %upper |
| call void @noundef(ptr %upper) |
| ret i1 %cmp.upper.1 |
| |
| else: |
| %cmp.upper.2 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.2 |
| } |
| |
| declare void @llvm.assume(i1) |
| |
| ; %start + %n.ext is guaranteed to not overflow (due to inbounds). |
| ; %start + %idx.ext does not overflow if %idx.ext <= %n.ext. |
| define i1 @inbounds_poison_is_ub_in_use_block_by_assume(ptr %src, i32 %n, i32 %idx) { |
| ; CHECK-LABEL: @inbounds_poison_is_ub_in_use_block_by_assume( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64 |
| ; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]] |
| ; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]] |
| ; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 |
| ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] |
| ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] |
| ; CHECK: then: |
| ; CHECK-NEXT: [[CMP_NE:%.*]] = icmp ule ptr null, [[UPPER]] |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[CMP_NE]]) |
| ; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] |
| ; CHECK: else: |
| ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] |
| ; |
| entry: |
| %n.ext = zext i32 %n to i64 |
| %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext |
| %cmp.idx = icmp ult i32 %idx, %n |
| %idx.ext = zext i32 %idx to i64 |
| %src.idx = getelementptr i32, ptr %src, i64 %idx.ext |
| br i1 %cmp.idx, label %then, label %else |
| |
| then: |
| %cmp.ne = icmp ule ptr null, %upper |
| call void @llvm.assume(i1 %cmp.ne) |
| %cmp.upper.1 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.1 |
| |
| else: |
| %cmp.upper.2 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.2 |
| } |
| |
| |
| ; %start + %n.ext is guaranteed to not overflow (due to inbounds). |
| ; %start + %idx.ext does not overflow if %idx.ext <= %n.ext. |
| define i1 @inbounds_poison_is_ub_in_in_multiple_use_blocks_1(ptr %src, i32 %n, i32 %idx, i1 %c) { |
| ; CHECK-LABEL: @inbounds_poison_is_ub_in_in_multiple_use_blocks_1( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64 |
| ; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]] |
| ; CHECK-NEXT: br i1 [[C:%.*]], label [[CHECK_BB:%.*]], label [[EXIT:%.*]] |
| ; CHECK: check.bb: |
| ; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) |
| ; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]] |
| ; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 |
| ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] |
| ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] |
| ; CHECK: then: |
| ; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) |
| ; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] |
| ; CHECK: else: |
| ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: ret i1 false |
| ; |
| entry: |
| %n.ext = zext i32 %n to i64 |
| %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext |
| br i1 %c, label %check.bb, label %exit |
| |
| check.bb: |
| call void @noundef(ptr %upper) |
| %cmp.idx = icmp ult i32 %idx, %n |
| %idx.ext = zext i32 %idx to i64 |
| %src.idx = getelementptr i32, ptr %src, i64 %idx.ext |
| br i1 %cmp.idx, label %then, label %else |
| |
| then: |
| call void @noundef(ptr %upper) |
| %cmp.upper.1 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.1 |
| |
| else: |
| %cmp.upper.2 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.2 |
| |
| exit: |
| ret i1 false |
| } |
| |
| define i1 @may_exit_before_ub_is_caused(ptr %src, i32 %n, i32 %idx) { |
| ; CHECK-LABEL: @may_exit_before_ub_is_caused( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64 |
| ; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]] |
| ; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]] |
| ; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 |
| ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] |
| ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] |
| ; CHECK: then: |
| ; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: call void @use(i1 [[CMP_UPPER_1]]) |
| ; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] |
| ; CHECK: else: |
| ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] |
| ; |
| entry: |
| %n.ext = zext i32 %n to i64 |
| %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext |
| %cmp.idx = icmp ult i32 %idx, %n |
| %idx.ext = zext i32 %idx to i64 |
| %src.idx = getelementptr i32, ptr %src, i64 %idx.ext |
| br i1 %cmp.idx, label %then, label %else |
| |
| then: |
| %cmp.upper.1 = icmp ule ptr %src.idx, %upper |
| call void @use(i1 %cmp.upper.1); |
| call void @noundef(ptr %upper) |
| ret i1 %cmp.upper.1 |
| |
| else: |
| %cmp.upper.2 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.2 |
| } |
| |
| define i1 @only_UB_in_false_block(ptr %src, i32 %n, i32 %idx) { |
| ; CHECK-LABEL: @only_UB_in_false_block( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64 |
| ; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]] |
| ; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]] |
| ; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 |
| ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] |
| ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] |
| ; CHECK: then: |
| ; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] |
| ; CHECK: else: |
| ; CHECK-NEXT: call void @noundef(ptr [[UPPER]]) |
| ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] |
| ; |
| entry: |
| %n.ext = zext i32 %n to i64 |
| %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext |
| %cmp.idx = icmp ult i32 %idx, %n |
| %idx.ext = zext i32 %idx to i64 |
| %src.idx = getelementptr i32, ptr %src, i64 %idx.ext |
| br i1 %cmp.idx, label %then, label %else |
| |
| then: |
| %cmp.upper.1 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.1 |
| |
| else: |
| call void @noundef(ptr %upper) |
| %cmp.upper.2 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.2 |
| } |
| |
| define i1 @only_ub_by_assume_in_false_block(ptr %src, i32 %n, i32 %idx) { |
| ; CHECK-LABEL: @only_ub_by_assume_in_false_block( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64 |
| ; CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]] |
| ; CHECK-NEXT: [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]] |
| ; CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64 |
| ; CHECK-NEXT: [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]] |
| ; CHECK-NEXT: br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]] |
| ; CHECK: then: |
| ; CHECK-NEXT: [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_1]] |
| ; CHECK: else: |
| ; CHECK-NEXT: [[CMP_NE:%.*]] = icmp ule ptr null, [[UPPER]] |
| ; CHECK-NEXT: call void @llvm.assume(i1 [[CMP_NE]]) |
| ; CHECK-NEXT: [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]] |
| ; CHECK-NEXT: ret i1 [[CMP_UPPER_2]] |
| ; |
| entry: |
| %n.ext = zext i32 %n to i64 |
| %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext |
| %cmp.idx = icmp ult i32 %idx, %n |
| %idx.ext = zext i32 %idx to i64 |
| %src.idx = getelementptr i32, ptr %src, i64 %idx.ext |
| br i1 %cmp.idx, label %then, label %else |
| |
| then: |
| %cmp.upper.1 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.1 |
| |
| else: |
| %cmp.ne = icmp ule ptr null, %upper |
| call void @llvm.assume(i1 %cmp.ne) |
| %cmp.upper.2 = icmp ule ptr %src.idx, %upper |
| ret i1 %cmp.upper.2 |
| } |