r213990 - MS ABI: Ensure 'this' is first for byval+sret methods

Reid Kleckner reid at kleckner.net
Fri Jul 25 17:12:26 PDT 2014


Author: rnk
Date: Fri Jul 25 19:12:26 2014
New Revision: 213990

URL: http://llvm.org/viewvc/llvm-project?rev=213990&view=rev
Log:
MS ABI: Ensure 'this' is first for byval+sret methods

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.

Modified:
    cfe/trunk/lib/CodeGen/TargetInfo.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-byval-sret.cpp

Modified: cfe/trunk/lib/CodeGen/TargetInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/TargetInfo.cpp?rev=213990&r1=213989&r2=213990&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/TargetInfo.cpp (original)
+++ cfe/trunk/lib/CodeGen/TargetInfo.cpp Fri Jul 25 19:12:26 2014
@@ -1002,6 +1002,26 @@ X86_32ABIInfo::addFieldToArgStruct(Small
   }
 }
 
+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(
   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(
   }
 
   // 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,

Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-byval-sret.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-byval-sret.cpp?rev=213990&r1=213989&r2=213990&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-byval-sret.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-byval-sret.cpp Fri Jul 25 19:12:26 2014
@@ -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 at A@@QAE?AU1 at U1@@Z"
-// CHECK:       (%struct.A* %this, <{ %struct.A*, %struct.A }>* inalloca)
+// CHECK-LABEL: define x86_thiscallcc %struct.A* @"\01?foo at 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 at 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 at 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 at 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 at A@@QAE?AU1 at U1@@Z"
-// CHECK:       (%struct.A* %{{[^,]*}}, <{ %struct.A*, %struct.A }>* inalloca %{{[^,]*}})
+// CHECK: call x86_thiscallcc %struct.A* @"\01?foo at B@@QAE?AUA@@U2@@Z"
+// CHECK:       (%struct.B* %{{[^,]*}}, <{ %struct.A*, %struct.A }>* inalloca %{{[^,]*}})
+// CHECK: call %struct.A* @"\01?bar at B@@QAA?AUA@@U2@@Z"
+// CHECK:       (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca %{{[^,]*}})
+// CHECK: call x86_stdcallcc %struct.A* @"\01?baz at B@@QAG?AUA@@U2@@Z"
+// CHECK:       (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca %{{[^,]*}})
+// CHECK: call x86_fastcallcc void @"\01?qux at B@@QAI?AUA@@U2@@Z"
+// CHECK:       (%struct.B* inreg %{{[^,]*}}, %struct.A* inreg sret %{{.*}}, <{ %struct.A }>* inalloca %{{[^,]*}})





More information about the cfe-commits mailing list