diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp index 6b7a3e1ffe347..1c20b33b8bae1 100644 --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -683,29 +683,30 @@ combinePossibleConstantValues(std::optional LHS, } static std::optional aggregatePossibleConstantValuesImpl( - const Value *V, ObjectSizeOpts::Mode EvalMode, unsigned recursionDepth) { + const Value *V, ObjectSizeOpts::Mode EvalMode, unsigned BitWidth, + unsigned recursionDepth) { constexpr unsigned maxRecursionDepth = 4; if (recursionDepth == maxRecursionDepth) return std::nullopt; if (const auto *CI = dyn_cast(V)) { - return CI->getValue(); + return CI->getValue().sextOrTrunc(BitWidth); } else if (const auto *SI = dyn_cast(V)) { return combinePossibleConstantValues( aggregatePossibleConstantValuesImpl(SI->getTrueValue(), EvalMode, - recursionDepth + 1), + BitWidth, recursionDepth + 1), aggregatePossibleConstantValuesImpl(SI->getFalseValue(), EvalMode, - recursionDepth + 1), + BitWidth, recursionDepth + 1), EvalMode); } else if (const auto *PN = dyn_cast(V)) { unsigned Count = PN->getNumIncomingValues(); if (Count == 0) return std::nullopt; auto Acc = aggregatePossibleConstantValuesImpl( - PN->getIncomingValue(0), EvalMode, recursionDepth + 1); + PN->getIncomingValue(0), EvalMode, BitWidth, recursionDepth + 1); for (unsigned I = 1; Acc && I < Count; ++I) { auto Tmp = aggregatePossibleConstantValuesImpl( - PN->getIncomingValue(I), EvalMode, recursionDepth + 1); + PN->getIncomingValue(I), EvalMode, BitWidth, recursionDepth + 1); Acc = combinePossibleConstantValues(Acc, Tmp, EvalMode); } return Acc; @@ -715,9 +716,10 @@ static std::optional aggregatePossibleConstantValuesImpl( } static std::optional -aggregatePossibleConstantValues(const Value *V, ObjectSizeOpts::Mode EvalMode) { +aggregatePossibleConstantValues(const Value *V, ObjectSizeOpts::Mode EvalMode, + unsigned BitWidth) { if (auto *CI = dyn_cast(V)) - return CI->getValue(); + return CI->getValue().sextOrTrunc(BitWidth); if (EvalMode != ObjectSizeOpts::Mode::Min && EvalMode != ObjectSizeOpts::Mode::Max) @@ -726,7 +728,7 @@ aggregatePossibleConstantValues(const Value *V, ObjectSizeOpts::Mode EvalMode) { // Not using computeConstantRange here because we cannot guarantee it's not // doing optimization based on UB which we want to avoid when expanding // __builtin_object_size. - return aggregatePossibleConstantValuesImpl(V, EvalMode, 0u); + return aggregatePossibleConstantValuesImpl(V, EvalMode, BitWidth, 0u); } /// Align \p Size according to \p Alignment. If \p Size is greater than @@ -788,9 +790,14 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) { Options.EvalMode == ObjectSizeOpts::Mode::Min ? ObjectSizeOpts::Mode::Max : ObjectSizeOpts::Mode::Min; - auto OffsetRangeAnalysis = [EvalMode](Value &VOffset, APInt &Offset) { + // For a GEPOperator the indices are first converted to offsets in the + // pointer’s index type, so we need to provide the index type to make sure + // the min/max operations are performed in correct type. + unsigned IdxTyBits = DL.getIndexTypeSizeInBits(V->getType()); + auto OffsetRangeAnalysis = [EvalMode, IdxTyBits](Value &VOffset, + APInt &Offset) { if (auto PossibleOffset = - aggregatePossibleConstantValues(&VOffset, EvalMode)) { + aggregatePossibleConstantValues(&VOffset, EvalMode, IdxTyBits)) { Offset = *PossibleOffset; return true; } @@ -900,8 +907,9 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) { return OffsetSpan(Zero, align(Size, I.getAlign())); Value *ArraySize = I.getArraySize(); - if (auto PossibleSize = - aggregatePossibleConstantValues(ArraySize, Options.EvalMode)) { + if (auto PossibleSize = aggregatePossibleConstantValues( + ArraySize, Options.EvalMode, + ArraySize->getType()->getScalarSizeInBits())) { APInt NumElems = *PossibleSize; if (!CheckedZextOrTrunc(NumElems)) return ObjectSizeOffsetVisitor::unknown(); @@ -932,8 +940,8 @@ OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) { if (!V->getType()->isIntegerTy()) return V; - if (auto PossibleBound = - aggregatePossibleConstantValues(V, Options.EvalMode)) + if (auto PossibleBound = aggregatePossibleConstantValues( + V, Options.EvalMode, V->getType()->getScalarSizeInBits())) return ConstantInt::get(V->getType(), *PossibleBound); return V; 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..c65fe664d6ac5 --- /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. +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 3, i32 0 +; 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: [[RES:%.*]] = select i1 [[C1]], i32 -1, i32 0 +; CHECK-NEXT: ret i32 [[RES]] +; +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 }