Index: lib/Transforms/Utils/InlineFunction.cpp =================================================================== --- lib/Transforms/Utils/InlineFunction.cpp +++ lib/Transforms/Utils/InlineFunction.cpp @@ -1500,10 +1500,21 @@ IFI.reset(); Function *CalledFunc = CS.getCalledFunction(); - if (!CalledFunc || // Can't inline external function or indirect - CalledFunc->isDeclaration() || - (!ForwardVarArgsTo && CalledFunc->isVarArg())) // call, or call to a vararg function! - return false; + if (!CalledFunc || // Can't inline external function or indirect + CalledFunc->isDeclaration()) // call! + return false; + + // Inline vararg functions only if varargs are ignored in the function body. + if (CalledFunc->isVarArg()) + for (auto &BB : *CalledFunc) + if (any_of(BB, [](Instruction &I) { + if (const CallInst *CI = dyn_cast(&I)) + if (const Function *F = CI->getCalledFunction()) + return F->getIntrinsicID() == Intrinsic::vastart || + F->getIntrinsicID() == Intrinsic::vaend; + return false; + })) + return false; // The inliner does not know how to inline through calls with operand bundles // in general ... @@ -1630,9 +1641,6 @@ auto &DL = Caller->getParent()->getDataLayout(); - assert((CalledFunc->arg_size() == CS.arg_size() || ForwardVarArgsTo) && - "Varargs calls can only be inlined if the Varargs are forwarded!"); - // Calculate the vector of arguments to pass into the function cloner, which // matches up the formal to the actual argument values. CallSite::arg_iterator AI = CS.arg_begin(); @@ -1833,6 +1841,23 @@ if (!CI) continue; + // Forward varargs from inlined call site to calls to the + // ForwardVarArgsTo function, if requested, and to musttail calls. + if (!VarArgsToForward.empty() && + ((ForwardVarArgsTo && + CI->getCalledFunction() == ForwardVarArgsTo) || + CI->isMustTailCall())) { + SmallVector Params(CI->arg_operands()); + Params.append(VarArgsToForward.begin(), VarArgsToForward.end()); + CallInst *Call = + CallInst::Create(CI->getCalledFunction() ? CI->getCalledFunction() + : CI->getCalledValue(), + Params, "", CI); + Call->setDebugLoc(CI->getDebugLoc()); + CI->replaceAllUsesWith(Call); + CI->eraseFromParent(); + } + if (Function *F = CI->getCalledFunction()) InlinedDeoptimizeCalls |= F->getIntrinsicID() == Intrinsic::experimental_deoptimize; @@ -1860,16 +1885,6 @@ // 'nounwind'. if (MarkNoUnwind) CI->setDoesNotThrow(); - - if (ForwardVarArgsTo && !VarArgsToForward.empty() && - CI->getCalledFunction() == ForwardVarArgsTo) { - SmallVector Params(CI->arg_operands()); - Params.append(VarArgsToForward.begin(), VarArgsToForward.end()); - CallInst *Call = CallInst::Create(CI->getCalledFunction(), Params, "", CI); - Call->setDebugLoc(CI->getDebugLoc()); - CI->replaceAllUsesWith(Call); - CI->eraseFromParent(); - } } } } Index: test/Transforms/Inline/inline-musttail-varargs.ll =================================================================== --- test/Transforms/Inline/inline-musttail-varargs.ll +++ /dev/null @@ -1,23 +0,0 @@ -; RUN: opt < %s -inline -instcombine -S | FileCheck %s -; RUN: opt < %s -passes='cgscc(inline,function(instcombine))' -S | FileCheck %s - -; We can't inline this thunk yet, but one day we will be able to. And when we -; do, this test case will be ready. - -declare void @ext_method(i8*, i32) - -define linkonce_odr void @thunk(i8* %this, ...) { - %this_adj = getelementptr i8, i8* %this, i32 4 - musttail call void (i8*, ...) bitcast (void (i8*, i32)* @ext_method to void (i8*, ...)*)(i8* %this_adj, ...) - ret void -} - -define void @thunk_caller(i8* %p) { - call void (i8*, ...) @thunk(i8* %p, i32 42) - ret void -} -; CHECK-LABEL: define void @thunk_caller(i8* %p) -; CHECK: call void (i8*, ...) @thunk(i8* %p, i32 42) - -; FIXME: Inline the thunk. This should be significantly easier than inlining -; general varargs functions. Index: test/Transforms/Inline/inline-varargs.ll =================================================================== --- /dev/null +++ test/Transforms/Inline/inline-varargs.ll @@ -0,0 +1,55 @@ +; RUN: opt < %s -inline -instcombine -S | FileCheck %s +; RUN: opt < %s -passes='cgscc(inline,function(instcombine))' -S | FileCheck %s + +; We can't inline this thunk yet, but one day we will be able to. And when we +; do, this test case will be ready. + +declare void @ext_method(i8*, i32) +declare void @vararg_fn(i8*, ...) + +define linkonce_odr void @thunk(i8* %this, ...) { + %this_adj = getelementptr i8, i8* %this, i32 4 + musttail call void (i8*, ...) bitcast (void (i8*, i32)* @ext_method to void (i8*, ...)*)(i8* %this_adj, ...) + ret void +} + +define void @thunk_caller(i8* %p) { + call void (i8*, ...) @thunk(i8* %p, i32 42) + ret void +} +; CHECK-LABEL: define void @thunk_caller(i8* %p) +; CHECK: call void (i8*, ...) bitcast (void (i8*, i32)* @ext_method to void (i8*, ...)*)(i8* %this_adj.i, i32 42) + +define void @test_callee_2(i8* %this, ...) { + %this_adj = getelementptr i8, i8* %this, i32 4 + musttail call void (i8*, ...) @vararg_fn(i8* %this_adj, ...) + ret void +} + +define void @test_caller_2(i8* %p) { + call void (i8*, ...) @test_callee_2(i8* %p) + ret void +} +; CHECK-LABEL: define void @test_caller_2(i8* %p) +; CHECK: call void (i8*, ...) @vararg_fn(i8* %this_adj.i) + + +define internal i32 @varg_accessed(...) { +entry: + %vargs = alloca i8*, align 8 + %vargs.ptr = bitcast i8** %vargs to i8* + call void @llvm.va_start(i8* %vargs.ptr) + %va1 = va_arg i8** %vargs, i32 + call void @llvm.va_end(i8* %vargs.ptr) + ret i32 %va1 +} + +define i32 @call_vargs() { + %res = call i32 (...) @varg_accessed(i32 10) + ret i32 %res +} +; CHECK-LABEL: @call_vargs +; CHECK: %res = call i32 (...) @varg_accessed(i32 10) + +declare void @llvm.va_start(i8*) +declare void @llvm.va_end(i8*)