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 UpdateReturnAttributes( + "update-return-attrs", cl::init(true), cl::Hidden, + cl::desc("Update return attributes on calls within inlined body")); + static cl::opt PreserveAlignmentAssumptions("preserve-alignment-assumptions-during-inlining", cl::init(true), cl::Hidden, @@ -1135,6 +1139,38 @@ } } +static void AddReturnAttributes(CallSite CS, ValueToValueMapTy &VMap) { + if (!UpdateReturnAttributes) + return; + AttrBuilder AB(CS.getAttributes(), AttributeList::ReturnIndex); + if (AB.empty()) + return; + + auto *CalledFunction = CS.getCalledFunction(); + auto &Context = CalledFunction->getContext(); + for (auto &BB : *CalledFunction) { + auto *RI = dyn_cast(BB.getTerminator()); + if (!RI) + continue; + auto *RetVal = dyn_cast(RI->getOperand(0)); + if (!RetVal) + continue; + ValueToValueMapTy::iterator VMI = VMap.find(RetVal); + // Sanity check that the cloned instruction exists and is a call. + if (VMI == VMap.end() || !VMI->second) + continue; + auto *NewRetVal = dyn_cast(VMI->second); + if (!NewRetVal) + continue; + // Add to the existing attributes. + // Should we check for conflicting attributes? + AttributeList AL = NewRetVal->getAttributes(); + AttributeList NewAL = AL.addAttributes(Context, AttributeList::ReturnIndex, AB); + NewRetVal->setAttributes(NewAL); + } + +} + /// 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) { @@ -1795,6 +1831,10 @@ // Add noalias metadata if necessary. AddAliasScopeMetadata(CS, VMap, DL, CalleeAAR); + // Clone return attributes on the callsite into the calls within the inlined + // function which feed into its return value. + AddReturnAttributes(CS, VMap); + // Propagate llvm.mem.parallel_loop_access if necessary. PropagateParallelLoopAccessMetadata(CS, VMap); diff --git a/llvm/test/Transforms/Inline/ret_attr_update.ll b/llvm/test/Transforms/Inline/ret_attr_update.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Inline/ret_attr_update.ll @@ -0,0 +1,62 @@ +; 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 i8* @callee(i8 *%p) alwaysinline { +; CHECK: @callee( +; CHECK: call i8* @foo(i8* noalias %p) + %r = call i8* @foo(i8* noalias %p) + ret i8* %r +} + +define i8* @caller(i8* %ptr, i64 %x) { +; CHECK-LABEL: @caller +; CHECK: call nonnull i8* @foo(i8* noalias + %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 nonnull 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 noalias nonnull i8* @foo +; CHECK: call noalias i8* @baz +; CHECK-NOT: @bar + %gep = getelementptr inbounds i8, i8* %ptr, i64 %x + %p = call noalias i8* @callee_with_multiple_returns(i8* %gep, i1 %cond) + ret i8* %p +} + +define internal i8* @callee3(i8 *%p) alwaysinline { +; CHECK-NOT: callee3 + %r = call i8* @foo(i8* noalias %p) + ret i8* %r +} + +define i8* @caller3(i8* %ptr, i64 %x) { +; CHECK-LABEL: caller3 +; CHECK: call dereferenceable_or_null(12) i8* @foo + %gep = getelementptr inbounds i8, i8* %ptr, i64 %x + %p = call dereferenceable_or_null(12) i8* @callee3(i8* %gep) + ret i8* %p +}