Index: include/llvm/Analysis/MemoryBuiltins.h =================================================================== --- include/llvm/Analysis/MemoryBuiltins.h +++ include/llvm/Analysis/MemoryBuiltins.h @@ -32,6 +32,11 @@ 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 @@ -130,8 +135,11 @@ /// 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 SizeOffsetType; @@ -143,6 +151,7 @@ const DataLayout &DL; const TargetLibraryInfo *TLI; bool RoundToAlign; + ObjSizeMode Mode; unsigned IntTyBits; APInt Zero; SmallPtrSet SeenInsts; @@ -155,7 +164,8 @@ 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); @@ -171,6 +181,8 @@ return knownSize(SizeOffset) && knownOffset(SizeOffset); } + APInt getSizeWithOverflow(const SizeOffsetType &Data); + // These are "private", except they can't actually be made private. Only // compute() should be used by external users. SizeOffsetType visitAllocaInst(AllocaInst &I); Index: lib/Analysis/MemoryBuiltins.cpp =================================================================== --- lib/Analysis/MemoryBuiltins.cpp +++ lib/Analysis/MemoryBuiltins.cpp @@ -352,18 +352,15 @@ /// 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(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 = Visitor.getSizeWithOverflow(Data).getZExtValue(); return true; } @@ -383,12 +380,19 @@ 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. } +APInt ObjectSizeOffsetVisitor::getSizeWithOverflow(const SizeOffsetType &Data) { + if (Data.second.isNegative() || Data.first.ult(Data.second)) + return APInt(Data.first.getBitWidth(), 0); + return Data.first - Data.second; +} + SizeOffsetType ObjectSizeOffsetVisitor::compute(Value *V) { IntTyBits = DL.getPointerTypeSizeInBits(V->getType()); Zero = APInt::getNullValue(IntTyBits); @@ -560,8 +564,28 @@ 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(); } Index: lib/CodeGen/CodeGenPrepare.cpp =================================================================== --- lib/CodeGen/CodeGenPrepare.cpp +++ lib/CodeGen/CodeGenPrepare.cpp @@ -21,6 +21,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" @@ -1778,10 +1779,18 @@ default: break; case Intrinsic::objectsize: { // Lower all uses of llvm.objectsize.* - bool Min = (cast(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(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. Index: test/CodeGen/X86/builtin-condition.ll =================================================================== --- test/CodeGen/X86/builtin-condition.ll +++ test/CodeGen/X86/builtin-condition.ll @@ -0,0 +1,90 @@ +; RUN: opt -codegenprepare -S < %s | FileCheck %s + +; #include +; #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, ...) \ No newline at end of file Index: test/Transforms/InstCombine/builtin-object-size-offset.ll =================================================================== --- test/Transforms/InstCombine/builtin-object-size-offset.ll +++ test/Transforms/InstCombine/builtin-object-size-offset.ll @@ -0,0 +1,58 @@ +; RUN: opt -instcombine -S < %s | FileCheck %s + +; #include +; #include +; +; 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, ...) +