diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h --- a/llvm/include/llvm/Analysis/MemoryBuiltins.h +++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h @@ -134,17 +134,21 @@ struct ObjectSizeOpts { /// Controls how we handle conditional statements with unknown conditions. enum class Mode : uint8_t { - /// Fail to evaluate an unknown condition. - Exact, + /// All branches must be known and have the same size, starting from the + /// offset, to be merged. + ExactSizeFromOffset, + /// All branches must be known and have the same underlying size and offset + /// to be merged. + ExactUnderlyingSizeAndOffset, /// Evaluate all branches of an unknown condition. If all evaluations /// succeed, pick the minimum size. Min, /// Same as Min, except we pick the maximum size of all of the branches. - Max + Max, }; /// How we want to evaluate this object's size. - Mode EvalMode = Mode::Exact; + Mode EvalMode = Mode::ExactSizeFromOffset; /// Whether to round the result up to the alignment of allocas, byval /// arguments, and global variables. bool RoundToAlign = false; diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -641,7 +641,7 @@ EvalOptions.EvalMode = MaxVal ? ObjectSizeOpts::Mode::Max : ObjectSizeOpts::Mode::Min; else - EvalOptions.EvalMode = ObjectSizeOpts::Mode::Exact; + EvalOptions.EvalMode = ObjectSizeOpts::Mode::ExactSizeFromOffset; EvalOptions.NullIsUnknownSize = cast(ObjectSize->getArgOperand(2))->isOne(); @@ -999,9 +999,11 @@ return (getSizeWithOverflow(LHS).slt(getSizeWithOverflow(RHS))) ? LHS : RHS; case ObjectSizeOpts::Mode::Max: return (getSizeWithOverflow(LHS).sgt(getSizeWithOverflow(RHS))) ? LHS : RHS; - case ObjectSizeOpts::Mode::Exact: + case ObjectSizeOpts::Mode::ExactSizeFromOffset: return (getSizeWithOverflow(LHS).eq(getSizeWithOverflow(RHS))) ? LHS : unknown(); + case ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset: + return LHS == RHS && LHS.second.eq(RHS.second) ? LHS : unknown(); } llvm_unreachable("missing an eval mode"); } diff --git a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp --- a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp +++ b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp @@ -146,6 +146,7 @@ const DataLayout &DL = F.getParent()->getDataLayout(); ObjectSizeOpts EvalOpts; EvalOpts.RoundToAlign = true; + EvalOpts.EvalMode = ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset; ObjectSizeOffsetEvaluator ObjSizeEval(DL, &TLI, F.getContext(), EvalOpts); // check HANDLE_MEMORY_INST in include/llvm/Instruction.def for memory diff --git a/llvm/test/Instrumentation/BoundsChecking/simple.ll b/llvm/test/Instrumentation/BoundsChecking/simple.ll --- a/llvm/test/Instrumentation/BoundsChecking/simple.ll +++ b/llvm/test/Instrumentation/BoundsChecking/simple.ll @@ -330,7 +330,6 @@ } ; Check that merging sizes in a phi works. -; FIXME: bounds-checking thinks that %alloc has an underlying size of 0. define i8 @f14(i1 %i) { ; CHECK-LABEL: @f14( ; CHECK-NEXT: entry: @@ -340,16 +339,18 @@ ; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[A]], i32 32 ; CHECK-NEXT: br label [[BB2]] ; CHECK: bb2: -; CHECK-NEXT: [[ALLOC:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[G]], [[BB1]] ] +; CHECK-NEXT: [[TMP0:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ 32, [[BB1]] ] +; CHECK-NEXT: [[TMP1:%.*]] = phi i64 [ 0, [[ENTRY]] ], [ 32, [[BB1]] ] +; CHECK-NEXT: [[ALLOC:%.*]] = phi ptr [ null, [[ENTRY]] ], [ [[G]], [[BB1]] ] ; CHECK-NEXT: [[IND:%.*]] = phi i64 [ 0, [[ENTRY]] ], [ -4, [[BB1]] ] -; CHECK-NEXT: [[TMP0:%.*]] = add i64 0, [[IND]] +; CHECK-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], [[IND]] ; CHECK-NEXT: [[P:%.*]] = getelementptr i8, ptr [[ALLOC]], i64 [[IND]] -; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[TMP0]] -; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i64 0, [[TMP0]] -; CHECK-NEXT: [[TMP3:%.*]] = icmp ult i64 [[TMP1]], 1 -; CHECK-NEXT: [[TMP4:%.*]] = or i1 [[TMP2]], [[TMP3]] -; CHECK-NEXT: br i1 [[TMP4]], label [[TRAP:%.*]], label [[TMP5:%.*]] -; CHECK: 5: +; CHECK-NEXT: [[TMP3:%.*]] = sub i64 [[TMP0]], [[TMP2]] +; CHECK-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP0]], [[TMP2]] +; CHECK-NEXT: [[TMP5:%.*]] = icmp ult i64 [[TMP3]], 1 +; CHECK-NEXT: [[TMP6:%.*]] = or i1 [[TMP4]], [[TMP5]] +; CHECK-NEXT: br i1 [[TMP6]], label [[TRAP:%.*]], label [[TMP7:%.*]] +; CHECK: 7: ; CHECK-NEXT: [[RET:%.*]] = load i8, ptr [[P]], align 1 ; CHECK-NEXT: ret i8 [[RET]] ; CHECK: trap: