LEA命令の目的は何ですか?


676

私にとっては、ファンキーなMOVのようです。その目的は何ですか、いつ使用する必要がありますか?


2
アドレス/ポインタではない値でのLEAの使用も参照してください:LEAはシフトアンドアドの命令にすぎません。ハードウェアがすでにアドレス指定モードのデコードと計算を行うために存在しているため、8086に追加された可能性があります。アドレスでの使用のみが「意図されている」ためではありません。ポインタは、アセンブリでは単なる整数であることに注意してください。
Peter Cordes

回答:


798

他の人が指摘したように、LEA(ロード実効アドレス)は特定の計算を行うための「トリック」としてよく使用されますが、それはその主な目的ではありません。x86命令セットは、PascalやCなどの高水準言語をサポートするように設計されており、配列(特に、intまたは小さな構造体の配列)が一般的です。たとえば、(x、y)座標を表す構造体について考えます。

struct Point
{
     int xcoord;
     int ycoord;
};

次のようなステートメントを想像してみてください。

int y = points[i].ycoord;

ここでpoints[]の配列ですPoint。アレイのベースを想定することに既にあるEBX、変数はiでありEAX、且つxcoord、およびycoord各32ビット(そうであるycoord構造体のオフセット4バイトである)、このステートメントは、にコンパイルすることができます。

MOV EDX, [EBX + 8*EAX + 4]    ; right side is "effective address"

着陸yEDXます。8のスケール係数は、それぞれのPointサイズが8バイトであるためです。次に、 "address of"演算子&で使用される同じ式について考えます。

int *p = &points[i].ycoord;

この場合、の値でycoordはなく、そのアドレスが必要です。ところですLEA(負荷実効アドレス)に入っています。代わりにMOV、コンパイラが生成することができます

LEA ESI, [EBX + 8*EAX + 4]

でアドレスをロードしますESI


112
mov命令を拡張して括弧を省くほうがきれいではなかっただろうか?MOV EDX, EBX + 8*EAX + 4
Natan Yellin、2011

14
@imacake LEAを専用のMOVに置き換えることで、構文をきれいに保つことができます。
Natan Yellin

139
MOV命令(EBX + 8 * EAX + 4)での計算は無効です。LEA ESI、[EBX + 8 * EAX + 4]はx86がサポートするアドレッシングモードであるため有効です。en.wikipedia.org/wiki/X86#Addressing_modes
Erik

29
@JonathanDickinson LEAはMOV、間接ソースのみの場合と似ていますが、を行わず、間接ソースのみを行う点が異なりMOVます。実際に計算されたアドレスから読み取るのではなく、計算するだけです。
ホッブズ2013

24
エリック、ツアーのコメントは正確ではありません。MOV eax、[ebx + 8 * ecx + 4]は有効です。ただし、MOVはth番目のメモリロケーションの内容を返しますが、LEAはアドレスを返します
Olorin

562

Abrash の"Zen of Assembly"から:

LEA、メモリアドレッシング計算を実行するが、実際にはメモリをアドレッシングしない唯一の命令。LEA標準のメモリアドレッシングオペランドを受け入れますが、計算されたメモリオフセットを指定されたレジスタに格納するだけです。これは、任意の汎用レジスタである場合があります。

それは私たちに何を与えますか?ADD提供されない2つのこと:

  1. 2つまたは3つのオペランドを使用して加算を実行する機能、および
  2. 結果を任意のレジスタに保存する機能。ソースオペランドの1つだけではありません。

そして、LEAフラグを変更しません。

  • LEA EAX, [ EAX + EBX + 1234567 ]計算EAX + EBX + 1234567(3つのオペランド)
  • LEA EAX, [ EBX + ECX ]EBX + ECX結果を上書きせずに計算します。
  • 定数による乗算(LEA EAX, [ EBX + N * EBX ]2、3、5 、または9 による)。

差:他のユースケースは、ループに便利であるLEA EAX, [ EAX + 1 ]INC EAX後者変更することであるEFLAGSが、前者はありません。これはCMP状態を保持します。


