[ms-cxxabi] Add a new calling convention that swaps 'this' and 'sret'
authorReid Kleckner <reid@kleckner.net>
Fri, 31 Jan 2014 17:41:22 +0000 (17:41 +0000)
committerReid Kleckner <reid@kleckner.net>
Fri, 31 Jan 2014 17:41:22 +0000 (17:41 +0000)
MSVC always places the 'this' parameter for a method first.  The
implicit 'sret' pointer for methods always comes second.  We already
implement this for __thiscall by putting sret parameters on the stack,
but __cdecl methods require putting both parameters on the stack in
opposite order.

Using a special calling convention allows frontends to keep the sret
parameter first, which avoids breaking lots of assumptions in LLVM and
Clang.

Fixes PR15768 with the corresponding change in Clang.

Reviewers: ributzka, majnemer

Differential Revision: http://llvm-reviews.chandlerc.com/D2663

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

docs/BitCodeFormat.rst
docs/CodeGenerator.rst
include/llvm/IR/CallingConv.h
lib/AsmParser/LLLexer.cpp
lib/AsmParser/LLParser.cpp
lib/AsmParser/LLToken.h
lib/IR/AsmWriter.cpp
lib/Target/X86/X86CallingConv.h
lib/Target/X86/X86CallingConv.td
test/CodeGen/X86/cdecl-method-return.ll [new file with mode: 0644]

index 38b4010483a13b084b43cc3e12f13d822f057e6e..4c1c94abd68b1c049d8486876124857d8c703c9d 100644 (file)
@@ -746,6 +746,8 @@ function. The operand fields are:
   * ``arm_apcscc``: code 66
   * ``arm_aapcscc``: code 67
   * ``arm_aapcs_vfpcc``: code 68
+  * ``x86_thiscallcc``: code 70
+  * ``x86_cdeclmethodcc``: code 80
 
 * isproto*: Non-zero if this entry represents a declaration rather than a
   definition
index b89d2f426f388a25cb206b4095246ff33d2a8707..d7d98bc385be66a86c3c65b460403ddd740dc7d0 100644 (file)
@@ -2145,6 +2145,10 @@ The following target-specific calling conventions are known to backend:
   others via stack. Callee is responsible for stack cleaning. This convention is
   used by MSVC by default for methods in its ABI (CC ID = 70).
 
+* **X86_CDeclMethod** --- Identical to the standard x86_32 C calling convention,
+  except that an sret paramter, if present, is placed on the stack after the
+  second parameter, which must an integer or pointer.  (CC ID = 80).
+
 .. _X86 addressing mode:
 
 Representing X86 addressing modes in MachineInstrs
index 1eaf4f7f469fe8b252940e816655d971706ccdac..af44e8a30c347a8d0f1bf88f17057dbff7de9ccc 100644 (file)
@@ -137,7 +137,13 @@ namespace CallingConv {
     /// convention differs from the more common \c X86_64_SysV convention
     /// in a number of ways, most notably in that XMM registers used to pass
     /// arguments are shadowed by GPRs, and vice versa.
-    X86_64_Win64 = 79
+    X86_64_Win64 = 79,
+
+    /// \brief The calling convention used for __cdecl methods on win32.
+    /// Differs from the C calling convention only in that the order of the
+    /// first parameter and the sret parameter are swapped.
+    X86_CDeclMethod = 80
+
   };
 } // End CallingConv namespace
 
index 7fb8032ca99ab0d4638b3fac58be79d83d617b19..00b137d5a7aa4d943acac6c1206ec2041d42f846 100644 (file)
@@ -550,6 +550,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(x86_stdcallcc);
   KEYWORD(x86_fastcallcc);
   KEYWORD(x86_thiscallcc);
+  KEYWORD(x86_cdeclmethodcc);
   KEYWORD(arm_apcscc);
   KEYWORD(arm_aapcscc);
   KEYWORD(arm_aapcs_vfpcc);
