Skip to content

Commit

Permalink
Calculate __builtin_object_size when pointer depends on a condition
Browse files Browse the repository at this point in the history
This patch fixes calculating of builtin_object_size if it depends on a
condition. Before this patch compiler did not know how to calculate the
object size when it finds a condition that cannot be eliminated.
This patch enables calculating of builtin_object_size even in case when
condition cannot be eliminated by choosing minimum or maximum value as a
result from condition. Choosing minimum or maximum value from condition
is based on the second argument of __builtin_object_size function.

Patch by Strahinja Petrovic.

Differential Revision: http://reviews.llvm.org/D18438


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@266193 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
petar-jovanovic committed Apr 13, 2016
1 parent 5654752 commit 396d592
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 19 deletions.
14 changes: 12 additions & 2 deletions include/llvm/Analysis/MemoryBuiltins.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class TargetLibraryInfo;
class Type;
class Value;

enum class ObjSizeMode {
Exact = 0,
Min = 1,
Max = 2
};

/// \brief Tests if a value is a call or invoke to a library function that
/// allocates or reallocates memory (either malloc, calloc, realloc, or strdup
Expand Down Expand Up @@ -130,8 +135,11 @@ static inline CallInst *isFreeCall(Value *I, const TargetLibraryInfo *TLI) {
/// underlying object pointed to by Ptr.
/// If RoundToAlign is true, then Size is rounded up to the aligment of allocas,
/// byval arguments, and global variables.
/// If Mode is Min or Max the size will be evaluated even if it depends on
/// a condition and corresponding value will be returned (min or max).
bool getObjectSize(const Value *Ptr, uint64_t &Size, const DataLayout &DL,
const TargetLibraryInfo *TLI, bool RoundToAlign = false);
const TargetLibraryInfo *TLI, bool RoundToAlign = false,
ObjSizeMode Mode = ObjSizeMode::Exact);

typedef std::pair<APInt, APInt> SizeOffsetType;

Expand All @@ -143,6 +151,7 @@ class ObjectSizeOffsetVisitor
const DataLayout &DL;
const TargetLibraryInfo *TLI;
bool RoundToAlign;
ObjSizeMode Mode;
unsigned IntTyBits;
APInt Zero;
SmallPtrSet<Instruction *, 8> SeenInsts;
Expand All @@ -155,7 +164,8 @@ class ObjectSizeOffsetVisitor

public:
ObjectSizeOffsetVisitor(const DataLayout &DL, const TargetLibraryInfo *TLI,
LLVMContext &Context, bool RoundToAlign = false);
LLVMContext &Context, bool RoundToAlign = false,
ObjSizeMode Mode = ObjSizeMode::Exact);

SizeOffsetType compute(Value *V);

