From: Reid Kleckner Date: Fri, 2 May 2014 00:51:20 +0000 (+0000) Subject: Win64: Pass member pointers larger than 8 bytes by reference X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e0526bf22e4db32891ce6779242dc909a65c5676;p=clang Win64: Pass member pointers larger than 8 bytes by reference The Win64 ABI docs on MSDN say that arguments bigger than 8 bytes are passed by reference. Prior to this change, we were only applying this logic to RecordType arguments. This affects both the Itanium and Microsoft C++ ABIs. Reviewers: majnemer Differential Revision: http://reviews.llvm.org/D3587 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@207817 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 436e887d59..e45195dd26 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -2717,7 +2717,8 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, bool IsReturnType) const { uint64_t Size = getContext().getTypeSize(Ty); - if (const RecordType *RT = Ty->getAs()) { + const RecordType *RT = Ty->getAs(); + if (RT) { if (IsReturnType) { if (isRecordReturnIndirect(RT, getCXXABI())) return ABIArgInfo::getIndirect(0, false); @@ -2733,15 +2734,31 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, bool IsReturnType) const { if (Size == 128 && getTarget().getTriple().isWindowsGNUEnvironment()) return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(), Size)); + } + + if (const auto *MPT = Ty->getAs()) { + // If the member pointer is not an aggregate, pass it directly. + if (getTarget().getCXXABI().isMicrosoft()) { + // For Microsoft, check with the inheritance model. + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + if (MSInheritanceAttr::hasOnlyOneField(MPT->isMemberFunctionPointer(), + RD->getMSInheritanceModel())) + return ABIArgInfo::getDirect(); + } else { + // For Itanium, data pointers are simple and function pointers are big. + if (MPT->isMemberDataPointer()) + return ABIArgInfo::getDirect(); + } + } + if (RT || Ty->isMemberPointerType()) { // MS x64 ABI requirement: "Any argument that doesn't fit in 8 bytes, or is // not 1, 2, 4, or 8 bytes, must be passed by reference." - if (Size <= 64 && - (Size & (Size - 1)) == 0) - return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(), - Size)); + if (Size > 64 || !llvm::isPowerOf2_64(Size)) + return ABIArgInfo::getIndirect(0, /*ByVal=*/false); - return ABIArgInfo::getIndirect(0, /*ByVal=*/false); + // Otherwise, coerce it to a small integer. + return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(), Size)); } if (Ty->isPromotableIntegerType()) diff --git a/test/CodeGenCXX/member-function-pointer-calls.cpp b/test/CodeGenCXX/member-function-pointer-calls.cpp index 99162ebd58..67417ef046 100644 --- a/test/CodeGenCXX/member-function-pointer-calls.cpp +++ b/test/CodeGenCXX/member-function-pointer-calls.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -O3 -o - | FileCheck %s +// RUN: %clang_cc1 %s -triple=x86_64-windows-gnu -emit-llvm -o - | FileCheck %s -check-prefix MINGW64 struct A { virtual int vf1() { return 1; } virtual int vf2() { return 2; } @@ -10,6 +11,8 @@ int f(A* a, int (A::*fp)()) { // CHECK-LABEL: define i32 @_Z2g1v() // CHECK: ret i32 1 +// MINGW64-LABEL: define i32 @_Z2g1v() +// MINGW64: call i32 @_Z1fP1AMS_FivE(%struct.A* %{{.*}}, { i64, i64 }* %{{.*}}) int g1() { A a; return f(&a, &A::vf1); @@ -17,6 +20,8 @@ int g1() { // CHECK-LABEL: define i32 @_Z2g2v() // CHECK: ret i32 2 +// MINGW64-LABEL: define i32 @_Z2g2v() +// MINGW64: call i32 @_Z1fP1AMS_FivE(%struct.A* %{{.*}}, { i64, i64 }* %{{.*}}) int g2() { A a; return f(&a, &A::vf2); diff --git a/test/CodeGenCXX/microsoft-abi-member-pointers.cpp b/test/CodeGenCXX/microsoft-abi-member-pointers.cpp index c5eeb4ef49..4ce8a02dcb 100644 --- a/test/CodeGenCXX/microsoft-abi-member-pointers.cpp +++ b/test/CodeGenCXX/microsoft-abi-member-pointers.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -triple=x86_64-pc-win32 | FileCheck %s -check-prefix=X64 // RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -triple=i386-pc-win32 -DINCOMPLETE_VIRTUAL -fms-extensions -verify // RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -triple=i386-pc-win32 -DINCOMPLETE_VIRTUAL -DMEMFUN -fms-extensions -verify // FIXME: Test x86_64 member pointers when codegen no longer asserts on records @@ -235,6 +236,10 @@ bool nullTestDataUnspecified(int Unspecified::*mp) { // CHECK: %[[and1:.*]] = or i1 %[[and0]], %[[cmp2]] // CHECK: ret i1 %[[and1]] // CHECK: } + +// Pass this large type indirectly. +// X64-LABEL: define zeroext i1 @"\01?nullTestDataUnspecified@@ +// X64: ({ i32, i32, i32 }*) } bool nullTestFunctionUnspecified(void (Unspecified::*mp)()) { @@ -271,6 +276,11 @@ int loadDataMemberPointerVirtual(Virtual *o, int Virtual::*memptr) { // CHECK: %[[v12:.*]] = load i32* %[[v11]] // CHECK: ret i32 %[[v12]] // CHECK: } + +// A two-field data memptr on x64 gets coerced to i64 and is passed in a +// register or memory. +// X64-LABEL: define i32 @"\01?loadDataMemberPointerVirtual@@YAHPEAUVirtual@@PEQ1@H@Z" +// X64: (%struct.Virtual* %o, i64 %memptr.coerce) } int loadDataMemberPointerUnspecified(Unspecified *o, int Unspecified::*memptr) { @@ -312,6 +322,11 @@ void callMemberPointerSingle(Single *o, void (Single::*memptr)()) { // CHECK: call x86_thiscallcc void %{{.*}}(%{{.*}} %{{.*}}) // CHECK: ret void // CHECK: } + +// X64-LABEL: define void @"\01?callMemberPointerSingle@@ +// X64: (%struct.Single* %o, i8* %memptr) +// X64: bitcast i8* %{{[^ ]*}} to void (%struct.Single*)* +// X64: ret void } void callMemberPointerMultiple(Multiple *o, void (Multiple::*memptr)()) { @@ -358,6 +373,9 @@ bool compareSingleFunctionMemptr(void (Single::*l)(), void (Single::*r)()) { // CHECK-NOT: icmp // CHECK: ret i1 %[[r]] // CHECK: } + +// X64-LABEL: define zeroext i1 @"\01?compareSingleFunctionMemptr@@ +// X64: (i8* %{{[^,]*}}, i8* %{{[^)]*}}) } bool compareNeqSingleFunctionMemptr(void (Single::*l)(), void (Single::*r)()) { @@ -393,6 +411,9 @@ bool unspecFuncMemptrEq(void (Unspecified::*l)(), void (Unspecified::*r)()) { // CHECK: %{{.*}} = and i1 %[[bits_or_null]], %[[cmp0]] // CHECK: ret i1 %{{.*}} // CHECK: } + +// X64-LABEL: define zeroext i1 @"\01?unspecFuncMemptrEq@@ +// X64: ({ i8*, i32, i32, i32 }*, { i8*, i32, i32, i32 }*) } bool unspecFuncMemptrNeq(void (Unspecified::*l)(), void (Unspecified::*r)()) { @@ -435,6 +456,9 @@ bool unspecDataMemptrEq(int Unspecified::*l, int Unspecified::*r) { // CHECK: and i1 // CHECK: ret i1 // CHECK: } + +// X64-LABEL: define zeroext i1 @"\01?unspecDataMemptrEq@@ +// X64: ({ i32, i32, i32 }*, { i32, i32, i32 }*) } void (Multiple::*convertB2FuncToMultiple(void (B2::*mp)()))() {