注釈付き逆アセンブリは、関数またはオブジェクトモジュールの命令のアセンブリコードのリストを提供します。このリストには、各命令に関連付けられているパフォーマンスメトリックスが含まれています。注釈付き逆アセンブリは複数の方法で表示することができ、どの方法で表示されるかは、行番号のマッピング情報およびソースファイルが存在するかどうか、また注釈付き逆アセンブリが要求されている関数のオブジェクトモジュールが既知かどうかによって決まります。
オブジェクトモジュールが既知でない場合、アナライザによって単に指定された関数の命令が逆アセンブルされ、逆アセンブリでソース行は表示されません。
オブジェクトモジュールが既知の場合、オブジェクトモジュール内のすべての関数が逆アセンブルされます。
ソースファイルが利用可能で、行番号データが記録されている場合、アナライザによって、表示方式に応じてソースと逆アセンブリコードがインタリーブされます。
コンパイラによってオブジェクトコードにコメントが挿入されている場合、対応する表示方式が設定されていれば、逆アセンブリでそれらのコメントもインタリーブされます。
逆アセンブリコードの各命令には、注釈として次の情報が付けられます。
コンパイラによって報告されたソース行番号
相対アドレス
命令の 16 進表現 (要求があった場合)
命令のアセンブラの ASCII 表現
呼び出しアドレスの解決が可能な場合、それらのアドレスは関数名などのシンボルに変換されます。メトリックスは、命令行について表示されます。対応する表示方式が設定されていれば、インタリーブされるソースコードについても表示することができます。表示可能なメトリック値は、表 7–1 に示されているソースコードの注釈で説明しているとおりです。
複数の場所で #include が指定されているコードの逆アセンブリリストでは、そのコードが #include されている回数だけ逆アセンブリ命令が繰り返されます。ソースコードは、ファイル内で最初に出現する逆アセンブリコードの繰り返しブロックのみインタリーブされます。たとえば、inc_body.h というヘッダーに定義されているコードブロックが、inc_body、inc_entry、inc_middle、および inc_exit という 4 つの関数によって #include されている場合、逆アセンブリ命令のブロックは inc_body.h の逆アセンブリリストに 4 回現れますが、ソースコードは逆アセンブリ命令の 4 つのブロックの最初のもののみインタリーブされます。「ソース」タブに切り替えると、逆アセンブリコードの繰り返しにそれぞれ対応するインデックス行が表示されます。
インデックス行は「逆アセンブリ」タブ内に表示される場合があります。「ソース」タブの場合とは異なり、これらのインデックス行を直接ナビゲーションの目的に使用することはできません。ただし、インデックス行の直下の命令の 1 つにカーソルを置いて「ソース」タブを選択すると、インデックス行で参照されているファイルに移動できます。
ほかのファイルのコードを #include するファイルでは、ソースコードのインタリーブなしで、インクルードされたコードが逆アセンブリ命令として表示されます。ただし、これらの命令の 1 つにカーソルを置いて「ソース」タブを選択すると、#include されているコードを含むファイルが開かれます。このファイルを表示した状態で「逆アセンブリ」タブを選択すると、インタリーブされたソースコードとともに逆アセンブリコードが表示されます。
インライン関数の場合はソースコードと逆アセンブリコードをインタリーブできますが、マクロの場合はできません。
コードが最適化されていない場合、各命令の行番号は逐次順であり、ソース行と逆アセンブリされた命令は予想どおりにインタリーブされます。最適化されている場合は、あとの行の命令が前の行の命令よりも前に表示されることがあります。アナライザのインタリーブアルゴリズムでは、命令が行 N にあると表示される場合は、常に、行 N とその行までのすべてのソース行がその命令の前に挿入されます。最適化を行なった結果、制御転送命令とその遅延スロット命令の間にソースコードが現れます。ソース行の N に関連するコンパイラのコメントは、その行の直前に挿入されます。
注釈付き逆アセンブリを解釈するのは簡単ではありません。 リーフ PC とは、次に実行する命令のアドレスです。このため、命令の属性メトリックスは、命令の実行待ちに費やされた時間とみなされます。ただし、命令の実行は必ずしも順に行われるわけではなく、呼び出しスタックの記録に遅延があることもあります。注釈付き逆アセンブリコードを利用するには、実験の記録先であるハードウェアと、そのハードウェアが命令を読み込み、実行する方法を理解しておいてください。
次では、注釈付き逆アセンブリコードを解釈する上での問題点をいくつか説明します。
命令は、命令発行グループと呼ばれるグループ単位で読み込まれ、発行されます。グループに含まれる命令は、ハードウェア、命令の種類、すでに実行された命令、およびほかの命令またはレジスタの依存関係によって異なります。その結果、ある命令が常に前の命令と同じクロックで実行され、次に実行される命令として現れない場合、その命令の出現回数は実際よりも少なくなることを意味します。また、呼び出しスタックが記録されるときに、「次」に実行する命令が複数存在する可能性もあります。
命令発行規則はプロセッサの種類ごとに異なり、キャッシュ行内の命令位置合わせに依存します。リンカーはキャッシュ行よりも高い精度による命令位置合わせを強制的に行うので、関連性がないと思える関数を変更すると、異なる命令の位置合わせが生じる可能性があります。位置合わせが異なると、パフォーマンスの向上や劣化が発生することがあります。
次の例では、同じ関数をわずかに異なる状況でコンパイルしてリンクしています。2 つの出力例は、er_print ユーティリティーからの注釈付き逆アセンブリリストを示しています。2 つの例の命令は同じですが、位置合わせが異なっています。
この例の命令位置合わせでは、cmp と bl,a の 2 つの命令を別々のキャッシュ行にマップし、この 2 つの命令の実行待ちに多大な時間が消費されます。
Excl. Incl. User CPU User CPU sec. sec. 1. static int 2. ifunc() 3. { 4. int i; 5. 6. for (i=0; i<10000; i++) <function: ifunc> 0.010 0.010 [ 6] 1066c: clr %o0 0. 0. [ 6] 10670: sethi %hi(0x2400), %o5 0. 0. [ 6] 10674: inc 784, %o5 7. i++; 0. 0. [ 7] 10678: inc 2, %o0 ## 1.360 1.360 [ 7] 1067c: cmp %o0, %o5 ## 1.510 1.510 [ 7] 10680: bl,a 0x1067c 0. 0. [ 7] 10684: inc 2, %o0 0. 0. [ 7] 10688: retl 0. 0. [ 7] 1068c: nop 8. return i; 9. } |
この例の命令位置合わせでは、cmp と bl,a の 2 つの命令を 1 つのキャッシュ行にマップし、この 2 つの命令の内 1 つの命令のみの実行待ちに多大な時間が消費されます。
Excl. Incl. User CPU User CPU sec. sec. 1. static int 2. ifunc() 3. { 4. int i; 5. 6. for (i=0; i<10000; i++) <function: ifunc> 0. 0. [ 6] 10684: clr %o0 0. 0. [ 6] 10688: sethi %hi(0x2400), %o5 0. 0. [ 6] 1068c: inc 784, %o5 7. i++; 0. 0. [ 7] 10690: inc 2, %o0 ## 1.440 1.440 [ 7] 10694: cmp %o0, %o5 0. 0. [ 7] 10698: bl,a 0x10694 0. 0. [ 7] 1069c: inc 2, %o0 0. 0. [ 7] 106a0: retl 0. 0. [ 7] 106a4: nop 8. return i; 9. } |
特定のリーフ PC の示す命令の発行前に遅延があると、そのリーフ PC の出現回数が多くなることがあります。これは、次のケースを初めとして、さまざまな多くの理由で発生する可能性があります。
命令がカーネルにトラップされたときのように、前の命令の実行に時間がかかり、割り込みが不可能な場合。
算術演算命令が必要とするレジスタの内容が前の命令によって設定されていて、その命令がまだ完了していない場合。このような遅延の例としては、たとえば、データキャッシュミスが発生したロード命令があります。
浮動小数点演算命令が、別の浮動小数点演算命令の終了待ちになっている場合。このような状況は、平方根や浮動小数点除算などのパイプライン化が不可能な命令で発生します。
命令を含むメモリーワードが命令キャッシュに含まれていない場合 (I キャッシュミス)。
UltraSPARC ® III プロセッサ上では、ロード命令でキャッシュミスが発生すると、ミスが解決されないかぎり、その後の命令は、読み込み中のデータを使用する命令であるかどうかに関係なく、すべてブロックされます。UltraSPARC II プロセッサの場合には、読み込み中のデータ項目を使用する命令だけがブロックされます。
UltraSPARC プラットフォームでの TLB ミスを除き、オーバーフローにより生成される割り込みの処理に要する時間などのいくつかの理由から、ハードウェアカウンタのオーバーフローイベントの呼び出しスタックは、オーバーフローが発生した時点ではなく命令シーケンスの後ろの方で記録されます。 サイクルおよび発行された命令などの一部のカウンタの場合、この遅延は問題になりません。しかし、キャッシュミスや浮動小数点演算をカウントするようなカウンタの場合は、そのオーバーフローの原因となっているものとは別の命令がメトリックの原因とされます。多くの場合、イベントを発生させた PC は、記録されている PC の数命令前のもので、逆アセンブリリストでその命令を正確に特定できます。ただし、この命令範囲内に分岐先がある場合、イベントを発生させた PC に対 応する命令を見分けることは難しいか、または不可能です。メモリーアクセスイベントをカウントするハードウェアカウンタについて、コレクタはカウンタ名の前に「+」が付いている場合、イベントを発生させた PC を検索します。この方法で記録されたデータは、データ空間プロファイリングをサポートします。詳細は、「データ空間プロファイリング」および 「-h counter_definition_1...[, counter_definition_n]」を参照してください。