42
いくつかの例を@AbidRahmanK:LEA EAX, [ EAX + EBX + 1234567 ]の合計を計算しEAXEBXそして1234567(3つのオペランドだという)。結果上書きせずにLEA EAX, [ EBX + ECX ]計算EBX + ECX ます。3つ目は、LEA(フランクによって記載されていない)のために使用されている定数による乗算あなたのようにそれを使用する場合、(2、3、5または9による)LEA EAX, [ EBX + N * EBX ]N1,2,4,8することができます)。差:他のユースケースは、ループに便利であるLEA EAX, [ EAX + 1 ]INC EAX後者変更することであるEFLAGSが、前者はありません。これはCMP状態を保持します
FrankH。

@FrankH。まだわからないので、ポインターを別の場所にロードしますか?

6
@ ripDaddy69はい、一種の-「ロード」とは、「アドレス計算/ポインタ演算を実行する」ことを意味します。メモリにアクセスしませ(つまり、Cプログラミング用語で呼び出されるポインターを「逆参照」しません)。
FrankH。

2
+1:これにより、どのような「トリック」LEAを使用できるかが明確になります...(上記のIJケネディの人気のある回答で「LEA(実効アドレスのロード)は、特定の計算を行うための「トリック」としてよく使用されます)
アサドエブラヒム

3
高速の2オペランドLEAと低速の3オペランドLEAの間には大きな違いがあります。Intel Optimizationマニュアルでは、高速パスLEAはシングルサイクルで、低速パスLEAは3サイクルかかると記載されています。さらに、Skylakeには2つの高速パス機能ユニット(ポート1および5)があり、低速パス機能ユニット(ポート1)は1つしかありません。マニュアルのアセンブリ/コンパイラコーディングルール33は、3オペランドLEAの使用を警告しています。
Olsonist

110

LEA命令のもう1つの重要な機能は、またはのような算術命令によってアドレスを計算するときに、CFandのような条件コードを変更しないことです。この機能により、命令間の依存関係のレベルが低下し、コンパイラまたはハードウェアスケジューラによるさらなる最適化の余地が生まれます。ZFADDMUL


1
はい、leaコンパイラ(または人間のコーダー)がフラグの結果を壊さずに数学を実行するのに役立つ場合があります。しかしlea、よりも速くはありませんadd。ほとんどのx86命令はフラグを書き込みます。高性能のx86実装では、EFLAGSの名前を変更するか、通常のコードが高速に実行されるように書き込み後の危険を回避する必要があるため、フラグの書き込みを回避する命令の方が適していません。(部分的なフラグのものは問題を作成する可能性があります。INC命令とADD 1を参照してください。それは問題ですか?
Peter Cordes 2018

2
@PeterCordes:これをここに持ち込むのは嫌いですが、この新しい[x86-lea]タグは冗長で不必要だと考えるのは私一人ですか?
マイケルペッチ2018

2
@MichaelPetch:ええ、具体的すぎると思います。機械語を理解していない初心者を混乱させ、すべて(ポインタを含む)が単なるビット/バイト/整数であることを混同しているように思われるため、膨大な数の投票を伴う疑問がたくさんあります。しかし、そのためのタグがあるということは、実際には重複していない合計が約2つか3つある場合でも、将来の質問の数が無制限にある余地があることを意味します。(それは何ですか?整数を乗算するためにそれをどのように使用するのですか?AGUとALUで内部的にどのように実行され、どのようなレイテンシ/スループットで実行されます。そして、おそらく「意図された」目的です)
Peter Cordes

@PeterCordes:私も同意します。編集されているこれらのすべての投稿は、LEAに関連する既存のいくつかの質問とほぼ同じです。タグではなく、重複を識別し、imhoとマークする必要があります。
Michael

1
@EvanCarroll:まだ終わっていない場合は、すべてのLEA質問にタグ付けしてください。上記で説明したように、x86-leaはタグに対して具体的すぎると考えており、重複しない将来の質問にはあまり適用範囲がありません。私はそれが多くの仕事だと思う実際にそれらのほとんどのために、しかし、または実際にマージする改造を取得するものを決めたDUPの目標として「最高」のQ&Aを選択してください。
Peter Cordes

93

すべての説明にもかかわらず、LEAは算術演算です。

LEA Rt, [Rs1+a*Rs2+b] =>  Rt = Rs1 + a*Rs2 + b

その名前が、shift + add操作に対して非常に愚かであるというだけです。その理由は、すでに上位の回答で説明されています(つまり、高レベルのメモリ参照を直接マップするように設計されています)。


8
そして、その計算はアドレス計算ハードウェアによって実行されます。
Ben Voigt 2013

30
@BenVoigt私は昔、私は古い人なのでそれを言っていました:-)伝統的に、x86 CPUはこれにアドレッシングユニットを使用していました、同意しました。しかし、「分離」は最近非常にぼやけています。専用の AGU がまったくないCPUもあればLEA、AGUではなく通常の整数ALUで実行するように選択したCPUもあります。最近、CPU仕様をよく読んで、「どこで実行されるか」を見つけなければなりません...
FrankH。

2
@FrankH .:通常、アウトオブオーダーのCPUはALUでLEAを実行しますが、一部のインオーダーのCPU(Atomなど)は、AGUでそれを実行することがあります(メモリアクセスの処理でビジーではないため)。
Peter Cordes

3
いいえ、名前は愚かではありません。 LEAメモリ関連のアドレッシングモードから生じるアドレスを提供します。これはシフトおよび追加操作ではありません。
Kaz

3
FWIW AGUで操作を実行する現在のx86 CPUはほとんどありません(存在する場合)。ほとんどまたはすべてが、他の算術演算と同様にALUを使用します。
BeeOnRope

77

LEA命令については、もう1つだけかもしれません。LEAを使用して、レジスタを3、5、または9で高速乗算することもできます。

LEA EAX, [EAX * 2 + EAX]   ;EAX = EAX * 3
LEA EAX, [EAX * 4 + EAX]   ;EAX = EAX * 5
LEA EAX, [EAX * 8 + EAX]   ;EAX = EAX * 9

13
トリックの+1。しかし、私は質問をしたい(愚かかもしれません)、なぜこのように3つを直接掛けてみませんLEA EAX, [EAX*3]か?
Abid Rahman K

13
@Abid Rahman K:x86 CPU命令セットなどの命令はありません。
GJ。

50
@AbidRahmanKは、intel asm構文を使用しても乗算のように見えるため、lea命令はシフト演算のみをエンコードできます。オペコードは、したがってことができます乗算のみ1,2,4または8によって、シフトを記述するために2ビットを有する
イスクイル

6
@Koray Tugay:shlレジスタを2,4,8,16倍する命令のように左シフトを使用できます...高速で短いです。しかし、2の累乗の異なる数を乗算する場合、通常mulはより大げさで遅い命令を使用します。
GJ。

7
@GJ。そのようなエンコーディングはありませんが、一部のアセンブラはこれをショートカットとして受け入れます(例:fasm)。したがって、たとえばlea eax,[eax*3]はに相当しlea eax,[eax+eax*2]ます。
ルスラン

59

lea「ロード実効アドレス」の略です。ソースオペランドによるロケーション参照のアドレスをデスティネーションオペランドにロードします。たとえば、次の目的で使用できます。

lea ebx, [ebx+eax*8]

1つの命令でebxポインターeax項目をさらに(64ビット/要素配列で)移動します。基本的に、ポインタを効率的に操作するために、x86アーキテクチャでサポートされている複雑なアドレッシングモードを利用できます。


23

あなたが使用することを最大の理由LEAの上にはMOV、あなたがアドレスを計算するために使用していることをレジスタに算術演算を実行する必要がある場合です。効果的には、いくつかのレジスタでポインタ演算に相当するものを効果的に組み合わせて「無料」で実行できます。

それについて本当に混乱しているのは、通常LEAaと同じように書いても、MOV実際にはメモリを逆参照していないということです。言い換えると:

MOV EAX, [ESP+4]

これにより、ESP+4ポイントするコンテンツがに移動しEAXます。

LEA EAX, [EBX*8]

これにより、有効なアドレスEBX * 8がEAXに移動します。その場所にあるものではありません。ご覧のとおり、a MOVは加算/減算に制限されていますが、2の係数(スケーリング)を乗算することもできます。


みなさんごめんなさい。@ big.heartは、3時間前にこれに答えを出すことによって私をだまし、それを私の議会の質問の精査で「新規」として表示させました。
David Hoelzer、2015年

1
メモリアドレス指定を行わないのに、構文で角かっこが使用されるのはなぜですか?
ゴロポット2017年

3
@ q4w56これは、「それがまさにそれを行う方法です」という答えの答えの1つです。それが人々が何をLEAしているかを理解するのがとても難しい理由の一つだと思います。
David Hoelzer 2017年

2
@ q4w56:メモリオペランド構文マシンコードエンコーディングを使用するshift + add命令です。一部のCPUでは、AGUハードウェアを使用することもありますが、これは歴史的な詳細です。依然として関連する事実は、この種のシフト+加算をデコードするためのデコーダーハードウェアがすでに存在しており、LEAを使用すると、メモリアドレス指定の代わりに算術にそれを使用できます。(または、1つの入力が実際にポインターである場合のアドレス計算用)。
Peter Cordes 2017

20

8086には、レジスタオペランドと実効アドレスを受け入れ、その実効アドレスのオフセット部分を計算するためにいくつかの計算を実行し、計算されたアドレスによって参照されるレジスタとメモリを含むいくつかの演算を実行する大きな命令ファミリがあります。そのファミリの命令の1つを実際のメモリ操作をスキップすることを除いて上記のように動作させることは、かなり簡単でした。これ、指示:

mov ax,[bx+si+5]
lea ax,[bx+si+5]

ほぼ同じように内部的に実装されました。違いはスキップされたステップです。どちらの手順も次のように機能します。

temp = fetched immediate operand (5)
temp += bx
temp += si
address_out = temp  (skipped for LEA)
trigger 16-bit read  (skipped for LEA)
temp = data_in  (skipped for LEA)
ax = temp

Intelがこの命令を含める価値があると思った理由については、はっきりとはわかりませんが、実装するのが安かったという事実が大きな要因だったでしょう。別の要因は、インテルのアセンブラーがシンボルをBPレジスターに関連して定義することを許可したという事実でした。fnordがBP相対シンボル(例:BP + 8)として定義されている場合、次のように言うことができます。

mov ax,fnord  ; Equivalent to "mov ax,[BP+8]"

stoswのようなものを使用してデータをBP相対アドレスに保存したい場合、

mov ax,0 ; Data to store
mov cx,16 ; Number of words
lea di,fnord
rep movs fnord  ; Address is ignored EXCEPT to note that it's an SS-relative word ptr

より便利でした:

mov ax,0 ; Data to store
mov cx,16 ; Number of words
mov di,bp
add di,offset fnord (i.e. 8)
rep movs fnord  ; Address is ignored EXCEPT to note that it's an SS-relative word ptr

世界の「オフセット」を忘れると、値8ではなく場所[BP + 8]のコンテンツがDIに追加されることに注意してください。おっとっと。


12

既存の回答が述べたように、LEAメモリにアクセスせずにメモリアドレッシング演算を実行するという利点があり、単純な形式のadd命令ではなく、演算結果を別のレジスタに保存します。本当の根本的なパフォーマンスの利点は、最新のプロセッサが効果的なアドレス生成(LEAおよびその他のメモリ参照アドレスを含む)のために独立したLEA ALUユニットとポートをLEA備えていることです。芯。

LEAユニットの詳細については、Haswellアーキテクチャのこの記事を確認してください。http//www.realworldtech.com/haswell-cpu/4/

他の回答で言及されていないもう1つの重要な点は、LEA REG, [MemoryAddress]命令ですMemoryAddress。この命令のPC相対アドレスを参照するようにエンコードするPIC(位置独立コード)です。これは、MOV REG, MemoryAddress相対仮想アドレスをエンコードするものとは異なり、最新のオペレーティングシステムでは再配置/パッチが必要です(ASLRは一般的な機能です)。したがって、そのLEAような非PICをPICに変換するために使用できます。


