Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[llvm] Use computeConstantRange to improve llvm.objectsize computation #114673

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

serge-sans-paille
Copy link
Collaborator

Using LazyValueInfo, it is possible to compute valuable information for allocation functions, GEP and alloca, even in the presence of dynamic information.

llvm.objectsize plays an important role in _FORTIFY_SOURCE definitions, so improving its diagnostic in turns improves the security of compiled application.

As a side note, as a result of recent optimization improvements, clang no longer passes https://github.com/serge-sans-paille/builtin_object_size-test-suite This commit restores the situation and greatly improves the scope of code handled by the static version of __builtin_object_size.

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 2, 2024

@llvm/pr-subscribers-llvm-ir
@llvm/pr-subscribers-llvm-analysis

@llvm/pr-subscribers-llvm-transforms

Author: None (serge-sans-paille)

Changes

Using LazyValueInfo, it is possible to compute valuable information for allocation functions, GEP and alloca, even in the presence of dynamic information.

llvm.objectsize plays an important role in _FORTIFY_SOURCE definitions, so improving its diagnostic in turns improves the security of compiled application.

As a side note, as a result of recent optimization improvements, clang no longer passes https://github.com/serge-sans-paille/builtin_object_size-test-suite This commit restores the situation and greatly improves the scope of code handled by the static version of __builtin_object_size.


Patch is 46.40 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/114673.diff

17 Files Affected:

  • (modified) llvm/include/llvm/Analysis/MemoryBuiltins.h (+11-1)
  • (modified) llvm/include/llvm/Transforms/InstCombine/InstCombiner.h (+9-6)
  • (modified) llvm/include/llvm/Transforms/Scalar/LowerConstantIntrinsics.h (+2-1)
  • (modified) llvm/lib/Analysis/MemoryBuiltins.cpp (+76-3)
  • (modified) llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp (+2-1)
  • (modified) llvm/lib/Transforms/InstCombine/InstCombineInternal.h (+6-5)
  • (modified) llvm/lib/Transforms/InstCombine/InstructionCombining.cpp (+18-11)
  • (modified) llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp (+5-3)
  • (modified) llvm/test/Other/new-pm-defaults.ll (+5-2)
  • (modified) llvm/test/Other/new-pm-lto-defaults.ll (+1-1)
  • (modified) llvm/test/Other/new-pm-thinlto-postlink-defaults.ll (+12-9)
  • (modified) llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll (+5-2)
  • (modified) llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll (+5-2)
  • (modified) llvm/test/Other/new-pm-thinlto-prelink-defaults.ll (+4-2)
  • (modified) llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll (+5-2)
  • (modified) llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll (+4-2)
  • (added) llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-range.ll (+107)
diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h
index c3b11cdf5cf5db..feb4bc7b8c98da 100644
--- a/llvm/include/llvm/Analysis/MemoryBuiltins.h
+++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h
@@ -42,6 +42,7 @@ class IntegerType;
 class IntrinsicInst;
 class IntToPtrInst;
 class LLVMContext;
+class LazyValueInfo;
 class LoadInst;
 class PHINode;
 class SelectInst;
@@ -160,8 +161,10 @@ struct ObjectSizeOpts {
   /// though they can't be evaluated. Otherwise, null is always considered to
   /// point to a 0 byte region of memory.
   bool NullIsUnknownSize = false;
-  /// If set, used for more accurate evaluation
+  /// If set, used for more accurate evaluation.
   AAResults *AA = nullptr;
+  /// If set, used for more accurate evaluation.
+  LazyValueInfo *LVI = nullptr;
 };
 
 /// Compute the size of the object pointed by Ptr. Returns true and the
@@ -186,6 +189,12 @@ Value *lowerObjectSizeCall(
     const TargetLibraryInfo *TLI, AAResults *AA, bool MustSucceed,
     SmallVectorImpl<Instruction *> *InsertedInstructions = nullptr);
 
+Value *lowerObjectSizeCall(
+    IntrinsicInst *ObjectSize, const DataLayout &DL,
+    const TargetLibraryInfo *TLI, AAResults *AA, LazyValueInfo *LVI,
+    bool MustSucceed,
+    SmallVectorImpl<Instruction *> *InsertedInstructions = nullptr);
+
 /// SizeOffsetType - A base template class for the object size visitors. Used
 /// here as a self-documenting way to handle the values rather than using a
 /// \p std::pair.
