From 78696b31cd5f23c5a1aea1f85af7678505d1b259 Mon Sep 17 00:00:00 2001 From: Wei Mi Date: Wed, 6 Sep 2017 16:05:17 +0000 Subject: [PATCH] [TailCall] Allow llvm.memcpy/memset/memmove to be tail calls when parent function return the intrinsics's first argument. llvm.memcpy/memset/memmove return void but they will return the first argument after they are expanded as libcalls. Now if the parent function has any return value, llvm.memcpy cannot be turned into tail call after expansion. The patch is to handle that case in SelectionDAGBuilder so when caller function return the same value as the first argument of llvm.memcpy, tail call is allowed. Differential Revision: https://reviews.llvm.org/D37406 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@312641 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/Analysis.cpp | 11 +++++++++++ test/CodeGen/X86/tailcall-mem-intrinsics.ll | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/CodeGen/Analysis.cpp b/lib/CodeGen/Analysis.cpp index c2aecc651b7..f561577e1e2 100644 --- a/lib/CodeGen/Analysis.cpp +++ b/lib/CodeGen/Analysis.cpp @@ -565,6 +565,17 @@ bool llvm::returnTypeIsEligibleForTailCall(const Function *F, return false; const Value *RetVal = Ret->getOperand(0), *CallVal = I; + // Intrinsic like llvm.memcpy has no return value, but will return the + // first argument if it is expanded as libcall. + const CallInst *Call = cast(I); + if (Function *F = Call->getCalledFunction()) { + Intrinsic::ID IID = F->getIntrinsicID(); + if ((IID == Intrinsic::memcpy || IID == Intrinsic::memmove || + IID == Intrinsic::memset) && + RetVal == Call->getArgOperand(0)) + return true; + } + SmallVector RetPath, CallPath; SmallVector RetSubTypes, CallSubTypes; diff --git a/test/CodeGen/X86/tailcall-mem-intrinsics.ll b/test/CodeGen/X86/tailcall-mem-intrinsics.ll index 8e1e4f464ba..7491ea659ba 100644 --- a/test/CodeGen/X86/tailcall-mem-intrinsics.ll +++ b/test/CodeGen/X86/tailcall-mem-intrinsics.ll @@ -24,6 +24,30 @@ entry: ret void } +; CHECK-LABEL: tail_memcpy_ret +; CHECK: jmp memcpy +define i8* @tail_memcpy_ret(i8* nocapture %p, i8* nocapture readonly %q, i32 %n) #0 { +entry: + tail call void @llvm.memcpy.p0i8.p0i8.i32(i8* %p, i8* %q, i32 %n, i32 1, i1 false) + ret i8* %p +} + +; CHECK-LABEL: tail_memmove_ret +; CHECK: jmp memmove +define i8* @tail_memmove_ret(i8* nocapture %p, i8* nocapture readonly %q, i32 %n) #0 { +entry: + tail call void @llvm.memmove.p0i8.p0i8.i32(i8* %p, i8* %q, i32 %n, i32 1, i1 false) + ret i8* %p +} + +; CHECK-LABEL: tail_memset_ret +; CHECK: jmp memset +define i8* @tail_memset_ret(i8* nocapture %p, i8 %c, i32 %n) #0 { +entry: + tail call void @llvm.memset.p0i8.i32(i8* %p, i8 %c, i32 %n, i32 1, i1 false) + ret i8* %p +} + declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i32, i1) #0 declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i32, i1) #0 declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) #0 -- 2.11.0