というわけで、次は「インライン関数」です。
これ、C言語とかでは、高速化の1つのテクニック的な「おまけ?みたいな機能」の扱いを受ける位の状況で、代わりにまぁ「inline」って書くだけ、みたいな位のモンなんですが。。。ことDWARF上での表現は「そーとー難しい」です。
原文も、これだけの内容にしては、長く、そして英語が難しい。。。ので、どこまで訳せているか、あんま自信なしです。という前提で、以下デス。
No. | 属性名 | 値 | インライン 宣言の有無 | コンパイラが インライン展開 したかどうか |
1 | DW_INL_not_inlined | 0x00 | × | × |
2 | DW_INL_inlined | 0x01 | × | ○ |
3 | DW_INL_declared_not_imlined | ○ | × | |
4 | DW_INL_declared_inlined | ○ | ○ |
例えば、以下の様なケースがあったとします。(C言語で)
inline int funcA(int n) { /* 関数A */ return n + 10; } int funcB(void) { /* 関数B */ int result; result = funcA(5); printf("%d", result ); return result; }この時、
という関係になるはずです。
ただし、これいっつも関数B内に関数Aが取り込まれるかというと、そうでもなくて、コンパイラの最適化(勝手、とも、都合、ともいう)で、せっかく関数Aに「inline」って教えてあげたにもかかわらず、ちゃっかり「インライン展開されずに、ふつーの関数Aとして使われる」的な事態にもなりえるのです。
ので、DWARFではこのケースでの「関数A」と「関数B内に展開される関数A」、それぞれの情報を持たせるようにできています。
そして、このケースで
と定義してるみたいです。
以下、この「抽象インスタンス」「実体インスタンス」のそれぞれ、およびインライン展開される場合、されない場合、などでのこいつらのDWARF表現方法になりますんで、これは押えておく必要ありっぽいです。
DW_AT_inlineを持っている(一般的にはDW_TAG_subprogramのタグを持つ?)DIE、つまり上記例での関数Aは、引数やら関数内の変数宣言やら、を示す「子DIE」を持ってることが一般的です。
んで、これらは(その内容次第だけど)、場合によっては孫、ひ孫、と子孫がツリーになることも、あるわけですね。
このとき、
と呼んじゃうことにします。(これは、原文での「決め」なんで、まぁこーいうもんだってことで理解です)
ただし、1つ例外があります。
インライン関数は、例えば上の例で、仮に関数C(こいつもinline指定)があって、関数A内からこれをcallしていたケースで、コンパイラ君がお気を使ってくれて、わざわざ関数B内でのコール箇所に、関数Aはもちろん関数Cまで全部展開しきった状態にしてくれたとします。
このケースでちょっと気づきたいのは、「インライン関数がネスト(入れ子)」になる可能性があるってことです。
この時、関数CにもAにも「抽象インスタンスツリー」ができるわけですが、関数A内の「抽象インスタンスツリー」の子か孫か、どっかに「関数Cの抽象インスタンスツリー」が単純に考えると、登場する可能性があります。
これ自体はやむを得ないのですが、この時、「関数Aの抽象インスタンスツリーは、関数Cの抽象インスタンスツリーの部分を除いた、子や孫、子孫だけのツリー」になるってのが、例外です。
んで、この「抽象インスタンスツリー」内の「抽象インスタンスエントリ」(つまり、子や孫、子孫になるDIE)には、(本来そのDIEが持つTAGとしては、持っているべきAttributeであっても)インライン展開されるか、(コンパイラ都合で)インライン展開されないか、によって値が変わってしまうAttributeは含まないことがある。という重大な注意事項があるです。
(原文には、「含んではいけない!」ということはない、とも明記してあるので注意です)
例えば、「DW_AT_low_pc」「DW_AT_high_pc」「DW_AT_ranges」「DW_AT_entry_pc」「DW_AT_location」「DW_AT_return_addr」「DW_AT_start_scope」「DW_AT_segment」など、マシンコードアドレスの範囲を示すAttributeが好例です。
(こいつらは、展開されない場合はちゃんとマシンコードアドレス空間内のアドレスを貰え定住できますが、インライン展開された場合は、他の関数のマシンアドレス内に埋め込まれるわけですから、アドレスは住み込み先の住所になるわけです)
なお、これは上でも書いた様に、ソースコード上「inline」と書かなくても、コンパイラが勝手にあなたはinlineなので、と決めつけて、住み込みにした場合も同様です。
インライン展開可能な関数のインライン展開部分は、「DW_TAG_inlined_subroutine」なるTAGでその情報が示されます。つまり、上の例では、関数B内で関数Aを呼び出してる部分ですね。
ということで、まずは、このTAGの特徴や注意事項なんか、箇条書きにしてみますです。
次に、また面倒な話なんですが。。。
と呼ぶことにします。
ここで、1つ注意したいのが、この名称っす。これ「実体インラインインスタンス」と「インライン」が明記されています。これは、コンパイラ都合でインライン展開されなかった場合、つまり「アウトライン」になってしまった場合と区別するために、こーしています。
んで、こちらも「抽象インスタンスxxx」とおんなじで、他の「実体インラインインスタンスツリー」にネストされることあありますが、こちらも抽象インスタンスと同様で、「実体インスタンスツリー」の子、孫。。。には、「実体インスタンスツリー」は含まないものとします。
この2つですが、まぁなんとなく想像付く部分もあるわけですが、やっぱり関係があります。ということで、どーいう関係なのかちょっと覗きます。
これらの点を前提として、「抽象インスタンス」と「実体インラインインスタンス」の構造や内容は、ほぼ類似しています。(というか、そーでないと、困る!)
ただ、以下3点は、例外です。。。
インライン関数は、コンパイラによるありがたい「最適化」の結果による判断や、時にはコンパイラの「一身上の勝手な都合」によって、インライン宣言してあげたのにインライン展開しないことがあります。
で、このケースになると、インライン関数は、通常ふつーの関数として存在し、コード領域上にもきちんとアドレスを陣取る訳です。
こーなった場合は、上の説明の「抽象インスタンス」と何にも変わらなくなるよーにも思えますが、同じく上の説明であげた通り抽象インスタンスにはDW_AT_low_pcなど、実コードアドレス等の情報がないも含めて、DWARFでは以下2つを並存させる形で扱ってるです。
ということで、以下はこの「実体アウトラインインスタンス」の説明でごわすです。
言語やコンパイラによっては、他の関数やサブルーチンをある関数やサブルーチンで論理的に入れ子(ネスト)することができるものがあるです。(何の言語か、忘れた。。。)
んで、さらに外部やネストされたサブルーチンのインライン化ができるものまであります。ので、ちょっと触れておきますです。
[PageInfo]
LastUpdate: 2013-09-08 13:28:33, ModifiedBy: koinec
[License]
FreeBSD Documentation License
[Permissions]
view:all, edit:members, delete/config:members