Skip to content

Commit

Permalink
[ms-cxxabi] Add a new calling convention that swaps 'this' and 'sret'
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rnk committed Jan 31, 2014
1 parent 0cb94d9 commit 65c98b9
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 2 deletions.
2 changes: 2 additions & 0 deletions docs/BitCodeFormat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/CodeGenerator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion include/llvm/IR/CallingConv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,7 @@ bool LLParser::ParseOptionalDLLStorageClass(unsigned &Res) {
/// ::= 'x86_stdcallcc'
/// ::= 'x86_fastcallcc'
/// ::= 'x86_thiscallcc'
/// ::= 'x86_cdeclmethodcc'
/// ::= 'arm_apcscc'
/// ::= 'arm_aapcscc'
/// ::= 'arm_aapcs_vfpcc'
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion lib/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
27 changes: 27 additions & 0 deletions lib/Target/X86/X86CallingConv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions lib/Target/X86/X86CallingConv.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>>,
Expand Down
69 changes: 69 additions & 0 deletions test/CodeGen/X86/cdecl-method-return.ll
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 65c98b9

Please sign in to comment.