残暑がきびしいざんしょ
こうやって年に一度は亀仙人ジョークを(以下、面倒なので略)。
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
ついでに mingw の gcc 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 してくれないあたりがボーランドだな、とか微妙にオチがついたような気分になってしまったり。
1.0.3.2 きたー
開発版のほうだけ修正されると勝手に思い込んでおりました。わはー。
構造体を戻り値にする __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; }
.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++ もたぶんこうなると思う。だれか教えてください)
次は mingw の gcc 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 の場合はこういう非互換性はなかった。