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

Harald van Dijk via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Nov 12 15:20:02 PST 2024


https://github.com/hvdijk updated https://github.com/llvm/llvm-project/pull/114786

>From fb70629e196c516ebd5083f8624ba614f746ef67 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Sat, 2 Nov 2024 09:14:35 +0000
Subject: [PATCH 1/2] =?UTF-8?q?[llvm]=20Fix=20=5F=5Fbuiltin=5Fobject=5Fsiz?=
 =?UTF-8?q?e=20interaction=20between=20Negative=20Offset=20=E2=80=A6=20(#1?=
 =?UTF-8?q?11827)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

…and Select/Phi

When picking a SizeOffsetAPInt through combineSizeOffset, the behavior
differs if we're going to apply a constant offset that's positive or
negative: If it's positive, then we need to compare the remaining bytes
(i.e. Size
- Offset), but if it's negative, we need to compare the preceding bytes
(i.e. Offset).

Fix #111709

(cherry picked from commit 01a103b0b9c449e8dec17950835991757d1c4f88)
---
 llvm/include/llvm/Analysis/MemoryBuiltins.h   |  71 +++--
 llvm/lib/Analysis/MemoryBuiltins.cpp          | 145 +++++-----
 .../builtin-object-size-phi.ll                | 254 ++++++++++++++++++
 .../objectsize_basic.ll                       |  24 ++
 4 files changed, 405 insertions(+), 89 deletions(-)

diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h
index bb282a1b73d320..a21f116db7e70d 100644
--- a/llvm/include/llvm/Analysis/MemoryBuiltins.h
+++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h
@@ -222,21 +222,43 @@ struct SizeOffsetAPInt : public SizeOffsetType<APInt, SizeOffsetAPInt> {
   static bool known(const APInt &V) { return V.getBitWidth() > 1; }
 };
 
+/// OffsetSpan - Used internally by \p ObjectSizeOffsetVisitor. Represents a
+/// point in memory as a pair of allocated bytes before and after it.
+struct OffsetSpan {
+  APInt Before; /// Number of allocated bytes before this point.
+  APInt After;  /// Number of allocated bytes after this point.
+
+  OffsetSpan() = default;
+  OffsetSpan(APInt Before, APInt After) : Before(Before), After(After) {}
+
+  bool knownBefore() const { return known(Before); }
+  bool knownAfter() const { return known(After); }
+  bool anyKnown() const { return knownBefore() || knownAfter(); }
+  bool bothKnown() const { return knownBefore() && knownAfter(); }
+
+  bool operator==(const OffsetSpan &RHS) const {
+    return Before == RHS.Before && After == RHS.After;
+  }
+  bool operator!=(const OffsetSpan &RHS) const { return !(*this == RHS); }
+
+  static bool known(const APInt &V) { return V.getBitWidth() > 1; }
+};
+
 /// Evaluate the size and offset of an object pointed to by a Value*
 /// statically. Fails if size or offset are not known at compile time.
 class ObjectSizeOffsetVisitor