index ab4b48bf6eca5902bb4706b634a7c236b251674a..b36f0e840cf090147061e0b57723ddc77e063649 100644 (file)
@@ -1357,6 +1357,7 @@ bool LLParser::ParseOptionalDLLStorageClass(unsigned &Res) {
 ///   ::= 'x86_stdcallcc'
 ///   ::= 'x86_fastcallcc'
 ///   ::= 'x86_thiscallcc'
+///   ::= 'x86_cdeclmethodcc'
 ///   ::= 'arm_apcscc'
 ///   ::= 'arm_aapcscc'
 ///   ::= 'arm_aapcs_vfpcc'
@@ -1382,6 +1383,7 @@ bool LLParser::ParseOptionalCallingConv(CallingConv::ID &CC) {
   case lltok::kw_x86_stdcallcc:  CC = CallingConv::X86_StdCall; break;
   case lltok::kw_x86_fastcallcc: CC = CallingConv::X86_FastCall; break;
   case lltok::kw_x86_thiscallcc: CC = CallingConv::X86_ThisCall; break;
+  case lltok::kw_x86_cdeclmethodcc:CC = CallingConv::X86_CDeclMethod; break;
   case lltok::kw_arm_apcscc:     CC = CallingConv::ARM_APCS; break;
   case lltok::kw_arm_aapcscc:    CC = CallingConv::ARM_AAPCS; break;
   case lltok::kw_arm_aapcs_vfpcc:CC = CallingConv::ARM_AAPCS_VFP; break;
index 50318500bf4847b2aa47a05a2ceaf6786aca783c..67f2c0c42b7788e3b63eca07bb7f828ca7765150 100644 (file)
@@ -85,7 +85,7 @@ namespace lltok {
 
     kw_cc, kw_ccc, kw_fastcc, kw_coldcc,
     kw_intel_ocl_bicc,
-    kw_x86_stdcallcc, kw_x86_fastcallcc, kw_x86_thiscallcc,
+    kw_x86_stdcallcc, kw_x86_fastcallcc, kw_x86_thiscallcc, kw_x86_cdeclmethodcc,
     kw_arm_apcscc, kw_arm_aapcscc, kw_arm_aapcs_vfpcc,
     kw_msp430_intrcc,
     kw_ptx_kernel, kw_ptx_device,
index 2b23278a88cbad422b108cf5f8affab4879bbe89..c48214cc2fab4b11d18b3d0f17336dad23707d9f 100644 (file)
@@ -78,6 +78,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) {
   case CallingConv::X86_StdCall:   Out << "x86_stdcallcc"; break;
   case CallingConv::X86_FastCall:  Out << "x86_fastcallcc"; break;
   case CallingConv::X86_ThisCall:  Out << "x86_thiscallcc"; break;
+  case CallingConv::X86_CDeclMethod:Out << "x86_cdeclmethodcc"; break;
   case CallingConv::Intel_OCL_BI:  Out << "intel_ocl_bicc"; break;
   case CallingConv::ARM_APCS:      Out << "arm_apcscc"; break;
   case CallingConv::ARM_AAPCS:     Out << "arm_aapcscc"; break;
index e76f9fda2db94c44acd98d510f03046b0ec99188..040da3535e805203e21c964cbffefd4a45b57070 100644 (file)
@@ -29,6 +29,33 @@ inline bool CC_X86_AnyReg_Error(unsigned &, MVT &, MVT &,
   return false;
 }
 
+inline bool CC_X86_CDeclMethod_SRet(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
+                                    CCValAssign::LocInfo &LocInfo,
+                                    ISD::ArgFlagsTy &ArgFlags, CCState &State) {
+  // Swap the order of the first two parameters if the first parameter is sret.
+  if (ArgFlags.isSRet()) {
+    assert(ValNo == 0);
+    assert(ValVT == MVT::i32);
+    State.AllocateStack(8, 4);
+    State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT, 4, LocVT, LocInfo));
+
+    // Indicate that we need to swap the order of the first and second
+    // parameters by "allocating" register zero.  There are no register
+    // parameters with cdecl methods, so we can use this to communicate to the
+    // next call.
+    State.AllocateReg(1);
+    return true;
+  } else if (ValNo == 1 && State.isAllocated(1)) {
+    assert(ValVT == MVT::i32 && "non-i32-sized this param unsupported");
+    // Stack was already allocated while processing sret.
+    State.addLoc(CCValAssign::getCustomMem(ValNo, ValVT, 0, LocVT, LocInfo));
+    return true;
+  }
+
+  // All other args use the C calling convention.
+  return false;
+}
+
 } // End llvm namespace
 
 #endif
index 5b51a1e8e0b6d778eab96b97c39a0be8ce0e19db..f40a42ee9b9a753f93b8d5eda8a4bc1ec6c25e1d 100644 (file)
@@ -485,6 +485,15 @@ def CC_X86_32_ThisCall_Win : CallingConv<[
   CCDelegateTo<CC_X86_32_ThisCall_Common>
 ]>;
 