@@ -275,6 +284,7 @@ class ObjectSizeOffsetVisitor
   OffsetSpan visitExtractValueInst(ExtractValueInst &I);
   OffsetSpan visitGlobalAlias(GlobalAlias &GA);
   OffsetSpan visitGlobalVariable(GlobalVariable &GV);
+  OffsetSpan visitGetElementPtr(GetElementPtrInst &GEP);
   OffsetSpan visitIntToPtrInst(IntToPtrInst &);
   OffsetSpan visitLoadInst(LoadInst &I);
   OffsetSpan visitPHINode(PHINode &);
diff --git a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
index 3075b7ebae59e6..72861684a7f754 100644
--- a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
+++ b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
@@ -37,6 +37,7 @@ namespace llvm {
 class AAResults;
 class AssumptionCache;
 class OptimizationRemarkEmitter;
+class LazyValueInfo;
 class ProfileSummaryInfo;
 class TargetLibraryInfo;
 class TargetTransformInfo;
@@ -72,6 +73,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
   // Required analyses.
   AssumptionCache &AC;
   TargetLibraryInfo &TLI;
+  LazyValueInfo &LVI;
   DominatorTree &DT;
   const DataLayout &DL;
   SimplifyQuery SQ;
@@ -101,14 +103,15 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
   InstCombiner(InstructionWorklist &Worklist, BuilderTy &Builder,
                bool MinimizeSize, AAResults *AA, AssumptionCache &AC,
                TargetLibraryInfo &TLI, TargetTransformInfo &TTI,
-               DominatorTree &DT, OptimizationRemarkEmitter &ORE,
-               BlockFrequencyInfo *BFI, BranchProbabilityInfo *BPI,
-               ProfileSummaryInfo *PSI, const DataLayout &DL,
+               LazyValueInfo &LVI, DominatorTree &DT,
+               OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI,
+               BranchProbabilityInfo *BPI, ProfileSummaryInfo *PSI,
+               const DataLayout &DL,
                ReversePostOrderTraversal<BasicBlock *> &RPOT)
       : TTIForTargetIntrinsicsOnly(TTI), Builder(Builder), Worklist(Worklist),
-        MinimizeSize(MinimizeSize), AA(AA), AC(AC), TLI(TLI), DT(DT), DL(DL),
-        SQ(DL, &TLI, &DT, &AC, nullptr, /*UseInstrInfo*/ true,
-           /*CanUseUndef*/ true, &DC),
+        MinimizeSize(MinimizeSize), AA(AA), AC(AC), TLI(TLI), LVI(LVI), DT(DT),
+        DL(DL), SQ(DL, &TLI, &DT, &AC, nullptr, /*UseInstrInfo*/ true,
+                   /*CanUseUndef*/ true, &DC),
         ORE(ORE), BFI(BFI), BPI(BPI), PSI(PSI), RPOT(RPOT) {}
 
   virtual ~InstCombiner() = default;
diff --git a/llvm/include/llvm/Transforms/Scalar/LowerConstantIntrinsics.h b/llvm/include/llvm/Transforms/Scalar/LowerConstantIntrinsics.h
index 3d4d1034c0b24d..86f542109c1cf0 100644
--- a/llvm/include/llvm/Transforms/Scalar/LowerConstantIntrinsics.h
+++ b/llvm/include/llvm/Transforms/Scalar/LowerConstantIntrinsics.h
@@ -22,9 +22,10 @@ namespace llvm {
 class DominatorTree;
 class Function;
 class TargetLibraryInfo;
+class LazyValueInfo;
 
 bool lowerConstantIntrinsics(Function &F, const TargetLibraryInfo &TLI,
-                             DominatorTree *DT);
+                             DominatorTree *DT, LazyValueInfo *LVI);
 
 struct LowerConstantIntrinsicsPass :
     PassInfoMixin<LowerConstantIntrinsicsPass> {
diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp
index 71400ac46bdcbf..a934fb7cc52558 100644
--- a/llvm/lib/Analysis/MemoryBuiltins.cpp
+++ b/llvm/lib/Analysis/MemoryBuiltins.cpp
@@ -16,6 +16,8 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/AssumptionCache.h"
+#include "llvm/Analysis/LazyValueInfo.h"
 #include "llvm/Analysis/TargetFolder.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/Utils/Local.h"
@@ -590,19 +592,28 @@ Value *llvm::lowerObjectSizeCall(IntrinsicInst *ObjectSize,
                                  const TargetLibraryInfo *TLI,
                                  bool MustSucceed) {
   return lowerObjectSizeCall(ObjectSize, DL, TLI, /*AAResults=*/nullptr,
-                             MustSucceed);
+                             /*LazyValueInfo=*/nullptr, MustSucceed);
 }
 
 Value *llvm::lowerObjectSizeCall(
     IntrinsicInst *ObjectSize, const DataLayout &DL,
     const TargetLibraryInfo *TLI, AAResults *AA, bool MustSucceed,
     SmallVectorImpl<Instruction *> *InsertedInstructions) {
+  return lowerObjectSizeCall(ObjectSize, DL, TLI, AA, /*LazyValueInfo=*/nullptr,
+                             MustSucceed, InsertedInstructions);
+}
+
+Value *llvm::lowerObjectSizeCall(
+    IntrinsicInst *ObjectSize, const DataLayout &DL,
+    const TargetLibraryInfo *TLI, AAResults *AA, LazyValueInfo *LVI,
+    bool MustSucceed, SmallVectorImpl<Instruction *> *InsertedInstructions) {
   assert(ObjectSize->getIntrinsicID() == Intrinsic::objectsize &&
          "ObjectSize must be a call to llvm.objectsize!");
 
   bool MaxVal = cast<ConstantInt>(ObjectSize->getArgOperand(1))->isZero();
   ObjectSizeOpts EvalOptions;
   EvalOptions.AA = AA;
+  EvalOptions.LVI = LVI;
 
   // Unless we have to fold this to something, try to be as accurate as
   // possible.
@@ -716,7 +727,6 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
   // value that is passed to computeImpl.
   IntTyBits = DL.getIndexTypeSizeInBits(V->getType());
   Zero = APInt::getZero(IntTyBits);
-
   OffsetSpan ORT = computeValue(V);
 
   bool IndexTypeSizeChanged = InitialIntTyBits != IntTyBits;
@@ -794,6 +804,17 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
     Size = Size.umul_ov(NumElems, Overflow);
     return Overflow ? ObjectSizeOffsetVisitor::unknown()
                     : OffsetSpan(Zero, align(Size, I.getAlign()));
+  } else if (Options.LVI) {
+    ConstantRange CR =
+        Options.LVI->getConstantRange(const_cast<Value *>(ArraySize), &I, true);
+    if (CR.isFullSet())
+      return ObjectSizeOffsetVisitor::unknown();
+    APInt Bound;
+    if (Options.EvalMode == ObjectSizeOpts::Mode::Max)
+      Bound = CR.getUnsignedMax();
+    else
+      Bound = CR.getUnsignedMin();
+    return OffsetSpan(Zero, align(Bound, I.getAlign()));
   }
   return ObjectSizeOffsetVisitor::unknown();
 }
