]> granicus.if.org Git - clang/commitdiff
MS ABI: Ensure 'this' is first for byval+sret methods
authorReid Kleckner <reid@kleckner.net>
Sat, 26 Jul 2014 00:12:26 +0000 (00:12 +0000)
committerReid Kleckner <reid@kleckner.net>
Sat, 26 Jul 2014 00:12:26 +0000 (00:12 +0000)
Previously we were building up the inalloca struct in the usual pattern
of return type followed by arguments.  However, on Windows, 'this'
always precedes the 'sret' parameter, so we need to insert it into the
struct first as a special case.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@213990 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/TargetInfo.cpp
test/CodeGenCXX/microsoft-abi-byval-sret.cpp

index 40fc51146aa80161c79ef110148a191ba63f27c1..1a1ac8bd925944232d7a9f2e56b5688c5a27e9d9 100644 (file)
@@ -1002,6 +1002,26 @@ X86_32ABIInfo::addFieldToArgStruct(SmallVector<llvm::Type *, 6> &FrameFields,
   }
 }
 
+static bool isArgInAlloca(const ABIArgInfo &Info) {
+  // Leave ignored and inreg arguments alone.
+  switch (Info.getKind()) {
+  case ABIArgInfo::InAlloca:
+    return true;
+  case ABIArgInfo::Indirect:
+    assert(Info.getIndirectByVal());
+    return true;
+  case ABIArgInfo::Ignore:
+    return false;
+  case ABIArgInfo::Direct:
+  case ABIArgInfo::Extend:
+  case ABIArgInfo::Expand:
+    if (Info.getInReg())
+      return false;
+    return true;
+  }
+  llvm_unreachable("invalid enum");
+}
+
 void X86_32ABIInfo::rewriteWithInAlloca(CGFunctionInfo &FI) const {
   assert(IsWin32StructABI && "inalloca only supported on win32");
 
@@ -1009,9 +1029,19 @@ void X86_32ABIInfo::rewriteWithInAlloca(CGFunctionInfo &FI) const {
   SmallVector<llvm::Type *, 6> FrameFields;
 
   unsigned StackOffset = 0;
+  CGFunctionInfo::arg_iterator I = FI.arg_begin(), E = FI.arg_end();
 
-  // Put the sret parameter into the inalloca struct if it's in memory.
+  // Put 'this' into the struct before 'sret', if necessary.
+  bool IsThisCall =
+      FI.getCallingConvention() == llvm::CallingConv::X86_ThisCall;
   ABIArgInfo &Ret = FI.getReturnInfo();
+  if (Ret.isIndirect() && Ret.isSRetAfterThis() && !IsThisCall &&
+      isArgInAlloca(I->info)) {
+    addFieldToArgStruct(FrameFields, StackOffset, I->info, I->type);
+    ++I;
+  }
+
+  // Put the sret parameter into the inalloca struct if it's in memory.
   if (Ret.isIndirect() && !Ret.getInReg()) {
     CanQualType PtrTy = getContext().getPointerType(FI.getReturnType());
     addFieldToArgStruct(FrameFields, StackOffset, Ret, PtrTy);
@@ -1020,30 +1050,13 @@ void X86_32ABIInfo::rewriteWithInAlloca(CGFunctionInfo &FI) const {
   }
 
   // Skip the 'this' parameter in ecx.
-  CGFunctionInfo::arg_iterator I = FI.arg_begin(), E = FI.arg_end();
-  if (FI.getCallingConvention() == llvm::CallingConv::X86_ThisCall)
+  if (IsThisCall)
     ++I;
 
   // Put arguments passed in memory into the struct.
   for (; I != E; ++I) {
-
-    // Leave ignored and inreg arguments alone.
-    switch (I->info.getKind()) {
-    case ABIArgInfo::Indirect:
-      assert(I->info.getIndirectByVal());
-      break;
-    case ABIArgInfo::Ignore:
-      continue;
-    case ABIArgInfo::Direct:
-    case ABIArgInfo::Extend:
-      if (I->info.getInReg())
-        continue;
-      break;
-    default:
-      break;
-    }
-
-    addFieldToArgStruct(FrameFields, StackOffset, I->info, I->type);
+    if (isArgInAlloca(I->info))
+      addFieldToArgStruct(FrameFields, StackOffset, I->info, I->type);
   }
 
   FI.setArgStruct(llvm::StructType::get(getVMContext(), FrameFields,
index 985b1ce62e924d80637d5c0360bb948b23f0a733..a34a2455c36f6c2eb6de89a35debd125e55334ae 100644 (file)
@@ -5,25 +5,66 @@ struct A {
   A(const A &o) : a(o.a) {}
   ~A() {}
   int a;
+};
+
+struct B {
   A foo(A o);
+  A __cdecl bar(A o);
+  A __stdcall baz(A o);
+  A __fastcall qux(A o);
 };
 
-A A::foo(A x) {
-  A y(*this);
-  y.a += x.a;
-  return y;
+A B::foo(A x) {
+  return x;
 }
 
-// CHECK-LABEL: define x86_thiscallcc %struct.A* @"\01?foo@A@@QAE?AU1@U1@@Z"
-// CHECK:       (%struct.A* %this, <{ %struct.A*, %struct.A }>* inalloca)
+// CHECK-LABEL: define x86_thiscallcc %struct.A* @"\01?foo@B@@QAE?AUA@@U2@@Z"
+// CHECK:       (%struct.B* %this, <{ %struct.A*, %struct.A }>* inalloca)
 // CHECK:   getelementptr inbounds <{ %struct.A*, %struct.A }>* %{{.*}}, i32 0, i32 0
 // CHECK:   load %struct.A**
 // CHECK:   ret %struct.A*
 
+A B::bar(A x) {
+  return x;
+}
+
+// CHECK-LABEL: define %struct.A* @"\01?bar@B@@QAA?AUA@@U2@@Z"
+// CHECK:       (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca)
+// CHECK:   getelementptr inbounds <{ %struct.B*, %struct.A*, %struct.A }>* %{{.*}}, i32 0, i32 1
+// CHECK:   load %struct.A**
+// CHECK:   ret %struct.A*
+
+A B::baz(A x) {
+  return x;
+}
+
+// CHECK-LABEL: define x86_stdcallcc %struct.A* @"\01?baz@B@@QAG?AUA@@U2@@Z"
+// CHECK:       (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca)
+// CHECK:   getelementptr inbounds <{ %struct.B*, %struct.A*, %struct.A }>* %{{.*}}, i32 0, i32 1
+// CHECK:   load %struct.A**
+// CHECK:   ret %struct.A*
+
+A B::qux(A x) {
+  return x;
+}
+
+// CHECK-LABEL: define x86_fastcallcc void @"\01?qux@B@@QAI?AUA@@U2@@Z"
+// CHECK:       (%struct.B* inreg %this, %struct.A* inreg noalias sret %agg.result, <{ %struct.A }>* inalloca)
+// CHECK:   ret void
+
 int main() {
-  A x;
-  A y = x.foo(x);
+  B b;
+  A a = b.foo(A());
+  a = b.bar(a);
+  a = b.baz(a);
+  a = b.qux(a);
 }
 
-// CHECK: call x86_thiscallcc %struct.A* @"\01?foo@A@@QAE?AU1@U1@@Z"
-// CHECK:       (%struct.A* %{{[^,]*}}, <{ %struct.A*, %struct.A }>* inalloca %{{[^,]*}})
+// CHECK: call x86_thiscallcc %struct.A* @"\01?foo@B@@QAE?AUA@@U2@@Z"
+// CHECK:       (%struct.B* %{{[^,]*}}, <{ %struct.A*, %struct.A }>* inalloca %{{[^,]*}})
+// CHECK: call %struct.A* @"\01?bar@B@@QAA?AUA@@U2@@Z"
+// CHECK:       (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca %{{[^,]*}})
+// CHECK: call x86_stdcallcc %struct.A* @"\01?baz@B@@QAG?AUA@@U2@@Z"
+// CHECK:       (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca %{{[^,]*}})
+// CHECK: call x86_fastcallcc void @"\01?qux@B@@QAI?AUA@@U2@@Z"
+// CHECK:       (%struct.B* inreg %{{[^,]*}}, %struct.A* inreg sret %{{.*}}, <{ %struct.A }>* inalloca %{{[^,]*}})