Skip to content

Commit c20605b

Browse files
committed
[MemoryBuiltins] Consider index type size when aggregating gep offsets
Main goal here is to fix some bugs seen with LowerConstantIntrinsics pass and the lowering of llvm.objectsize. In ObjectSizeOffsetVisitor::computeImpl we are using an external analysis together with stripAndAccumulateConstantOffsets. The idea is to compute the Min/Max value of individual offsets within a GEP. The bug solved here is that when doing the Min/Max comparisons the external analysis wasn't considering the index type size (given by the data layout), it was simply using the type from the IR. Since a GEP is defined as sext/truncating indices we need to consider the index type size in the external analysis. This solves a regression (false ubsan warnings) seen after commit 02b8ee2 (llvm#117849).
1 parent 984a3f8 commit c20605b

File tree

2 files changed

+26
-18
lines changed

2 files changed

+26
-18
lines changed

llvm/lib/Analysis/MemoryBuiltins.cpp

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -683,29 +683,30 @@ combinePossibleConstantValues(std::optional<APInt> LHS,
683683
}
684684

685685
static std::optional<APInt> aggregatePossibleConstantValuesImpl(
686-
const Value *V, ObjectSizeOpts::Mode EvalMode, unsigned recursionDepth) {
686+
const Value *V, ObjectSizeOpts::Mode EvalMode, unsigned BitWidth,
687+
unsigned recursionDepth) {
687688
constexpr unsigned maxRecursionDepth = 4;
688689
if (recursionDepth == maxRecursionDepth)
689690
return std::nullopt;
690691

691692
if (const auto *CI = dyn_cast<ConstantInt>(V)) {
692-
return CI->getValue();
693+
return CI->getValue().sextOrTrunc(BitWidth);
693694
} else if (const auto *SI = dyn_cast<SelectInst>(V)) {
694695
return combinePossibleConstantValues(
695696
aggregatePossibleConstantValuesImpl(SI->getTrueValue(), EvalMode,
696-
recursionDepth + 1),
697+
BitWidth, recursionDepth + 1),
697698
aggregatePossibleConstantValuesImpl(SI->getFalseValue(), EvalMode,
698-
recursionDepth + 1),
699+
BitWidth, recursionDepth + 1),
699700
EvalMode);
700701
} else if (const auto *PN = dyn_cast<PHINode>(V)) {
701702
unsigned Count = PN->getNumIncomingValues();
702703
if (Count == 0)
703704
return std::nullopt;
704705
auto Acc = aggregatePossibleConstantValuesImpl(
705-
PN->getIncomingValue(0), EvalMode, recursionDepth + 1);
706+
PN->getIncomingValue(0), EvalMode, BitWidth, recursionDepth + 1);
706707
for (unsigned I = 1; Acc && I < Count; ++I) {
707708
auto Tmp = aggregatePossibleConstantValuesImpl(
708-
PN->getIncomingValue(I), EvalMode, recursionDepth + 1);
709+
PN->getIncomingValue(I), EvalMode, BitWidth, recursionDepth + 1);
709710
Acc = combinePossibleConstantValues(Acc, Tmp, EvalMode);
710711
}
711712
return Acc;
@@ -715,9 +716,10 @@ static std::optional<APInt> aggregatePossibleConstantValuesImpl(
715716
}
716717

