Skip to content

Conversation

nikic
Copy link
Contributor

@nikic nikic commented Aug 28, 2025

getObjectSize() is based on ObjectSizeOffsetVisitor, which has become very expensive over time. The implementation is geared towards computing as-good-as-possible results for the objectsize intrinsics and similar. However, we also use it in BasicAA, which is very hot, and really only cares about the base cases like alloca/malloc/global, not any of the analysis for GEPs, phis, or loads.

Add a new getBaseObjectSize() API for this use case, which only handles the non-recursive cases. As a bonus, this API can easily return a TypeSize and thus support scalable vectors. For now, I'm explicitly discarding the scalable sizes in BasicAA just to avoid unnecessary behavior changes during this refactor.

Compile-time: https://llvm-compile-time-tracker.com/compare.php?from=24924a8be1bb7c6083303330ecc0e7dc647247d3&to=061852858448ad2b549fc2b5a0dcd2d38581a859&stat=instructions:u

No changes on llvm-opt-benchmark.

getObjectSize() is based on ObjectSizeOffsetVisitor, which has
become very expensive over time. The implementation is geared
towards computing as-good-as-possible results for the objectsize
intrinsics and similar. However, we also use it in BasicAA, which
is very hot, and really only cares about the base cases like
alloca/malloc/global, not any of the analysis for GEPs, phis, or
loads.

Add a new getBaseObjectSize() API for this use case, which only
handles the non-recursive cases. As a bonus, this API can easily
return a TypeSize and thus support scalable vectors. For now, I'm
explicitly discarding the scalable sizes in BasicAA just to avoid
unnecessary behavior changes during this refactor.
@nikic nikic requested review from fhahn and dtcxzyw August 28, 2025 20:02
@llvmbot llvmbot added the llvm:analysis Includes value tracking, cost tables and constant folding label Aug 28, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 28, 2025

@llvm/pr-subscribers-llvm-analysis

Author: Nikita Popov (nikic)

Changes

getObjectSize() is based on ObjectSizeOffsetVisitor, which has become very expensive over time. The implementation is geared towards computing as-good-as-possible results for the objectsize intrinsics and similar. However, we also use it in BasicAA, which is very hot, and really only cares about the base cases like alloca/malloc/global, not any of the analysis for GEPs, phis, or loads.

Add a new getBaseObjectSize() API for this use case, which only handles the non-recursive cases. As a bonus, this API can easily return a TypeSize and thus support scalable vectors. For now, I'm explicitly discarding the scalable sizes in BasicAA just to avoid unnecessary behavior changes during this refactor.

Compile-time: https://llvm-compile-time-tracker.com/compare.php?from=24924a8be1bb7c6083303330ecc0e7dc647247d3&to=061852858448ad2b549fc2b5a0dcd2d38581a859&stat=instructions:u

No changes on llvm-opt-benchmark.


Full diff: https://github.com/llvm/llvm-project/pull/155911.diff

3 Files Affected:

  • (modified) llvm/include/llvm/Analysis/MemoryBuiltins.h (+7)
  • (modified) llvm/lib/Analysis/BasicAliasAnalysis.cpp (+6-3)
  • (modified) llvm/lib/Analysis/MemoryBuiltins.cpp (+53)
diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h
index 0f0605e4f01b2..85c1560a4732d 100644
--- a/llvm/include/llvm/Analysis/MemoryBuiltins.h
+++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h
@@ -181,6 +181,13 @@ LLVM_ABI bool getObjectSize(const Value *Ptr, uint64_t &Size,
                             const DataLayout &DL, const TargetLibraryInfo *TLI,
                             ObjectSizeOpts Opts = {});
 