+def CC_X86_CDeclMethod : CallingConv<[
+  // Promote i8/i16 arguments to i32.
+  CCIfType<[i8, i16], CCPromoteToType<i32>>,
+
+  CCCustom<"CC_X86_CDeclMethod_SRet">,
+
+  CCDelegateTo<CC_X86_32_Common>
+]>;
+
 def CC_X86_32_ThisCall : CallingConv<[
   CCIfSubtarget<"isTargetCygMing()", CCDelegateTo<CC_X86_32_ThisCall_Mingw>>,
   CCDelegateTo<CC_X86_32_ThisCall_Win>
@@ -574,6 +583,7 @@ def CC_Intel_OCL_BI : CallingConv<[
 def CC_X86_32 : CallingConv<[
   CCIfCC<"CallingConv::X86_FastCall", CCDelegateTo<CC_X86_32_FastCall>>,
   CCIfCC<"CallingConv::X86_ThisCall", CCDelegateTo<CC_X86_32_ThisCall>>,
+  CCIfCC<"CallingConv::X86_CDeclMethod", CCDelegateTo<CC_X86_CDeclMethod>>,
   CCIfCC<"CallingConv::Fast", CCDelegateTo<CC_X86_32_FastCC>>,
   CCIfCC<"CallingConv::GHC", CCDelegateTo<CC_X86_32_GHC>>,
   CCIfCC<"CallingConv::HiPE", CCDelegateTo<CC_X86_32_HiPE>>,
diff --git a/test/CodeGen/X86/cdecl-method-return.ll b/test/CodeGen/X86/cdecl-method-return.ll
new file mode 100644 (file)
index 0000000..e7af597
--- /dev/null
@@ -0,0 +1,69 @@
+; RUN: llc < %s -mtriple=i686-pc-win32 | FileCheck %s
+
+; The sret flag causes the first two parameters to be reordered on the stack.
+
+define x86_cdeclmethodcc void @foo(i32* sret %dst, i32* %src) {
+  %v = load i32* %src
+  store i32 %v, i32* %dst
+  ret void
+}
+
+; CHECK-LABEL: _foo:
+; CHECK:  movl    8(%esp), %[[dst:[^ ]*]]
+; CHECK:  movl    4(%esp), %[[src:[^ ]*]]
+; CHECK:  movl    (%[[src]]), %[[v:[^ ]*]]
+; CHECK:  movl    %[[v]], (%[[dst]])
+; CHECK:  retl
+
+define i32 @bar() {
+  %src = alloca i32
+  %dst = alloca i32
+  store i32 42, i32* %src
+  call x86_cdeclmethodcc void @foo(i32* sret %dst, i32* %src)
+  %v = load i32* %dst
+  ret i32 %v
+}
+
+; CHECK-LABEL: _bar:
+; CHECK:  movl    $42, [[src:[^,]*]]
+; CHECK:  leal    [[src]], %[[reg:[^ ]*]]
+; CHECK:  movl    %[[reg]], (%esp)
+; CHECK:  leal    [[dst:[^,]*]], %[[reg:[^ ]*]]
+; CHECK:  movl    %[[reg]], 4(%esp)
+; CHECK:  calll   _foo
+; CHECK:  movl    [[dst]], %eax
+; CHECK:  retl
+
+; If we don't have the sret flag, parameters are not reordered.
+
+define x86_cdeclmethodcc void @baz(i32* %dst, i32* %src) {
+  %v = load i32* %src
+  store i32 %v, i32* %dst
+  ret void
+}
+
+; CHECK-LABEL: _baz:
+; CHECK:  movl    4(%esp), %[[dst:[^ ]*]]
+; CHECK:  movl    8(%esp), %[[src:[^ ]*]]
+; CHECK:  movl    (%[[src]]), %[[v:[^ ]*]]
+; CHECK:  movl    %[[v]], (%[[dst]])
+; CHECK:  retl
+
+define i32 @qux() {
+  %src = alloca i32
+  %dst = alloca i32
+  store i32 42, i32* %src
+  call x86_cdeclmethodcc void @baz(i32* %dst, i32* %src)
+  %v = load i32* %dst
+  ret i32 %v
+}
+
+; CHECK-LABEL: _qux:
+; CHECK:  movl    $42, [[src:[^,]*]]
+; CHECK:  leal    [[src]], %[[reg:[^ ]*]]
+; CHECK:  movl    %[[reg]], 4(%esp)
+; CHECK:  leal    [[dst:[^,]*]], %[[reg:[^ ]*]]
+; CHECK:  movl    %[[reg]], (%esp)
+; CHECK:  calll   _baz
+; CHECK:  movl    [[dst]], %eax
+; CHECK:  retl