Skip to content

release/19.x: [llvm] Fix __builtin_object_size interaction between Negative Offset … (#111827) #114786

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions llvm/lib/Analysis/MemoryBuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -997,16 +997,29 @@ ObjectSizeOffsetVisitor::combineSizeOffset(SizeOffsetAPInt LHS,
return ObjectSizeOffsetVisitor::unknown();

switch (Options.EvalMode) {
case ObjectSizeOpts::Mode::Min:
return (getSizeWithOverflow(LHS).slt(getSizeWithOverflow(RHS))) ? LHS : RHS;
case ObjectSizeOpts::Mode::Max:
return (getSizeWithOverflow(LHS).sgt(getSizeWithOverflow(RHS))) ? LHS : RHS;
case ObjectSizeOpts::Mode::Min: {
APInt RemainingSizeLHS = LHS.Size - LHS.Offset;
APInt RemainingSizeRHS = RHS.Size - RHS.Offset;
APInt RemainingSize = RemainingSizeLHS.slt(RemainingSizeRHS)
? RemainingSizeLHS
: RemainingSizeRHS;
APInt Offset = LHS.Offset.slt(RHS.Offset) ? LHS.Offset : RHS.Offset;
return {RemainingSize + Offset, Offset};
}
case ObjectSizeOpts::Mode::Max: {
APInt RemainingSizeLHS = LHS.Size - LHS.Offset;
APInt RemainingSizeRHS = RHS.Size - RHS.Offset;
APInt RemainingSize = RemainingSizeLHS.sgt(RemainingSizeRHS)
? RemainingSizeLHS
: RemainingSizeRHS;
APInt Offset = LHS.Offset.sgt(RHS.Offset) ? LHS.Offset : RHS.Offset;
return {RemainingSize + Offset, Offset};
}
case ObjectSizeOpts::Mode::ExactSizeFromOffset:
return (getSizeWithOverflow(LHS).eq(getSizeWithOverflow(RHS)))
? LHS
: ObjectSizeOffsetVisitor::unknown();
// Treat this as ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset to work
// around incorrect uses of the result.
case ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset:
return LHS == RHS ? LHS : ObjectSizeOffsetVisitor::unknown();
return (LHS == RHS) ? LHS : ObjectSizeOffsetVisitor::unknown();
}
llvm_unreachable("missing an eval mode");
}
Expand Down Expand Up @@ -1084,7 +1097,13 @@ SizeOffsetValue ObjectSizeOffsetEvaluator::compute(Value *V) {
}

SizeOffsetValue ObjectSizeOffsetEvaluator::compute_(Value *V) {
ObjectSizeOffsetVisitor Visitor(DL, TLI, Context, EvalOpts);

// Only trust ObjectSizeOffsetVisitor in exact mode, otherwise fallback on
// dynamic computation.
ObjectSizeOpts VisitorEvalOpts(EvalOpts);
VisitorEvalOpts.EvalMode = ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset;
ObjectSizeOffsetVisitor Visitor(DL, TLI, Context, VisitorEvalOpts);

SizeOffsetAPInt Const = Visitor.compute(V);
if (Const.bothKnown())
return SizeOffsetValue(ConstantInt::get(Context, Const.Size),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: opt -passes=instcombine -S < %s | FileCheck %s
; RUN: opt -passes=instcombine,lower-constant-intrinsics -S < %s | FileCheck %s

; #include <stdlib.h>
; #include <stdio.h>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,257 @@ if.end:
%size = call i64 @llvm.objectsize.i64.p0(ptr %p, i1 true, i1 true, i1 false)
ret i64 %size
}

define i64 @pick_negative_offset(i32 %n) {
; CHECK-LABEL: @pick_negative_offset(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[BUFFER0:%.*]] = alloca i8, i64 20, align 1
; CHECK-NEXT: [[OFFSETED0:%.*]] = getelementptr i8, ptr [[BUFFER0]], i64 20
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[N:%.*]], 0
; CHECK-NEXT: br i1 [[COND]], label [[IF_ELSE:%.*]], label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: [[BUFFER1:%.*]] = alloca i8, i64 20, align 1
; CHECK-NEXT: [[OFFSETED1:%.*]] = getelementptr i8, ptr [[BUFFER1]], i64 20
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[P:%.*]] = phi ptr [ [[OFFSETED1]], [[IF_ELSE]] ], [ [[OFFSETED0]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[POFFSETED:%.*]] = getelementptr i8, ptr [[P]], i64 -4
; CHECK-NEXT: ret i64 4
;
entry:
%buffer0 = alloca i8, i64 20
%offseted0 = getelementptr i8, ptr %buffer0, i64 20
%cond = icmp eq i32 %n, 0
br i1 %cond, label %if.else, label %if.end

if.else:
%buffer1 = alloca i8, i64 20
%offseted1 = getelementptr i8, ptr %buffer1, i64 20
br label %if.end

if.end:
%p = phi ptr [ %offseted1, %if.else ], [ %offseted0, %entry ]
%poffseted = getelementptr i8, ptr %p, i64 -4
%size = call i64 @llvm.objectsize.i64.p0(ptr %poffseted, i1 false, i1 false, i1 false)
ret i64 %size
}

define i64 @pick_negative_offset_different_width(i32 %n) {
; CHECK-LABEL: @pick_negative_offset_different_width(
; 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_ELSE:%.*]], label [[IF_END:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[OFFSETED0:%.*]] = getelementptr i8, ptr [[BUFFER0]], i64 1
; CHECK-NEXT: br label [[IF_END1:%.*]]
; CHECK: if.else:
; CHECK-NEXT: [[OFFSETED1:%.*]] = getelementptr i8, ptr [[BUFFER1]], i64 6
; CHECK-NEXT: br label [[IF_END1]]
; 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
;
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
%size = call i64 @llvm.objectsize.i64.p0(ptr %poffseted, i1 false, i1 false, i1 false)
ret i64 %size
}

define i64 @pick_negative_offset_with_nullptr(i32 %n) {
; CHECK-LABEL: @pick_negative_offset_with_nullptr(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[BUFFER0:%.*]] = alloca i8, i64 20, align 1
; CHECK-NEXT: [[OFFSETED0:%.*]] = getelementptr i8, ptr [[BUFFER0]], i64 20
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[N:%.*]], 0
; CHECK-NEXT: br i1 [[COND]], label [[IF_ELSE:%.*]], label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[P0:%.*]] = phi ptr [ [[OFFSETED0]], [[ENTRY:%.*]] ], [ null, [[IF_ELSE]] ]
; CHECK-NEXT: [[P1:%.*]] = phi ptr [ null, [[IF_ELSE]] ], [ [[OFFSETED0]], [[ENTRY]] ]
; CHECK-NEXT: [[P0OFFSETED:%.*]] = getelementptr i8, ptr [[P0]], i64 -4
; CHECK-NEXT: [[P1OFFSETED:%.*]] = getelementptr i8, ptr [[P1]], i64 -4
; CHECK-NEXT: ret i64 4
;
entry:
%buffer0 = alloca i8, i64 20
%offseted0 = getelementptr i8, ptr %buffer0, i64 20
%cond = icmp eq i32 %n, 0
br i1 %cond, label %if.else, label %if.end

