tosyama
@tosyama

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

x86-64の構造体の値渡しのABIについて

Linux(Ubuntu)x86-64の構造体の値渡しのABI

趣味でx86-64のコンパイラ作っているのですが、Linux(Ubuntu)での構造体のABIがわかりません。それが記述されている資料もどこにあるのか、ググっても探せませんでした。
ご存じの方いましたら、資料の場所でもいいので教えていただけないでしょうか。
知りたいことは、構造体の値渡し(引数)および戻り値ついてですが、いろいろわからないことだらけです。

  • メンバー渡しなのか? 配列がメンバーの場合は?
  • 構造体のサイズによりレジスタ渡しだったり、スタック渡しだったりするのか?
  • 浮動小数点はどうなる?
  • スタック渡しの場合、呼び出し側でメモリ確保するのか?どこに?

Cで書いてアセンブリ見ろという話もあるかもしれませんが、正式な方法が定義されていれば知りたいです。

float を3つもつ構造体、例えば下記のコードの構造体を関数の戻り値で返した場合、アセンブリを見るとxmm0に最初の2つのx, yが格納。xmm1にzが格納されているように思いましたが、合っているでしょうか。下記のようなイメージです。

struct Vec3f {
 float x, y, z;
};

Vec3f createVec() { 
 struct Vec3f t;
 t.x = t.y = t.z = 0.0f;
 return t;
}

このような構造体の受け渡しに詳しい方がいらっしゃいましたら教えてください。
資料の在りかでもよいです。

よろしくお願いします。

0

2Answer

コンパイラの内部に関してそこまで詳しいわけではないのですが、資料を集めてみました。質問の答えに直接結びつかないものもありますが列挙します。

  • ABI Policy and Guidelines
    • libstdc++ がどの ABI に従っているかの資料です。これによると、 Itnanium ABI を利用していると書かれています。( x86_64 なのにItnaniumなのが面白いですね)
  • Itanium C++ ABI
    • Itanium ABI の資料です。
    • こちらを見ますと、 "In general, this document is meant to serve as a generic specification which can be used by C++ implementations on a variety of platforms. It does this by layering on top of a platform's base C ABI." とあるので、単純な関数呼び出し規約などはこの資料では定義されていないようです。
  • LSB Specifications
    • Linux Standard Base の資料です。 ABI 以外にも色々ありますが、 C ABI に関してもこちらから辿ることができます。
    • AMD64 の資料をたどって関数呼び出しの項目を見てみると、 "System V Application Binary Interface" が参照されています。まさかの System V. ABI はアーキテクチャの歴史が詰まっている
  • System V Application Binary Interface
    AMD64 Architecture Processor Supplement Draft Version 0.95
    • という訳で本命の System V ABI の資料です。
    • "3.2 Function Calling Sequence" のあたりを読むと良さそうですね。 "3.2.3 Parameter Passing" の部分でまさにレジスタの話などが書かれています。
    • それにしても、 Draft Version なんですね……
5Like

Comments

  1. @tosyama

    Questioner

    貴重なリンクありがとうございます!
    実はどこで拾ったのか覚えてませんが、System V ABIの資料は Draft Version 0.99.7を持っていました。この資料が本命ということですね。そこがわかっただけでもかなりの収穫です。 "3.2.3 Parameter Passing"の章
    >The classification of aggregate (structures and arrays) and union types works
    as follows:
    の下からの謎のルールがそうでしょうかね。英語力の問題なのかここは何度みても頭に入ってこないので、これとは別の資料があるのかと思ったのですが。しかもV0.95とV0.99.7でルール変わっているような。。。頑張って読み解いてみます。

本命とアドバイスいただいたABIの文書、さらに探してみました。System V ABI Versoin 1.0 もあったようです。

また、3.2.3 Parameter Passingを読み解こうと努力してみました。
とりあえず私の興味の範囲での理解を記載。(検証してないです。間違ってたらご指摘ください)

クラス判別

  • クラスという型のような定義があり、どのレジスタにのせるかが対応づけられる。

    • INTEGER - 汎用レジスタ (データ:整数系)
    • SSE - ベクタレジスタ (データ:浮動小数点数系)
    • NO_CLASS - データなし。パディング部分
    • MEMORY - スタック
    • ※(SSEUP/X87/X87UP/COMPLEX_X87)は分からないので放置
  • 構造体8x8byte(64byte)より大きい、または、アラインされていないフィールドを含む場合はMEMORYのクラスを持つとする。

    • → Ver1.0の場合。このバイト数がABIのバージョンで異なるみたい? Ver0.95では2x8 = 16バイト、Ver 0.99.7では4x8 = 32バイト
    • → アラインされていないとは、、unionとかビットフィールドとか?
  • C++のクラス(コンストラクタとかあるもの)はinvisible Reference.

    • C++のABI?(つまりはメモリ渡し?) → ポインタはINTEGERとなる
  • 恐らく残りのケースは8バイトごとに区切って考える。(たぶん、ここ重要)

    • → 8バイトの中のメンバー(field)のクラスで優先度の高いクラスになる。
    • 優先度は MEMORY > INTEGER > X87系 > SSE > NO_CLASS
    • → ということは8バイト区切りの中にshort/floatが混在した場合は2つそのまま1個の汎用レジスタに格納?
  • 1個でもMEMORYがあれば、構造体全体はメモリで渡す?

引数での渡し方

  • MEMORYはスタックで渡す
  • INTEGERは%rdi/%rsi/%dx/%rcx/%r8/%r9で渡す。
  • SSEは%xmm0-%xmm7で渡す。
  • レジスタが足りなくなったら、全部スタックで渡す。
    • → ここ分からない。。構造体全部なのかはみ出した部分だけなのか。。
  • 可変長引数 → ( ゚д゚)ポカーン

戻り値での渡し方

  • MEMORYの場合、呼び出し元でメモリを確保して隠し第一引数(%rdi)でアドレスを渡す。戻り値はそのアドレスをそのまま%raxに返す。
  • INTEGERの場合、%rax/%rdxで返す。
  • SSEの場合、%xmm0/%xmm1で返す。
  • レジスタが足りなくなったら、、記載がない? たぶんMEMORYとして扱う? →ここ分からない。
2Like

Comments

  1. @tosyama

    Questioner

    gccで検証してみたので補足。
    ・メモリ渡しになるバイト数は16バイトより大きいとき。Ver0.95のABIに従う?
    ・unionだからといってメモリ渡しにならない。8byte毎にクラスを定める。
    ・レジスタが足りなくなったら、構造体すべてをメモリ渡し。

    レジスタ足りなくなったらどうせメモリ渡しなので、ABIが修正されているように制限バイト数増やしたほうがレジスタ使えるのかも。でもレジスタ渡しにしたから速くなるかといえばちょっと疑問。増やしたら互換性なくなるのでもう16バイトなのかな。

    完全に理解したので、クローズします。

Your answer might help someone💌