Expand Down
49 changes: 35 additions & 14 deletions lib/Analysis/MemoryBuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,29 +367,29 @@ const CallInst *llvm::isFreeCall(const Value *I, const TargetLibraryInfo *TLI) {
//===----------------------------------------------------------------------===//
// Utility functions to compute size of objects.
//

static APInt getSizeWithOverflow(const SizeOffsetType &Data) {
if (Data.second.isNegative() || Data.first.ult(Data.second))
return APInt(Data.first.getBitWidth(), 0);
return Data.first - Data.second;
}

/// \brief Compute the size of the object pointed by Ptr. Returns true and the
/// object size in Size if successful, and false otherwise.
/// If RoundToAlign is true, then Size is rounded up to the aligment of allocas,
/// byval arguments, and global variables.
bool llvm::getObjectSize(const Value *Ptr, uint64_t &Size, const DataLayout &DL,
const TargetLibraryInfo *TLI, bool RoundToAlign) {
ObjectSizeOffsetVisitor Visitor(DL, TLI, Ptr->getContext(), RoundToAlign);
const TargetLibraryInfo *TLI, bool RoundToAlign,
llvm::ObjSizeMode Mode) {
ObjectSizeOffsetVisitor Visitor(DL, TLI, Ptr->getContext(),
RoundToAlign, Mode);
SizeOffsetType Data = Visitor.compute(const_cast<Value*>(Ptr));
if (!Visitor.bothKnown(Data))
return false;

APInt ObjSize = Data.first, Offset = Data.second;
// check for overflow
if (Offset.slt(0) || ObjSize.ult(Offset))
Size = 0;
else
Size = (ObjSize - Offset).getZExtValue();
Size = getSizeWithOverflow(Data).getZExtValue();
return true;
}


STATISTIC(ObjectVisitorArgument,
"Number of arguments with unsolved size and offset");
STATISTIC(ObjectVisitorLoad,
Expand All @@ -405,8 +405,9 @@ APInt ObjectSizeOffsetVisitor::align(APInt Size, uint64_t Align) {
ObjectSizeOffsetVisitor::ObjectSizeOffsetVisitor(const DataLayout &DL,
const TargetLibraryInfo *TLI,
LLVMContext &Context,
bool RoundToAlign)
: DL(DL), TLI(TLI), RoundToAlign(RoundToAlign) {
bool RoundToAlign,
ObjSizeMode Mode)
: DL(DL), TLI(TLI), RoundToAlign(RoundToAlign), Mode(Mode) {
// Pointer size must be rechecked for each object visited since it could have
// a different address space.
}
Expand Down Expand Up @@ -606,8 +607,28 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitPHINode(PHINode&) {
SizeOffsetType ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) {
SizeOffsetType TrueSide = compute(I.getTrueValue());
SizeOffsetType FalseSide = compute(I.getFalseValue());
if (bothKnown(TrueSide) && bothKnown(FalseSide) && TrueSide == FalseSide)
return TrueSide;
if (bothKnown(TrueSide) && bothKnown(FalseSide)) {
if (TrueSide == FalseSide) {
return TrueSide;
}

APInt TrueResult = getSizeWithOverflow(TrueSide);
APInt FalseResult = getSizeWithOverflow(FalseSide);

if (TrueResult == FalseResult) {
return TrueSide;
}
if (Mode == ObjSizeMode::Min) {
if (TrueResult.slt(FalseResult))
return TrueSide;
return FalseSide;
}
if (Mode == ObjSizeMode::Max) {
if (TrueResult.sgt(FalseResult))
return TrueSide;
return FalseSide;
}
}
return unknown();
}

Expand Down
15 changes: 12 additions & 3 deletions lib/CodeGen/CodeGenPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
Expand Down Expand Up @@ -1807,10 +1808,18 @@ bool CodeGenPrepare::optimizeCallInst(CallInst *CI, bool& ModifiedDT) {
default: break;
case Intrinsic::objectsize: {
// Lower all uses of llvm.objectsize.*
bool Min = (cast<ConstantInt>(II->getArgOperand(1))->getZExtValue() == 1);
uint64_t Size;
Type *ReturnTy = CI->getType();
Constant *RetVal = ConstantInt::get(ReturnTy, Min ? 0 : -1ULL);

Constant *RetVal = nullptr;
ConstantInt *Op1 = cast<ConstantInt>(II->getArgOperand(1));
ObjSizeMode Mode = Op1->isZero() ? ObjSizeMode::Max : ObjSizeMode::Min;
if (getObjectSize(II->getArgOperand(0),
Size, *DL, TLInfo, false, Mode)) {
RetVal = ConstantInt::get(ReturnTy, Size);
} else {
RetVal = ConstantInt::get(ReturnTy,
Mode == ObjSizeMode::Min ? 0 : -1ULL);
}
// Substituting this can cause recursive simplifications, which can
// invalidate our iterator. Use a WeakVH to hold onto it in case this
// happens.
Expand Down
90 changes: 90 additions & 0 deletions test/Transforms/CodeGenPrepare/builtin-condition.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
; RUN: opt -codegenprepare -S < %s | FileCheck %s

; #include<stdlib.h>
; #define STATIC_BUF_SIZE 10
; #define LARGER_BUF_SIZE 30
;
; size_t foo1(int flag) {
; char *cptr;
; char chararray[LARGER_BUF_SIZE];
; char chararray2[STATIC_BUF_SIZE];
; if(flag)
; cptr = chararray2;
; else
; cptr = chararray;
;
; return __builtin_object_size(cptr, 2);
; }
;
; size_t foo2(int n) {
; char Small[10];
; char Large[20];
; char *Ptr = n ? Small : Large + 19;
; return __builtin_object_size(Ptr, 0);
; }
;
; void foo() {
; size_t ret;
; size_t ret1;
; ret = foo1(0);
; ret1 = foo2(0);
; printf("\n%d %d\n", ret, ret1);
; }

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@.str = private unnamed_addr constant [8 x i8] c"\0A%d %d\0A\00", align 1

define i64 @foo1(i32 %flag) {
entry:
%chararray = alloca [30 x i8], align 16
%chararray2 = alloca [10 x i8], align 1
%0 = getelementptr inbounds [30 x i8], [30 x i8]* %chararray, i64 0, i64 0
call void @llvm.lifetime.start(i64 30, i8* %0)
%1 = getelementptr inbounds [10 x i8], [10 x i8]* %chararray2, i64 0, i64 0
call void @llvm.lifetime.start(i64 10, i8* %1)
%tobool = icmp eq i32 %flag, 0
%cptr.0 = select i1 %tobool, i8* %0, i8* %1
%2 = call i64 @llvm.objectsize.i64.p0i8(i8* %cptr.0, i1 true)
call void @llvm.lifetime.end(i64 10, i8* %1)
call void @llvm.lifetime.end(i64 30, i8* %0)
ret i64 %2
; CHECK-LABEL: foo1
; CHECK: ret i64 10
}

declare void @llvm.lifetime.start(i64, i8* nocapture)

declare i64 @llvm.objectsize.i64.p0i8(i8*, i1)

declare void @llvm.lifetime.end(i64, i8* nocapture)

define i64 @foo2(i32 %n) {
entry:
%Small = alloca [10 x i8], align 1
%Large = alloca [20 x i8], align 16
%0 = getelementptr inbounds [10 x i8], [10 x i8]* %Small, i64 0, i64 0
call void @llvm.lifetime.start(i64 10, i8* %0)
%1 = getelementptr inbounds [20 x i8], [20 x i8]* %Large, i64 0, i64 0
call void @llvm.lifetime.start(i64 20, i8* %1)
%tobool = icmp ne i32 %n, 0
%add.ptr = getelementptr inbounds [20 x i8], [20 x i8]* %Large, i64 0, i64 19
%cond = select i1 %tobool, i8* %0, i8* %add.ptr
%2 = call i64 @llvm.objectsize.i64.p0i8(i8* %cond, i1 false)
call void @llvm.lifetime.end(i64 20, i8* %1)
call void @llvm.lifetime.end(i64 10, i8* %0)
ret i64 %2
; CHECK-LABEL: foo2
; CHECK: ret i64 10
}

define void @foo() {
entry:
%call = tail call i64 @foo1(i32 0)
%call1 = tail call i64 @foo2(i32 0)
%call2 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i64 0, i64 0), i64 %call, i64 %call1)
ret void
}

declare i32 @printf(i8* nocapture readonly, ...)
58 changes: 58 additions & 0 deletions test/Transforms/InstCombine/builtin-object-size-offset.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
; RUN: opt -instcombine -S < %s | FileCheck %s

; #include <stdlib.h>
; #include <stdio.h>
;
; int foo1(int N) {
; char Big[20];
; char Small[10];
; char *Ptr = N ? Big + 10 : Small;
; return __builtin_object_size(Ptr, 0);
; }
;
; void foo() {
; size_t ret;
; ret = foo1(0);
; printf("\n %d", ret);
; }

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@.str = private unnamed_addr constant [5 x i8] c"\0A %d\00", align 1

define i32 @foo1(i32 %N) {
entry:
%Big = alloca [20 x i8], align 16
%Small = alloca [10 x i8], align 1
%0 = getelementptr inbounds [20 x i8], [20 x i8]* %Big, i64 0, i64 0
call void @llvm.lifetime.start(i64 20, i8* %0)
%1 = getelementptr inbounds [10 x i8], [10 x i8]* %Small, i64 0, i64 0
call void @llvm.lifetime.start(i64 10, i8* %1)
%tobool = icmp ne i32 %N, 0
%add.ptr = getelementptr inbounds [20 x i8], [20 x i8]* %Big, i64 0, i64 10
%cond = select i1 %tobool, i8* %add.ptr, i8* %1
%2 = call i64 @llvm.objectsize.i64.p0i8(i8* %cond, i1 false)
%conv = trunc i64 %2 to i32
call void @llvm.lifetime.end(i64 10, i8* %1)
call void @llvm.lifetime.end(i64 20, i8* %0)
ret i32 %conv
; CHECK: ret i32 10
}

declare void @llvm.lifetime.start(i64, i8* nocapture)

declare i64 @llvm.objectsize.i64.p0i8(i8*, i1)

declare void @llvm.lifetime.end(i64, i8* nocapture)

define void @foo() {
entry:
%call = tail call i32 @foo1(i32 0)
%conv = sext i32 %call to i64
%call1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i64 %conv)
ret void
}

declare i32 @printf(i8* nocapture readonly, ...)

0 comments on commit 396d592

Please sign in to comment.