717718
static std::optional<APInt>
718-
aggregatePossibleConstantValues(const Value *V, ObjectSizeOpts::Mode EvalMode) {
719+
aggregatePossibleConstantValues(const Value *V, ObjectSizeOpts::Mode EvalMode,
720+
unsigned BitWidth) {
719721
if (auto *CI = dyn_cast<ConstantInt>(V))
720-
return CI->getValue();
722+
return CI->getValue().sextOrTrunc(BitWidth);
721723

722724
if (EvalMode != ObjectSizeOpts::Mode::Min &&
723725
EvalMode != ObjectSizeOpts::Mode::Max)
@@ -726,7 +728,7 @@ aggregatePossibleConstantValues(const Value *V, ObjectSizeOpts::Mode EvalMode) {
726728
// Not using computeConstantRange here because we cannot guarantee it's not
727729
// doing optimization based on UB which we want to avoid when expanding
728730
// __builtin_object_size.
729-
return aggregatePossibleConstantValuesImpl(V, EvalMode, 0u);
731+
return aggregatePossibleConstantValuesImpl(V, EvalMode, BitWidth, 0u);
730732
}
731733

732734
/// Align \p Size according to \p Alignment. If \p Size is greater than
@@ -788,9 +790,14 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
788790
Options.EvalMode == ObjectSizeOpts::Mode::Min
789791
? ObjectSizeOpts::Mode::Max
790792
: ObjectSizeOpts::Mode::Min;
791-
auto OffsetRangeAnalysis = [EvalMode](Value &VOffset, APInt &Offset) {
793+
// For a GEPOperator the indices are first converted to offsets in the
794+
// pointer’s index type, so we need to provide the index type to make sure
795+
// the min/max operations are performed in correct type.
796+
unsigned IdxTyBits = DL.getIndexTypeSizeInBits(V->getType());
797+
auto OffsetRangeAnalysis = [EvalMode, IdxTyBits](Value &VOffset,
798+
APInt &Offset) {
792799
if (auto PossibleOffset =
793-
aggregatePossibleConstantValues(&VOffset, EvalMode)) {
800+
aggregatePossibleConstantValues(&VOffset, EvalMode, IdxTyBits)) {
794801
Offset = *PossibleOffset;
795802
return true;
796803
}
@@ -900,8 +907,9 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
900907
return OffsetSpan(Zero, align(Size, I.getAlign()));
901908

902909
Value *ArraySize = I.getArraySize();
903-
if (auto PossibleSize =
904-
aggregatePossibleConstantValues(ArraySize, Options.EvalMode)) {
910+
if (auto PossibleSize = aggregatePossibleConstantValues(
911+
ArraySize, Options.EvalMode,
912+
ArraySize->getType()->getScalarSizeInBits())) {
905913
APInt NumElems = *PossibleSize;
906914
if (!CheckedZextOrTrunc(NumElems))
907915
return ObjectSizeOffsetVisitor::unknown();
@@ -932,8 +940,8 @@ OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
932940
if (!V->getType()->isIntegerTy())
933941
return V;
934942

935-
if (auto PossibleBound =
936-
aggregatePossibleConstantValues(V, Options.EvalMode))
943+
if (auto PossibleBound = aggregatePossibleConstantValues(
944+
V, Options.EvalMode, V->getType()->getScalarSizeInBits()))
937945
return ConstantInt::get(V->getType(), *PossibleBound);
938946

939947
return V;

llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-idxsize.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,14 @@ entry:
9696
; Indices are truncated to the pointer size in a gep. So "i32 -65526" should
9797
; be treated as "i16 10" and we expect same result as for
9898
; @possible_out_of_bounds_gep_i16 above.
99-
; FIXME: The result here is incorrect (max/min is swapped).
10099
define i32 @possible_out_of_bounds_gep_i32_trunc(i1 %c0, i1 %c1) {
101100
; CHECK-LABEL: define i32 @possible_out_of_bounds_gep_i32_trunc(
102101
; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
103102
; CHECK-NEXT: [[ENTRY:.*:]]
104103
; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1
105104
; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i32 2, i32 -65526
106105
; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i32 [[OFFSET]]
107-
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 0, i32 3
106+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 3, i32 0
108107
; CHECK-NEXT: ret i32 [[RES]]
109108
;
110109
entry:
@@ -207,7 +206,8 @@ define i32 @out_of_bounds_gep_i32_trunc_select(i1 %c0, i1 %c1) {
207206
; CHECK-NEXT: [[OBJ:%.*]] = alloca [5 x i8], align 1
208207
; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[C0]], i32 32767, i32 32768
209208
; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr i8, ptr [[OBJ]], i32 [[OFFSET]]
210-
; CHECK-NEXT: ret i32 0
209+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 -1, i32 0
210+
; CHECK-NEXT: ret i32 [[RES]]
211211
;
212212
entry:
213213
%obj = alloca [5 x i8]

0 commit comments

Comments
 (0)