Skip to content

Commit d03c8b3

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 (#117849).
1 parent e9c1274 commit d03c8b3

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
@@ -739,29 +739,30 @@ combinePossibleConstantValues(std::optional<APInt> LHS,
739739
}
740740

741741
static std::optional<APInt> aggregatePossibleConstantValuesImpl(
742-
const Value *V, ObjectSizeOpts::Mode EvalMode, unsigned recursionDepth) {
742+
const Value *V, ObjectSizeOpts::Mode EvalMode, unsigned BitWidth,
743+
unsigned recursionDepth) {
743744
constexpr unsigned maxRecursionDepth = 4;
744745
if (recursionDepth == maxRecursionDepth)
745746
return std::nullopt;
746747

747748
if (const auto *CI = dyn_cast<ConstantInt>(V)) {
748-
return CI->getValue();
749+
return CI->getValue().sextOrTrunc(BitWidth);
749750
} else if (const auto *SI = dyn_cast<SelectInst>(V)) {
750751
return combinePossibleConstantValues(
751752
aggregatePossibleConstantValuesImpl(SI->getTrueValue(), EvalMode,
752-
recursionDepth + 1),
753+
BitWidth, recursionDepth + 1),
753754
aggregatePossibleConstantValuesImpl(SI->getFalseValue(), EvalMode,
754-
recursionDepth + 1),
755+
BitWidth, recursionDepth + 1),
755756
EvalMode);
756757
} else if (const auto *PN = dyn_cast<PHINode>(V)) {
757758
unsigned Count = PN->getNumIncomingValues();
758759
if (Count == 0)
759760
return std::nullopt;
760761
auto Acc = aggregatePossibleConstantValuesImpl(
761-
PN->getIncomingValue(0), EvalMode, recursionDepth + 1);
762+
PN->getIncomingValue(0), EvalMode, BitWidth, recursionDepth + 1);
762763
for (unsigned I = 1; Acc && I < Count; ++I) {
763764
auto Tmp = aggregatePossibleConstantValuesImpl(
764-
PN->getIncomingValue(I), EvalMode, recursionDepth + 1);
765+
PN->getIncomingValue(I), EvalMode, BitWidth, recursionDepth + 1);
765766
Acc = combinePossibleConstantValues(Acc, Tmp, EvalMode);
766767
}
767768
return Acc;
@@ -771,9 +772,10 @@ static std::optional<APInt> aggregatePossibleConstantValuesImpl(
771772
}
772773

773774
static std::optional<APInt>
774-
aggregatePossibleConstantValues(const Value *V, ObjectSizeOpts::Mode EvalMode) {
775+
aggregatePossibleConstantValues(const Value *V, ObjectSizeOpts::Mode EvalMode,
776+
unsigned BitWidth) {
775777
if (auto *CI = dyn_cast<ConstantInt>(V))
776-
return CI->getValue();
778+
return CI->getValue().sextOrTrunc(BitWidth);
777779

778780
if (EvalMode != ObjectSizeOpts::Mode::Min &&
779781
EvalMode != ObjectSizeOpts::Mode::Max)
@@ -782,7 +784,7 @@ aggregatePossibleConstantValues(const Value *V, ObjectSizeOpts::Mode EvalMode) {
782784
// Not using computeConstantRange here because we cannot guarantee it's not
783785
// doing optimization based on UB which we want to avoid when expanding
784786
// __builtin_object_size.
785-
return aggregatePossibleConstantValuesImpl(V, EvalMode, 0u);
787+
return aggregatePossibleConstantValuesImpl(V, EvalMode, BitWidth, 0u);
786788
}
787789

788790
/// Align \p Size according to \p Alignment. If \p Size is greater than
@@ -844,9 +846,14 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
844846
Options.EvalMode == ObjectSizeOpts::Mode::Min
845847
? ObjectSizeOpts::Mode::Max
846848
: ObjectSizeOpts::Mode::Min;
847-
auto OffsetRangeAnalysis = [EvalMode](Value &VOffset, APInt &Offset) {
849+
// For a GEPOperator the indices are first converted to offsets in the
850+
// pointer’s index type, so we need to provide the index type to make sure
851+
// the min/max operations are performed in correct type.
852+
unsigned IdxTyBits = DL.getIndexTypeSizeInBits(V->getType());
853+
auto OffsetRangeAnalysis = [EvalMode, IdxTyBits](Value &VOffset,
854+
APInt &Offset) {
848855
if (auto PossibleOffset =
849-
aggregatePossibleConstantValues(&VOffset, EvalMode)) {
856+
aggregatePossibleConstantValues(&VOffset, EvalMode, IdxTyBits)) {
850857
Offset = *PossibleOffset;
851858
return true;
852859
}
@@ -956,8 +963,9 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
956963
return OffsetSpan(Zero, align(Size, I.getAlign()));
957964

958965
Value *ArraySize = I.getArraySize();
959-
if (auto PossibleSize =
960-
aggregatePossibleConstantValues(ArraySize, Options.EvalMode)) {
966+
if (auto PossibleSize = aggregatePossibleConstantValues(
967+
ArraySize, Options.EvalMode,
968+
ArraySize->getType()->getScalarSizeInBits())) {
961969
APInt NumElems = *PossibleSize;
962970
if (!CheckedZextOrTrunc(NumElems))
963971
return ObjectSizeOffsetVisitor::unknown();
@@ -988,8 +996,8 @@ OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
988996
if (!V->getType()->isIntegerTy())
989997
return V;
990998

991-
if (auto PossibleBound =
992-
aggregatePossibleConstantValues(V, Options.EvalMode))
999+
if (auto PossibleBound = aggregatePossibleConstantValues(
1000+
V, Options.EvalMode, V->getType()->getScalarSizeInBits()))
9931001
return ConstantInt::get(V->getType(), *PossibleBound);
9941002

9951003
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)