[llvm] 5f34281 - [llvm] Use computeConstantRange to improve llvm.objectsize computation (#114673)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 7 01:01:18 PST 2024
Author: serge-sans-paille
Date: 2024-11-07T09:01:14Z
New Revision: 5f342816efe1854333f2be41a03fdd25fa0db433
URL: https://github.com/llvm/llvm-project/commit/5f342816efe1854333f2be41a03fdd25fa0db433
DIFF: https://github.com/llvm/llvm-project/commit/5f342816efe1854333f2be41a03fdd25fa0db433.diff
LOG: [llvm] Use computeConstantRange to improve llvm.objectsize computation (#114673)
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.
Added:
llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-range.ll
Modified:
llvm/include/llvm/Analysis/MemoryBuiltins.h
llvm/include/llvm/IR/Value.h
llvm/lib/Analysis/MemoryBuiltins.cpp
llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h
index c3b11cdf5cf5db..ff95473505bdfe 100644
--- a/llvm/include/llvm/Analysis/MemoryBuiltins.h
+++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h
@@ -32,6 +32,7 @@ class AAResults;
class Argument;
class ConstantPointerNull;
class DataLayout;
+class DominatorTree;
class ExtractElementInst;
class ExtractValueInst;
class GEPOperator;
@@ -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.
+ DominatorTree *DT = 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, DominatorTree *DT,
+ 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.
diff --git a/llvm/include/llvm/IR/Value.h b/llvm/include/llvm/IR/Value.h
index 945081b77e9536..d444a768a65436 100644
--- a/llvm/include/llvm/IR/Value.h
+++ b/llvm/include/llvm/IR/Value.h
@@ -723,12 +723,16 @@ class Value {
bool AllowInvariantGroup = false,
function_ref<bool(Value &Value, APInt &Offset)> ExternalAnalysis =
nullptr) const;
- Value *stripAndAccumulateConstantOffsets(const DataLayout &DL, APInt &Offset,
- bool AllowNonInbounds,
- bool AllowInvariantGroup = false) {
+
+ Value *stripAndAccumulateConstantOffsets(
+ const DataLayout &DL, APInt &Offset, bool AllowNonInbounds,
+ bool AllowInvariantGroup = false,
+ function_ref<bool(Value &Value, APInt &Offset)> ExternalAnalysis =
+ nullptr) {
return const_cast<Value *>(
static_cast<const Value *>(this)->stripAndAccumulateConstantOffsets(
- DL, Offset, AllowNonInbounds, AllowInvariantGroup));
+ DL, Offset, AllowNonInbounds, AllowInvariantGroup,
+ ExternalAnalysis));
}
/// This is a wrapper around stripAndAccumulateConstantOffsets with the
diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp
index d9769c48f26560..7ba5759f87eab7 100644
--- a/llvm/lib/Analysis/MemoryBuiltins.cpp
+++ b/llvm/lib/Analysis/MemoryBuiltins.cpp
@@ -25,6 +25,7 @@
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalVariable.h"
@@ -589,19 +590,28 @@ Value *llvm::lowerObjectSizeCall(IntrinsicInst *ObjectSize,
const TargetLibraryInfo *TLI,
bool MustSucceed) {
return lowerObjectSizeCall(ObjectSize, DL, TLI, /*AAResults=*/nullptr,
- MustSucceed);
+ /*DT=*/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, /*DT=*/nullptr,
+ MustSucceed, InsertedInstructions);
+}
+
+Value *llvm::lowerObjectSizeCall(
+ IntrinsicInst *ObjectSize, const DataLayout &DL,
+ const TargetLibraryInfo *TLI, AAResults *AA, DominatorTree *DT,
+ 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.DT = DT;
// Unless we have to fold this to something, try to be as accurate as
// possible.
@@ -708,14 +718,46 @@ OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
// readjust the APInt as we pass it upwards in order for the APInt to match
// the type the caller passed in.
APInt Offset(InitialIntTyBits, 0);
+
+ // External Analysis used to compute the Min/Max value of individual Offsets
+ // within a GEP.
+ auto OffsetRangeAnalysis = [this, V](Value &VOffset, APInt &Offset) {
+ if (auto *C = dyn_cast<ConstantInt>(&VOffset)) {
+ Offset = C->getValue();
+ return true;
+ }
+ if (Options.EvalMode != ObjectSizeOpts::Mode::Min &&
+ Options.EvalMode != ObjectSizeOpts::Mode::Max) {
+ return false;
+ }
+ ConstantRange CR = computeConstantRange(
+ &VOffset, /*ForSigned*/ true, /*UseInstrInfo*/ true, /*AC=*/nullptr,
+ /*CtxtI=*/dyn_cast<Instruction>(V), /*DT=*/Options.DT);
+ if (CR.isFullSet())
+ return false;
+
+ if (Options.EvalMode == ObjectSizeOpts::Mode::Min) {
+ Offset = CR.getSignedMax();
+ // Upper bound actually unknown.
+ if (Offset.isMaxSignedValue())
+ return false;
+ } else {
+ Offset = CR.getSignedMin();
+ // Lower bound actually unknown.
+ if (Offset.isMinSignedValue())
+ return false;
+ }
+ return true;
+ };
+
V = V->stripAndAccumulateConstantOffsets(
- DL, Offset, /* AllowNonInbounds */ true, /* AllowInvariantGroup */ true);
+ DL, Offset, /* AllowNonInbounds */ true, /* AllowInvariantGroup */ true,
+ /*ExternalAnalysis=*/OffsetRangeAnalysis);
// Later we use the index type size and zero but it will match the type of the
// value that is passed to computeImpl.
IntTyBits = DL.getIndexTypeSizeInBits(V->getType());
Zero = APInt::getZero(IntTyBits);
-
OffsetSpan ORT = computeValue(V);
bool IndexTypeSizeChanged = InitialIntTyBits != IntTyBits;
@@ -793,6 +835,26 @@ OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
Size = Size.umul_ov(NumElems, Overflow);
return Overflow ? ObjectSizeOffsetVisitor::unknown()
: OffsetSpan(Zero, align(Size, I.getAlign()));
+ } else {
+ ConstantRange CR =
+ computeConstantRange(ArraySize, /*ForSigned*/ false,
+ /*UseInstrInfo*/ true, /*AC=*/nullptr,
+ /*CtxtI=*/&I, /*DT=*/Options.DT);
+ if (CR.isFullSet())
+ return ObjectSizeOffsetVisitor::unknown();
+ APInt Bound;
+ if (Options.EvalMode == ObjectSizeOpts::Mode::Max) {
+ Bound = CR.getUnsignedMax();
+ // Upper bound actually unknown.
+ if (Bound.isMaxValue())
+ return ObjectSizeOffsetVisitor::unknown();
+ } else {
+ Bound = CR.getUnsignedMin();
+ // Lower bound actually unknown.
+ if (Bound.isMinValue())
+ return ObjectSizeOffsetVisitor::unknown();
+ }
+ return OffsetSpan(Zero, align(Bound, I.getAlign()));
}
return ObjectSizeOffsetVisitor::unknown();
}
@@ -810,7 +872,32 @@ 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 (!V->getType()->isIntegerTy())
+ return V;
+ if (isa<ConstantInt>(V))
+ return V;
+ ConstantRange CR = computeConstantRange(
+ V, /*ForSigned*/ false, /*UseInstrInfo*/ true, /*AC=*/nullptr,
+ /*CtxtI=*/&CB, /*DT=*/Options.DT);
+ if (CR.isFullSet())
+ return V;
+
+ APInt Bound;
+ if (Options.EvalMode == ObjectSizeOpts::Mode::Max) {
+ Bound = CR.getUnsignedMax();
+ // Upper bound actually unknown.
+ if (Bound.isMaxValue())
+ return V;
+ } else {
+ Bound = CR.getUnsignedMin();
+ // Lower bound actually unknown.
+ if (Bound.isMinValue())
+ return V;
+ }
+ return ConstantInt::get(V->getType(), Bound);
+ }))
return OffsetSpan(Zero, *Size);
return ObjectSizeOffsetVisitor::unknown();
}
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 563b45de49836f..16e1f4aaa16718 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3318,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, &DT,
+ /*MustSucceed=*/true, &InsertedInstructions);
for (Instruction *Inserted : InsertedInstructions)
Worklist.add(Inserted);
replaceInstUsesWith(*I, Result);
diff --git a/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp b/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp
index dcbec927e55703..1b164042502343 100644
--- a/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp
+++ b/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp
@@ -143,7 +143,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, /*AA=*/nullptr, DT, true);
LLVM_DEBUG(dbgs() << "Folding " << *II << " to " << *NewValue << "\n");
ObjectSizeIntrinsicsHandled++;
break;
diff --git a/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-range.ll b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-range.ll
new file mode 100644
index 00000000000000..29632c617974ac
--- /dev/null
+++ b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-range.ll
@@ -0,0 +1,93 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=lower-constant-intrinsics -S < %s | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare i64 @llvm.objectsize.i64.p0(ptr, i1 immarg, i1 immarg, i1 immarg)
+declare noalias ptr @malloc(i64 noundef) #0
+
+define i64 @select_alloc_size(i1 %cond) {
+; CHECK-LABEL: @select_alloc_size(
+; CHECK-NEXT: [[SIZE:%.*]] = select i1 [[COND:%.*]], i64 3, i64 4
+; CHECK-NEXT: [[PTR:%.*]] = alloca i8, i64 [[SIZE]], align 1
+; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 4, i64 3
+; CHECK-NEXT: ret i64 [[RES]]
+;
+ %size = select i1 %cond, i64 3, i64 4
+ %ptr = alloca i8, i64 %size
+ %objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 false, i1 true, i1 false)
+ %objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 true, i1 true, i1 false)
+ %res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
+ ret i64 %res
+}
+
+define i64 @select_malloc_size(i1 %cond) {
+; CHECK-LABEL: @select_malloc_size(
+; CHECK-NEXT: [[SIZE:%.*]] = select i1 [[COND:%.*]], i64 3, i64 4
+; CHECK-NEXT: [[PTR:%.*]] = call noalias ptr @malloc(i64 noundef [[SIZE]])
+; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 4, i64 3
+; CHECK-NEXT: ret i64 [[RES]]
+;
+ %size = select i1 %cond, i64 3, i64 4
+ %ptr = call noalias ptr @malloc(i64 noundef %size)
+ %objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 false, i1 true, i1 false)
+ %objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr, i1 true, i1 true, i1 false)
+ %res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
+ ret i64 %res
+}
+
+define i64 @select_gep_offset(i1 %cond) {
+; CHECK-LABEL: @select_gep_offset(
+; CHECK-NEXT: [[PTR:%.*]] = alloca i8, i64 10, align 1
+; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[COND:%.*]], i64 3, i64 4
+; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[OFFSET]]
+; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 7, i64 6
+; CHECK-NEXT: ret i64 [[RES]]
+;
+ %ptr = alloca i8, i64 10
+ %offset = select i1 %cond, i64 3, i64 4
+ %ptr.slide = getelementptr inbounds i8, ptr %ptr, i64 %offset
+ %objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide, i1 false, i1 true, i1 false)
+ %objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide, i1 true, i1 true, i1 false)
+ %res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
+ ret i64 %res
+}
+
+define i64 @select_gep_neg_offset(i1 %cond) {
+; CHECK-LABEL: @select_gep_neg_offset(
+; CHECK-NEXT: [[PTR:%.*]] = alloca i8, i64 10, align 1
+; CHECK-NEXT: [[PTR_SLIDE_1:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 5
+; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[COND:%.*]], i64 -3, i64 -4
+; CHECK-NEXT: [[PTR_SLIDE_2:%.*]] = getelementptr inbounds i8, ptr [[PTR_SLIDE_1]], i64 [[OFFSET]]
+; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 9, i64 8
+; CHECK-NEXT: ret i64 [[RES]]
+;
+ %ptr = alloca i8, i64 10
+ %ptr.slide.1 = getelementptr inbounds i8, ptr %ptr, i64 5
+ %offset = select i1 %cond, i64 -3, i64 -4
+ %ptr.slide.2 = getelementptr inbounds i8, ptr %ptr.slide.1, i64 %offset
+ %objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide.2, i1 false, i1 true, i1 false)
+ %objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide.2, i1 true, i1 true, i1 false)
+ %res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
+ ret i64 %res
+}
+
+define i64 @select_gep_offsets(i1 %cond) {
+; CHECK-LABEL: @select_gep_offsets(
+; CHECK-NEXT: [[PTR:%.*]] = alloca [10 x i8], i64 2, align 1
+; CHECK-NEXT: [[OFFSET:%.*]] = select i1 [[COND:%.*]], i32 0, i32 1
+; CHECK-NEXT: [[PTR_SLIDE:%.*]] = getelementptr inbounds [10 x i8], ptr [[PTR]], i32 [[OFFSET]], i32 5
+; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND]], i64 15, i64 5
+; CHECK-NEXT: ret i64 [[RES]]
+;
+ %ptr = alloca [10 x i8], i64 2
+ %offset = select i1 %cond, i32 0, i32 1
+ %ptr.slide = getelementptr inbounds [10 x i8], ptr %ptr, i32 %offset, i32 5
+ %objsize_max = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide, i1 false, i1 true, i1 false)
+ %objsize_min = call i64 @llvm.objectsize.i64.p0(ptr %ptr.slide, i1 true, i1 true, i1 false)
+ %res = select i1 %cond, i64 %objsize_max, i64 %objsize_min
+ ret i64 %res
+}
+
+attributes #0 = { nounwind allocsize(0) }
More information about the llvm-commits
mailing list