diff --git a/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-idxsize.ll b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-idxsize.ll new file mode 100644 index 0000000000000..3fb2789ce0e49 --- /dev/null +++ b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-idxsize.ll @@ -0,0 +1,262 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=lower-constant-intrinsics -S < %s | FileCheck %s + +; Some extra tests using 16-bit pointers and 16-bit index type size. This +; allows us to for example test what happens when the index type used in a +; getelementptr does not match with the index type size (e.g. when not running +; full opt pipeline before the lower-constant-intrinsics pass). + +target datalayout = "e-p:16:16:16" + + +define i32 @possible_out_of_bounds_gep_i8(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @possible_out_of_bounds_gep_i8( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i8 2, i8 10 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i8 [[OFFSET]] +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 3, i32 0 +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %obj = alloca [5 x i8] + %offset = select i1 %c0, i8 2, i8 10 + %ptr.slide = getelementptr i8, ptr %obj, i8 %offset + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +define i32 @possible_out_of_bounds_gep_i16(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @possible_out_of_bounds_gep_i16( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i16 2, i16 10 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i16 [[OFFSET]] +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 3, i32 0 +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %obj = alloca [5 x i8] + %offset = select i1 %c0, i16 2, i16 10 + %ptr.slide = getelementptr i8, ptr %obj, i16 %offset + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +define i32 @possible_out_of_bounds_gep_i32(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @possible_out_of_bounds_gep_i32( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i32 2, i32 10 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i32 [[OFFSET]] +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 3, i32 0 +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %obj = alloca [5 x i8] + %offset = select i1 %c0, i32 2, i32 10 + %ptr.slide = getelementptr i8, ptr %obj, i32 %offset + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +; SROA would produce IR like this if applied to @possible_out_of_bounds_gep_i16. +; FIXME: The %objsize_min result here is invalid. +define i32 @possible_out_of_bounds_gep_i16_sroa(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @possible_out_of_bounds_gep_i16_sroa( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[DOTSROA_GEP:%.*]] = getelementptr i8, ptr [[OBJ]], i16 2 +; CHECK-NEXT: [[DOTSROA_GEP1:%.*]] = getelementptr i8, ptr [[OBJ]], i16 10 +; CHECK-NEXT: [[OFFSET_SROA_SEL:%.*]] = select i1 [[C0]], ptr [[DOTSROA_GEP]], ptr [[DOTSROA_GEP1]] +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 3, i32 65531 +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %obj = alloca [5 x i8], align 1 + %.sroa.gep = getelementptr i8, ptr %obj, i16 2 + %.sroa.gep1 = getelementptr i8, ptr %obj, i16 10 + %offset.sroa.sel = select i1 %c0, ptr %.sroa.gep, ptr %.sroa.gep1 + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %offset.sroa.sel, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %offset.sroa.sel, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +; Indices are truncated to the pointer size in a gep. So "i32 -65526" should +; be treated as "i16 10" and we expect same result as for +; @possible_out_of_bounds_gep_i16 above. +; FIXME: The result here is incorrect (max/min is swapped). +define i32 @possible_out_of_bounds_gep_i32_trunc(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @possible_out_of_bounds_gep_i32_trunc( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i32 2, i32 -65526 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i32 [[OFFSET]] +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 0, i32 3 +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %obj = alloca [5 x i8] + %offset = select i1 %c0, i32 2, i32 -65526 ; 0xffff000a + %ptr.slide = getelementptr i8, ptr %obj, i32 %offset + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +define i32 @out_of_bounds_gep_i8(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @out_of_bounds_gep_i8( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i8 -128 +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 -1, i32 0 +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %obj = alloca [5 x i8] + %ptr.slide = getelementptr i8, ptr %obj, i8 -128 + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +define i32 @out_of_bounds_gep_i32(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @out_of_bounds_gep_i32( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i32 10 +; CHECK-NEXT: ret i32 0 +; +entry: + %obj = alloca [5 x i8] + %ptr.slide = getelementptr i8, ptr %obj, i32 10 + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +define i32 @out_of_bounds_gep_i32_trunc(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @out_of_bounds_gep_i32_trunc( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i32 -65526 +; CHECK-NEXT: ret i32 0 +; +entry: + %obj = alloca [5 x i8] + %ptr.slide = getelementptr i8, ptr %obj, i32 -65526 + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +; In this test the index will be out-of-bounds, but the current analysis won't +; detect that. The analysis will find out that %offset is in the range [-2, +; 10] which includes valid offsets that aren't out-of-bounds. Therefore we can +; expect to get -1 for %objsize_max even if an advanced analysis should be +; able to derive that we are out-of-bounds (returning 0 also for +; %objsize_max). +define i32 @out_of_bounds_gep_i16_pos_neg(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @out_of_bounds_gep_i16_pos_neg( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i32 10, i32 -2 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i32 [[OFFSET]] +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 -1, i32 0 +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %obj = alloca [5 x i8] + %offset = select i1 %c0, i32 10, i32 -2 + %ptr.slide = getelementptr i8, ptr %obj, i32 %offset + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +; With 16-bit index size %offset is either 32767 or -32768. Thus, when +; aggregating the possible offsets it we know that it is in the range [-32768, +; 32767], which includes valid offsets that aren't out-of-bounds. This is +; similar to the out_of_bounds_gep_i16_pos_neg test above, and the current +; implementation is expected to derive the result -1 for %objsize_max. +define i32 @out_of_bounds_gep_i32_trunc_select(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @out_of_bounds_gep_i32_trunc_select( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i32 32767, i32 32768 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i32 [[OFFSET]] +; CHECK-NEXT: ret i32 0 +; +entry: + %obj = alloca [5 x i8] + %offset = select i1 %c0, i32 32767, i32 32768 + %ptr.slide = getelementptr i8, ptr %obj, i32 %offset + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +; FIXME: Is 3 really correct for %objsize_min here? +define i32 @possible_out_of_bounds_gep_i8_neg(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @possible_out_of_bounds_gep_i8_neg( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i8 2, i8 -10 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i8 [[OFFSET]] +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 -1, i32 3 +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %obj = alloca [5 x i8] + %offset = select i1 %c0, i8 2, i8 -10 + %ptr.slide = getelementptr i8, ptr %obj, i8 %offset + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} + +; FIXME: Is 3 really correct for %objsize_min here? +define i32 @possible_out_of_bounds_gep_i16_neg(i1 %c0, i1 %c1) { +; CHECK-LABEL: define i32 @possible_out_of_bounds_gep_i16_neg( +; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1 +; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i16 2, i16 -10 +; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i16 [[OFFSET]] +; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 -1, i32 3 +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %obj = alloca [5 x i8] + %offset = select i1 %c0, i16 2, i16 -10 + %ptr.slide = getelementptr i8, ptr %obj, i16 %offset + %objsize_max = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 false, i1 true, i1 false) + %objsize_min = call i32 @llvm.objectsize.i32.p0(ptr %ptr.slide, i1 true, i1 true, i1 false) + %res = select i1 %c1, i32 %objsize_max, i32 %objsize_min + ret i32 %res +} diff --git a/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll index 564311da64a81..d294650d7f3e2 100644 --- a/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll +++ b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll @@ -200,8 +200,12 @@ if.end: ret i64 %size } -define i64 @pick_negative_offset_different_width(i32 %n) { -; CHECK-LABEL: @pick_negative_offset_different_width( +; FIXME: The result here looks weird. Either we reference into buffer0 with an +; oob offset. Or we reference buffer1 (8 bytes) with a 4 byte +; offset. The result 5 is wrong in both cases. Probably better to +; return -1 here since we do not know if we have an oob pointer. +define i64 @pick_negative_offset_different_width_index_maybe_too_small(i32 %n, i1 %c) { +; CHECK-LABEL: @pick_negative_offset_different_width_index_maybe_too_small( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[BUFFER0:%.*]] = alloca i8, i64 4, align 1 ; CHECK-NEXT: [[BUFFER1:%.*]] = alloca i8, i64 8, align 1 @@ -216,7 +220,8 @@ define i64 @pick_negative_offset_different_width(i32 %n) { ; CHECK: if.end: ; CHECK-NEXT: [[P:%.*]] = phi ptr [ [[OFFSETED0]], [[IF_ELSE]] ], [ [[OFFSETED1]], [[IF_END]] ] ; CHECK-NEXT: [[POFFSETED:%.*]] = getelementptr i8, ptr [[P]], i64 -2 -; CHECK-NEXT: ret i64 5 +; CHECK-NEXT: [[SIZE:%.*]] = select i1 [[C:%.*]], i64 5, i64 0 +; CHECK-NEXT: ret i64 [[SIZE]] ; entry: %buffer0 = alloca i8, i64 4 @@ -235,7 +240,51 @@ if.else: if.end: %p = phi ptr [ %offseted0, %if.then ], [ %offseted1, %if.else ] %poffseted = getelementptr i8, ptr %p, i64 -2 - %size = call i64 @llvm.objectsize.i64.p0(ptr %poffseted, i1 false, i1 false, i1 false) + %sizemax = call i64 @llvm.objectsize.i64.p0(ptr %poffseted, i1 false, i1 false, i1 false) + %sizemin = call i64 @llvm.objectsize.i64.p0(ptr %poffseted, i1 true, i1 false, i1 false) + %size = select i1 %c, i64 %sizemax, i64 %sizemin + ret i64 %size +} + +define i64 @pick_negative_offset_different_width_index_maybe_too_large(i32 %n, i1 %c) { +; CHECK-LABEL: @pick_negative_offset_different_width_index_maybe_too_large( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BUFFER0:%.*]] = alloca i8, i64 4, align 1 +; CHECK-NEXT: [[BUFFER1:%.*]] = alloca i8, i64 8, align 1 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[N:%.*]], 0 +; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[OFFSETED0:%.*]] = getelementptr i8, ptr [[BUFFER0]], i64 1 +; CHECK-NEXT: br label [[IF_END:%.*]] +; CHECK: if.else: +; CHECK-NEXT: [[OFFSETED1:%.*]] = getelementptr i8, ptr [[BUFFER1]], i64 6 +; CHECK-NEXT: br label [[IF_END]] +; CHECK: if.end: +; CHECK-NEXT: [[P:%.*]] = phi ptr [ [[OFFSETED0]], [[IF_THEN]] ], [ [[OFFSETED1]], [[IF_ELSE]] ] +; CHECK-NEXT: [[POFFSETED:%.*]] = getelementptr i8, ptr [[P]], i64 2 +; CHECK-NEXT: [[SIZE:%.*]] = select i1 [[C:%.*]], i64 1, i64 0 +; CHECK-NEXT: ret i64 [[SIZE]] +; +entry: + %buffer0 = alloca i8, i64 4 + %buffer1 = alloca i8, i64 8 + %cond = icmp eq i32 %n, 0 + br i1 %cond, label %if.then, label %if.else + +if.then: + %offseted0 = getelementptr i8, ptr %buffer0, i64 1 + br label %if.end + +if.else: + %offseted1 = getelementptr i8, ptr %buffer1, i64 6 + br label %if.end + +if.end: + %p = phi ptr [ %offseted0, %if.then ], [ %offseted1, %if.else ] + %poffseted = getelementptr i8, ptr %p, i64 2 + %sizemax = call i64 @llvm.objectsize.i64.p0(ptr %poffseted, i1 false, i1 false, i1 false) + %sizemin = call i64 @llvm.objectsize.i64.p0(ptr %poffseted, i1 true, i1 false, i1 false) + %size = select i1 %c, i64 %sizemax, i64 %sizemin ret i64 %size }