From 7e57cd2b32e31edae5160f72b51ed77d69ccb1cc Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 8 Jul 2014 02:24:27 +0000 Subject: [PATCH] MS ABI: "Fix" passing non-POD structs by value to variadic functions Of course, such code is horribly broken and will explode on impact. That said, ATL does it, and we have to support them, at least a little bit. Fixes PR20191. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@212508 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGCall.cpp | 45 ++++++++++--------- .../CodeGenCXX/microsoft-abi-byval-vararg.cpp | 31 +++++++++++-- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 5ccb8bdf2c..44fd4d8b59 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -2859,28 +2859,33 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (ArgMemory) { llvm::Value *Arg = ArgMemory; - llvm::Type *LastParamTy = - IRFuncTy->getParamType(IRFuncTy->getNumParams() - 1); - if (Arg->getType() != LastParamTy) { + if (CallInfo.isVariadic()) { + // When passing non-POD arguments by value to variadic functions, we will + // end up with a variadic prototype and an inalloca call site. In such + // cases, we can't do any parameter mismatch checks. Give up and bitcast + // the callee. + unsigned CalleeAS = + cast(Callee->getType())->getAddressSpace(); + Callee = Builder.CreateBitCast( + Callee, getTypes().GetFunctionType(CallInfo)->getPointerTo(CalleeAS)); + } else { + llvm::Type *LastParamTy = + IRFuncTy->getParamType(IRFuncTy->getNumParams() - 1); + if (Arg->getType() != LastParamTy) { #ifndef NDEBUG - // Assert that these structs have equivalent element types. - llvm::StructType *FullTy = CallInfo.getArgStruct(); - llvm::StructType *Prefix = cast( - cast(LastParamTy)->getElementType()); - - // For variadic functions, the caller might supply a larger struct than - // the callee expects, and that's OK. - assert(Prefix->getNumElements() == FullTy->getNumElements() || - (CallInfo.isVariadic() && - Prefix->getNumElements() <= FullTy->getNumElements())); - - for (llvm::StructType::element_iterator PI = Prefix->element_begin(), - PE = Prefix->element_end(), - FI = FullTy->element_begin(); - PI != PE; ++PI, ++FI) - assert(*PI == *FI); + // Assert that these structs have equivalent element types. + llvm::StructType *FullTy = CallInfo.getArgStruct(); + llvm::StructType *DeclaredTy = cast( + cast(LastParamTy)->getElementType()); + assert(DeclaredTy->getNumElements() == FullTy->getNumElements()); + for (llvm::StructType::element_iterator DI = DeclaredTy->element_begin(), + DE = DeclaredTy->element_end(), + FI = FullTy->element_begin(); + DI != DE; ++DI, ++FI) + assert(*DI == *FI); #endif - Arg = Builder.CreateBitCast(Arg, LastParamTy); + Arg = Builder.CreateBitCast(Arg, LastParamTy); + } } Args.push_back(Arg); } diff --git a/test/CodeGenCXX/microsoft-abi-byval-vararg.cpp b/test/CodeGenCXX/microsoft-abi-byval-vararg.cpp index 33619216e8..6a0a8601c0 100644 --- a/test/CodeGenCXX/microsoft-abi-byval-vararg.cpp +++ b/test/CodeGenCXX/microsoft-abi-byval-vararg.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i686-pc-win32 -mconstructor-aliases -fno-rtti | FileCheck %s +// RUN: %clang_cc1 -Wno-non-pod-varargs -emit-llvm %s -o - -triple=i686-pc-win32 -mconstructor-aliases -fno-rtti | FileCheck %s #include @@ -19,9 +19,34 @@ int foo(A a, ...) { return sum; } +// CHECK-LABEL: define i32 @"\01?foo@@YAHUA@@ZZ"(<{ %struct.A }>* inalloca, ...) + int main() { return foo(A(3), 1, 2, 3); } // CHECK-LABEL: define i32 @main() -// CHECK: %[[argmem_cast:[^ ]*]] = bitcast <{ %struct.A, i32, i32, i32 }>* %argmem to <{ %struct.A }>* -// CHECK: call i32 (<{ %struct.A }>*, ...)* @"\01?foo@@YAHUA@@ZZ"(<{ %struct.A }>* inalloca %[[argmem_cast]]) +// CHECK: %[[argmem:[^ ]*]] = alloca inalloca <{ %struct.A, i32, i32, i32 }> +// CHECK: call i32 {{.*bitcast.*}}@"\01?foo@@YAHUA@@ZZ"{{.*}}(<{ %struct.A, i32, i32, i32 }>* inalloca %[[argmem]]) + +void varargs_zero(...); +void varargs_one(int, ...); +void varargs_two(int, int, ...); +void varargs_three(int, int, int, ...); +void call_var_args() { + A x(3); + varargs_zero(x); + varargs_one(1, x); + varargs_two(1, 2, x); + varargs_three(1, 2, 3, x); +} + +// CHECK-LABEL: define void @"\01?call_var_args@@YAXXZ"() +// CHECK: call void {{.*bitcast.*varargs_zero.*}}(<{ %struct.A }>* inalloca %{{.*}}) +// CHECK: call void {{.*bitcast.*varargs_one.*}}(<{ i32, %struct.A }>* inalloca %{{.*}}) +// CHECK: call void {{.*bitcast.*varargs_two.*}}(<{ i32, i32, %struct.A }>* inalloca %{{.*}}) +// CHECK: call void {{.*bitcast.*varargs_three.*}}(<{ i32, i32, i32, %struct.A }>* inalloca %{{.*}}) + +// CHECK-LABEL: declare void @"\01?varargs_zero@@YAXZZ"(...) +// CHECK-LABEL: declare void @"\01?varargs_one@@YAXHZZ"(i32, ...) +// CHECK-LABEL: declare void @"\01?varargs_two@@YAXHHZZ"(i32, i32, ...) +// CHECK-LABEL: declare void @"\01?varargs_three@@YAXHHHZZ"(i32, i32, i32, ...)