# HG changeset patch # User Tassilo Philipp # Date 1650103802 -7200 # Node ID 585dcb68f55dcc9b67b04ea792d986ce7fc76881 # Parent fe694c7677b4b0236f74038bacec906e45405191 - more doc and disas examples for x86 fastcall and non-trivial aggregates diff -r fe694c7677b4 -r 585dcb68f55d doc/disas_examples/x86.cdecl.disas --- a/doc/disas_examples/x86.cdecl.disas Thu Apr 14 21:18:02 2022 +0200 +++ b/doc/disas_examples/x86.cdecl.disas Sat Apr 16 12:10:02 2022 +0200 @@ -775,7 +775,7 @@ 1c00095f: 8d 45 f4 lea 0xfffffff4(%ebp),%eax ; | 1c000962: 83 00 7b addl $0x7b,(%eax) ; | a += 12 1c000965: e8 b6 ff ff ff call 1c000920 ; call f1() -1c00096a: 89 45 f0 mov %eax,0xfffffff0(%ebp) ; +1c00096a: 89 45 f0 mov %eax,0xfffffff0(%ebp) ; retval (trivial struct <= 32bits, returned via eax) 1c00096d: 8d 45 f4 lea 0xfffffff4(%ebp),%eax ; | 1c000970: 83 28 7b subl $0x7b,(%eax) ; | a -= 123 1c000973: 8d 45 d8 lea 0xffffffd8(%ebp),%eax ; space (at top of stack) to hold non-triv retval -> eax diff -r fe694c7677b4 -r 585dcb68f55d doc/disas_examples/x86.fastcall_ms.disas --- a/doc/disas_examples/x86.fastcall_ms.disas Thu Apr 14 21:18:02 2022 +0200 +++ b/doc/disas_examples/x86.fastcall_ms.disas Sat Apr 16 12:10:02 2022 +0200 @@ -184,5 +184,221 @@ +; ---------- C++ trivial and non-trivial aggrs passed to C funcs ----------> +; +; struct Trivial { int a; }; +; struct NonTrivial { +; int a; +; NonTrivial() : a(0) {} +; NonTrivial(const NonTrivial& rhs) : a(rhs.a) { } +; }; +; +; extern "C" { +; +; void f1(struct Trivial s) { } +; void f2(struct NonTrivial s) { } +; +; void f() +; { +; struct Trivial t; +; struct NonTrivial n; +; int a=1; +; a += 123; +; f1(t); +; a -= 123; +; f2(n); +; a -= 12; +; } +; } + + + +; output from godbolt compiler explorer w/ msvc 19.31 (/Gr for fastcall) + +_this$ = -4 +NonTrivial::NonTrivial(void) PROC + push ebp + mov ebp, esp + push ecx + mov DWORD PTR _this$[ebp], ecx + mov eax, DWORD PTR _this$[ebp] + mov DWORD PTR [eax], 0 + mov eax, DWORD PTR _this$[ebp] + mov esp, ebp + pop ebp + ret 0 +NonTrivial::NonTrivial(void) ENDP + +_this$ = -4 +_rhs$ = 8 +NonTrivial::NonTrivial(NonTrivial const &) PROC + push ebp + mov ebp, esp + push ecx + mov DWORD PTR _this$[ebp], ecx + mov eax, DWORD PTR _this$[ebp] + mov ecx, DWORD PTR _rhs$[ebp] + mov edx, DWORD PTR [ecx] + mov DWORD PTR [eax], edx + mov eax, DWORD PTR _this$[ebp] + mov esp, ebp + pop ebp + ret 4 +NonTrivial::NonTrivial(NonTrivial const &) ENDP + +_s$ = 8 +@f1@4 PROC + push ebp + mov ebp, esp + pop ebp + ret 4 +@f1@4 ENDP + +_s$ = 8 +@f2@4 PROC + push ebp + mov ebp, esp + pop ebp + ret 4 +@f2@4 ENDP + +_t$ = -12 +_n$ = -8 +_a$ = -4 +@f@0 PROC + push ebp ; + mov ebp, esp ; + sub esp, 12 ; + lea ecx, DWORD PTR _n$[ebp] ; + call NonTrivial::NonTrivial(void) ; + mov DWORD PTR _a$[ebp], 1 ; + mov eax, DWORD PTR _a$[ebp] ; + add eax, 123 ; + mov DWORD PTR _a$[ebp], eax ; + mov ecx, DWORD PTR _t$[ebp] ; + push ecx ; + call @f1@4 ; + mov edx, DWORD PTR _a$[ebp] ; + sub edx, 123 ; + mov DWORD PTR _a$[ebp], edx ; + push ecx ; + mov ecx, esp ; + lea eax, DWORD PTR _n$[ebp] ; + push eax ; + call NonTrivial::NonTrivial(NonTrivial const &) ; + call @f2@4 ; + mov ecx, DWORD PTR _a$[ebp] ; + sub ecx, 12 ; + mov DWORD PTR _a$[ebp], ecx ; + mov esp, ebp ; + pop ebp ; + ret 0 ; +@f@0 ENDP + + + +; ---------- C++ trivial and non-trivial aggrs as return values ----------> +; +; struct Trivial { int a; }; +; struct NonTrivial { +; int a; +; NonTrivial() : a(0) {} +; NonTrivial(const NonTrivial& rhs) : a(rhs.a) { } +; }; +; +; extern "C" { +; struct Trivial f1() { return Trivial(); } +; } +; +; struct NonTrivial f2() { return NonTrivial(); } +; +; extern "C" { +; void f() +; { +; int a=1; +; a += 123; +; struct Trivial t = f1(); +; a -= 123; +; struct NonTrivial n = f2(); +; a -= 12; +; } +; } + + + +; output from godbolt compiler explorer w/ msvc 19.31 (/Gr for fastcall) + +_this$ = -4 +NonTrivial::NonTrivial(void) PROC + push ebp + mov ebp, esp + push ecx + mov DWORD PTR _this$[ebp], ecx + mov eax, DWORD PTR _this$[ebp] + mov DWORD PTR [eax], 0 + mov eax, DWORD PTR _this$[ebp] + mov esp, ebp + pop ebp + ret 0 +NonTrivial::NonTrivial(void) ENDP + +$T1 = -4 +@f1@0 PROC + push ebp + mov ebp, esp + push ecx + xor eax, eax + mov DWORD PTR $T1[ebp], eax + mov eax, DWORD PTR $T1[ebp] + mov esp, ebp + pop ebp + ret 0 +@f1@0 ENDP + +___$ReturnUdt$ = -4 +NonTrivial f2(void) PROC + push ebp ; + mov ebp, esp ; + push ecx ; ptr to hidden retval space as first arg (fastcall, in ecx) + mov DWORD PTR ___$ReturnUdt$[ebp], ecx ; | + mov ecx, DWORD PTR ___$ReturnUdt$[ebp] ; | a bit pointless + call NonTrivial::NonTrivial(void) ; + mov eax, DWORD PTR ___$ReturnUdt$[ebp] ; return passed-in ptr ptr to hidden retval space in eax + mov esp, ebp ; + pop ebp ; + ret 0 ; +NonTrivial f2(void) ENDP + +_n$ = -16 +_t$ = -12 +$T1 = -8 +_a$ = -4 +@f@0 PROC + push ebp ; + mov ebp, esp ; + sub esp, 16 ; + mov DWORD PTR _a$[ebp], 1 ; a = 1 + mov eax, DWORD PTR _a$[ebp] ; | + add eax, 123 ; | a += 123 + mov DWORD PTR _a$[ebp], eax ; | + call @f1@0 ; call f1() + mov DWORD PTR $T1[ebp], eax ; retval (trivial struct <= 32bits, returned via eax) + mov ecx, DWORD PTR $T1[ebp] ; | copy of retval from stack to stack + mov DWORD PTR _t$[ebp], ecx ; / + mov edx, DWORD PTR _a$[ebp] ; \ + sub edx, 123 ; | a -= 123 + mov DWORD PTR _a$[ebp], edx ; | + lea ecx, DWORD PTR _n$[ebp] ; hidden first arg: ptr to space for (non-trivial) retval + call NonTrivial f2(void) ; call f2() + mov eax, DWORD PTR _a$[ebp] ; | + sub eax, 12 ; | a -= 12 + mov DWORD PTR _a$[ebp], eax ; | + mov esp, ebp ; + pop ebp ; + ret 0 ; +@f@0 ENDP + + + ; vim: ft=asm diff -r fe694c7677b4 -r 585dcb68f55d doc/manual/callconvs/callconv_ppc32.tex --- a/doc/manual/callconvs/callconv_ppc32.tex Thu Apr 14 21:18:02 2022 +0200 +++ b/doc/manual/callconvs/callconv_ppc32.tex Sat Apr 16 12:10:02 2022 +0200 @@ -261,7 +261,7 @@ \subsubsection{System V PPC 32-bit / Linux Standard Base version} This is in essence the same as the System V PPC 32-bit calling convention, but differs for aggregate return values: -% @@@STRUCT make this more obvious +% @@@AGGR could be more verbose \begin{itemize} \item for all aggregates, the caller allocates space, passes pointer to it to the callee as a hidden first param diff -r fe694c7677b4 -r 585dcb68f55d doc/manual/callconvs/callconv_x86.tex --- a/doc/manual/callconvs/callconv_x86.tex Thu Apr 14 21:18:02 2022 +0200 +++ b/doc/manual/callconvs/callconv_x86.tex Sat Apr 16 12:10:02 2022 +0200 @@ -188,10 +188,9 @@ \paragraph{Return values} \begin{itemize} -\item return values of pointer or integral type (\textless=\ 32 bits) are returned via the eax register +\item return values of pointer or integral type, as well as aggregates (structs, unions) \textless=\ 64 are returned via the eax and edx registers \item for {\it non-trivial} C++ aggregates, the caller allocates space, passes pointer to it to the callee as a hidden first param (meaning via ecx), and callee writes return value to this space; the ptr to the aggregate is returned in eax -\item integers and aggregates (structs, unions) \textgreater\ 32 and \textless=\ 64 bits are returned via the eax and edx registers \item return values \textgreater\ 64 bits (e.g. aggregates) are returned by the caller allocating the space and passing a pointer to the callee as a new, implicit first parameter (always via the stack, never via a register) \item floating point types are returned via the st0 register @@ -510,7 +509,7 @@ \item arguments \textgreater\ 64 bits are pushed as a sequence of dwords \item aggregates (structs, unions) are pushed as a sequence of dwords \item {\it non-trivial} C++ aggregates (as defined by the language) of any size, are passed indirectly via a pointer to a copy of the aggregate -\item stack is usually 4 byte aligned (GCC \textgreater=\ 3.x seems to use a 16byte alignement) +\item stack is usually 4 byte aligned (GCC \textgreater=\ 3.x seems to use a 16byte alignment) \item the direction flag is clear on entry and must be returned clear % mention it first, above @@@ \end{itemize}