From 3b3a16548568f5b6c4146ca5129eb6af5000e4ff Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Fri, 18 Sep 2020 11:22:15 -0700 Subject: [PATCH] [MS] On x86_32, pass overaligned, non-copyable arguments indirectly This updates the C++ ABI argument classification code to use the logic from D72114, fixing an ABI incompatibility with MSVC. Part of PR44395. Differential Revision: https://reviews.llvm.org/D87923 --- clang/lib/CodeGen/MicrosoftCXXABI.cpp | 13 +++++--- clang/test/CodeGenCXX/inalloca-overaligned.cpp | 45 +++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 45c6cb6..4f72579 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -827,10 +827,14 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const { // copy ctor. return !RD->canPassInRegisters() ? RAA_Indirect : RAA_Default; - case llvm::Triple::x86: - // All record arguments are passed in memory on x86. Decide whether to - // construct the object directly in argument memory, or to construct the - // argument elsewhere and copy the bytes during the call. + case llvm::Triple::x86: { + // If the argument has *required* alignment greater than four bytes, pass + // it indirectly. Prior to MSVC version 19.14, passing overaligned + // arguments was not supported and resulted in a compiler error. In 19.14 + // and later versions, such arguments are now passed indirectly. + TypeInfo Info = getContext().getTypeInfo(RD->getTypeForDecl()); + if (Info.AlignIsRequired && Info.Align > 4) + return RAA_Indirect; // If C++ prohibits us from making a copy, construct the arguments directly // into argument memory. @@ -840,6 +844,7 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const { // Otherwise, construct the argument into a temporary and copy the bytes // into the outgoing argument memory. return RAA_Default; + } case llvm::Triple::x86_64: case llvm::Triple::aarch64: diff --git a/clang/test/CodeGenCXX/inalloca-overaligned.cpp b/clang/test/CodeGenCXX/inalloca-overaligned.cpp index 910f0d9..83eb4d1 100644 --- a/clang/test/CodeGenCXX/inalloca-overaligned.cpp +++ b/clang/test/CodeGenCXX/inalloca-overaligned.cpp @@ -4,11 +4,6 @@ // MSVC passes overaligned types indirectly since MSVC 2015. Make sure that // works with inalloca. -// FIXME: Pass non-trivial *and* overaligned types indirectly. Right now the C++ -// ABI rules say to use inalloca, and they take precedence, so it's not easy to -// implement this. - - struct NonTrivial { NonTrivial(); NonTrivial(const NonTrivial &o); @@ -20,6 +15,12 @@ struct __declspec(align(64)) OverAligned { int buf[16]; }; +struct __declspec(align(8)) Both { + Both(); + Both(const Both &o); + int x, y; +}; + extern int gvi32; int receive_inalloca_overaligned(NonTrivial nt, OverAligned o) { @@ -50,3 +51,37 @@ int pass_inalloca_overaligned() { // CHECK: getelementptr inbounds <{ %struct.NonTrivial, %struct.OverAligned* }>, <{ %struct.NonTrivial, %struct.OverAligned* }>* %{{.*}}, i32 0, i32 1 // CHECK: store %struct.OverAligned* [[TMP]], %struct.OverAligned** %{{.*}}, align 4 // CHECK: call i32 @"?receive_inalloca_overaligned@@Y{{.*}}"(<{ %struct.NonTrivial, %struct.OverAligned* }>* inalloca %argmem) + +int receive_both(Both o) { + return o.x + o.y; +} + +// CHECK-LABEL: define dso_local i32 @"?receive_both@@Y{{.*}}" +// CHECK-SAME: (%struct.Both* %o) + +int pass_both() { + gvi32 = receive_both(Both()); + return gvi32; +} + +// CHECK-LABEL: define dso_local i32 @"?pass_both@@Y{{.*}}" +// CHECK: [[TMP:%[^ ]*]] = alloca %struct.Both, align 8 +// CHECK: call x86_thiscallcc %struct.Both* @"??0Both@@QAE@XZ"(%struct.Both* [[TMP]]) +// CHECK: call i32 @"?receive_both@@Y{{.*}}"(%struct.Both* [[TMP]]) + +int receive_inalloca_both(NonTrivial nt, Both o) { + return nt.x + o.x + o.y; +} + +// CHECK-LABEL: define dso_local i32 @"?receive_inalloca_both@@Y{{.*}}" +// CHECK-SAME: (<{ %struct.NonTrivial, %struct.Both* }>* inalloca %0) + +int pass_inalloca_both() { + gvi32 = receive_inalloca_both(NonTrivial(), Both()); + return gvi32; +} + +// CHECK-LABEL: define dso_local i32 @"?pass_inalloca_both@@Y{{.*}}" +// CHECK: [[TMP:%[^ ]*]] = alloca %struct.Both, align 8 +// CHECK: call x86_thiscallcc %struct.Both* @"??0Both@@QAE@XZ"(%struct.Both* [[TMP]]) +// CHECK: call i32 @"?receive_inalloca_both@@Y{{.*}}"(<{ %struct.NonTrivial, %struct.Both* }>* inalloca %argmem) -- 2.7.4