+===========+ |P/ECE研究室| +===========+ P/ECE研究記録 2019年 ==================== * Sun Jun 02 23:59:59 JST 2019 Naoyuki Sawa - 構造体引数の呼び出し規約がマニュアルの説明と違う □マニュアルに説明されている、構造体引数の呼び出し規約 P/ECE開発環境のCコンパイラは、構造体引数をスタックに積んで渡します。 「S5U1C33000C Manual (S1C33 Family Cコンパイラパッケージ) (Ver.4)」(C:\usr\PIECE\docs\datasheet\EPSON\S5U1C33000C_J.pdf) p.85『6.5.4 関数呼び出し』●構造体引数の扱い │引数が構造体データの場合、構造体のメンバーの値はスタックを介して渡されます。 │例: struct _foo { │ int a; │ short b; │ char c; │ }; │ callee(struct _foo foo, int d); │上記例ではdのみが引数渡し用レジスタ(R12)に格納され、fooはすべてスタックに格納されます。 □マニュアルに説明されていない、構造体引数の呼び出し規約 ほとんどの場合はマニュアルの説明どおりのコードが生成されるのですが、特定のケースで説明と違うコードが生成される事に気付きました。 特定のケースとは、『構造体のメンバが32ビット以内で,且つ,一要素だけである場合』です。 │例: struct _foo { │ int a; //shortでもcharでも同じ。 │ }; │ callee(struct _foo foo, int d); │上記例では、fooもdも引数渡し用レジスタ(R12,R13)に格納されます。 │例: struct _foo { │ int a[1]; //shortでもcharでも同じ。 │ }; │ callee(struct _foo foo, int d); │上記例でも、fooもdも引数渡し用レジスタ(R12,R13)に格納されます。 │例: struct _foo { │ int a[2]; //shortでもcharでも同じ。 │ }; │ callee(struct _foo foo, int d); │上記例ではdのみが引数渡し用レジスタ(R12)に格納され、fooはすべてスタックに格納されます。 │例: struct _foo { │ long long a; //a[1]でもa[2]でも同じ。 │ }; │ callee(struct _foo foo, int d); │上記例ではdのみが引数渡し用レジスタ(R12)に格納され、fooはすべてスタックに格納されます。 □検証 いろいろなパターンで検証して、上記のとおりの結果になる事を確認しました。 右端に「※」を付けたパターンが前述の『特定のケース』に相当し、「S5U1C33000C Manual」では説明されていない呼び出し規約となります。 ┏━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━┓ ┃構造体 │関数 │引数レジスタ │戻り値レジスタ ┃ ┠─────┬───┬───────────────┼──────┬───┬─────────────────────────┼────────────────┬───────────────┬───────────────┬──┬───────┼────────────────┬──┨ ┃要素の型 │要素数│定義例 │引数 │戻り値│定義例 │%r12 │%r13 │%r14 │%r15│[%sp+4〜] │%r10 │%r11┃ ┣━━━━━┿━━━┿━━━━━━━━━━━━━━━┿━━━━━━┿━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━┿━━┿━━━━━━━┿━━━━━━━━━━━━━━━━┿━━┫ ┃char │1 │struct _8_1 { int8_t a[1]; } │構造体 │無し │void callee_8_1_V_(struct _8_1 s) │最上位8ビットに構造体引数の値 │ │ │ │ │ │ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │void callee_8_1_VA(struct _8_1 s, int t) │最上位8ビットに構造体引数の値 │整数引数の値 │ │ │ │ │ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │void callee_8_1_VB(int t, struct _8_1 s) │整数引数の値 │最上位8ビットに構造体引数の値 │ │ │ │ │ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体 │構造体│struct _8_1 callee_8_1_S_(struct _8_1 s) │戻り値を格納する構造体のポインタ│最上位8ビットに構造体引数の値 │ │ │ │戻り値を格納した構造体のポインタ│ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │struct _8_1 callee_8_1_SA(struct _8_1 s, int t) │戻り値を格納する構造体のポインタ│最上位8ビットに構造体引数の値 │整数引数の値 │ │ │戻り値を格納した構造体のポインタ│ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │struct _8_1 callee_8_1_SB(int t, struct _8_1 s) │戻り値を格納する構造体のポインタ│整数引数の値 │最上位8ビットに構造体引数の値 │ │ │戻り値を格納した構造体のポインタ│ ┃※ ┃ ├───┼───────────────┼──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │2 │struct _8_2 { int8_t a[2]; } │構造体 │無し │void callee_8_2_V_(struct _8_2 s) │ │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │void callee_8_2_VA(struct _8_2 s, int t) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │void callee_8_2_VB(int t, struct _8_2 s) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体 │構造体│struct _8_2 callee_8_2_S_(struct _8_2 s) │戻り値を格納する構造体のポインタ│ │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │struct _8_2 callee_8_2_SA(struct _8_2 s, int t) │戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │struct _8_2 callee_8_2_SB(int t, struct _8_2 s) │戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┠─────┼───┼───────────────┼──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃short │1 │struct _16_1 { int16_t a[1]; }│構造体 │無し │void callee_16_1_V_(struct _16_1 s) │上位16ビットに構造体引数の値 │ │ │ │ │ │ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │void callee_16_1_VA(struct _16_1 s, int t) │上位16ビットに構造体引数の値 │整数引数の値 │ │ │ │ │ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │void callee_16_1_VB(int t, struct _16_1 s) │整数引数の値 │上位16ビットに構造体引数の値 │ │ │ │ │ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体 │構造体│struct _16_1 callee_16_1_S_(struct _16_1 s) │戻り値を格納する構造体のポインタ│上位16ビットに構造体引数の値 │ │ │ │戻り値を格納した構造体のポインタ│ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │struct _16_1 callee_16_1_SA(struct _16_1 s, int t)│戻り値を格納する構造体のポインタ│上位16ビットに構造体引数の値 │整数引数の値 │ │ │戻り値を格納した構造体のポインタ│ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │struct _16_1 callee_16_1_SB(int t, struct _16_1 s)│戻り値を格納する構造体のポインタ│整数引数の値 │上位16ビットに構造体引数の値 │ │ │戻り値を格納した構造体のポインタ│ ┃※ ┃ ├───┼───────────────┼──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │2 │struct _16_2 { int16_t a[2]; }│構造体 │無し │void callee_16_2_V_(struct _16_2 s) │ │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │void callee_16_2_VA(struct _16_2 s, int t) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │void callee_16_2_VB(int t, struct _16_2 s) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体 │構造体│struct _16_2 callee_16_2_S_(struct _16_2 s) │戻り値を格納する構造体のポインタ│ │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │struct _16_2 callee_16_2_SA(struct _16_2 s, int t)│戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │struct _16_2 callee_16_2_SB(int t, struct _16_2 s)│戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┠─────┼───┼───────────────┼──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃int │1 │struct _32_1 { int32_t a[1]; }│構造体 │無し │void callee_32_1_V_(struct _32_1 s) │構造体引数の値 │ │ │ │ │ │ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │void callee_32_1_VA(struct _32_1 s, int t) │構造体引数の値 │整数引数の値 │ │ │ │ │ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │void callee_32_1_VB(int t, struct _32_1 s) │整数引数の値 │構造体引数の値 │ │ │ │ │ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体 │構造体│struct _32_1 callee_32_1_S_(struct _32_1 s) │戻り値を格納する構造体のポインタ│構造体引数の値 │ │ │ │戻り値を格納した構造体のポインタ│ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │struct _32_1 callee_32_1_SA(struct _32_1 s, int t)│戻り値を格納する構造体のポインタ│構造体引数の値 │整数引数の値 │ │ │戻り値を格納した構造体のポインタ│ ┃※ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │struct _32_1 callee_32_1_SB(int t, struct _32_1 s)│戻り値を格納する構造体のポインタ│整数引数の値 │構造体引数の値 │ │ │戻り値を格納した構造体のポインタ│ ┃※ ┃ ├───┼───────────────┼──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │2 │struct _32_2 { int32_t a[2]; }│構造体 │無し │void callee_32_2_V_(struct _32_2 s) │ │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │void callee_32_2_VA(struct _32_2 s, int t) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │void callee_32_2_VB(int t, struct _32_2 s) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体 │構造体│struct _32_2 callee_32_2_S_(struct _32_2 s) │戻り値を格納する構造体のポインタ│ │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │struct _32_2 callee_32_2_SA(struct _32_2 s, int t)│戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │struct _32_2 callee_32_2_SB(int t, struct _32_2 s)│戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┠─────┼───┼───────────────┼──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃long long │1 │struct _64_1 { int64_t a[1]; }│構造体 │無し │void callee_64_1_V_(struct _64_1 s) │ │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │void callee_64_1_VA(struct _64_1 s, int t) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │void callee_64_1_VB(int t, struct _64_1 s) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体 │構造体│struct _64_1 callee_64_1_S_(struct _64_1 s) │戻り値を格納する構造体のポインタ│ │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │struct _64_1 callee_64_1_SA(struct _64_1 s, int t)│戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │struct _64_1 callee_64_1_SB(int t, struct _64_1 s)│戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ ├───┼───────────────┼──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │2 │struct _64_2 { int64_t a[2]; }│構造体 │無し │void callee_64_2_V_(struct _64_2 s) │ │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │void callee_64_2_VA(struct _64_2 s, int t) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │void callee_64_2_VB(int t, struct _64_2 s) │整数引数の値 │ │ │ │構造体引数の値│ │ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体 │構造体│struct _64_2 callee_64_2_S_(struct _64_2 s) │戻り値を格納する構造体のポインタ│ │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │構造体,整数 │ │struct _64_2 callee_64_2_SA(struct _64_2 s, int t)│戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┃ │ │ ├──────┼───┼─────────────────────────┼────────────────┼───────────────┼───────────────┼──┼───────┼────────────────┼──┨ ┃ │ │ │整数,構造体 │ │struct _64_2 callee_64_2_SB(int t, struct _64_2 s)│戻り値を格納する構造体のポインタ│整数引数の値 │ │ │構造体引数の値│戻り値を格納した構造体のポインタ│ ┃ ┗━━━━━┷━━━┷━━━━━━━━━━━━━━━┷━━━━━━┷━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┷━━┷━━━━━━━┷━━━━━━━━━━━━━━━━┷━━┛ 上の表のエクセル版: http://www.piece-me.org/piece-lab/struct/struct-20190602.xlsx 検証プログラム一式: http://www.piece-me.org/piece-lab/struct/struct-20190602.zip □構造体引数がレジスタに格納される方式 ちなみに、『特定のケース』において構造体引数がレジスタに格納される方式も、少々変則的です。 8ビットや16ビットの構造体の場合、何故か、上位ビット側に詰めて格納されるのです。 P/ECEのCPU S1C33はリトルエンディアンで、こんな格納の仕方をしても利点は無いように思います。 何故こんなコードが生成されているのか謎なのですが、もしかすると、元になったCコンパイラ(MIPSのビッグエンディアンモード?)の仕様がそのまま残ってしまっているのかも知れません。 実際の動作としては、ビットシフト命令が増えて少し性能低下しますが、結果には影響ありません。 □構造体の戻り値の呼び出し規約は、マニュアルどおり 構造体引数の呼び出し規約が、『特定のケース』において、「S5U1C33000C Manual」の説明とは異なるコードが生成される事を説明しました。 一方、構造体の戻り値の呼び出し規約については、例外は無く、「S5U1C33000C Manual」の説明どおりです。 たとえ、『構造体のメンバが32ビット以内で,且つ,一要素だけである場合』であっても、常に、説明どおりのコードが生成されます。 構造体の戻り値の呼び出し規約について、「S5U1C33000C Manual」の該当箇所を引用しておきます。 「S5U1C33000C Manual (S1C33 Family Cコンパイラパッケージ) (Ver.4)」(C:\usr\PIECE\docs\datasheet\EPSON\S5U1C33000C_J.pdf) p.85『6.5.4 関数呼び出し』●構造体を返す関数への引数渡し │構造体データを返す関数を呼び出す場合、結果を格納する構造体のアドレスが第1引数としてR12レジスタに格納され、関数に渡されます。 │したがって、ソース上で記述されている引数は1つずつ繰り下げられます。 │〜略〜 │呼び出された関数は第1引数で渡されたポインタを戻り値として返します。 □まとめ 呼び出し側関数も,呼び出される側の関数も、全部C言語で書く場合は、今回説明した点について、特に意識する必要はありません。 呼び出し側関数と,呼び出される側の関数の呼び出し規約が一致してさえいれば、問題は無いからです。 しかし、片方をC言語,片方をアセンブラで書く場合は、『特定のケース』に注意して、アセンブラ版の関数を書く必要があります。