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 correspondig 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); Index: lib/Analysis/MemoryBuiltins.cpp =================================================================== --- lib/Analysis/MemoryBuiltins.cpp +++ lib/Analysis/MemoryBuiltins.cpp @@ -352,8 +352,10 @@ /// 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; @@ -383,8 +385,9 @@ 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. } @@ -560,8 +563,21 @@ 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; + } + if (Mode == ObjSizeMode::Min) { + if(TrueSide.first.ult(FalseSide.first)) + return TrueSide; + return FalseSide; + } + if (Mode == ObjSizeMode::Max) { + if(TrueSide.first.ugt(FalseSide.first)) + 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,64 @@ +; RUN: opt -codegenprepare -S < %s | FileCheck %s + +; In this test there is no inlining, so the result +; should be 10. + +; #include +; #define STATIC_BUF_SIZE 10 +; #define LARGER_BUF_SIZE 30 + +; size_t foo(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); +; } + +; int main() { +; size_t ret; +; ret = foo(0); +; printf("\n%d\n", ret); +; return 0; +; } + +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\0A\00", align 1 + +define i64 @foo(i32 %flag) #0 { +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) #5 + %1 = getelementptr inbounds [10 x i8], [10 x i8]* %chararray2, i64 0, i64 0 + call void @llvm.lifetime.start(i64 10, i8* %1) #5 + %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) #5 + call void @llvm.lifetime.end(i64 30, i8* %0) #5 + ret i64 %2 +; CHECK: ret i64 10 +} + +declare void @llvm.lifetime.start(i64, i8* nocapture) #1 + +declare i64 @llvm.objectsize.i64.p0i8(i8*, i1) #2 + +declare void @llvm.lifetime.end(i64, i8* nocapture) #1 + +define i32 @main() #3 { +entry: + %call = tail call i64 @foo(i32 0) + %call1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i64 %call) + ret i32 0 +} + +declare i32 @printf(i8* nocapture readonly, ...) #4