diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h --- a/llvm/include/llvm/Analysis/MemoryBuiltins.h +++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h @@ -229,6 +229,7 @@ SizeOffsetType visitInstruction(Instruction &I); private: + SizeOffsetType combineSizeOffset(SizeOffsetType LHS, SizeOffsetType RHS); SizeOffsetType computeImpl(Value *V); bool CheckedZextOrTrunc(APInt &I); }; diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -812,37 +813,35 @@ return unknown(); } -SizeOffsetType ObjectSizeOffsetVisitor::visitPHINode(PHINode&) { - // too complex to analyze statically. - return unknown(); -} +SizeOffsetType ObjectSizeOffsetVisitor::combineSizeOffset(SizeOffsetType LHS, + SizeOffsetType RHS) { + if (!bothKnown(LHS) || !bothKnown(RHS)) + return unknown(); -SizeOffsetType ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) { - SizeOffsetType TrueSide = compute(I.getTrueValue()); - SizeOffsetType FalseSide = compute(I.getFalseValue()); - if (bothKnown(TrueSide) && bothKnown(FalseSide)) { - if (TrueSide == FalseSide) { - return TrueSide; - } + switch (Options.EvalMode) { + case ObjectSizeOpts::Mode::Min: + return (getSizeWithOverflow(LHS).slt(getSizeWithOverflow(RHS))) ? LHS : RHS; + case ObjectSizeOpts::Mode::Max: + return (getSizeWithOverflow(LHS).sgt(getSizeWithOverflow(RHS))) ? LHS : RHS; + case ObjectSizeOpts::Mode::Exact: + return (getSizeWithOverflow(LHS).eq(getSizeWithOverflow(RHS))) ? LHS + : unknown(); + } + llvm_unreachable("missing an eval mode"); +} - APInt TrueResult = getSizeWithOverflow(TrueSide); - APInt FalseResult = getSizeWithOverflow(FalseSide); +SizeOffsetType ObjectSizeOffsetVisitor::visitPHINode(PHINode &PN) { + auto IncomingValues = PN.incoming_values(); + return std::accumulate(IncomingValues.begin() + 1, IncomingValues.end(), + compute(*IncomingValues.begin()), + [this](SizeOffsetType LHS, Value *VRHS) { + return combineSizeOffset(LHS, compute(VRHS)); + }); +} - if (TrueResult == FalseResult) { - return TrueSide; - } - if (Options.EvalMode == ObjectSizeOpts::Mode::Min) { - if (TrueResult.slt(FalseResult)) - return TrueSide; - return FalseSide; - } - if (Options.EvalMode == ObjectSizeOpts::Mode::Max) { - if (TrueResult.sgt(FalseResult)) - return TrueSide; - return FalseSide; - } - } - return unknown(); +SizeOffsetType ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) { + return combineSizeOffset(compute(I.getTrueValue()), + compute(I.getFalseValue())); } SizeOffsetType ObjectSizeOffsetVisitor::visitUndefValue(UndefValue&) { diff --git a/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/LowerConstantIntrinsics/builtin-object-size-phi.ll @@ -0,0 +1,63 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -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 dso_local noalias noundef i8* @malloc(i64 noundef) local_unnamed_addr +declare i64 @llvm.objectsize.i64.p0i8(i8*, i1 immarg, i1 immarg, i1 immarg) + +@buffer = dso_local global [4 x i8] zeroinitializer, align 1 + +define dso_local i64 @pick_max(i32 noundef %n) local_unnamed_addr { +; CHECK-LABEL: @pick_max( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[N:%.*]], 0 +; CHECK-NEXT: br i1 [[COND]], label [[IF_ELSE:%.*]], label [[IF_END:%.*]] +; CHECK: if.else: +; CHECK-NEXT: [[MALLOCED:%.*]] = call noalias dereferenceable_or_null(8) i8* @malloc(i64 noundef 8) +; CHECK-NEXT: br label [[IF_END]] +; CHECK: if.end: +; CHECK-NEXT: [[P:%.*]] = phi i8* [ [[MALLOCED]], [[IF_ELSE]] ], [ getelementptr inbounds ([4 x i8], [4 x i8]* @buffer, i64 0, i64 0), [[ENTRY:%.*]] ] +; CHECK-NEXT: ret i64 8 +; +entry: + %cond = icmp eq i32 %n, 0 + br i1 %cond, label %if.else, label %if.end + +if.else: + %malloced = call noalias dereferenceable_or_null(8) i8* @malloc(i64 noundef 8) + br label %if.end + +if.end: + %p = phi i8* [ %malloced, %if.else ], [ getelementptr inbounds ([4 x i8], [4 x i8]* @buffer, i64 0, i64 0), %entry ] + %size = call i64 @llvm.objectsize.i64.p0i8(i8* %p, i1 false, i1 true, i1 false) + ret i64 %size +} + +define dso_local i64 @pick_min(i32 noundef %n) local_unnamed_addr { +; CHECK-LABEL: @pick_min( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[N:%.*]], 0 +; CHECK-NEXT: br i1 [[COND]], label [[IF_ELSE:%.*]], label [[IF_END:%.*]] +; CHECK: if.else: +; CHECK-NEXT: [[MALLOCED:%.*]] = call noalias dereferenceable_or_null(8) i8* @malloc(i64 noundef 8) +; CHECK-NEXT: br label [[IF_END]] +; CHECK: if.end: +; CHECK-NEXT: [[P:%.*]] = phi i8* [ [[MALLOCED]], [[IF_ELSE]] ], [ getelementptr inbounds ([4 x i8], [4 x i8]* @buffer, i64 0, i64 0), [[ENTRY:%.*]] ] +; CHECK-NEXT: ret i64 4 +; +entry: + %cond = icmp eq i32 %n, 0 + br i1 %cond, label %if.else, label %if.end + +if.else: + %malloced = call noalias dereferenceable_or_null(8) i8* @malloc(i64 noundef 8) + br label %if.end + +if.end: + %p = phi i8* [ %malloced, %if.else ], [ getelementptr inbounds ([4 x i8], [4 x i8]* @buffer, i64 0, i64 0), %entry ] + %size = call i64 @llvm.objectsize.i64.p0i8(i8* %p, i1 true, i1 true, i1 false) + ret i64 %size +}