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/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: {{.*}}