diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -79,6 +79,10 @@ cl::Hidden, cl::desc("Convert noalias attributes to metadata during inlining.")); +static cl::opt EnableNonNullUpdate( + "enable-nonnull-update", cl::init(true), cl::Hidden, + cl::desc("Allow nonnull attribute or metadata during inlining")); + static cl::opt PreserveAlignmentAssumptions("preserve-alignment-assumptions-during-inlining", cl::init(true), cl::Hidden, @@ -1135,6 +1139,27 @@ } } +static void AddNonNull(CallSite CS) { + if (!EnableNonNullUpdate || !CS.isReturnNonNull()) + return; + auto *CalledFunction = CS.getCalledFunction(); + for (auto &BB : *CalledFunction) { + auto *RI = dyn_cast(BB.getTerminator()); + if (!RI) + continue; + auto *RetVal = RI->getOperand(0); + // FIXME: Handle loads as well by adding the nonnull metadata on the load, + // indicating that the value loaded is nonnull. + auto *Call = dyn_cast(RetVal); + if (!Call) + continue; + AttributeList AS = Call->getAttributes(); + AS = AS.addAttribute(Call->getContext(), AttributeList::ReturnIndex, + Attribute::NonNull); + Call->setAttributes(AS); + } +} + /// If the inlined function has non-byval align arguments, then /// add @llvm.assume-based alignment assumptions to preserve this information. static void AddAlignmentAssumptions(CallSite CS, InlineFunctionInfo &IFI) { @@ -1700,6 +1725,10 @@ // check what will be known at the start of the inlined code. AddAlignmentAssumptions(CS, IFI); + // Non-null attribute is added to relevant instructions if the callsite is + // has nonnull attribute. + AddNonNull(CS); + // We want the inliner to prune the code as it copies. We would LOVE to // have no dead or constant instructions leftover after inlining occurs // (which can happen, e.g., because an argument was constant), but we'll be diff --git a/llvm/test/Transforms/Inline/nonnull_update.ll b/llvm/test/Transforms/Inline/nonnull_update.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Inline/nonnull_update.ll @@ -0,0 +1,65 @@ +; RUN: opt < %s -inline-threshold=0 -always-inline -S | FileCheck %s +; RUN: opt < %s -passes=always-inline -S | FileCheck %s + +declare i8* @foo(i8*) +declare i8* @bar(i8*) +declare i8* @baz(i8*) + +define internal i8* @callee(i8 *%p) alwaysinline { +; CHECK-NOT: @callee( + %r = call i8* @foo(i8* %p) + ret i8* %r +} + +define i8* @caller(i8* %ptr, i64 %x) { +; CHECK-LABEL: @caller +; CHECK: call nonnull i8* @foo + %gep = getelementptr inbounds i8, i8* %ptr, i64 %x + %p = call nonnull i8* @callee(i8* %gep) + ret i8* %p +} + +define internal i8* @callee_with_multiple_returns(i8* %p, i1 %cond) alwaysinline { +; CHECK-NOT: callee_with_multiple_returns + br i1 %cond, label %pass, label %fail + +pass: + %r = call i8* @foo(i8* %p) + ret i8* %r + +fail: + %vfail = call i8* @baz(i8* %p) + ret i8* %vfail + +Unreachable: + %val = call i8* @bar(i8* %p) + ret i8* %val +} + +define i8* @caller2(i8* %ptr, i64 %x, i1 %cond) { +; CHECK-LABEL: @caller +; CHECK: call nonnull i8* @foo +; CHECK: call nonnull i8* @baz +; CHECK-NOT: @bar + %gep = getelementptr inbounds i8, i8* %ptr, i64 %x + %p = call nonnull i8* @callee_with_multiple_returns(i8* %gep, i1 %cond) + ret i8* %p +} + +; FIXME: We should add nonnull metadata on the load for this callee +; once it is inlined into caller3 +define internal i8* @callee3(i8 *%p, i64 %idx) alwaysinline { +; CHECK-NOT: callee3 + %cast = bitcast i8* %p to i8** + %gep = getelementptr inbounds i8*, i8** %cast, i64 %idx + %val = load i8*, i8** %gep, align 4 + ret i8* %val +} + +define i8* @caller3(i8* %ptr, i64 %x) { +; CHECK-LABEL: caller3 +; CHECK-NOT: nonnull + %gep = getelementptr inbounds i8, i8* %ptr, i64 %x + %p = call nonnull i8* @callee3(i8* %gep, i64 %x) + ret i8* %p +}