diff --git a/llvm/lib/Transforms/Utils/CallPromotionUtils.cpp b/llvm/lib/Transforms/Utils/CallPromotionUtils.cpp --- a/llvm/lib/Transforms/Utils/CallPromotionUtils.cpp +++ b/llvm/lib/Transforms/Utils/CallPromotionUtils.cpp @@ -403,9 +403,12 @@ // The number of formal arguments of the callee. unsigned NumParams = Callee->getFunctionType()->getNumParams(); + // The number of actual arguments in the call. + unsigned NumArgs = CB.arg_size(); + // Check the number of arguments. The callee and call site must agree on the // number of arguments. - if (CB.arg_size() != NumParams && !Callee->isVarArg()) { + if (NumArgs != NumParams && !Callee->isVarArg()) { if (FailureReason) *FailureReason = "The number of arguments mismatch"; return false; @@ -414,7 +417,8 @@ // Check the argument types. The callee's formal argument types must be // bitcast compatible with the corresponding actual argument types of the call // site. - for (unsigned I = 0; I < NumParams; ++I) { + unsigned I = 0; + for (; I < NumParams; ++I) { Type *FormalTy = Callee->getFunctionType()->getFunctionParamType(I); Type *ActualTy = CB.getArgOperand(I)->getType(); if (FormalTy == ActualTy) @@ -425,6 +429,14 @@ return false; } } + for (; I < NumArgs; I++) { + // Vararg functions can have more arguments than paramters. + assert(Callee->isVarArg()); + if (CB.paramHasAttr(I, Attribute::StructRet)) { + *FailureReason = "SRet arg to vararg function"; + return false; + } + } return true; } diff --git a/llvm/test/Transforms/PGOProfile/icp_vararg_sret.ll b/llvm/test/Transforms/PGOProfile/icp_vararg_sret.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/PGOProfile/icp_vararg_sret.ll @@ -0,0 +1,29 @@ +; RUN: opt < %s -pgo-icall-prom -S | FileCheck %s +; RUN: opt < %s -passes=pgo-icall-prom -S | FileCheck %s +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @va_func(i32 %num, ...) { +entry: + ret void +} + +%struct = type { i32 } +@func_ptr = common global void (i32, %struct*)* null, align 8 + +define void @test() { +; Even though value profiling suggests @va_func is the call target, don't do +; call promotion because the sret argument is not compatible with the varargs +; function. +; CHECK-LABEL: @test +; CHECK-NOT: call void (i32, ...) @va_func +; CHECK: call void %tmp +; CHECK: ret void + + %s = alloca %struct + %tmp = load void (i32, %struct*)*, void (i32, %struct*)** @func_ptr, align 8 + call void %tmp(i32 1, %struct* sret %s), !prof !1 + ret void +} + +!1 = !{!"VP", i32 0, i64 12345, i64 989055279648259519, i64 12345}