Adaと64bit呼び出し規約とlldiv

lldivを呼ぶ関数を書いてたらちょっと驚きました。
こういうの。

procedure call_lldiv (x, y : long_long_integer; q, r : out long_long_integer) is
   type lldiv_t is record
      q, r : long_long_integer;
   end record;
   pragma Convention (C, lldiv_t);
   function lldiv (x, y : long_long_integer) return lldiv_t;
   pragma Import (C, lldiv, "lldiv");
   result : lldiv_t;
begin
   result := lldiv (x, y);
   q := result.q;
   r := result.r;
end call_lldiv;

最適化してコンパイルする。

gcc -S -O3 call_lldiv.adb

すると……

	.text
	.align 4,0x90
	.globl __ada_call_lldiv
__ada_call_lldiv:
LFB1:
	subq	$40, %rsp
LCFI0:
	call	_lldiv
	addq	$40, %rsp
LCFI1:
	ret

えっ。

GNATフロントエンドのスカラー値のoutパラメータの扱いは参照渡しではなくて所謂multi-value returnで、%rax、%rdxで返すっぽいです。で、64ビット呼び出し規約では小さな構造体もレジスタ返しってことになっているらしくて、lldivも値を%rax、%rdxで返すっぽくて、引数並びも一致してるから何もせずにcallして何もせずにretすることになったようです。

ですもんで-m32ですとこうなる。

	.text
	.align 4,0x90
	.globl __ada_call_lldiv
__ada_call_lldiv:
LFB1:
	pushl	%esi
LCFI0:
	subl	$56, %esp
LCFI1:
	movl	76(%esp), %eax
	leal	32(%esp), %ecx
	movl	80(%esp), %edx
	movl	%ecx, (%esp)
	movl	64(%esp), %esi
	movl	%eax, 12(%esp)
	movl	68(%esp), %eax
	movl	%edx, 16(%esp)
	movl	72(%esp), %edx
	movl	%eax, 4(%esp)
	movl	%edx, 8(%esp)
	call	_lldiv
LCFI2:
	subl	$4, %esp
LCFI3:
	movl	32(%esp), %eax
	movl	36(%esp), %edx
	movl	%eax, (%esi)
	movl	40(%esp), %eax
	movl	%edx, 4(%esi)
	movl	44(%esp), %edx
	movl	%eax, 8(%esi)
	movl	%edx, 12(%esi)
	movl	%esi, %eax
	addl	$56, %esp
LCFI4:
	popl	%esi
LCFI5:
	ret	$4

で、これを見てしまうと、どうせならスタックを$40ほど取って戻してしてるのも最適化して欲しくて、そうすれば-foptimize-sibling-callsも働いてjmp一個にできるはずなんですけれど、このスタックが強敵で色々オプションを試しても消えてくれません。なんでー?