LoginSignup
3
1

More than 3 years have passed since last update.

MASM, Visual C++におけるソフトウェア規約

Last updated at Posted at 2020-06-28

Microsoft Macro Assembler(以降、MASMと略)Version 6以降でアプリケーションを開発する際のメモ書きです。
この記事では、Microsoftのソフトウェア規約について記します。

  1. 16bit(Intel 8086、及びリアルモード)におけるソフトウェア規約
  2. 32bit (プロテクトモード)におけるソフトウェア規約
  3. 64bit (x86-64)におけるソフトウェア規約
  4. C/C++とMASMの型の関係
  5. C++における名前修飾
  6. Tips: Visual C++における最適化について

16bit(Intel 8086、及びリアルモード)におけるソフトウェア規約

関数呼出規約

MASM (langtype) C(関数呼出規約) レジスタ スタック スタックの破棄 名前修飾
C __cdecl 右からPUSH 呼出側 _Label
PASCAL __pascal 左からPUSH 関数側 Label
非対応 __fastcall AX, DX, BX 左からPUSH 関数側 Label

※16bit版のWindows APIの呼出規約は、PASCALです。

レジスタの用途

  • 呼出元で保持  :AX, BX, CX, DX, ES, ST(0)~ST(7)
  • 関数内で保持  :SI, DI, BP, DS
  • 関数からの戻り値:AX, DX, ST(0)

戻り値について

レジスタ
char AL
short int AX
int AX
long int DX:AX
float DX:AX
double スタック経由
long double ST(0)
near pointer AX
far pointer DX:AX
1Byteの構造体または共用体 AL
2Byteの構造体または共用体 AX
4Byteの構造体または共用体 DX:AX
その他の構造体または共用体 スタック経由

32bit(プロテクトモード)におけるソフトウェア規約

関数呼出規約

MASM (langtype) C(関数呼出規約) レジスタ スタック スタックの破棄 名前修飾
C __cdecl 右からPUSH 呼出側 _Label
STDCALL __stdcall 右からPUSH 関数側 _Label@n
非対応 __fastcall ECX, EDX 右からPUSH 関数側 @Label@n
非対応 __thiscall (引数が固定長) ECX (thisとして) 右からPUSH 関数側 _Label@n
非対応 __thiscall (引数が可変長) 右からPUSH (thisは最後にPUSH ) 関数側 _Label
非対応 __vectorcall (VC++2013~) ECX, EDX, XMM0XMM5 右からPUSH 関数側 Label@@n
  • 名前修飾における関数名の最後の@nについて、nは引数の合計バイト数を10進数で記述する。尚、引数のサイズは実際のサイズであり、例えば32bitモードの時のcharは、4Byte(32bit)の幅を持つ。

※32bit版のWindows APIの呼出規約は、STDCALLです。

廃止された関数呼出規約(MASMでは使える)

MASM (langtype) C(関数呼出規約) レジスタ スタック スタックの破棄 名前修飾
SYSCALL __syscall 右からPUSH 呼出側 Label
FORTRAN __fortran 左からPUSH 関数側 Label
PASCAL __pascal 左からPUSH 関数側 Label

※MASMで非対応のlangtype(__fastcall, __thiscall等)は、別の関数呼出規約で書く必要がある。
 例えば__fastcallは、Visual C++では廃止されたSYSCALLを使い、関数名の先頭に@、後ろに@と引数のサイズを10進数で書き、ret命令のパラメータには、スタックに詰まれた引数の数を指定して関数から戻るように記述する(参考例:PROC~ENDPディレクティブ)。

レジスタの用途

  • 呼出元で保持  :EAX, ECX, EDX, ST(0)~ST(7)
  • 関数内で保持  :EBX, ESI, EDI, EBP
  • 関数からの戻り値:EAX, EDX, ST(0)

戻り値について

レジスタ
bool AL
char AL
short int AX
int EAX
long int EAX
long long int EDX:EAX
float ST(0)
double ST(0)
long double ST(0)
pointer EAX
1Byteの構造体または共用体 AL
2Byteの構造体または共用体 AX
4Byteの構造体または共用体 EAX
8Byteの構造体または共用体 EDX:EAX
その他の構造体または共用体 スタック経由

ストリーミングSIMD 拡張命令(Streaming SIMD Extensions)のソフトウェア規則

既存の呼出規約の拡張

  • 最初の 3 つの__m128パラメータは、レジスタxmm0, xmm1, およびxmm2内で渡される。
  • その他の__m128パラメータは、通常どおりスタック上で渡される。
  • __m128の戻り値は、レジスタxmm0内で渡される。
  • レジスタxmm0xmm7は呼び出し元保存である。