+/// Like getObjectSize(), but only supports base objects (like allocas,
+/// global variables and allocator calls). Requires ExactSizeFromOffset mode.
+LLVM_ABI std::optional<TypeSize> getBaseObjectSize(const Value *Ptr,
+                                                   const DataLayout &DL,
+                                                   const TargetLibraryInfo *TLI,
+                                                   ObjectSizeOpts Opts = {});
+
 /// Try to turn a call to \@llvm.objectsize into an integer value of the given
 /// Type. Returns null on failure. If MustSucceed is true, this function will
 /// not return null, and may return conservative values governed by the second
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 86a2edbd8bd41..a8e2f221e2cbb 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -103,12 +103,15 @@ static std::optional<TypeSize> getObjectSize(const Value *V,
                                              const TargetLibraryInfo &TLI,
                                              bool NullIsValidLoc,
                                              bool RoundToAlign = false) {
-  uint64_t Size;
   ObjectSizeOpts Opts;
   Opts.RoundToAlign = RoundToAlign;
   Opts.NullIsUnknownSize = NullIsValidLoc;
-  if (getObjectSize(V, Size, DL, &TLI, Opts))
-    return TypeSize::getFixed(Size);
+  if (std::optional<TypeSize> Size = getBaseObjectSize(V, DL, &TLI, Opts)) {
+    // FIXME: Remove this check, only exists to preserve previous behavior.
+    if (Size->isScalable())
+      return std::nullopt;
+    return Size;
+  }
   return std::nullopt;
 }
 
diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp
index e0b7f65d18a30..1df4eda2580df 100644
--- a/llvm/lib/Analysis/MemoryBuiltins.cpp
+++ b/llvm/lib/Analysis/MemoryBuiltins.cpp
@@ -589,6 +589,59 @@ bool llvm::getObjectSize(const Value *Ptr, uint64_t &Size, const DataLayout &DL,
   return true;
 }
 
+std::optional<TypeSize> llvm::getBaseObjectSize(const Value *Ptr,
+                                                const DataLayout &DL,
+                                                const TargetLibraryInfo *TLI,
+                                                ObjectSizeOpts Opts) {
+  assert(Opts.EvalMode == ObjectSizeOpts::Mode::ExactSizeFromOffset &&
+         "Other modes are currently not supported");
+
+  auto Align = [&](TypeSize Size, MaybeAlign Alignment) {
+    if (Opts.RoundToAlign && Alignment && !Size.isScalable())
+      return TypeSize::getFixed(alignTo(Size.getFixedValue(), *Alignment));
+    return Size;
+  };
+
+  if (isa<UndefValue>(Ptr))
+    return TypeSize::getZero();
+
+  if (isa<ConstantPointerNull>(Ptr)) {
+    if (Opts.NullIsUnknownSize || Ptr->getType()->getPointerAddressSpace())
+      return std::nullopt;
+    return TypeSize::getZero();
+  }
+
+  if (auto *GV = dyn_cast<GlobalVariable>(Ptr)) {
+    if (!GV->getValueType()->isSized() || GV->hasExternalWeakLinkage() ||
+        !GV->hasInitializer() || GV->isInterposable())
+      return std::nullopt;
+    return Align(DL.getTypeAllocSize(GV->getValueType()), GV->getAlign());
+  }
+
+  if (auto *A = dyn_cast<Argument>(Ptr)) {
+    Type *MemoryTy = A->getPointeeInMemoryValueType();
+    if (!MemoryTy || !MemoryTy->isSized())
+      return std::nullopt;
+    return Align(DL.getTypeAllocSize(MemoryTy), A->getParamAlign());
+  }
+
+  if (auto *AI = dyn_cast<AllocaInst>(Ptr)) {
+    if (std::optional<TypeSize> Size = AI->getAllocationSize(DL))
+      return Align(*Size, AI->getAlign());
+    return std::nullopt;
+  }
+
+  if (auto *CB = dyn_cast<CallBase>(Ptr)) {
+    if (std::optional<APInt> Size = getAllocSize(CB, TLI)) {
+      if (std::optional<uint64_t> ZExtSize = Size->tryZExtValue())
+        return TypeSize::getFixed(*ZExtSize);
+    }
+    return std::nullopt;
+  }
+
+  return std::nullopt;
+}
+
 Value *llvm::lowerObjectSizeCall(IntrinsicInst *ObjectSize,
                                  const DataLayout &DL,
                                  const TargetLibraryInfo *TLI,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:analysis Includes value tracking, cost tables and constant folding
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants