[PATCH] D53342: [SimplifyLibCalls] Mark known arguments with nonnull

Dávid Bolvanský via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 20 04:56:06 PDT 2019


xbolva00 marked an inline comment as done.
xbolva00 added a comment.

Found a bug in LLVM.

Intrinsic builder does not copy attributes, it seems.

define i8* @memset_size_select4(i1 %b, i8* %ptr) {
; CHECK-LABEL: @memset_size_select4(
; CHECK-NEXT:    [[SIZE:%.*]] = select i1 [[B:%.*]], i32 10, i32 50
; CHECK-NEXT:    call void @llvm.memset.p0i8.i32(i8* align 1 [[PTR:%.*]], i8 0, i32 [[SIZE]], i1 false)
; CHECK-NEXT:    ret i8* [[PTR]]
;

  %size = select i1 %b, i32 10, i32 50
  %memset = tail call i8* @memset(i8* nonnull dereferenceable_or_null(40) %ptr, i32 0, i32 %size)
  ret i8* %memset

}



================
Comment at: lib/Transforms/Utils/SimplifyLibCalls.cpp:2381
+  else
+    removeNonNull(CI, {0});
+
----------------
jdoerfert wrote:
> xbolva00 wrote:
> > jdoerfert wrote:
> > > We could even use "isKnownNonZero" or other helper functions here.
> > This is true but since we we need to compute “N” anyway, I think isKnownNonZero is a overkill a bit, since it computes what we already have - “N”. - should I use it even here?
> > 
> > But I could use it in some places, yes,  (memcpy/memmove/memset)..
> For a lot of calls a nonnull size implies other stuff, especially, noalias, deref_or_null.
> I think this is worth a separate patch but doing better nonnull checks makes sense because any value (not necessarily a constant) that is not zero will allow us to infer (1) nonnull, (2) noalias, (3) deref_or_null. If we want the constant size for some other transformations we should first look for a constant and only as a fallback call `isknownnonzero`.
Until I start heavy mechanical work here I would like to know if this approch is acceptable.

Value *LibCallSimplifier::optimizeMemSet(CallInst *CI, IRBuilder<> &B,
                                         bool isIntrinsic) {
  Value *Size = CI->getArgOperand(2);
  if (ConstantInt *LenC = dyn_cast<ConstantInt>(Size)) {
    annotateDereferenceableBytes(CI, {0}, LenC->getZExtValue());
    annotateNonNull(CI, {0});
  } else if (isKnownNonZero(Size, DL)) {
    annotateDereferenceableBytes(CI, {0}, 1);
    annotateNonNull(CI, {0});
  } else if (!isIntrinsic) {
    removeNonNull(CI, {0});
  }

Now we catch even this case :)
define i8* @memset_size_select(i1 %b, i8* %ptr) {
; CHECK-LABEL: @memset_size_select(
; CHECK-NEXT:    [[SIZE:%.*]] = select i1 [[B:%.*]], i32 10, i32 50
; CHECK-NEXT:    call void @llvm.memset.p0i8.i32(i8* nonnull align 1 dereferenceable(1) [[PTR:%.*]], i8 0, i32 [[SIZE]], i1 false)
; CHECK-NEXT:    ret i8* [[PTR]]
;
  %size = select i1 %b, i32 10, i32 50
  %memset = tail call i8* @memset(i8* nonnull %ptr, i32 0, i32 %size) #1
  ret i8* %memset
}


define i8* @memset_size_select_or(i1 %b, i8* %ptr, i32 %v) {
; CHECK-LABEL: @memset_size_select_or(
; CHECK-NEXT:    [[SIZE:%.*]] = ashr i32 -2, [[V:%.*]]
; CHECK-NEXT:    call void @llvm.memset.p0i8.i32(i8* nonnull align 1 dereferenceable(1) [[PTR:%.*]], i8 0, i32 [[SIZE]], i1 false)
; CHECK-NEXT:    ret i8* [[PTR]]
;
  %size = ashr i32 -2, %v
  %memset = tail call i8* @memset(i8* nonnull %ptr, i32 0, i32 %size) #1
  ret i8* %memset
}


for select of constants we can do better job than deref(1)

if (ConstantInt *LenC = dyn_cast<ConstantInt>(Size)) {
    annotateDereferenceableBytes(CI, {0}, LenC->getZExtValue());
    annotateNonNull(CI, {0});
  } else if (isKnownNonZero(Size, DL)) {
    const  APInt *X, *Y;
    uint64_t DerefMin = 1;
    if (match(Size, m_Select(m_Value(), m_APInt(X), m_APInt(Y)))) {
      Min = std::min(X->getZExtValue(), Y->getZExtValue());
          annotateDereferenceableBytes(CI, {0}, DerefMin);
}
    annotateNonNull(CI, {0});
  } else if (!isIntrinsic) {
    removeNonNull(CI, {0});
  }

Ok?


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D53342/new/

https://reviews.llvm.org/D53342





More information about the llvm-commits mailing list