@@ -811,7 +832,23 @@ OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
 }
 
 OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
-  if (std::optional<APInt> Size = getAllocSize(&CB, TLI))
+  if (std::optional<APInt> Size =
+          getAllocSize(&CB, TLI, [&CB, this](const Value *V) -> const Value * {
+            if (!Options.LVI)
+              return V;
+            if (!V->getType()->isIntegerTy())
+              return V;
+            ConstantRange CR = Options.LVI->getConstantRange(
+                const_cast<Value *>(V), &CB, true);
+            if (CR.isFullSet())
+              return V;
+            APInt Bound;
+            if (Options.EvalMode == ObjectSizeOpts::Mode::Max)
+              Bound = CR.getUnsignedMax();
+            else
+              Bound = CR.getUnsignedMin();
+            return ConstantInt::get(V->getType(), Bound);
+          }))
     return OffsetSpan(Zero, *Size);
   return ObjectSizeOffsetVisitor::unknown();
 }
@@ -856,6 +893,42 @@ OffsetSpan ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
   return OffsetSpan(Zero, align(Size, GV.getAlign()));
 }
 
+OffsetSpan ObjectSizeOffsetVisitor::visitGetElementPtr(GetElementPtrInst &GEP) {
+  OffsetSpan PtrData = computeImpl(GEP.getPointerOperand());
+  if (!PtrData.bothKnown())
+    return ObjectSizeOffsetVisitor::unknown();
+
+  if (!Options.LVI)
+    return ObjectSizeOffsetVisitor::unknown();
+
+  if (Options.EvalMode == ObjectSizeOpts::Mode::Min ||
+      Options.EvalMode == ObjectSizeOpts::Mode::Max) {
+    unsigned BitWidth = PtrData.After.getBitWidth();
+    APInt ConstantOffset = Zero;
+    SmallMapVector<Value *, APInt, 4> VariableOffsets;
+    if (!GEP.collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset))
+      return ObjectSizeOffsetVisitor::unknown();
+
+    ConstantRange AccumulatedRange = ConstantOffset;
+    for (auto const &VO : VariableOffsets) {
+      ConstantRange CR = Options.LVI->getConstantRange(VO.first, &GEP, true);
+      if (CR.isFullSet())
+        return ObjectSizeOffsetVisitor::unknown();
+
+      AccumulatedRange = AccumulatedRange.add(CR.multiply(VO.second));
+    }
+
+    APInt Bound;
+    if (Options.EvalMode == ObjectSizeOpts::Mode::Min)
+      Bound = AccumulatedRange.getSignedMax();
+    else
+      Bound = AccumulatedRange.getSignedMin();
+
+    return {PtrData.Before + Bound, PtrData.After - Bound};
+  }
+  return ObjectSizeOffsetVisitor::unknown();
+}
+
 OffsetSpan ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
   // clueless
   return ObjectSizeOffsetVisitor::unknown();