if.else:
br label %if.end

if.end:
%p0 = phi ptr [ %offseted0, %entry ], [ null, %if.else ]
%p1 = phi ptr [ null, %if.else ], [ %offseted0, %entry ]
%p0offseted = getelementptr i8, ptr %p0, i64 -4
%p1offseted = getelementptr i8, ptr %p1, i64 -4
%size0 = call i64 @llvm.objectsize.i64.p0(ptr %p0offseted, i1 false, i1 false, i1 false)
%size1 = call i64 @llvm.objectsize.i64.p0(ptr %p1offseted, i1 false, i1 false, i1 false)
%size = select i1 %cond, i64 %size0, i64 %size1
ret i64 %size
}

define i64 @pick_negative_offset_with_unsized_nullptr(i32 %n) {
; CHECK-LABEL: @pick_negative_offset_with_unsized_nullptr(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[BUFFER0:%.*]] = alloca i8, i64 20, align 1
; CHECK-NEXT: [[OFFSETED0:%.*]] = getelementptr i8, ptr [[BUFFER0]], i64 20
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[N:%.*]], 0
; CHECK-NEXT: br i1 [[COND]], label [[IF_ELSE:%.*]], label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[P0:%.*]] = phi ptr [ [[OFFSETED0]], [[ENTRY:%.*]] ], [ null, [[IF_ELSE]] ]
; CHECK-NEXT: [[P1:%.*]] = phi ptr [ null, [[IF_ELSE]] ], [ [[OFFSETED0]], [[ENTRY]] ]
; CHECK-NEXT: [[P0OFFSETED:%.*]] = getelementptr i8, ptr [[P0]], i64 -4
; CHECK-NEXT: [[P1OFFSETED:%.*]] = getelementptr i8, ptr [[P1]], i64 -4
; CHECK-NEXT: ret i64 -1
;
entry:
%buffer0 = alloca i8, i64 20
%offseted0 = getelementptr i8, ptr %buffer0, i64 20
%cond = icmp eq i32 %n, 0
br i1 %cond, label %if.else, label %if.end

