OpenWatcom 1.7 出てたヨ。

Win32 版 C/C++ に関する限り、RC2 からの変更はまったくないと思われます。(インストーラの exe がバイナリ完全一致)。

変更点は例によって訳してみた(なんかだいぶ適当にやってしまった気が…)。個人的には以下のバグ修正がけっこう驚いた。

  • cdecl 呼び出し規約の互換性に問題があった。
  • Win32 の stat がバグってた(当方の言語処理能力の不具合により原文の説明がうまく訳せなかったので、適当に意訳して説明を補填してしまった)

ちなみに cdecl の不具合。たとえば

double __cdecl test(void)
{
    return 1;
}

これをコンパイル&逆アセ(wcl386 -c -s test.c && wdis test.obj -a)すると、以前の OpenWatcom ではこんな感じ(OpenWatcom 1.3)。

.387
.386p
.model flat
		PUBLIC	_test
DGROUP		GROUP	CONST,CONST2,_DATA
_TEXT		SEGMENT	BYTE PUBLIC USE32 'CODE'
		ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
_test:
    mov         eax,offset FLAT:L$1
    xor         edx,edx
    mov         dword ptr FLAT:L$1,edx
    mov         dword ptr FLAT:L$2,3ff00000H
    ret         
_TEXT		ENDS
CONST		SEGMENT	DWORD PUBLIC USE32 'DATA'
L$1:
    DB	0, 0, 0, 0
L$2:
    DB	0, 0, 0, 0

CONST		ENDS
CONST2		SEGMENT	DWORD PUBLIC USE32 'DATA'
CONST2		ENDS
_DATA		SEGMENT	DWORD PUBLIC USE32 'DATA'
_DATA		ENDS
		END

で、1.7 だとこんな感じ。

.387
.386p
		PUBLIC	_test
		EXTRN	__8087:BYTE
		EXTRN	__init_387_emulator:BYTE
DGROUP		GROUP	CONST,CONST2,_DATA
_TEXT		SEGMENT	BYTE PUBLIC USE32 'CODE'
		ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
_test:
    fld1        
    ret         
_TEXT		ENDS
CONST		SEGMENT	DWORD PUBLIC USE32 'DATA'
CONST		ENDS
CONST2		SEGMENT	DWORD PUBLIC USE32 'DATA'
CONST2		ENDS
_DATA		SEGMENT	DWORD PUBLIC USE32 'DATA'
_DATA		ENDS
		END

ついでに mingwgcc 3.3.3 (gcc -S -fomit-frame-pointer test.c)

	.file	"test.c"
	.text
.globl _test
	.def	_test;	.scl	2;	.type	32;	.endef
_test:
	fld1
	ret

ついでなんで Borland C++ 5.5.1 でもやってみたヨ(bcc32 -S test.c デバッグ情報とコメントは長いので削除した)

	.386p
	model flat
_TEXT	segment dword public use32 'CODE'
_TEXT	ends
_DATA	segment dword public use32 'DATA'
_DATA	ends
_BSS	segment dword public use32 'BSS'
_BSS	ends
DGROUP	group	_BSS,_DATA
_TEXT	segment dword public use32 'CODE'
_test	proc	near
	push      ebp
	mov       ebp,esp
@1:
	fld       qword ptr [@2]
@4:
@3:
	pop       ebp
	ret 
	align 4        
@2:
	db        0,0,0,0,0,0,240,63
_test	endp
_TEXT	ends
	public	_test
	end

fld1 してくれないあたりがボーランドだな、とか微妙にオチがついたような気分になってしまったり。

構造体を戻り値にする __cdecl 関数はコンパイラ間の互換性がない!?

んでまあ、OpenWatcom で実数を返す __cdecl 関数についてすこし確認してみたらちょっと気になったので、手持ちのコンパイラで構造体を返す関数のコード生成をみてみたら……意外と互換性に難がありそうに思えたのでした。

とりあえず C のソース (fcdecl.c)