呼出規約__vectorcall

※Visual C++.net 2013より対応

  • 最初の 2 つのDWORD、またはこれより小さい引数は、ECX、及びEDXレジスタに渡される。
  • さらに、最初の 6 つの浮動小数点型(floatまたはdouble)及びベクター型の引数は、引数のサイズに応じたSIMDレジスタ0~5に渡される。
    • 浮動小数点型(floatまたはdouble):xmm
    • __m128型:xmm
    • __m256型:ymm
  • 残りの引数は、通常どおりスタック上で渡される。
  • __m128, float, doubleの戻り値はレジスタxmm0内、__m256の戻り値はレジスタymm0内で渡される。

64bit(x86-64)におけるソフトウェア規約

関数呼出規約

MASM (langtype) C(関数呼出し規約) レジスタ スタック スタックの破棄
非対応 Microsoft x64 RCX, RDX, R8, R9, XMM0XMM4 右からPUSH 呼出側

レジスタの用途

  • 呼出元で保持  :RAX, RCX, RDX, R8~R11, XMM0~XMM5, YMM0~YMM5
  • 関数内で保持  :RBX, RSI, RDI, RBP, R12~R15, XMM6~XMM15, YMM6~YMM15
  • 関数からの戻り値:RAX, XMM0, YMM0

戻り値について

レジスタ
bool AL
char AL
short int AX
int EAX
long int EAX
long long int RAX
float XMM0
double XMM0
__mm64 RAX
__mm128 XMM0
__mm256 YMM0
1Byteの構造体または共用体 AL
2Byteの構造体または共用体 AX
4Byteの構造体または共用体 EAX
8Byteの構造体または共用体 RAX
その他の構造体または共用体 スタック経由

C/C++とMASMの型の関係

16bit 32bit 64bit
bool (S)BYTE (S)BYTE
char (S)BYTE (S)BYTE (S)BYTE
wchar_t (S)WORD (S)WORD
short int (S)WORD (S)WORD (S)WORD
int (S)WORD (S)DWORD (S)DWORD
long int (S)DWORD (S)DWORD (S)DWORD
long long int (S)QWORD (S)QWORD
enum (S)WORD (S)DWORD (S)DWORD
float REAL4 REAL4 REAL4
double REAL8 REAL8 REAL8
long double REAL10 REAL8 REAL8
pointer PTR (2Byte) PTR (4Byte) PTR (8Byte)
near pointer NEAR PTR (2Byte)
far pointer FAR PTR (4Byte)

C++における名前修飾

C++においては、extern "C"を宣言しない限り、独自の名前修飾が行われる。
従って、C言語やアセンブリ言語で書かれた関数をC++から呼ぶ場合は、extern宣言に"C"を追加する必要がある。

example1.cpp
extern "C" void func();

また、C++で書かれた関数を、C言語やアセンブリ言語から呼ぶ場合は、従来の名前修飾修飾にする為、関数にextern "C"と宣言する。

example2.cpp
extern "C" __cdecl void func(){
    //statement;
}

Tips: Visual C++における最適化について

以下の条件時について、以下の条件を満たす場合、

  1. リンク時のコード生成を有効(/LTCG)をした場合、且つ、
  2. ある関数において、その関数のある".obj"ファイル及び、その関数を呼ぶコードがある".obj"ファイルについて、コンパイル時に、リンク時のコード生成を有効(/GT)をした場合

Visual C++のコンパイラは、最適化の為、関数規約を無視し、関数の入出力について以下の最適化を行う。

  • レジスターに引数を渡す(__fastcallとか関係無しに)。
  • 配置の為に、引数の順番を並べ替える。
  • 未使用の引数を削除する。

但し、一つでも、リンク時のコード生成が有効(/GT)でない".obj"ファイルがあった場合は、関数呼出規約が守られる。
例えば、アセンブリ言語で書かれたソースからアセンブルされた"
.obj"ファイル等。
つまり、アセンブリ言語で書くよりも、コンパイラとリンカに最適化を任せてしまった方が、 多くの場合は、アセンブリ言語で関数を記述するよりも実行速度が早くなる。

参考資料

  • Microsoft®: MASM Version 6.1 Programmer’s Guide
  • Intel Corporation: AP-589 ストリーミングSIMD 拡張命令のソフトウェア規則 バージョン2.1 1999年1月(注文番号: 243873J-002)
  • Microsoft Docs: 呼び出し規則
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1