if.else:
br label %if.end

if.end:
%p0 = phi ptr [ %offseted0, %entry ], [ null, %if.else ]
%p1 = phi ptr [ null, %if.else ], [ %offseted0, %entry ]
%p0offseted = getelementptr i8, ptr %p0, i64 -4
%p1offseted = getelementptr i8, ptr %p1, i64 -4
%size0 = call i64 @llvm.objectsize.i64.p0(ptr %p0offseted, i1 false, i1 true, i1 false)
%size1 = call i64 @llvm.objectsize.i64.p0(ptr %p1offseted, i1 false, i1 true, i1 false)
%size = select i1 %cond, i64 %size0, i64 %size1
ret i64 %size
}

define i64 @chain_pick_negative_offset_with_nullptr(i32 %x) {
; CHECK-LABEL: @chain_pick_negative_offset_with_nullptr(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARRAY:%.*]] = alloca [4 x i32], align 4
; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[X:%.*]], 0
; CHECK-NEXT: [[P:%.*]] = getelementptr i8, ptr [[ARRAY]], i64 8
; CHECK-NEXT: [[COND:%.*]] = select i1 [[C]], ptr [[P]], ptr null
; CHECK-NEXT: [[P4:%.*]] = getelementptr i8, ptr [[COND]], i64 8
; CHECK-NEXT: [[COND6:%.*]] = select i1 [[C]], ptr [[P4]], ptr null
; CHECK-NEXT: [[P7:%.*]] = getelementptr i8, ptr [[COND6]], i64 -4
; CHECK-NEXT: ret i64 4
;
entry:
%array = alloca [4 x i32]
%c = icmp eq i32 %x, 0
%p = getelementptr i8, ptr %array, i64 8
%cond = select i1 %c, ptr %p, ptr null
%p4 = getelementptr i8, ptr %cond, i64 8
%cond6 = select i1 %c, ptr %p4, ptr null
%p7 = getelementptr i8, ptr %cond6, i64 -4
%size = call i64 @llvm.objectsize.i64.p0(ptr %p7, i1 false, i1 false, i1 false)
ret i64 %size
}


define i64 @negative_offset_dynamic_eval(i32 %x, i64 %i) {
; CHECK-LABEL: @negative_offset_dynamic_eval(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARRAY1:%.*]] = alloca [4 x i32], align 16
; CHECK-NEXT: [[ARRAY2:%.*]] = alloca [8 x i32], align 16
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X:%.*]], 0
; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[ARRAY2]], i64 16
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[TMP0:%.*]] = phi i64 [ 16, [[IF_THEN]] ], [ 32, [[IF_ELSE]] ]
; CHECK-NEXT: [[TMP5:%.*]] = phi i64 [ 0, [[IF_THEN]] ], [ 16, [[IF_ELSE]] ]
; CHECK-NEXT: [[PTR:%.*]] = phi ptr [ [[ARRAY1]], [[IF_THEN]] ], [ [[ADD_PTR]], [[IF_ELSE]] ]
; CHECK-NEXT: [[ADD_PTR2_IDX:%.*]] = mul i64 [[I:%.*]], 4
; CHECK-NEXT: [[TMP6:%.*]] = add i64 [[TMP5]], [[ADD_PTR2_IDX]]
; CHECK-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i32, ptr [[PTR]], i64 [[I]]
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 [[TMP0]], [[TMP6]]
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i64 [[TMP0]], [[TMP6]]
; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 0, i64 [[TMP1]]
; CHECK-NEXT: [[TMP4:%.*]] = icmp ne i64 [[TMP3]], -1
; CHECK-NEXT: call void @llvm.assume(i1 [[TMP4]])
; CHECK-NEXT: ret i64 [[TMP3]]
;
entry:
%array1 = alloca [4 x i32], align 16
%array2 = alloca [8 x i32], align 16
%tobool.not = icmp eq i32 %x, 0
br i1 %tobool.not, label %if.else, label %if.then