diff --git a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
index 3373b76edb268f..ba42af6151e27b 100644
--- a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
+++ b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
@@ -350,7 +350,8 @@ bool PreISelIntrinsicLowering::lowerIntrinsics(Module &M) const {
         Function *Parent = CI->getParent()->getParent();
         TargetLibraryInfo &TLI = LookupTLI(*Parent);
         // Intrinsics in unreachable code are not lowered.
-        bool Changed = lowerConstantIntrinsics(*Parent, TLI, /*DT=*/nullptr);
+        bool Changed = lowerConstantIntrinsics(*Parent, TLI, /*DT=*/nullptr,
+                                               /*LVI=*/nullptr);
         return Changed;
       });
       break;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index adbd9186c59c5a..820be6fc2bb1f6 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -63,12 +63,13 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   InstCombinerImpl(InstructionWorklist &Worklist, BuilderTy &Builder,
                    bool MinimizeSize, AAResults *AA, AssumptionCache &AC,
                    TargetLibraryInfo &TLI, TargetTransformInfo &TTI,
-                   DominatorTree &DT, OptimizationRemarkEmitter &ORE,
-                   BlockFrequencyInfo *BFI, BranchProbabilityInfo *BPI,
-                   ProfileSummaryInfo *PSI, const DataLayout &DL,
+                   LazyValueInfo &LVI, DominatorTree &DT,
+                   OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI,
+                   BranchProbabilityInfo *BPI, ProfileSummaryInfo *PSI,
+                   const DataLayout &DL,
                    ReversePostOrderTraversal<BasicBlock *> &RPOT)
-      : InstCombiner(Worklist, Builder, MinimizeSize, AA, AC, TLI, TTI, DT, ORE,
-                     BFI, BPI, PSI, DL, RPOT) {}
+      : InstCombiner(Worklist, Builder, MinimizeSize, AA, AC, TLI, TTI, LVI, DT,
+                     ORE, BFI, BPI, PSI, DL, RPOT) {}
 
   virtual ~InstCombinerImpl() = default;
 
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 2a54390c0f1882..9466d17272888c 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -48,6 +48,7 @@
 #include "llvm/Analysis/GlobalsModRef.h"
 #include "llvm/Analysis/InstructionSimplify.h"
 #include "llvm/Analysis/LazyBlockFrequencyInfo.h"
