5

次のCコードをコンパイルしました。

typedef struct {
    long x, y, z;
} Foo;

long Bar(Foo *f, long i)
{
    return f[i].x + f[i].y + f[i].z;
}

コマンドを使用しgcc -S -O3 test.cます。出力のBar関数は次のとおりです。

    .section    __TEXT,__text,regular,pure_instructions
    .globl  _Bar
    .align  4, 0x90
_Bar:
Leh_func_begin1:
    pushq   %rbp
Ltmp0:
    movq    %rsp, %rbp
Ltmp1:
    leaq    (%rsi,%rsi,2), %rcx
    movq    8(%rdi,%rcx,8), %rax
    addq    (%rdi,%rcx,8), %rax
    addq    16(%rdi,%rcx,8), %rax
    popq    %rbp
    ret
Leh_func_end1:

このアセンブリコードについていくつか質問があります。

  1. 関数の本体で使用されていない場合、pushq %rbp「」、「」、「」movq %rsp, %rbpの目的は何ですか?popq %rbprbprsp
  2. スタックから引数を読み取らずに、C関数(それぞれと)への引数rsiを自動的に含めるのはなぜですか?rdiif
  3. Fooのサイズを88バイト(11long秒)に増やしてみたところ、leaq命令はになりましたimulq。(配列アクセスを最適化するために)乗算命令を回避するために、構造体を「より丸い」サイズにするように設計することは理にかなっていますか?命令は次のleaqように置き換えられました。

    imulq   $88, %rsi, %rcx
    
4

3 に答える 3

7
  1. この関数は、これらの命令を使用して独自のスタックフレームを構築するだけです。それらについて本当に珍しいことは何もありません。ただし、この関数はサイズが小さいため、コードで使用するとインライン化される可能性があることに注意してください。ただし、コンパイラは常に関数の「通常の」バージョンを生成する必要があります。また、@ouahが彼の答えで言ったこと。

  2. これは、AMD64ABIが引数を関数に渡す必要があることを指定する方法であるためです。

    クラスがINTEGERの場合、シーケンス%rdi、%rsi、%rdx、%rcx、%r8、および%r9の次に使用可能なレジスタが使用されます。

    ページ20、AMD64ABIドラフト0.99.5–2010年9月3日

  3. これは、構造体のサイズに直接関係するのではなく、関数がアクセスする必要のある絶対アドレスです。構造体のサイズが24バイトで、f構造体を含む配列のアドレスであり、i配列にアクセスする必要があるインデックスである場合、各構造体へのバイトオフセットはi*24です。この場合の24の乗算はlea、SIBアドレス指定の組み合わせによって実現されます。最初のlea命令は単純にを計算i*3し、その後のすべての命令はそれを使用しi*3てさらに8を乗算します。したがって、必要な絶対バイトオフセットで配列にアクセスし、即時変位を使用して個々の構造体メンバーにアクセスします((%rdi,%rcx,8)8(%rdi,%rcx,8)、および16(%rdi,%rcx,8))。構造体のサイズを88バイトにすると、アドレス指定とアドレス指定の組み合わせを使用して、このようなことを迅速に行う方法はleaありません。コンパイラーは、単純なものが一連のシフト、加算、s、またはその他のものよりもimull計算においてより効率的であると単純に想定しています。i*88lea

于 2012-06-04T19:16:40.313 に答える
2
  1. 関数の本体でrbpもrspも使用されていない場合、pushq%rbp、movq%rsp、%rbp、およびpopq%rbpの目的は何ですか?

デバッガーを使用するときにフレームを追跡するため。最適化するために追加します(で有効にする必要がありますが、使用した多くのバージョンで-fomit-frame-pointerは有効になっていないことに注意してください)。-O3gcc

于 2012-06-04T19:12:27.227 に答える
0
3. I tried increasing the size of Foo to 88 bytes (11 longs) and the leaq instruction became an imulq. Would it make sense to design my structs to have "rounder" sizes to avoid the multiply instructions (in order to optimize array access)?

leaq呼び出しは、(基本的に、このcaeでは)k * a + bを計算します。ここで、「k」は1、2、4、または8であり、「a」と「b」はレジスタです。「a」と「b」が同じ場合、1、2、3、4、5、8、および9の長さの構造に使用できます。

16 longのような大きな構造は、「k」のオフセットを計算して2倍にすることで最適化できますが、それがコンパイラーが実際に行うことかどうかはわかりません。テストする必要があります。

于 2012-06-04T19:26:17.650 に答える