2
「個別のLEA ALU」の部分はほとんど正しくありません。最近のCPU leaは、他の算術命令を実行する1つ以上の同じALUで実行されます(ただし、通常、他の算術命令よりも数は少なくなります)。例えば、ハズウエルCPUが実行することができる言及addまたはsubまたはで他のほとんどの基本的な算術演算四つの異なるのALU、のみ実行することができるlea一方(複合上lea)または2つ(単純lea)。さらに重要なことに、これらの2つのleaALUは、他の命令を実行できる4つのうちの2つにすぎないため、主張されているような並列処理の利点はありません。
BeeOnRope

(正しく)リンクした記事は、LEAが整数ALU(add / sub / boolean)と同じポートにあり、整数MULユニットがHaswellにあることを示しています。(そしてFP ADD / MUL / FMAを含むベクターALU)。シンプルのみのLEAユニットはポート5にあり、ADD / SUB / whateverも実行し、ベクトルシャッフルやその他のものも実行します。私が反対票を投じない唯一の理由は、RIP相対LEAの使用を指摘していることです(x86-64のみ)。
Peter Cordes 2017

8

LEA命令を使用すると、CPUによる有効アドレスの計算に時間がかかることを回避できます。アドレスが繰り返し使用される場合は、使用されるたびに実効アドレスを計算するのではなく、レジスタに格納する方が効果的です。


必ずしも最新のx86ではありません。ほとんどのアドレッシングモードのコストは同じですが、いくつかの注意点があります。その[esi]ため、言うよりめったに安く[esi + 4200]はなく、めったに安くはありません[esi + ecx*8 + 4200]
BeeOnRope 2016年

@BeeOnRope [esi][esi + ecx*8 + 4200]。よりも安くはありません。しかし、なぜ比較するのか。それらは同等ではありません。前者が後者と同じメモリ位置を指定するようにしたい場合は、追加の指示が必要です。8 esiecx掛けた値に追加する必要があります。次に、4200を追加する必要があります。これらの追加の命令は、コードサイズに追加されます(命令キャッシュのスペースを占有し、フェッチするサイクル)。
Kaz

2
@カズ-私はあなたが私のポイントを逃していたと思います(そうでなければ私はOPのポイントを逃しました)。私の理解では、OPは[esi + 4200]一連の命令で繰り返しのようなものを使用する場合、最初に実効アドレスをレジスターにロードしてそれを使用する方がよいと言っています。たとえばadd eax, [esi + 4200]; add ebx, [esi + 4200]; add ecx, [esi + 4200]、を書くのlea edi, [esi + 4200]; add eax, [edi]; add ebx, [edi]; add ecx, [edi]ではなく、を優先することをお勧めします。少なくともそれはこの答えの明白な解釈です。
BeeOnRope

私が比較された理由だから、[esi]および[esi + 4200](または[esi + ecx*8 + 4200]同じ複雑なアドレスを持つN個の命令は、単純な(1つのREG)のアドレス指定、プラスワンでN個の命令に変換されていること:これはOP単純化であるということですが、(私はそれを理解したように)提案していますlea、複雑なアドレッシングは「時間がかかる」ため、実際、最新のx86でも遅くなりますが、同じアドレスの連続する命令ではレイテンシが問題になる可能性は低い
BeeOnRope

1
おそらく、レジスタのプレッシャーを軽減できます。そうですが、逆の場合もあります。実効アドレスを生成したレジスタがライブの場合、結果を保存するために別のレジスタが必要にleaなるため、その場合のプレッシャーが増大します。一般的に、中間体を保存することはレジスター圧力の原因であり、解決策ではありませんが、ほとんどの場合それは洗浄であると思います。@カズ
BeeOnRope

7

LEA(実効アドレスのロード)命令は、インテルプロセッサーのメモリアドレス指定モードから発生するアドレスを取得する方法です。

つまり、次のようなデータ移動がある場合、

MOV EAX, <MEM-OPERAND>

指定されたメモリ位置の内容をターゲットレジスタに移動します。

MOVby を置き換える場合LEA、メモリ位置のアドレスは、<MEM-OPERAND>アドレッシング式によってまったく同じ方法で計算されます。ただし、メモリロケーションの内容ではなく、ロケーション自体を宛先に取得します。

LEA特定の算術命令ではありません。これは、プロセッサのメモリアドレス指定モードのいずれかから発生する実効アドレスを傍受する方法です。

たとえばLEA、単純な直接アドレスで使用できます。算術はまったく含まれていません。

MOV EAX, GLOBALVAR   ; fetch the value of GLOBALVAR into EAX
LEA EAX, GLOBALVAR   ; fetch the address of GLOBALVAR into EAX.

これは有効です。Linuxプロンプトでテストできます。

$ as
LEA 0, %eax
$ objdump -d a.out

a.out:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
   0:   8d 04 25 00 00 00 00    lea    0x0,%eax

ここでは、スケーリングされた値の追加やオフセットはありません。ゼロはEAXに移動されます。即値オペランドを使用してMOVを使用することもできます。

これが、括弧LEAが不必要であると考える人々がひどく誤っている理由です。大括弧はLEA構文ではなく、アドレッシングモードの一部です。

LEAはハードウェアレベルで現実のものです。生成された命令は実際のアドレッシングモードをエンコードし、プロセッサはそれを実行してアドレスを計算します。次に、メモリ参照を生成する代わりに、そのアドレスを宛先に移動します。(他の命令のアドレッシングモードのアドレス計算は、CPUフラグにLEAは影響しないため、CPUフラグには影響しません。)

アドレス0からの値のロードとは対照的です。

$ as
movl 0, %eax
$ objdump -d a.out | grep mov
   0:   8b 04 25 00 00 00 00    mov    0x0,%eax

これは非常によく似たエンコーディングです。ただ8dのは、LEAに変更されました8b

もちろん、このLEAエンコーディングは即ゼロをに移動するよりも長くなりますEAX

$ as
movl $0, %eax
$ objdump -d a.out | grep mov
   0:   b8 00 00 00 00          mov    $0x0,%eax

LEA短い代替案があるからといって、この可能性を排除する理由はありません。それは、利用可能なアドレッシングモードと直交する方法で組み合わせるだけです。


6

ここに例があります。

// compute parity of permutation from lexicographic index
int parity (int p)
{
  assert (p >= 0);
  int r = p, k = 1, d = 2;
  while (p >= k) {
    p /= d;
    d += (k << 2) + 6; // only one lea instruction
    k += 2;
    r ^= p;
  }
  return r & 1;
}

コンパイラー・オプションとして-O(最適化)を使用すると、gccは指定されたコード行のlea命令を見つけます。


6

すでに多くの回答が完了しているようです。同じ表現形式のlea and move命令がどのように異なる動作をするかを示すサンプルコードをもう1つ追加したいと思います。

長い話を短くするために、lea命令とmov命令の両方を、命令のsrcオペランドを囲む括弧と共に使用できます。それらはで囲まれている場合()における発現()と同じ方法で計算されます。ただし、2つの命令は、srcオペランドの計算値を異なる方法で解釈します。

式がleaとmovのどちらで使用されても、src値は次のように計算されます。

D(Rb、Ri、S) => (Reg [Rb] + S * Reg [Ri] + D)

ただし、mov命令とともに使用すると、上記の式で生成されたアドレスが指す値にアクセスして、宛先に格納しようとします。

これに対し、上記の式でlea命令を実行すると、生成された値をそのまま宛先にロードします。

以下のコードは、lea命令とmov命令を同じパラメーターで実行します。ただし、違いを把握するために、mov命令の結果として間違ったアドレスにアクセスすることによって引き起こされるセグメンテーション違反をキャッチするために、ユーザーレベルのシグナルハンドラーを追加しました。

コード例

#define _GNU_SOURCE 1  /* To pick up REG_RIP */
#include <stdio.h> 
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>


uint32_t
register_handler (uint32_t event, void (*handler)(int, siginfo_t*, void*))
{
        uint32_t ret = 0;
        struct sigaction act;

        memset(&act, 0, sizeof(act));
        act.sa_sigaction = handler;
        act.sa_flags = SA_SIGINFO;
        ret = sigaction(event, &act, NULL);
        return ret;
}

void
segfault_handler (int signum, siginfo_t *info, void *priv)
{
        ucontext_t *context = (ucontext_t *)(priv);
        uint64_t rip = (uint64_t)(context->uc_mcontext.gregs[REG_RIP]);
        uint64_t faulty_addr = (uint64_t)(info->si_addr);

        printf("inst at 0x%lx tries to access memory at %ld, but failed\n",
                rip,faulty_addr);
        exit(1);
}

int
main(void)
{
        int result_of_lea = 0;

        register_handler(SIGSEGV, segfault_handler);

        //initialize registers %eax = 1, %ebx = 2

        // the compiler will emit something like
           // mov $1, %eax
           // mov $2, %ebx
        // because of the input operands
        asm("lea 4(%%rbx, %%rax, 8), %%edx \t\n"
            :"=d" (result_of_lea)   // output in EDX
            : "a"(1), "b"(2)        // inputs in EAX and EBX
            : // no clobbers
         );

        //lea 4(rbx, rax, 8),%edx == lea (rbx + 8*rax + 4),%edx == lea(14),%edx
        printf("Result of lea instruction: %d\n", result_of_lea);

        asm volatile ("mov 4(%%rbx, %%rax, 8), %%edx"
                       :
                       : "a"(1), "b"(2)
                       : "edx"  // if it didn't segfault, it would write EDX
          );
}

実行結果

Result of lea instruction: 14
inst at 0x4007b5 tries to access memory at 14, but failed

1
インラインasmを個別のステートメントに分割するのは危険であり、clobbersリストは不完全です。basic-asmブロックは、コンパイラーにクローバーがないことを伝えますが、実際にはいくつかのレジスターを変更します。また、を使用=dして、結果がEDXにあることをコンパイラーに伝えることもできますmov。また、出力の初期クローバー宣言も省略しました。これは、あなたが説明しようとしていることを示していますが、他のコンテキストで使用すると壊れるインラインasmの誤解を招く悪い例でもあります。それはスタックオーバーフローの答えとしては悪いことです。
Peter Cordes

%%Extended asmでこれらすべてのレジスタ名に書き込みたくない場合は、入力制約を使用します。のようにasm("lea 4(%%ebx, %%eax, 8), %%edx" : "=d"(result_of_lea) : "a"(1), "b"(2));。コンパイラをレジスタに初期化させることで、クロバーを宣言する必要もなくなります。mov-immediateがレジスタ全体を上書きする前に、xor-zeroingによって複雑化しています。
Peter Cordes

@PeterCordesありがとうございます、ピーター、あなたのコメントに続いて、この回答を削除するか、それとも修正しますか?
Jaehyuk Lee

1
インラインasmを修正した場合、害はありません。他の回答を理解していなかった初心者にとって、良い具体例になるでしょう。削除する必要はありません。これは、前回のコメントで示したように簡単な修正です。インラインasmの悪い例が「良い」例に修正されたら、賛成票を投じる価値があると思います。(私は反対投票しませんでした)
Peter Cordes

1
どこmov 4(%ebx, %eax, 8), %edxが無効だと誰が言うのですか?とにかく、はい、64ビットの値があることをコンパイラに伝えるmovために書き込むのは理にかなって"a"(1ULL)いるので、レジスタ全体を埋めるように拡張されていることを確認する必要があります。実際にはmov $1, %eax、コンパイラーがRAX = 0xff00000001または何かを知っているコードを囲む奇妙な状況でない限り、EAXをゼロ拡張してRAXに書き込むため、引き続きを使用します。の場合lea、まだ32ビットのオペランドサイズを使用しているため、入力レジスタの迷子の上位ビットは32ビットの結果に影響を与えません。
Peter Cordes

4

LEA:単なる「算術」命令。

MOVはオペランド間でデータを転送しますが、leaは計算中です


LEAは明らかにデータを移動します。宛先オペランドがあります。LEAは常に計算するとは限りません。ソースオペランドで表される実効アドレスが計算するかどうかを計算します。LEA EAX、GLOBALVARは計算しません。GLOBALVARのアドレスをEAXに移動するだけです。
Kaz

@Kazあなたのフィードバックをありがとう。私の情報源は、「LEA(実効アドレスのロード)は基本的に算術命令です。実際のメモリアクセスは実行しませんが、アドレスの計算に一般的に使用されます(ただし、汎用整数を計算できます)。」形成のEldad-Eilam帳のページ149
会計士

@Kaz:アドレスがすでにリンク時定数である場合、LEAが冗長になるのはそのためです。mov eax, offset GLOBALVAR代わりに使用してください。あなたはできる LEAを使用しますが、それはよりもわずかに大きいコードサイズだmov r32, imm32と、少数のポート上で動作し、それはまだアドレス計算プロセスを通過しているためlea reg, symbolPICおよび/または下位32ビット外のアドレスが必要な場合、RIP相対LEAの64ビットでのみ役立ちます。32ビットまたは16ビットのコードでは、利点はありません。LEAは、アドレッシングモードをデコード/計算するCPUの機能を公開する算術命令です。
Peter Cordes

@Kaz:同じ議論で、あなたはそれimul eax, edx, 1が計算しないと言うことができます:それは単にedxをeaxにコピーします。しかし実際には、3サイクルのレイテンシで乗算器を介してデータを実行します。または、rorx eax, edx, 0単にコピーします(ゼロ回転)。
Peter Cordes

@PeterCordes私のポイントは、LEA EAX、GLOBALVALおよびMOV EAX、GLOBALVARの両方が直接のオペランドからアドレスを取得することです。適用される乗数1、オフセット0はありません。ハードウェアレベルではそのようになる可能性がありますが、アセンブリ言語や命令セットでは見られません。
Kaz

1

乗算の追加、排他的、またはゼロ、符号などのステータスフラグの設定など、すべての通常の「計算」命令。複雑なアドレスを使用する場合AX xor:= mem[0x333 +BX + 8*CX] 、フラグはxor操作に従って設定されます。

これで、アドレスを複数回使用することができます。このようなアドレスをレジスタにロードすることは、ステータスフラグを設定することを意図したものではなく、幸いにもそうではありません。「実効アドレスをロードする」というフレーズは、プログラマにそれを認識させます。それが奇妙な表現の源です。

プロセッサが複雑なアドレスを使用してそのコンテンツを処理できるようになると、他の目的のためにそれを計算できるようになることは明らかです。実際x <- 3*x+1、1つの命令で変換を実行するために使用できます。これは、アセンブリプログラミングの一般的な規則です。ボートを揺さぶる方法を使用してください。 重要なのは、命令によって具体化される特定の変換がユーザーにとって役立つかどうかだけです。

ボトムライン

MOV, X| T| AX'| R| BX|

そして

LEA, AX'| [BX]

AXには同じ効果がありますが、ステータスフラグには影響しません。(これはciasdis表記です。)


「これは、アセンブリプログラミングの一般的なルールです。ボートを揺るがす方法を使用してください。」call lbl lbl: pop raxの値を取得する方法として技術的に「機能している」などの理由により、私は個人的にそのアドバイスを提供しripませんが、ブランチ予測を非常に不幸にします。必要に応じて手順を使用しますが、トリッキーなことを実行しても予期しない結果になったとしても驚かないでください
The6P4C

@ The6P4Cこれは便利な警告です。しかし、分岐予測を不幸にする以外に方法がない場合は、それを実行する必要があります。アセンブリプログラミングには、もう1つの一般的な規則があります。何かを行うには別の方法があり、別の方法から賢く選択する必要があります。レジスタBLの内容をレジスタALに取得するには、何百もの方法があります。RAXの残りの部分を保持する必要がない場合は、LEAを選択できます。フラグに影響を及ぼさないことは、何千ものタイプのx86プロセッサーの一部では良い考えです。Groetjes Albert
Albert van der Horst
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.