struct skelton {
    int dummy0;
    int dummy1;
    int dummy2;
    int p;
};

struct skelton __cdecl test(void)
{
    struct skelton tmp;
    tmp.p = 1;
    return tmp;
}

で、Borland C++ 5.5.1

	.386p
	endif
	model flat
	endif
_TEXT	segment dword public use32 'CODE'
_TEXT	ends
_DATA	segment dword public use32 'DATA'
_DATA	ends
_BSS	segment dword public use32 'BSS'
_BSS	ends
DGROUP	group	_BSS,_DATA
_TEXT	segment dword public use32 'CODE'
_test	proc	near
	push      ebp
	mov       ebp,esp
	add       esp,-16
	push      esi
	push      edi
	mov       eax,dword ptr [ebp+8]
@1:
	mov       dword ptr [ebp-4],1
	lea       esi,dword ptr [ebp-16]
	mov       edi,eax
	mov       ecx,4
	rep   movsd
@3:
@2:
	pop       edi
	pop       esi
	mov       esp,ebp
	pop       ebp
	ret 
_test	endp
_TEXT	ends
	public	_test
	end

戻り値用の構造体は呼び出し側で確保しておいて、そのポインタをスタックに積んでから関数をコール。たぶんこれが「期待される挙動」にいちばん近いと思われます。(Visual C++ もたぶんこうなると思う。だれか教えてください)

次は mingwgcc 3.3.3 (-fomit-frame-pointer)。

	.file	"fcdecl.c"
	.text
.globl _test
	.def	_test;	.scl	2;	.type	32;	.endef
_test:
	subl	$28, %esp
	movl	32(%esp), %edx
	movl	$1, 12(%esp)
	movl	(%esp), %eax
	movl	%eax, (%edx)
	movl	4(%esp), %eax
	movl	%eax, 4(%edx)
	movl	8(%esp), %eax
	movl	%eax, 8(%edx)
	movl	12(%esp), %eax
	movl	%eax, 12(%edx)
	movl	%edx, %eax
	addl	$28, %esp
	ret	$4

え゛ー。
ちょ、これ、__stdcall とまったく同じ…(__cdecl を __stdcall にすると、パブリックシンボル名以外まったく同じコードが生成される)。

そして OpenWatcom は…これはこれで問題が。

.387
.386p
.model flat
		PUBLIC	_test
DGROUP		GROUP	CONST,CONST2,_DATA
_TEXT		SEGMENT	BYTE PUBLIC USE32 'CODE'
		ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
_test:
    push        esi 
    push        edi 
    sub         esp,10H 
    mov         dword ptr 0cH[esp],1 
    mov         eax,offset FLAT:L$1 
    mov         edi,offset FLAT:L$1 
    mov         esi,esp 
    movsd       
    movsd       
    movsd       
    movsd       
    add         esp,10H 
    pop         edi 
    pop         esi 
    ret         
_TEXT		ENDS
CONST		SEGMENT	DWORD PUBLIC USE32 'DATA'
L$1:
    DB	0, 0, 0, 0, 0, 0, 0, 0
    DB	0, 0, 0, 0, 0, 0, 0, 0

CONST		ENDS
CONST2		SEGMENT	DWORD PUBLIC USE32 'DATA'
CONST2		ENDS
_DATA		SEGMENT	DWORD PUBLIC USE32 'DATA'
_DATA		ENDS
		END

static の隠し構造体を関数側で確保し、リターン時にそのポインタを戻り値として渡す。
静的変数使っちゃってる時点でもう構造的にスレッドセーフじゃない。テンプレートで変数もクラスも無節操に使えちゃう C++ で __cdecl 関数を使ったら…ガクブル。

というわけで予想以上にバラバラでした(構造体のサイズが小さい場合は内容すべてをレジスタにぶっこんだりするかもしれないが、そのへんまで調べる気が失せました)。ちなみに __stdcall の場合はこういう非互換性はなかった。