#navi_header|C言語系| ''thiscall'' 呼び出し規約: - C++でクラスのメンバ関数(インスタンスメソッド)で使われる。 - 引数はスタック上にPUSHする。(主に右から左へPUSH) - ''スタックのクリーンアップは、コンパイラや可変長引数の有無により異なる。'' -- 関数(callee)側でスタックをクリーンアップする場合は、アセンブラレベルでは "RET imm16" 命令を使い、戻る時にスタックポインタを"imm16"バイト分だけ戻す。つまり、"imm16"バイト分だけSPが増える(x86ではスタックはアドレスの小さい方へ進んでいくので、「戻る」=SPのアドレス値は大きくなる)。 -- 関数を呼ぶ(caller)側でスタックをクリーンアップする場合は、アセンブラレベルではCALL命令の直後(=関数からRETした直後)に、スタックポインタ(SP)にPUSHした引数のバイト分だけ即値を加算 (ADD)する。(x86ではスタックはアドレスの小さい方へ進んでいくので、「スタックのクリーンアップ」=「戻る」=SPのアドレス値を大きくする)。 - thiscall呼び出し規約はC++専用のため、C言語での他の呼び出し規約における装飾名のルールは使われず、C++でのmanglingがそのまま適用される。 #more|| ---- #outline|| ---- * サンプルコード ** foo.cpp (メンバ関数の実装) #code|c|> #include "foo.hpp" foo::foo(int x) { this->x = x; } int foo::bar(int a, int b, int c, int d, int e) { return a + b + c + d + e + this->x; } int foo::baz(int argn, ...) { va_list ap; int r = this->x; va_start(ap, argn); for (int i = 0; i < argn; i++, va_arg(ap, int)) { r += *(int*)ap; } va_end(ap); return r; } ||< ** foo.hpp (クラス定義ヘッダーファイル) #code|c|> #include #ifndef FOO_HPP #define FOO_HPP class foo { protected: int x; public: foo(int x); int bar(int a, int b, int c, int d, int e); int baz(int argn, ...); }; #endif /* FOO_HPP */ ||< ** main.cpp (main()関数定義) #code|c|> #include #include "foo.hpp" using namespace std; int main(int argc, char *argv[]) { foo *f; f = new foo(10); int r = f->bar(1, 2, 3, 4, 5); cout << "foo::bar() = " << r << endl; r = f->baz(3, 10, 20, 30); cout << "foo::baz() = " << r << endl; return 0; } ||< * VC++2008 Express Edtion : 可変長引数を使わない時 : #block||> - 引数は右から左へスタックにPUSHされる。 - ''"this"ポインタはECX経由で渡される。'' - stdcallと同様、 ''関数(callee)側で'' スタッククリーンアップする。 ||< : 可変長引数を使う時 : #block||> - 引数は右から左へスタックにPUSHされる。 - ''"this"ポインタは最後にスタックにPUSHされる。'' - cdeclと同様、 ''関数を呼ぶ(caller)側で'' スタッククリーンアップする。 ||< コンパイル&リンク&実行 > cl /c /Od /FAs foo.cpp > cl /c /Od /FAs /EHsc main.cpp > cl main.obj foo.obj > main.exe foo::bar() = 25 foo::baz() = 70 ** foo.cpp のアセンブラ出力(foo.asm)(抜粋) #pre||> (...) PUBLIC ??0foo@@QAE@H@Z ; foo::foo ; Function compile flags: /Odtp ; File c:\in_vitro\c\cc_thiscall\foo.cpp _TEXT SEGMENT _this$ = -4 ; size = 4 _x$ = 8 ; size = 4 ??0foo@@QAE@H@Z PROC ; foo::foo ; _this$ = ecx ; 3 : foo::foo(int x) { this->x = x; } push ebp mov ebp, esp push ecx mov DWORD PTR _this$[ebp], ecx mov eax, DWORD PTR _this$[ebp] mov ecx, DWORD PTR _x$[ebp] mov DWORD PTR [eax], ecx mov eax, DWORD PTR _this$[ebp] mov esp, ebp pop ebp ret 4 ??0foo@@QAE@H@Z ENDP ; foo::foo _TEXT ENDS PUBLIC ?bar@foo@@QAEHHHHHH@Z ; foo::bar ; Function compile flags: /Odtp _TEXT SEGMENT _this$ = -4 ; size = 4 _a$ = 8 ; size = 4 _b$ = 12 ; size = 4 _c$ = 16 ; size = 4 _d$ = 20 ; size = 4 _e$ = 24 ; size = 4 ?bar@foo@@QAEHHHHHH@Z PROC ; foo::bar ; _this$ = ecx ; 5 : int foo::bar(int a, int b, int c, int d, int e) { push ebp mov ebp, esp push ecx mov DWORD PTR _this$[ebp], ecx ; 6 : return a + b + c + d + e + this->x; mov eax, DWORD PTR _a$[ebp] add eax, DWORD PTR _b$[ebp] add eax, DWORD PTR _c$[ebp] add eax, DWORD PTR _d$[ebp] add eax, DWORD PTR _e$[ebp] mov ecx, DWORD PTR _this$[ebp] add eax, DWORD PTR [ecx] ; 7 : } mov esp, ebp pop ebp ret 20 ; 00000014H ?bar@foo@@QAEHHHHHH@Z ENDP ; foo::bar _TEXT ENDS PUBLIC ?baz@foo@@QAAHHZZ ; foo::baz ; Function compile flags: /Odtp _TEXT SEGMENT _i$2881 = -12 ; size = 4 _ap$ = -8 ; size = 4 _r$ = -4 ; size = 4 _this$ = 8 ; size = 4 _argn$ = 12 ; size = 4 ?baz@foo@@QAAHHZZ PROC ; foo::baz ; 8 : int foo::baz(int argn, ...) { push ebp mov ebp, esp sub esp, 12 ; 0000000cH ; 9 : va_list ap; ; 10 : int r = this->x; mov eax, DWORD PTR _this$[ebp] mov ecx, DWORD PTR [eax] mov DWORD PTR _r$[ebp], ecx ; 11 : va_start(ap, argn); lea edx, DWORD PTR _argn$[ebp+4] mov DWORD PTR _ap$[ebp], edx ; 12 : for (int i = 0; i < argn; i++, va_arg(ap, int)) { mov DWORD PTR _i$2881[ebp], 0 jmp SHORT $LN3@baz $LN2@baz: mov eax, DWORD PTR _i$2881[ebp] add eax, 1 mov DWORD PTR _i$2881[ebp], eax mov ecx, DWORD PTR _ap$[ebp] add ecx, 4 mov DWORD PTR _ap$[ebp], ecx $LN3@baz: mov edx, DWORD PTR _i$2881[ebp] cmp edx, DWORD PTR _argn$[ebp] jge SHORT $LN1@baz ; 13 : r += *(int*)ap; mov eax, DWORD PTR _ap$[ebp] mov ecx, DWORD PTR _r$[ebp] add ecx, DWORD PTR [eax] mov DWORD PTR _r$[ebp], ecx ; 14 : } jmp SHORT $LN2@baz $LN1@baz: ; 15 : va_end(ap); mov DWORD PTR _ap$[ebp], 0 ; 16 : return r; mov eax, DWORD PTR _r$[ebp] ; 17 : } mov esp, ebp pop ebp ret 0 ?baz@foo@@QAAHHZZ ENDP ; foo::baz _TEXT ENDS (...) ||< ** main.cpp のアセンブラ出力(main.asm)(抜粋) #pre||> ; 11 : int r = f->bar(1, 2, 3, 4, 5); push 5 push 4 push 3 push 2 push 1 mov ecx, DWORD PTR _f$[ebp] call ?bar@foo@@QAEHHHHHH@Z ; foo::bar mov DWORD PTR _r$[ebp], eax ||< #pre||> ; 15 : r = f->baz(3, 10, 20, 30); push 30 ; 0000001eH push 20 ; 00000014H push 10 ; 0000000aH push 3 mov eax, DWORD PTR _f$[ebp] push eax call ?baz@foo@@QAAHHZZ ; foo::baz add esp, 20 ; 00000014H mov DWORD PTR _r$[ebp], eax ||< * Borland C++ Compiler * MinGW/MSYS * OpenWatcom Compiler(16bit) * OpenWatcom Compiler(32bit) * Turbo C++ 4.0J * GCC4.1.2 + binutils-2.7.50.0.6 (CentOS5.2) #navi_footer|C言語系|