-    : public InstVisitor<ObjectSizeOffsetVisitor, SizeOffsetAPInt> {
+    : public InstVisitor<ObjectSizeOffsetVisitor, OffsetSpan> {
   const DataLayout &DL;
   const TargetLibraryInfo *TLI;
   ObjectSizeOpts Options;
   unsigned IntTyBits;
   APInt Zero;
-  SmallDenseMap<Instruction *, SizeOffsetAPInt, 8> SeenInsts;
+  SmallDenseMap<Instruction *, OffsetSpan, 8> SeenInsts;
   unsigned InstructionsVisited;
 
   APInt align(APInt Size, MaybeAlign Align);
 
-  static SizeOffsetAPInt unknown() { return SizeOffsetAPInt(); }
+  static OffsetSpan unknown() { return OffsetSpan(); }
 
 public:
   ObjectSizeOffsetVisitor(const DataLayout &DL, const TargetLibraryInfo *TLI,
@@ -246,29 +268,30 @@ class ObjectSizeOffsetVisitor
 
   // These are "private", except they can't actually be made private. Only
   // compute() should be used by external users.
-  SizeOffsetAPInt visitAllocaInst(AllocaInst &I);
-  SizeOffsetAPInt visitArgument(Argument &A);
-  SizeOffsetAPInt visitCallBase(CallBase &CB);
-  SizeOffsetAPInt visitConstantPointerNull(ConstantPointerNull &);
-  SizeOffsetAPInt visitExtractElementInst(ExtractElementInst &I);
-  SizeOffsetAPInt visitExtractValueInst(ExtractValueInst &I);
-  SizeOffsetAPInt visitGlobalAlias(GlobalAlias &GA);
-  SizeOffsetAPInt visitGlobalVariable(GlobalVariable &GV);
-  SizeOffsetAPInt visitIntToPtrInst(IntToPtrInst &);
-  SizeOffsetAPInt visitLoadInst(LoadInst &I);
-  SizeOffsetAPInt visitPHINode(PHINode &);
-  SizeOffsetAPInt visitSelectInst(SelectInst &I);
-  SizeOffsetAPInt visitUndefValue(UndefValue &);
-  SizeOffsetAPInt visitInstruction(Instruction &I);
+  OffsetSpan visitAllocaInst(AllocaInst &I);
+  OffsetSpan visitArgument(Argument &A);
+  OffsetSpan visitCallBase(CallBase &CB);
+  OffsetSpan visitConstantPointerNull(ConstantPointerNull &);
+  OffsetSpan visitExtractElementInst(ExtractElementInst &I);
+  OffsetSpan visitExtractValueInst(ExtractValueInst &I);
+  OffsetSpan visitGlobalAlias(GlobalAlias &GA);
+  OffsetSpan visitGlobalVariable(GlobalVariable &GV);
+  OffsetSpan visitIntToPtrInst(IntToPtrInst &);
+  OffsetSpan visitLoadInst(LoadInst &I);
+  OffsetSpan visitPHINode(PHINode &);
+  OffsetSpan visitSelectInst(SelectInst &I);
+  OffsetSpan visitUndefValue(UndefValue &);
+  OffsetSpan visitInstruction(Instruction &I);
 
 private:
-  SizeOffsetAPInt findLoadSizeOffset(
-      LoadInst &LoadFrom, BasicBlock &BB, BasicBlock::iterator From,
-      SmallDenseMap<BasicBlock *, SizeOffsetAPInt, 8> &VisitedBlocks,
-      unsigned &ScannedInstCount);
-  SizeOffsetAPInt combineSizeOffset(SizeOffsetAPInt LHS, SizeOffsetAPInt RHS);
-  SizeOffsetAPInt computeImpl(Value *V);
-  SizeOffsetAPInt computeValue(Value *V);
+  OffsetSpan
+  findLoadOffsetRange(LoadInst &LoadFrom, BasicBlock &BB,
+                      BasicBlock::iterator From,
+                      SmallDenseMap<BasicBlock *, OffsetSpan, 8> &VisitedBlocks,
+                      unsigned &ScannedInstCount);
+  OffsetSpan combineOffsetRange(OffsetSpan LHS, OffsetSpan RHS);
+  OffsetSpan computeImpl(Value *V);
+  OffsetSpan computeValue(Value *V);
   bool CheckedZextOrTrunc(APInt &I);
 };
 
diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp
index 1edc51e9ce5da3..44cb2d0942f460 100644
--- a/llvm/lib/Analysis/MemoryBuiltins.cpp
+++ b/llvm/lib/Analysis/MemoryBuiltins.cpp
@@ -700,10 +700,21 @@ ObjectSizeOffsetVisitor::ObjectSizeOffsetVisitor(const DataLayout &DL,
 
 SizeOffsetAPInt ObjectSizeOffsetVisitor::compute(Value *V) {
   InstructionsVisited = 0;
-  return computeImpl(V);
+  OffsetSpan Span = computeImpl(V);
+
+  // In ExactSizeFromOffset mode, we don't care about the Before Field, so allow
+  // us to overwrite it if needs be.
+  if (Span.knownAfter() && !Span.knownBefore() &&
+      Options.EvalMode == ObjectSizeOpts::Mode::ExactSizeFromOffset)
+    Span.Before = APInt::getZero(Span.After.getBitWidth());
+
+  if (!Span.bothKnown())
+    return {};
+
+  return {Span.Before + Span.After, Span.Before};
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::computeImpl(Value *V) {
+OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
   unsigned InitialIntTyBits = DL.getIndexTypeSizeInBits(V->getType());
 
   // Stripping pointer casts can strip address space casts which can change the
@@ -720,28 +731,28 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::computeImpl(Value *V) {
   IntTyBits = DL.getIndexTypeSizeInBits(V->getType());
   Zero = APInt::getZero(IntTyBits);
 
-  SizeOffsetAPInt SOT = computeValue(V);
+  OffsetSpan ORT = computeValue(V);
 
   bool IndexTypeSizeChanged = InitialIntTyBits != IntTyBits;
   if (!IndexTypeSizeChanged && Offset.isZero())
-    return SOT;
+    return ORT;
 
   // We stripped an address space cast that changed the index type size or we
   // accumulated some constant offset (or both). Readjust the bit width to match
   // the argument index type size and apply the offset, as required.
   if (IndexTypeSizeChanged) {
-    if (SOT.knownSize() && !::CheckedZextOrTrunc(SOT.Size, InitialIntTyBits))
-      SOT.Size = APInt();
-    if (SOT.knownOffset() &&
-        !::CheckedZextOrTrunc(SOT.Offset, InitialIntTyBits))
-      SOT.Offset = APInt();
+    if (ORT.knownBefore() &&
+        !::CheckedZextOrTrunc(ORT.Before, InitialIntTyBits))
+      ORT.Before = APInt();
+    if (ORT.knownAfter() && !::CheckedZextOrTrunc(ORT.After, InitialIntTyBits))
+      ORT.After = APInt();
   }
-  // If the computed offset is "unknown" we cannot add the stripped offset.
-  return {SOT.Size,
-          SOT.Offset.getBitWidth() > 1 ? SOT.Offset + Offset : SOT.Offset};
+  // If the computed bound is "unknown" we cannot add the stripped offset.
+  return {(ORT.knownBefore() ? ORT.Before + Offset : ORT.Before),
+          (ORT.knownAfter() ? ORT.After - Offset : ORT.After)};
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::computeValue(Value *V) {
+OffsetSpan ObjectSizeOffsetVisitor::computeValue(Value *V) {
   if (Instruction *I = dyn_cast<Instruction>(V)) {
     // If we have already seen this instruction, bail out. Cycles can happen in
     // unreachable code after constant propagation.
@@ -751,7 +762,7 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::computeValue(Value *V) {
     ++InstructionsVisited;
     if (InstructionsVisited > ObjectSizeOffsetVisitorMaxVisitInstructions)
       return ObjectSizeOffsetVisitor::unknown();
-    SizeOffsetAPInt Res = visit(*I);
+    OffsetSpan Res = visit(*I);
     // Cache the result for later visits. If we happened to visit this during
     // the above recursion, we would consider it unknown until now.
     SeenInsts[I] = Res;
@@ -777,13 +788,13 @@ bool ObjectSizeOffsetVisitor::CheckedZextOrTrunc(APInt &I) {
   return ::CheckedZextOrTrunc(I, IntTyBits);
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
+OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
   TypeSize ElemSize = DL.getTypeAllocSize(I.getAllocatedType());
   if (ElemSize.isScalable() && Options.EvalMode != ObjectSizeOpts::Mode::Min)
     return ObjectSizeOffsetVisitor::unknown();
   APInt Size(IntTyBits, ElemSize.getKnownMinValue());
   if (!I.isArrayAllocation())
-    return SizeOffsetAPInt(align(Size, I.getAlign()), Zero);
+    return OffsetSpan(Zero, align(Size, I.getAlign()));
 
   Value *ArraySize = I.getArraySize();
   if (const ConstantInt *C = dyn_cast<ConstantInt>(ArraySize)) {
@@ -794,12 +805,12 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
     bool Overflow;
     Size = Size.umul_ov(NumElems, Overflow);
     return Overflow ? ObjectSizeOffsetVisitor::unknown()
-                    : SizeOffsetAPInt(align(Size, I.getAlign()), Zero);
+                    : OffsetSpan(Zero, align(Size, I.getAlign()));
   }
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
+OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
   Type *MemoryTy = A.getPointeeInMemoryValueType();
   // No interprocedural analysis is done at the moment.
   if (!MemoryTy|| !MemoryTy->isSized()) {
@@ -808,16 +819,16 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
   }
 
   APInt Size(IntTyBits, DL.getTypeAllocSize(MemoryTy));
-  return SizeOffsetAPInt(align(Size, A.getParamAlign()), Zero);
+  return OffsetSpan(Zero, align(Size, A.getParamAlign()));
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
+OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
   if (std::optional<APInt> Size = getAllocSize(&CB, TLI))
-    return SizeOffsetAPInt(*Size, Zero);
+    return OffsetSpan(Zero, *Size);
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-SizeOffsetAPInt
+OffsetSpan
 ObjectSizeOffsetVisitor::visitConstantPointerNull(ConstantPointerNull &CPN) {
   // If null is unknown, there's nothing we can do. Additionally, non-zero
   // address spaces can make use of null, so we don't presume to know anything
@@ -828,45 +839,43 @@ ObjectSizeOffsetVisitor::visitConstantPointerNull(ConstantPointerNull &CPN) {
   // addrspace(1) gets casted to addrspace(0) (or vice-versa).
   if (Options.NullIsUnknownSize || CPN.getType()->getAddressSpace())
     return ObjectSizeOffsetVisitor::unknown();
-  return SizeOffsetAPInt(Zero, Zero);
+  return OffsetSpan(Zero, Zero);
 }
 
-SizeOffsetAPInt
+OffsetSpan
 ObjectSizeOffsetVisitor::visitExtractElementInst(ExtractElementInst &) {
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-SizeOffsetAPInt
-ObjectSizeOffsetVisitor::visitExtractValueInst(ExtractValueInst &) {
+OffsetSpan ObjectSizeOffsetVisitor::visitExtractValueInst(ExtractValueInst &) {
   // Easy cases were already folded by previous passes.
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitGlobalAlias(GlobalAlias &GA) {
+OffsetSpan ObjectSizeOffsetVisitor::visitGlobalAlias(GlobalAlias &GA) {
   if (GA.isInterposable())
     return ObjectSizeOffsetVisitor::unknown();
   return computeImpl(GA.getAliasee());
 }
 
-SizeOffsetAPInt
-ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
+OffsetSpan ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
   if (!GV.getValueType()->isSized() || GV.hasExternalWeakLinkage() ||
       ((!GV.hasInitializer() || GV.isInterposable()) &&
        Options.EvalMode != ObjectSizeOpts::Mode::Min))
     return ObjectSizeOffsetVisitor::unknown();
 
   APInt Size(IntTyBits, DL.getTypeAllocSize(GV.getValueType()));
-  return SizeOffsetAPInt(align(Size, GV.getAlign()), Zero);
+  return OffsetSpan(Zero, align(Size, GV.getAlign()));
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
+OffsetSpan ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
   // clueless
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::findLoadSizeOffset(
+OffsetSpan ObjectSizeOffsetVisitor::findLoadOffsetRange(
     LoadInst &Load, BasicBlock &BB, BasicBlock::iterator From,
-    SmallDenseMap<BasicBlock *, SizeOffsetAPInt, 8> &VisitedBlocks,
+    SmallDenseMap<BasicBlock *, OffsetSpan, 8> &VisitedBlocks,
     unsigned &ScannedInstCount) {
   constexpr unsigned MaxInstsToScan = 128;
 
@@ -877,7 +886,7 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::findLoadSizeOffset(
   auto Unknown = [&BB, &VisitedBlocks]() {
     return VisitedBlocks[&BB] = ObjectSizeOffsetVisitor::unknown();
   };
-  auto Known = [&BB, &VisitedBlocks](SizeOffsetAPInt SO) {
+  auto Known = [&BB, &VisitedBlocks](OffsetSpan SO) {
     return VisitedBlocks[&BB] = SO;
   };
 
@@ -948,15 +957,15 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::findLoadSizeOffset(
       if (!C)
         return Unknown();
 
-      return Known({C->getValue(), APInt(C->getValue().getBitWidth(), 0)});
+      return Known({APInt(C->getValue().getBitWidth(), 0), C->getValue()});
     }
 
     return Unknown();
   } while (From-- != BB.begin());
 
-  SmallVector<SizeOffsetAPInt> PredecessorSizeOffsets;
+  SmallVector<OffsetSpan> PredecessorSizeOffsets;
   for (auto *PredBB : predecessors(&BB)) {
-    PredecessorSizeOffsets.push_back(findLoadSizeOffset(
+    PredecessorSizeOffsets.push_back(findLoadOffsetRange(
         Load, *PredBB, BasicBlock::iterator(PredBB->getTerminator()),
         VisitedBlocks, ScannedInstCount));
     if (!PredecessorSizeOffsets.back().bothKnown())
@@ -968,70 +977,70 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::findLoadSizeOffset(
 
   return Known(std::accumulate(
       PredecessorSizeOffsets.begin() + 1, PredecessorSizeOffsets.end(),
-      PredecessorSizeOffsets.front(),
-      [this](SizeOffsetAPInt LHS, SizeOffsetAPInt RHS) {
-        return combineSizeOffset(LHS, RHS);
+      PredecessorSizeOffsets.front(), [this](OffsetSpan LHS, OffsetSpan RHS) {
+        return combineOffsetRange(LHS, RHS);
       }));
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitLoadInst(LoadInst &LI) {
+OffsetSpan ObjectSizeOffsetVisitor::visitLoadInst(LoadInst &LI) {
   if (!Options.AA) {
     ++ObjectVisitorLoad;
     return ObjectSizeOffsetVisitor::unknown();
   }
 
-  SmallDenseMap<BasicBlock *, SizeOffsetAPInt, 8> VisitedBlocks;
+  SmallDenseMap<BasicBlock *, OffsetSpan, 8> VisitedBlocks;
   unsigned ScannedInstCount = 0;
-  SizeOffsetAPInt SO =
-      findLoadSizeOffset(LI, *LI.getParent(), BasicBlock::iterator(LI),
-                         VisitedBlocks, ScannedInstCount);
+  OffsetSpan SO =
+      findLoadOffsetRange(LI, *LI.getParent(), BasicBlock::iterator(LI),
+                          VisitedBlocks, ScannedInstCount);
   if (!SO.bothKnown())
     ++ObjectVisitorLoad;
   return SO;
 }
 
-SizeOffsetAPInt
-ObjectSizeOffsetVisitor::combineSizeOffset(SizeOffsetAPInt LHS,
-                                           SizeOffsetAPInt RHS) {
+OffsetSpan ObjectSizeOffsetVisitor::combineOffsetRange(OffsetSpan LHS,
+                                                       OffsetSpan RHS) {
   if (!LHS.bothKnown() || !RHS.bothKnown())
     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;
+    return {LHS.Before.slt(RHS.Before) ? LHS.Before : RHS.Before,
+            LHS.After.slt(RHS.After) ? LHS.After : RHS.After};
+  case ObjectSizeOpts::Mode::Max: {
+    return {LHS.Before.sgt(RHS.Before) ? LHS.Before : RHS.Before,
+            LHS.After.sgt(RHS.After) ? LHS.After : RHS.After};
+  }
   case ObjectSizeOpts::Mode::ExactSizeFromOffset:
-    return (getSizeWithOverflow(LHS).eq(getSizeWithOverflow(RHS)))
-               ? LHS
-               : ObjectSizeOffsetVisitor::unknown();
+    return {LHS.Before.eq(RHS.Before) ? LHS.Before : APInt(),
+            LHS.After.eq(RHS.After) ? LHS.After : APInt()};
   case ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset:
-    return LHS == RHS ? LHS : ObjectSizeOffsetVisitor::unknown();
+    return (LHS == RHS) ? LHS : ObjectSizeOffsetVisitor::unknown();
   }
   llvm_unreachable("missing an eval mode");
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitPHINode(PHINode &PN) {
+OffsetSpan ObjectSizeOffsetVisitor::visitPHINode(PHINode &PN) {
   if (PN.getNumIncomingValues() == 0)
     return ObjectSizeOffsetVisitor::unknown();
   auto IncomingValues = PN.incoming_values();
   return std::accumulate(IncomingValues.begin() + 1, IncomingValues.end(),
                          computeImpl(*IncomingValues.begin()),
-                         [this](SizeOffsetAPInt LHS, Value *VRHS) {
-                           return combineSizeOffset(LHS, computeImpl(VRHS));
+                         [this](OffsetSpan LHS, Value *VRHS) {
+                           return combineOffsetRange(LHS, computeImpl(VRHS));
                          });
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) {
-  return combineSizeOffset(computeImpl(I.getTrueValue()),
-                           computeImpl(I.getFalseValue()));
+OffsetSpan ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) {
+  return combineOffsetRange(computeImpl(I.getTrueValue()),
+                            computeImpl(I.getFalseValue()));
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitUndefValue(UndefValue &) {
-  return SizeOffsetAPInt(Zero, Zero);
+OffsetSpan ObjectSizeOffsetVisitor::visitUndefValue(UndefValue &) {
+  return OffsetSpan(Zero, Zero);
 }
 
-SizeOffsetAPInt ObjectSizeOffsetVisitor::visitInstruction(Instruction &I) {
+OffsetSpan ObjectSizeOffsetVisitor::visitInstruction(Instruction &I) {
   LLVM_DEBUG(dbgs() << "ObjectSizeOffsetVisitor unknown instruction:" << I
                     << '\n');
   return ObjectSizeOffsetVisitor::unknown();
@@ -1084,7 +1093,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),
diff --git a/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll
index 4f4d6a88e1693b..2974228e6a8303 100644
--- a/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll
+++ b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll
@@ -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
+}
diff --git a/llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll b/llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll
index c90d5152e1a096..568070a8660698 100644
--- a/llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll
+++ b/llvm/test/Transforms/LowerConstantIntrinsics/objectsize_basic.ll
@@ -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)

>From d2acbb68de21f3d2635f395bb04d2cc7cb045a13 Mon Sep 17 00:00:00 2001
From: Harald van Dijk <harald.vandijk at codeplay.com>
Date: Tue, 12 Nov 2024 23:15:56 +0000
Subject: [PATCH 2/2] Revert changes to MemoryBuiltins.h and make a more
 limited change

---
 llvm/include/llvm/Analysis/MemoryBuiltins.h   |  71 +++------
 llvm/lib/Analysis/MemoryBuiltins.cpp          | 146 +++++++++---------
 .../InstCombine/builtin-object-size-offset.ll |   2 +-
 3 files changed, 100 insertions(+), 119 deletions(-)

diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h
index a21f116db7e70d..bb282a1b73d320 100644
--- a/llvm/include/llvm/Analysis/MemoryBuiltins.h
+++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h
@@ -222,43 +222,21 @@ struct SizeOffsetAPInt : public SizeOffsetType<APInt, SizeOffsetAPInt> {
   static bool known(const APInt &V) { return V.getBitWidth() > 1; }
 };
 
-/// OffsetSpan - Used internally by \p ObjectSizeOffsetVisitor. Represents a
-/// point in memory as a pair of allocated bytes before and after it.
-struct OffsetSpan {
-  APInt Before; /// Number of allocated bytes before this point.
-  APInt After;  /// Number of allocated bytes after this point.
-
-  OffsetSpan() = default;
-  OffsetSpan(APInt Before, APInt After) : Before(Before), After(After) {}
-
-  bool knownBefore() const { return known(Before); }
-  bool knownAfter() const { return known(After); }
-  bool anyKnown() const { return knownBefore() || knownAfter(); }
-  bool bothKnown() const { return knownBefore() && knownAfter(); }
-
-  bool operator==(const OffsetSpan &RHS) const {
-    return Before == RHS.Before && After == RHS.After;
-  }
-  bool operator!=(const OffsetSpan &RHS) const { return !(*this == RHS); }
-
-  static bool known(const APInt &V) { return V.getBitWidth() > 1; }
-};
-
 /// Evaluate the size and offset of an object pointed to by a Value*
 /// statically. Fails if size or offset are not known at compile time.
 class ObjectSizeOffsetVisitor
-    : public InstVisitor<ObjectSizeOffsetVisitor, OffsetSpan> {
+    : public InstVisitor<ObjectSizeOffsetVisitor, SizeOffsetAPInt> {
   const DataLayout &DL;
   const TargetLibraryInfo *TLI;
   ObjectSizeOpts Options;
   unsigned IntTyBits;
   APInt Zero;
-  SmallDenseMap<Instruction *, OffsetSpan, 8> SeenInsts;
+  SmallDenseMap<Instruction *, SizeOffsetAPInt, 8> SeenInsts;
   unsigned InstructionsVisited;
 
   APInt align(APInt Size, MaybeAlign Align);
 
-  static OffsetSpan unknown() { return OffsetSpan(); }
+  static SizeOffsetAPInt unknown() { return SizeOffsetAPInt(); }
 
 public:
   ObjectSizeOffsetVisitor(const DataLayout &DL, const TargetLibraryInfo *TLI,
@@ -268,30 +246,29 @@ class ObjectSizeOffsetVisitor
 
   // These are "private", except they can't actually be made private. Only
   // compute() should be used by external users.
-  OffsetSpan visitAllocaInst(AllocaInst &I);
-  OffsetSpan visitArgument(Argument &A);
-  OffsetSpan visitCallBase(CallBase &CB);
-  OffsetSpan visitConstantPointerNull(ConstantPointerNull &);
-  OffsetSpan visitExtractElementInst(ExtractElementInst &I);
-  OffsetSpan visitExtractValueInst(ExtractValueInst &I);
-  OffsetSpan visitGlobalAlias(GlobalAlias &GA);
-  OffsetSpan visitGlobalVariable(GlobalVariable &GV);
-  OffsetSpan visitIntToPtrInst(IntToPtrInst &);
-  OffsetSpan visitLoadInst(LoadInst &I);
-  OffsetSpan visitPHINode(PHINode &);
-  OffsetSpan visitSelectInst(SelectInst &I);
-  OffsetSpan visitUndefValue(UndefValue &);
-  OffsetSpan visitInstruction(Instruction &I);
+  SizeOffsetAPInt visitAllocaInst(AllocaInst &I);
+  SizeOffsetAPInt visitArgument(Argument &A);
+  SizeOffsetAPInt visitCallBase(CallBase &CB);
+  SizeOffsetAPInt visitConstantPointerNull(ConstantPointerNull &);
+  SizeOffsetAPInt visitExtractElementInst(ExtractElementInst &I);
+  SizeOffsetAPInt visitExtractValueInst(ExtractValueInst &I);
+  SizeOffsetAPInt visitGlobalAlias(GlobalAlias &GA);
+  SizeOffsetAPInt visitGlobalVariable(GlobalVariable &GV);
+  SizeOffsetAPInt visitIntToPtrInst(IntToPtrInst &);
+  SizeOffsetAPInt visitLoadInst(LoadInst &I);
+  SizeOffsetAPInt visitPHINode(PHINode &);
+  SizeOffsetAPInt visitSelectInst(SelectInst &I);
+  SizeOffsetAPInt visitUndefValue(UndefValue &);
+  SizeOffsetAPInt visitInstruction(Instruction &I);
 
 private:
-  OffsetSpan
-  findLoadOffsetRange(LoadInst &LoadFrom, BasicBlock &BB,
-                      BasicBlock::iterator From,
-                      SmallDenseMap<BasicBlock *, OffsetSpan, 8> &VisitedBlocks,
-                      unsigned &ScannedInstCount);
-  OffsetSpan combineOffsetRange(OffsetSpan LHS, OffsetSpan RHS);
-  OffsetSpan computeImpl(Value *V);
-  OffsetSpan computeValue(Value *V);
+  SizeOffsetAPInt findLoadSizeOffset(
+      LoadInst &LoadFrom, BasicBlock &BB, BasicBlock::iterator From,
+      SmallDenseMap<BasicBlock *, SizeOffsetAPInt, 8> &VisitedBlocks,
+      unsigned &ScannedInstCount);
+  SizeOffsetAPInt combineSizeOffset(SizeOffsetAPInt LHS, SizeOffsetAPInt RHS);
+  SizeOffsetAPInt computeImpl(Value *V);
+  SizeOffsetAPInt computeValue(Value *V);
   bool CheckedZextOrTrunc(APInt &I);
 };
 
diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp
index 44cb2d0942f460..bf69d2c8fe430f 100644
--- a/llvm/lib/Analysis/MemoryBuiltins.cpp
+++ b/llvm/lib/Analysis/MemoryBuiltins.cpp
@@ -700,21 +700,10 @@ ObjectSizeOffsetVisitor::ObjectSizeOffsetVisitor(const DataLayout &DL,
 
 SizeOffsetAPInt ObjectSizeOffsetVisitor::compute(Value *V) {
   InstructionsVisited = 0;
-  OffsetSpan Span = computeImpl(V);
-
-  // In ExactSizeFromOffset mode, we don't care about the Before Field, so allow
-  // us to overwrite it if needs be.
-  if (Span.knownAfter() && !Span.knownBefore() &&
-      Options.EvalMode == ObjectSizeOpts::Mode::ExactSizeFromOffset)
-    Span.Before = APInt::getZero(Span.After.getBitWidth());
-
-  if (!Span.bothKnown())
-    return {};
-
-  return {Span.Before + Span.After, Span.Before};
+  return computeImpl(V);
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::computeImpl(Value *V) {
   unsigned InitialIntTyBits = DL.getIndexTypeSizeInBits(V->getType());
 
   // Stripping pointer casts can strip address space casts which can change the
@@ -731,28 +720,28 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
   IntTyBits = DL.getIndexTypeSizeInBits(V->getType());
   Zero = APInt::getZero(IntTyBits);
 
-  OffsetSpan ORT = computeValue(V);
+  SizeOffsetAPInt SOT = computeValue(V);
 
   bool IndexTypeSizeChanged = InitialIntTyBits != IntTyBits;
   if (!IndexTypeSizeChanged && Offset.isZero())
-    return ORT;
+    return SOT;
 
   // We stripped an address space cast that changed the index type size or we
   // accumulated some constant offset (or both). Readjust the bit width to match
   // the argument index type size and apply the offset, as required.
   if (IndexTypeSizeChanged) {
-    if (ORT.knownBefore() &&
-        !::CheckedZextOrTrunc(ORT.Before, InitialIntTyBits))
-      ORT.Before = APInt();
-    if (ORT.knownAfter() && !::CheckedZextOrTrunc(ORT.After, InitialIntTyBits))
-      ORT.After = APInt();
+    if (SOT.knownSize() && !::CheckedZextOrTrunc(SOT.Size, InitialIntTyBits))
+      SOT.Size = APInt();
+    if (SOT.knownOffset() &&
+        !::CheckedZextOrTrunc(SOT.Offset, InitialIntTyBits))
+      SOT.Offset = APInt();
   }
-  // If the computed bound is "unknown" we cannot add the stripped offset.
-  return {(ORT.knownBefore() ? ORT.Before + Offset : ORT.Before),
-          (ORT.knownAfter() ? ORT.After - Offset : ORT.After)};
+  // If the computed offset is "unknown" we cannot add the stripped offset.
+  return {SOT.Size,
+          SOT.Offset.getBitWidth() > 1 ? SOT.Offset + Offset : SOT.Offset};
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::computeValue(Value *V) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::computeValue(Value *V) {
   if (Instruction *I = dyn_cast<Instruction>(V)) {
     // If we have already seen this instruction, bail out. Cycles can happen in
     // unreachable code after constant propagation.
@@ -762,7 +751,7 @@ OffsetSpan ObjectSizeOffsetVisitor::computeValue(Value *V) {
     ++InstructionsVisited;
     if (InstructionsVisited > ObjectSizeOffsetVisitorMaxVisitInstructions)
       return ObjectSizeOffsetVisitor::unknown();
-    OffsetSpan Res = visit(*I);
+    SizeOffsetAPInt Res = visit(*I);
     // Cache the result for later visits. If we happened to visit this during
     // the above recursion, we would consider it unknown until now.
     SeenInsts[I] = Res;
@@ -788,13 +777,13 @@ bool ObjectSizeOffsetVisitor::CheckedZextOrTrunc(APInt &I) {
   return ::CheckedZextOrTrunc(I, IntTyBits);
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
   TypeSize ElemSize = DL.getTypeAllocSize(I.getAllocatedType());
   if (ElemSize.isScalable() && Options.EvalMode != ObjectSizeOpts::Mode::Min)
     return ObjectSizeOffsetVisitor::unknown();
   APInt Size(IntTyBits, ElemSize.getKnownMinValue());
   if (!I.isArrayAllocation())
-    return OffsetSpan(Zero, align(Size, I.getAlign()));
+    return SizeOffsetAPInt(align(Size, I.getAlign()), Zero);
 
   Value *ArraySize = I.getArraySize();
   if (const ConstantInt *C = dyn_cast<ConstantInt>(ArraySize)) {
@@ -805,12 +794,12 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
     bool Overflow;
     Size = Size.umul_ov(NumElems, Overflow);
     return Overflow ? ObjectSizeOffsetVisitor::unknown()
-                    : OffsetSpan(Zero, align(Size, I.getAlign()));
+                    : SizeOffsetAPInt(align(Size, I.getAlign()), Zero);
   }
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
   Type *MemoryTy = A.getPointeeInMemoryValueType();
   // No interprocedural analysis is done at the moment.
   if (!MemoryTy|| !MemoryTy->isSized()) {
@@ -819,16 +808,16 @@ OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
   }
 
   APInt Size(IntTyBits, DL.getTypeAllocSize(MemoryTy));
-  return OffsetSpan(Zero, align(Size, A.getParamAlign()));
+  return SizeOffsetAPInt(align(Size, A.getParamAlign()), Zero);
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
   if (std::optional<APInt> Size = getAllocSize(&CB, TLI))
-    return OffsetSpan(Zero, *Size);
+    return SizeOffsetAPInt(*Size, Zero);
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-OffsetSpan
+SizeOffsetAPInt
 ObjectSizeOffsetVisitor::visitConstantPointerNull(ConstantPointerNull &CPN) {
   // If null is unknown, there's nothing we can do. Additionally, non-zero
   // address spaces can make use of null, so we don't presume to know anything
@@ -839,43 +828,45 @@ ObjectSizeOffsetVisitor::visitConstantPointerNull(ConstantPointerNull &CPN) {
   // addrspace(1) gets casted to addrspace(0) (or vice-versa).
   if (Options.NullIsUnknownSize || CPN.getType()->getAddressSpace())
     return ObjectSizeOffsetVisitor::unknown();
-  return OffsetSpan(Zero, Zero);
+  return SizeOffsetAPInt(Zero, Zero);
 }
 
-OffsetSpan
+SizeOffsetAPInt
 ObjectSizeOffsetVisitor::visitExtractElementInst(ExtractElementInst &) {
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitExtractValueInst(ExtractValueInst &) {
+SizeOffsetAPInt
+ObjectSizeOffsetVisitor::visitExtractValueInst(ExtractValueInst &) {
   // Easy cases were already folded by previous passes.
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitGlobalAlias(GlobalAlias &GA) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitGlobalAlias(GlobalAlias &GA) {
   if (GA.isInterposable())
     return ObjectSizeOffsetVisitor::unknown();
   return computeImpl(GA.getAliasee());
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
+SizeOffsetAPInt
+ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
   if (!GV.getValueType()->isSized() || GV.hasExternalWeakLinkage() ||
       ((!GV.hasInitializer() || GV.isInterposable()) &&
        Options.EvalMode != ObjectSizeOpts::Mode::Min))
     return ObjectSizeOffsetVisitor::unknown();
 
   APInt Size(IntTyBits, DL.getTypeAllocSize(GV.getValueType()));
-  return OffsetSpan(Zero, align(Size, GV.getAlign()));
+  return SizeOffsetAPInt(align(Size, GV.getAlign()), Zero);
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
   // clueless
   return ObjectSizeOffsetVisitor::unknown();
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::findLoadOffsetRange(
+SizeOffsetAPInt ObjectSizeOffsetVisitor::findLoadSizeOffset(
     LoadInst &Load, BasicBlock &BB, BasicBlock::iterator From,
-    SmallDenseMap<BasicBlock *, OffsetSpan, 8> &VisitedBlocks,
+    SmallDenseMap<BasicBlock *, SizeOffsetAPInt, 8> &VisitedBlocks,
     unsigned &ScannedInstCount) {
   constexpr unsigned MaxInstsToScan = 128;
 
@@ -886,7 +877,7 @@ OffsetSpan ObjectSizeOffsetVisitor::findLoadOffsetRange(
   auto Unknown = [&BB, &VisitedBlocks]() {
     return VisitedBlocks[&BB] = ObjectSizeOffsetVisitor::unknown();
   };
-  auto Known = [&BB, &VisitedBlocks](OffsetSpan SO) {
+  auto Known = [&BB, &VisitedBlocks](SizeOffsetAPInt SO) {
     return VisitedBlocks[&BB] = SO;
   };
 
@@ -957,15 +948,15 @@ OffsetSpan ObjectSizeOffsetVisitor::findLoadOffsetRange(
       if (!C)
         return Unknown();
 
-      return Known({APInt(C->getValue().getBitWidth(), 0), C->getValue()});
+      return Known({C->getValue(), APInt(C->getValue().getBitWidth(), 0)});
     }
 
     return Unknown();
   } while (From-- != BB.begin());
 
-  SmallVector<OffsetSpan> PredecessorSizeOffsets;
+  SmallVector<SizeOffsetAPInt> PredecessorSizeOffsets;
   for (auto *PredBB : predecessors(&BB)) {
-    PredecessorSizeOffsets.push_back(findLoadOffsetRange(
+    PredecessorSizeOffsets.push_back(findLoadSizeOffset(
         Load, *PredBB, BasicBlock::iterator(PredBB->getTerminator()),
         VisitedBlocks, ScannedInstCount));
     if (!PredecessorSizeOffsets.back().bothKnown())
@@ -977,70 +968,83 @@ OffsetSpan ObjectSizeOffsetVisitor::findLoadOffsetRange(
 
   return Known(std::accumulate(
       PredecessorSizeOffsets.begin() + 1, PredecessorSizeOffsets.end(),
-      PredecessorSizeOffsets.front(), [this](OffsetSpan LHS, OffsetSpan RHS) {
-        return combineOffsetRange(LHS, RHS);
+      PredecessorSizeOffsets.front(),
+      [this](SizeOffsetAPInt LHS, SizeOffsetAPInt RHS) {
+        return combineSizeOffset(LHS, RHS);
       }));
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitLoadInst(LoadInst &LI) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitLoadInst(LoadInst &LI) {
   if (!Options.AA) {
     ++ObjectVisitorLoad;
     return ObjectSizeOffsetVisitor::unknown();
   }
 
-  SmallDenseMap<BasicBlock *, OffsetSpan, 8> VisitedBlocks;
+  SmallDenseMap<BasicBlock *, SizeOffsetAPInt, 8> VisitedBlocks;
   unsigned ScannedInstCount = 0;
-  OffsetSpan SO =
-      findLoadOffsetRange(LI, *LI.getParent(), BasicBlock::iterator(LI),
-                          VisitedBlocks, ScannedInstCount);
+  SizeOffsetAPInt SO =
+      findLoadSizeOffset(LI, *LI.getParent(), BasicBlock::iterator(LI),
+                         VisitedBlocks, ScannedInstCount);
   if (!SO.bothKnown())
     ++ObjectVisitorLoad;
   return SO;
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::combineOffsetRange(OffsetSpan LHS,
-                                                       OffsetSpan RHS) {
+SizeOffsetAPInt
+ObjectSizeOffsetVisitor::combineSizeOffset(SizeOffsetAPInt LHS,
+                                           SizeOffsetAPInt RHS) {
   if (!LHS.bothKnown() || !RHS.bothKnown())
     return ObjectSizeOffsetVisitor::unknown();
 
   switch (Options.EvalMode) {
-  case ObjectSizeOpts::Mode::Min:
-    return {LHS.Before.slt(RHS.Before) ? LHS.Before : RHS.Before,
-            LHS.After.slt(RHS.After) ? LHS.After : RHS.After};
+  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: {
-    return {LHS.Before.sgt(RHS.Before) ? LHS.Before : RHS.Before,
-            LHS.After.sgt(RHS.After) ? LHS.After : RHS.After};
+    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 {LHS.Before.eq(RHS.Before) ? LHS.Before : APInt(),
-            LHS.After.eq(RHS.After) ? LHS.After : APInt()};
+    // Treat this as ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset to work
+    // around incorrect uses of the result.
   case ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset:
     return (LHS == RHS) ? LHS : ObjectSizeOffsetVisitor::unknown();
   }
   llvm_unreachable("missing an eval mode");
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitPHINode(PHINode &PN) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitPHINode(PHINode &PN) {
   if (PN.getNumIncomingValues() == 0)
     return ObjectSizeOffsetVisitor::unknown();
   auto IncomingValues = PN.incoming_values();
   return std::accumulate(IncomingValues.begin() + 1, IncomingValues.end(),
                          computeImpl(*IncomingValues.begin()),
-                         [this](OffsetSpan LHS, Value *VRHS) {
-                           return combineOffsetRange(LHS, computeImpl(VRHS));
+                         [this](SizeOffsetAPInt LHS, Value *VRHS) {
+                           return combineSizeOffset(LHS, computeImpl(VRHS));
                          });
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) {
-  return combineOffsetRange(computeImpl(I.getTrueValue()),
-                            computeImpl(I.getFalseValue()));
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) {
+  return combineSizeOffset(computeImpl(I.getTrueValue()),
+                           computeImpl(I.getFalseValue()));
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitUndefValue(UndefValue &) {
-  return OffsetSpan(Zero, Zero);
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitUndefValue(UndefValue &) {
+  return SizeOffsetAPInt(Zero, Zero);
 }
 
-OffsetSpan ObjectSizeOffsetVisitor::visitInstruction(Instruction &I) {
+SizeOffsetAPInt ObjectSizeOffsetVisitor::visitInstruction(Instruction &I) {
   LLVM_DEBUG(dbgs() << "ObjectSizeOffsetVisitor unknown instruction:" << I
                     << '\n');
   return ObjectSizeOffsetVisitor::unknown();
diff --git a/llvm/test/Transforms/InstCombine/builtin-object-size-offset.ll b/llvm/test/Transforms/InstCombine/builtin-object-size-offset.ll
index b8919a76b122a2..8cce071308e0d8 100644
--- a/llvm/test/Transforms/InstCombine/builtin-object-size-offset.ll
+++ b/llvm/test/Transforms/InstCombine/builtin-object-size-offset.ll
@@ -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>



More information about the llvm-branch-commits mailing list