[MS] On x86_32, pass overaligned, non-copyable arguments indirectly
authorReid Kleckner <rnk@google.com>
Fri, 18 Sep 2020 18:22:15 +0000 (11:22 -0700)
committerReid Kleckner <rnk@google.com>
Mon, 21 Sep 2020 18:49:17 +0000 (11:49 -0700)
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
clang/test/CodeGenCXX/inalloca-overaligned.cpp

index 45c6cb6..4f72579 100644 (file)
@@ -827,10 +827,14 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
     // copy ctor.
     return !RD->canPassInRegisters() ? RAA_Indirect : RAA_Default;
 
     // 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.
 
     // 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;
     // 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:
 
   case llvm::Triple::x86_64:
   case llvm::Triple::aarch64:
index 910f0d9..83eb4d1 100644 (file)
@@ -4,11 +4,6 @@
 // MSVC passes overaligned types indirectly since MSVC 2015. Make sure that
 // works with inalloca.
 
 // 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);
 struct NonTrivial {
   NonTrivial();
   NonTrivial(const NonTrivial &o);
@@ -20,6 +15,12 @@ struct __declspec(align(64)) OverAligned {
   int buf[16];
 };
 
   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) {
 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)
 // 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)