Index: llvm/include/llvm/IR/IRBuilder.h =================================================================== --- llvm/include/llvm/IR/IRBuilder.h +++ llvm/include/llvm/IR/IRBuilder.h @@ -2543,6 +2543,9 @@ CallInst *CreateAlignmentAssumption(const DataLayout &DL, Value *PtrValue, Value *Alignment, Value *OffsetValue = nullptr); + /// Create an assume intrinsic call that represents a nonnull + /// assumption on the provided pointer. + CallInst *CreateNonNullAssumption(Value *PtrValue); }; /// This provides a uniform API for creating instructions and inserting Index: llvm/lib/Analysis/InlineCost.cpp =================================================================== --- llvm/lib/Analysis/InlineCost.cpp +++ llvm/lib/Analysis/InlineCost.cpp @@ -1732,7 +1732,7 @@ // parameter attribute, but that's a less interesting case because hopefully // the callee would already have been simplified based on that. if (Argument *A = dyn_cast(V)) - if (paramHasAttr(A, Attribute::NonNull)) + if (paramHasAttr(A, Attribute::NonNull) || paramHasAttr(A, Attribute::Dereferenceable)) return true; // Is this an alloca in the caller? This is distinct from the attribute case Index: llvm/lib/IR/IRBuilder.cpp =================================================================== --- llvm/lib/IR/IRBuilder.cpp +++ llvm/lib/IR/IRBuilder.cpp @@ -1410,6 +1410,12 @@ return CreateAlignmentAssumptionHelper(DL, PtrValue, Alignment, OffsetValue); } +CallInst *IRBuilderBase::CreateNonNullAssumption(Value *PtrValue) { + Value *Vals[] = { PtrValue }; + OperandBundleDefT NonNullOpB("nonnull", Vals); + return CreateAssumption(ConstantInt::getTrue(getContext()), {NonNullOpB}); +} + IRBuilderDefaultInserter::~IRBuilderDefaultInserter() = default; IRBuilderCallbackInserter::~IRBuilderCallbackInserter() = default; IRBuilderFolder::~IRBuilderFolder() = default; Index: llvm/lib/Transforms/Utils/InlineFunction.cpp =================================================================== --- llvm/lib/Transforms/Utils/InlineFunction.cpp +++ llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -1460,6 +1460,25 @@ } } +static void AddAssumptionsFromCallSiteAttrs(CallBase &CB, InlineFunctionInfo &IFI) { + if (!IFI.GetAssumptionCache) + return; + + AssumptionCache *AC = &IFI.GetAssumptionCache(*CB.getCaller()); + auto &DL = CB.getCaller()->getParent()->getDataLayout(); + Function *CalledFunc = CB.getCalledFunction(); + IRBuilder<> Builder(&CB); + + for (Argument &Arg : CalledFunc->args()) { + unsigned ArgNo = Arg.getArgNo(); + auto *ArgVal = CB.getArgOperand(ArgNo); + if (CB.getAttributes().hasParamAttr(ArgNo, Attribute::NonNull) && !isKnownNonZero(ArgVal, DL, 0, AC)) { + CallInst *NewAsmp = Builder.CreateNonNullAssumption(CB.getArgOperand(ArgNo)); + AC->registerAssumption(cast(NewAsmp)); + } + } +} + static void HandleByValArgumentInit(Type *ByValType, Value *Dst, Value *Src, Module *M, BasicBlock *InsertBlock, InlineFunctionInfo &IFI, @@ -2130,6 +2149,8 @@ VMap[&*I] = ActualArg; } + AddAssumptionsFromCallSiteAttrs(CB, IFI); + // TODO: Remove this when users have been updated to the assume bundles. // Add alignment assumptions if necessary. We do this before the inlined // instructions are actually cloned into the caller so that we can easily Index: llvm/test/Transforms/Coroutines/ex5.ll =================================================================== --- llvm/test/Transforms/Coroutines/ex5.ll +++ llvm/test/Transforms/Coroutines/ex5.ll @@ -1,7 +1,30 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 ; Fifth example from Doc/Coroutines.rst (final suspend) ; RUN: opt < %s -aa-pipeline=basic-aa -passes='default' -preserve-alignment-assumptions-during-inlining=false -S | FileCheck %s define ptr @f(i32 %n) presplitcoroutine { +; CHECK-LABEL: define noalias nonnull ptr @f +; CHECK-SAME: (i32 [[N:%.*]]) local_unnamed_addr { +; CHECK-NEXT: while.cond: +; CHECK-NEXT: [[ALLOC:%.*]] = tail call ptr @malloc(i32 24) +; CHECK-NEXT: store ptr @f.resume, ptr [[ALLOC]], align 8 +; CHECK-NEXT: [[DESTROY_ADDR:%.*]] = getelementptr inbounds [[F_FRAME:%.*]], ptr [[ALLOC]], i64 0, i32 1 +; CHECK-NEXT: store ptr @f.destroy, ptr [[DESTROY_ADDR]], align 8 +; CHECK-NEXT: [[N_VAL_SPILL_ADDR:%.*]] = getelementptr inbounds [[F_FRAME]], ptr [[ALLOC]], i64 0, i32 2 +; CHECK-NEXT: store i32 [[N]], ptr [[N_VAL_SPILL_ADDR]], align 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[N]], 0 +; CHECK-NEXT: br i1 [[CMP]], label [[AFTERCOROSUSPEND:%.*]], label [[AFTERCOROSUSPEND3:%.*]] +; CHECK: AfterCoroSuspend: +; CHECK-NEXT: tail call void @print(i32 [[N]]) +; CHECK-NEXT: [[INDEX_ADDR5:%.*]] = getelementptr inbounds [[F_FRAME]], ptr [[ALLOC]], i64 0, i32 3 +; CHECK-NEXT: store i1 false, ptr [[INDEX_ADDR5]], align 1 +; CHECK-NEXT: br label [[AFTERCOROEND:%.*]] +; CHECK: AfterCoroSuspend3: +; CHECK-NEXT: store ptr null, ptr [[ALLOC]], align 8 +; CHECK-NEXT: br label [[AFTERCOROEND]] +; CHECK: AfterCoroEnd: +; CHECK-NEXT: ret ptr [[ALLOC]] +; entry: %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null) %size = call i32 @llvm.coro.size.i32() @@ -18,12 +41,12 @@ call void @print(i32 %n.val) #4 %s = call i8 @llvm.coro.suspend(token none, i1 false) switch i8 %s, label %suspend [i8 0, label %while.cond - i8 1, label %cleanup] + i8 1, label %cleanup] while.end: %s.final = call i8 @llvm.coro.suspend(token none, i1 true) switch i8 %s.final, label %suspend [i8 0, label %trap - i8 1, label %cleanup] -trap: + i8 1, label %cleanup] +trap: call void @llvm.trap() unreachable cleanup: @@ -48,8 +71,17 @@ declare ptr @llvm.coro.free(token, ptr) declare i1 @llvm.coro.end(ptr, i1) -; CHECK-LABEL: @main define i32 @main() { +; CHECK-LABEL: define i32 @main() local_unnamed_addr { +; CHECK-NEXT: end: +; CHECK-NEXT: [[ALLOC_I:%.*]] = tail call ptr @malloc(i32 24) +; CHECK-NEXT: tail call void @print(i32 4) +; CHECK-NEXT: tail call void @print(i32 3) +; CHECK-NEXT: tail call void @print(i32 2) +; CHECK-NEXT: tail call void @print(i32 1) +; CHECK-NEXT: tail call void @free(ptr nonnull [[ALLOC_I]]) +; CHECK-NEXT: ret i32 0 +; entry: %hdl = call ptr @f(i32 4) br label %while @@ -61,11 +93,6 @@ call void @llvm.coro.destroy(ptr %hdl) ret i32 0 -; CHECK: call void @print(i32 4) -; CHECK: call void @print(i32 3) -; CHECK: call void @print(i32 2) -; CHECK: call void @print(i32 1) -; CHECK: ret i32 0 } declare i1 @llvm.coro.done(ptr) Index: llvm/test/Transforms/Inline/assumptions-from-callsite-attrs.ll =================================================================== --- llvm/test/Transforms/Inline/assumptions-from-callsite-attrs.ll +++ llvm/test/Transforms/Inline/assumptions-from-callsite-attrs.ll @@ -8,6 +8,8 @@ define void @f(ptr %p, ptr %q, ptr %z) { ; CHECK-LABEL: define void @f ; CHECK-SAME: (ptr [[P:%.*]], ptr [[Q:%.*]], ptr [[Z:%.*]]) { +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[P]]) ] +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[Z]]) ] ; CHECK-NEXT: call void @h(ptr [[P]], ptr [[Q]], ptr [[Z]]) ; CHECK-NEXT: ret void ; Index: llvm/test/Transforms/Inline/nonnull.ll =================================================================== --- llvm/test/Transforms/Inline/nonnull.ll +++ llvm/test/Transforms/Inline/nonnull.ll @@ -75,6 +75,7 @@ define void @caller3(ptr %arg) { ; CHECK-LABEL: define void @caller3 ; CHECK-SAME: (ptr [[ARG:%.*]]) { +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[ARG]]) ] ; CHECK-NEXT: [[CMP_I:%.*]] = icmp eq ptr [[ARG]], null ; CHECK-NEXT: br i1 [[CMP_I]], label [[EXPENSIVE_I:%.*]], label [[DONE_I:%.*]] ; CHECK: expensive.i: @@ -99,10 +100,11 @@ ret void } +; Positive test - arg is known non null define void @caller4(ptr dereferenceable(8) %arg) { ; CHECK-LABEL: define void @caller4 ; CHECK-SAME: (ptr dereferenceable(8) [[ARG:%.*]]) { -; CHECK-NEXT: call void @callee(ptr dereferenceable(8) [[ARG]]) +; CHECK-NEXT: call void @bar() ; CHECK-NEXT: ret void ; call void @callee(ptr dereferenceable(8) %arg) @@ -122,7 +124,24 @@ define void @caller6(ptr %arg) { ; CHECK-LABEL: define void @caller6 ; CHECK-SAME: (ptr [[ARG:%.*]]) { -; CHECK-NEXT: call void @callee(ptr dereferenceable(8) [[ARG]]) +; CHECK-NEXT: [[CMP_I:%.*]] = icmp eq ptr [[ARG]], null +; CHECK-NEXT: br i1 [[CMP_I]], label [[EXPENSIVE_I:%.*]], label [[DONE_I:%.*]] +; CHECK: expensive.i: +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: br label [[CALLEE_EXIT:%.*]] +; CHECK: done.i: +; CHECK-NEXT: call void @bar() +; CHECK-NEXT: br label [[CALLEE_EXIT]] +; CHECK: callee.exit: ; CHECK-NEXT: ret void ; call void @callee(ptr dereferenceable(8) %arg) Index: llvm/test/Transforms/PhaseOrdering/dae-dce.ll =================================================================== --- llvm/test/Transforms/PhaseOrdering/dae-dce.ll +++ llvm/test/Transforms/PhaseOrdering/dae-dce.ll @@ -14,9 +14,14 @@ } define internal void @capture_and_trap(ptr %ptr) noinline { -; CHECK-LABEL: @capture_and_trap( -; CHECK-NEXT: tail call void @llvm.trap() -; CHECK-NEXT: unreachable +; DEFAULT-LABEL: @capture_and_trap( +; DEFAULT-NEXT: tail call void @llvm.trap() +; DEFAULT-NEXT: unreachable +; +; LTO-LABEL: @capture_and_trap( +; LTO-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr poison) ] +; LTO-NEXT: tail call void @llvm.trap() +; LTO-NEXT: unreachable ; %alloca = alloca ptr, align 4 store ptr %ptr, ptr %alloca, align 4 @@ -47,6 +52,3 @@ call void @capture_and_trap(ptr @dead_fn2) unreachable } -;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: -; DEFAULT: {{.*}} -; LTO: {{.*}}