X86 lea命令の背後にある直感

North Denver C++ Meetup の前回のミーティングで、lea について言及した人もいました。 他の指示よりも紛らわしいです。 lea 「ロード有効アドレス」の頭字語です。通常の説明は、「メモリ アドレスをソースからデスティネーションに入れる」というものです。 lea の構文 インテルの構文では次のとおりです:

lea destination, source

たとえば、配列 points がある場合 struct Point の :

struct Point
{
    int x;
    int y;
    int z;
};

コンパイラは int x = points[i].y; に対して次の行を生成する場合があります

mov  eax, [rbx+rcx*4 + 4]

この場合、レジスタ rbx 配列 points を指します 、 rcx インデックス変数 i です 、および eax x を保持するレジスタです。 .同様に、int* x = &points[i].y; の場合 、コンパイラは

を生成できます
lea  eax, [rbx+rcx*4 + 4]

ただし、アドレス操作に使用する以外に、コンパイラは lea の使用を好むようです 効率上の理由から、他の算術命令にも。例:int y = x * 5; 発生する可能性があります

lea  eax, [rdi + 4*rdi]

のより直感的なバージョンの代わりに
imul  eax, [rdi], 5

lea 私の見解では、キャストで挟まれたポインター演算のプロセスです。前の例では、同等の c コードは

int y = (int)(&((int*)x)[x]);

上記のコードは、最初に x を扱います int として ポインター ((int*)x )、次にアドレス x を取得します そのポインタの - 番目の要素。その部分は本質的にアドレス [rdi + 4*rdi] です .次に、アドレスの下位 32 ビットを整数値として宛先に割り当てます。

この例で lea について直感的に理解できることを願っています .もちろん、正気の C プログラマーがこの種のコードを手作業で書くことはありません。上記のコードは、正当な理由で C++ に準拠していません (C++ では、ポインターから小さい型 int へのキャストが許可されていません)。 )。ただし、マシンの観点から見ると、この種の「reinterpret_cast」は基本的にノーオペレーションであり、マシン言語は常にそれを利用しています。