if.then:
br label %if.end

if.else:
%add.ptr = getelementptr inbounds i8, ptr %array2, i64 16
br label %if.end

if.end:
%ptr = phi ptr [ %array1, %if.then ], [ %add.ptr, %if.else ]
%add.ptr2 = getelementptr inbounds i32, ptr %ptr, i64 %i
%objsize = call i64 @llvm.objectsize.i64.p0(ptr %add.ptr2, i1 false, i1 true, i1 true)
ret i64 %objsize
}


define i64 @outofbound_offset_eval(i32 %x) {
; CHECK-LABEL: @outofbound_offset_eval(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARRAY:%.*]] = alloca [4 x i8], align 16
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[X:%.*]], 0
; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[ADD_PTR0:%.*]] = getelementptr i8, ptr [[ARRAY]], i64 10
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: [[ADD_PTR1:%.*]] = getelementptr i8, ptr [[ARRAY]], i64 12
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[PTR:%.*]] = phi ptr [ [[ADD_PTR0]], [[IF_THEN]] ], [ [[ADD_PTR1]], [[IF_ELSE]] ]
; CHECK-NEXT: [[ADD_PTR2:%.*]] = getelementptr i8, ptr [[PTR]], i64 -10
; CHECK-NEXT: ret i64 4
;
entry:
%array = alloca [4 x i8], align 16
%tobool.not = icmp eq i32 %x, 0
br i1 %tobool.not, label %if.else, label %if.then

if.then:
%add.ptr0 = getelementptr i8, ptr %array, i64 10
br label %if.end

if.else:
%add.ptr1 = getelementptr i8, ptr %array, i64 12
br label %if.end

if.end:
%ptr = phi ptr [ %add.ptr0, %if.then ], [ %add.ptr1, %if.else ]
%add.ptr2 = getelementptr i8, ptr %ptr, i64 -10
%objsize = call i64 @llvm.objectsize.i64.p0(ptr %add.ptr2, i1 false, i1 false, i1 false)
ret i64 %objsize
}
24 changes: 24 additions & 0 deletions llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,28 @@ define i32 @promote_with_objectsize_nullunknown_true() {
ret i32 %size
}

define i64 @out_of_bound_gep() {
; CHECK-LABEL: @out_of_bound_gep(
; CHECK-NEXT: [[OBJ:%.*]] = alloca i8, i32 4, align 1
; CHECK-NEXT: [[SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i8 8
; CHECK-NEXT: ret i64 0
;
%obj = alloca i8, i32 4
%slide = getelementptr i8, ptr %obj, i8 8
%objsize = call i64 @llvm.objectsize.i64(ptr %slide, i1 false, i1 false, i1 false)
ret i64 %objsize
}

define i64 @out_of_bound_negative_gep() {
; CHECK-LABEL: @out_of_bound_negative_gep(
; CHECK-NEXT: [[OBJ:%.*]] = alloca i8, i32 4, align 1
; CHECK-NEXT: [[SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i8 -8
; CHECK-NEXT: ret i64 0
;
%obj = alloca i8, i32 4
%slide = getelementptr i8, ptr %obj, i8 -8
%objsize = call i64 @llvm.objectsize.i64(ptr %slide, i1 false, i1 false, i1 false)
ret i64 %objsize
}

declare i32 @llvm.objectsize.i32.p0(ptr, i1, i1, i1)
Loading