Skip to content

Commit a53fc91

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 fb76d24 commit a53fc91

File tree

2 files changed

+30
-28
lines changed

2 files changed

+30
-28
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: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -89,19 +89,12 @@ entry:
8989

9090
; Indices are truncated to the pointer size in a gep. So "i32 -65526" should
9191
; be truncated to "i16 10".
92-
; FIXME: The TST result here is incorrect!
9392
define i32 @possible_out_of_bounds_gep_i32_trunc(i1 %c0, i1 %c1) {
94-
; CHECK-REF-LABEL: define i32 @possible_out_of_bounds_gep_i32_trunc(
95-
; CHECK-REF-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
96-
; CHECK-REF-NEXT: [[ENTRY:.*:]]
97-
; CHECK-REF-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 3, i32 0
98-
; CHECK-REF-NEXT: ret i32 [[RES]]
99-
;
100-
; CHECK-TST-LABEL: define i32 @possible_out_of_bounds_gep_i32_trunc(
101-
; CHECK-TST-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
102-
; CHECK-TST-NEXT: [[ENTRY:.*:]]
103-
; CHECK-TST-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 0, i32 3
104-
; CHECK-TST-NEXT: ret i32 [[RES]]
93+
; CHECK-LABEL: define i32 @possible_out_of_bounds_gep_i32_trunc(
94+
; CHECK-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
95+
; CHECK-NEXT: [[ENTRY:.*:]]
96+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 3, i32 0
97+
; CHECK-NEXT: ret i32 [[RES]]
10598
;
10699
entry:
107100
%obj = alloca [5 x i8]
@@ -207,7 +200,8 @@ define i32 @out_of_bounds_gep_i32_trunc_select(i1 %c0, i1 %c1) {
207200
; CHECK-TST-LABEL: define i32 @out_of_bounds_gep_i32_trunc_select(
208201
; CHECK-TST-SAME: i1 [[C0:%.*]], i1 [[C1:%.*]]) {
209202
; CHECK-TST-NEXT: [[ENTRY:.*:]]
210-
; CHECK-TST-NEXT: ret i32 0
203+
; CHECK-TST-NEXT: [[RES:%.*]] = select i1 [[C1]], i32 -1, i32 0
204+
; CHECK-TST-NEXT: ret i32 [[RES]]
211205
;
212206
entry:
213207
%obj = alloca [5 x i8]

0 commit comments

Comments
 (0)