Skip to content

Commit

Permalink
[LowerConstantIntrinsics] Support phi operand in __builtin_object_siz…
Browse files Browse the repository at this point in the history
…e folder

The implementation is just a generalization of the Select handler.
We're no trying to be smart and compute any kind of fixed point.

Differential Revision: https://reviews.llvm.org/D121897
  • Loading branch information
serge-sans-paille committed Mar 21, 2022
1 parent 0ebac76 commit d8e0a6d
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 28 deletions.
1 change: 1 addition & 0 deletions llvm/include/llvm/Analysis/MemoryBuiltins.h
Expand Up @@ -229,6 +229,7 @@ class ObjectSizeOffsetVisitor
SizeOffsetType visitInstruction(Instruction &I);

private:
SizeOffsetType combineSizeOffset(SizeOffsetType LHS, SizeOffsetType RHS);
SizeOffsetType computeImpl(Value *V);
bool CheckedZextOrTrunc(APInt &I);
};
Expand Down
55 changes: 27 additions & 28 deletions llvm/lib/Analysis/MemoryBuiltins.cpp
Expand Up @@ -42,6 +42,7 @@
#include <cassert>
#include <cstdint>
#include <iterator>
#include <numeric>
#include <type_traits>
#include <utility>

Expand Down Expand Up @@ -812,37 +813,35 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitLoadInst(LoadInst&) {
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&) {
Expand Down
@@ -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
}

0 comments on commit d8e0a6d

Please sign in to comment.