+#include "llvm/Analysis/LazyValueInfo.h"
 #include "llvm/Analysis/MemoryBuiltins.h"
 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/Analysis/ProfileSummaryInfo.h"
@@ -3317,8 +3318,9 @@ Instruction *InstCombinerImpl::visitAllocSite(Instruction &MI) {
       if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
         if (II->getIntrinsicID() == Intrinsic::objectsize) {
           SmallVector<Instruction *> InsertedInstructions;
-          Value *Result = lowerObjectSizeCall(
-              II, DL, &TLI, AA, /*MustSucceed=*/true, &InsertedInstructions);
+          Value *Result =
+              lowerObjectSizeCall(II, DL, &TLI, AA, &LVI, /*MustSucceed=*/true,
+                                  &InsertedInstructions);
           for (Instruction *Inserted : InsertedInstructions)
             Worklist.add(Inserted);
           replaceInstUsesWith(*I, Result);
@@ -5460,9 +5462,9 @@ void InstCombiner::computeBackEdges() {
 static bool combineInstructionsOverFunction(
     Function &F, InstructionWorklist &Worklist, AliasAnalysis *AA,
     AssumptionCache &AC, TargetLibraryInfo &TLI, TargetTransformInfo &TTI,
-    DominatorTree &DT, OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI,
-    BranchProbabilityInfo *BPI, ProfileSummaryInfo *PSI,
-    const InstCombineOptions &Opts) {
+    LazyValueInfo &LVI, DominatorTree &DT, OptimizationRemarkEmitter &ORE,
+    BlockFrequencyInfo *BFI, BranchProbabilityInfo *BPI,
+    ProfileSummaryInfo *PSI, const InstCombineOptions &Opts) {
   auto &DL = F.getDataLayout();
   bool VerifyFixpoint = Opts.VerifyFixpoint &&
                         !F.hasFnAttribute("instcombine-no-verify-fixpoint");
@@ -5501,8 +5503,8 @@ static bool combineInstructionsOverFunction(
     LLVM_DEBUG(dbgs() << "\n\nINSTCOMBINE ITERATION #" << Iteration << " on "
                       << F.getName() << "\n");
 
-    InstCombinerImpl IC(Worklist, Builder, F.hasMinSize(), AA, AC, TLI, TTI, DT,
-                        ORE, BFI, BPI, PSI, DL, RPOT);
+    InstCombinerImpl IC(Worklist, Builder, F.hasMinSize(), AA, AC, TLI, TTI,
+                        LVI, DT, ORE, BFI, BPI, PSI, DL, RPOT);
     IC.MaxArraySizeForCombine = MaxArraySize;
     bool MadeChangeInThisIteration = IC.prepareWorklist(F);
     MadeChangeInThisIteration |= IC.run();
@@ -5552,6 +5554,7 @@ PreservedAnalyses InstCombinePass::run(Function &F,
   auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
   auto &ORE = AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
   auto &TTI = AM.getResult<TargetIRAnalysis>(F);
+  auto &LVI = AM.getResult<LazyValueAnalysis>(F);
 
   auto *AA = &AM.getResult<AAManager>(F);
   auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
@@ -5561,8 +5564,8 @@ PreservedAnalyses InstCombinePass::run(Function &F,
       &AM.getResult<BlockFrequencyAnalysis>(F) : nullptr;
   auto *BPI = AM.getCachedResult<BranchProbabilityAnalysis>(F);
 
-  if (!combineInstructionsOverFunction(F, Worklist, AA, AC, TLI, TTI, DT, ORE,
-                                       BFI, BPI, PSI, Options))
+  if (!combineInstructionsOverFunction(F, Worklist, AA, AC, TLI, TTI, LVI, DT,
+                                       ORE, BFI, BPI, PSI, Options))
     // No changes, all analyses are preserved.
     return PreservedAnalyses::all();
 
@@ -5580,6 +5583,7 @@ void InstructionCombiningPass::getAnalysisUsage(AnalysisUsage &AU) const {
   AU.addRequired<TargetTransformInfoWrapperPass>();
   AU.addRequired<DominatorTreeWrapperPass>();
   AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
+  AU.addRequired<LazyValueInfoWrapperPass>();
   AU.addPreserved<DominatorTreeWrapperPass>();
   AU.addPreserved<AAResultsWrapperPass>();
   AU.addPreserved<BasicAAWrapperPass>();
@@ -5597,6 +5601,7 @@ bool InstructionCombiningPass::runOnFunction(Function &F) {
   auto &AC = getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
   auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
   auto &TTI = getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
+  auto &LVI = getAnalysis<LazyValueInfoWrapperPass>().getLVI();
   auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
   auto &ORE = getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
 
@@ -5612,8 +5617,9 @@ bool InstructionCombiningPass::runOnFunction(Function &F) {
           getAnalysisIfAvailable<BranchProbabilityInfoWrapperPass>())
     BPI = &WrapperPass->getBPI();
 
-  return combineInstructionsOverFunction(F, Worklist, AA, AC, TLI, TTI, DT, ORE,
-                                         BFI, BPI, PSI, InstCombineOptions());
+  return combineInstructionsOverFunction(F, Worklist, AA, AC, TLI, TTI, LVI, DT,
+                                         ORE, BFI, BPI, PSI,
+                                         InstCombineOptions());
 }
 
 char InstructionCombiningPass::ID = 0;
@@ -5630,6 +5636,7 @@ INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(GlobalsAAWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(LazyValueInfoWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(LazyBlockFrequencyInfoPass)
 INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass)
diff --git a/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp b/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp
index 8dd72b8f1414ed..f764752695afdc 100644
--- a/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp
+++ b/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp
@@ -18,6 +18,7 @@
 #include "llvm/Analysis/DomTreeUpdater.h"
 #include "llvm/Analysis/GlobalsModRef.h"
 #include "llvm/Analysis/InstructionSimplify.h"
+#include "llvm/Analysis/LazyValueInfo.h"
 #include "llvm/Analysis/MemoryBuiltins.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/IR/BasicBlock.h"
@@ -100,7 +101,7 @@ static bool replaceConditionalBranchesOnConstant(Instruction *II,
 }
 
 bool llvm::lowerConstantIntrinsics(Function &F, const TargetLibraryInfo &TLI,
-                                   DominatorTree *DT) {
+                                   DominatorTree *DT, LazyValueInfo *LVI) {
   std::optional<DomTreeUpdater> DTU;
   if (DT)
     DTU.emplace(DT, DomTreeUpdater::UpdateStrategy::Lazy);
@@ -144,7 +145,7 @@ bool llvm::lowerConstantIntrinsics(Function &F, const TargetLibraryInfo &TLI,
       IsConstantIntrinsicsHandled++;
       break;
     case Intrinsic::objectsize:
-      NewValue = lowerObjectSizeCall(II, DL, &TLI, true);
+      NewValue = lowerObjectSizeCall(II, DL, &TLI, nullptr, LVI, true);
       LLVM_DEBUG(dbgs() << "Folding " << *II << " to " << *NewValue << "\n");
       ObjectSizeIntrinsicsHandled++;
       break;
@@ -160,7 +161,8 @@ bool llvm::lowerConstantIntrinsics(Function &F, const TargetLibraryInfo &TLI,
 PreservedAnalyses
 LowerConstantIntrinsicsPass::run(Function &F, FunctionAnalysisManager &AM) {
   if (lowerConstantIntrinsics(F, AM.getResult<TargetLibraryAnalysis>(F),
-                              AM.getCachedResult<DominatorTreeAnalysis>(F))) {
+                              AM.getCachedResult<DominatorTreeAnalysis>(F),
+                              &AM.getResult<LazyValueAnalysis>(F))) {
     PreservedAnalyses PA;
     PA.preserve<DominatorTreeAnalysis>();
     return PA;
diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll
index 55dbdb1b8366d6..17211...
[truncated]

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely cannot use LVI inside InstCombine for invalidation reasons. Using it in LowerConstantIntrinsics may be theoretically possible, but I really don't want to go there.

Can your motivating cases be covered by ValueTracking helpers, either computeConstantRange() or computeKnownBits()?

@serge-sans-paille
Copy link
Collaborator Author

I've just submitted a version that uses computeConstantRange(). It is satisfying for my use case, although not as accurate as LVI.
Can you provide pointers to something that would help me understand

We definitely cannot use LVI inside InstCombine for invalidation reasons.

I'm not familiar with this. Thanks!

@nikic
Copy link
Contributor

nikic commented Nov 2, 2024

Can you provide pointers to something that would help me understand

We definitely cannot use LVI inside InstCombine for invalidation reasons.

I'm not familiar with this. Thanks!

InstCombine has a lot of different folds, and some of them do things like modifying instructions in place. It's essentially impossible to keep analyses that have non-trivial state up-to-date in InstCombine.

@serge-sans-paille
Copy link
Collaborator Author

more than 9k extra __builtin_object_size resolved in Firefox codebase thanks to this patch ;-)

@hvdijk
Copy link
Contributor

hvdijk commented Nov 4, 2024

It feels a bit risky to use llvm.assume to optimise llvm.objectsize, since the former means it's UB if the assumption is false, and the latter is used to detect various forms of UB. Your PR might be fine, but it is difficult to be sure: we would not want the presence of UB to result in diagnostics for UB being suppressed. I will look in more detail later.

@hvdijk
Copy link
Contributor

hvdijk commented Nov 5, 2024

The problem I hinted at in my previous comment turns out to be a problem already even without your PR. Consider something like

int f(int n) {
  __builtin_assume(n >= 20);
  int buf[n];
  buf[10] = 0;
  return buf[10];
}

int main(void) {
  return f(3);
}

It is good that in recent versions of Clang, with -fsanitize=undefined, this prints "runtime error: assumption is violated during execution".

It is less good that without that __builtin_assume, we would also diagnose "runtime error: index 10 out of bounds for type 'int[n]'", but with that __builtin_assume, we do not, we still have an out of bounds access but we now ignore it.

I am not sure what the intended behaviour is here. Is this something we want to diagnose, or is this something we want to optimise away?

@serge-sans-paille
Copy link
Collaborator Author

The problem I hinted at in my previous comment turns out to be a problem already even without your PR. Consider something like

int f(int n) {
  __builtin_assume(n >= 20);
  int buf[n];
  buf[10] = 0;
  return buf[10];
}

int main(void) {
  return f(3);
}

It is good that in recent versions of Clang, with -fsanitize=undefined, this prints "runtime error: assumption is violated during execution".

agreed.

It is less good that without that __builtin_assume, we would also diagnose "runtime error: index 10 out of bounds for type 'int[n]'", but with that __builtin_assume, we do not, we still have an out of bounds access but we now ignore it.

I am not sure what the intended behaviour is here. Is this something we want to diagnose, or is this something we want to optimise away?

It's indeed tricky. From a strict __builtin_object_size point of view, it makes sense to always use these information as it improves its accuracy. The problem is that __builtin_object_size is often used to implement security feature, so there's a tension there.

I'll update the patch to not use assumptions, that's already a strict improvement.

@hvdijk
Copy link
Contributor

hvdijk commented Nov 5, 2024

I'll update the patch to not use assumptions, that's already a strict improvement.

That is a good idea, we can always add assumptions in a later follow-up PR if we do want them. This approach looks good, thanks, just left a small comment about handling overflow but that should not change the main idea of your PR.

Copy link

github-actions bot commented Nov 5, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@hvdijk hvdijk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looks good to me!

You updated the commit according to the change requested by @nikic, but the PR title and description are still the original, so just make sure that when you merge and squash, you do not accidentally overwrite your correct commit message with the older PR description :)

@serge-sans-paille serge-sans-paille changed the title [llvm] Use LazyValueInfo to improve llvm.objectsize computation [llvm] Use computeConstantRange to improve llvm.objectsize computation Nov 6, 2024
Using computeConstantRange, it is possible to compute valuable information for
allocation functions, GEP and alloca, even in the presence of some dynamic
information.

llvm.objectsize plays an important role in _FORTIFY_SOURCE definitions,
so improving its diagnostic in turns improves the security of compiled
application.

As a side note, as a result of recent optimization improvements, clang no
longer passes https://github.com/serge-sans-paille/builtin_object_size-test-suite
This commit restores the situation and greatly improves the scope of
code handled by the static version of __builtin_object_size.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants