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
This commit is contained in:
Reid Kleckner 2014-07-08 02:24:27 +00:00
parent b6f54990d8
commit 7e57cd2b32
2 changed files with 53 additions and 23 deletions

View File

@ -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<llvm::PointerType>(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<llvm::StructType>(
cast<llvm::PointerType>(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<llvm::StructType>(
cast<llvm::PointerType>(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);
}

View File

@ -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 <stdarg.h>
@ -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, ...)