MOVとLEAの違いは何ですか?


138

これらの指示の違いは何ですか?

MOV AX, [TABLE-ADDR]

そして

LEA AX, [TABLE-ADDR]


8
ニックに感謝します。まず、このリンクを調べても、この質問に対する答えは見つかりませんでした。ここで私は特定の情報を探していました、あなたが提供したリンクでの議論は本質的にもっと一般的です。
naveen 2009年

3
私は@Nickのdupを何年も前に賛成しましたが、vtcはたった今です。振り返ってみると、私はあまりにも急いでいたので、今ではnaveenでa)他の質問は「違いは何ですか」と回答していません。b)これは有用な質問です。私の過ちをお詫び申し上げます-vtcを元に戻すことができれば...
Ruben Bartelink 2012


関連:アドレス/ポインタではない値でLEAを使用していますか?LEAの他の使用法について、恣意的な計算について説明します。
Peter Cordes

回答:


168
  • LEA ロード実効アドレスを意味します
  • MOV 負荷値を意味します

つまり、LEAMOVはそのアドレスの実際の値をロードするのに対し、アドレス指定している項目へのポインターをロードします。

の目的は、LEA重要なアドレス計算を実行して結果を保存できるようにすることです[後で使用するため]

LEA ax, [BP+SI+5] ; Compute address of value

MOV ax, [BP+SI+5] ; Load value at that address

関係する定数のみがある場合、MOV(アセンブラの定数計算により)がの最も単純な使用例と重複するように見えることがありLEAます。複数のベースアドレスなどのマルチパート計算がある場合に役立ちます。


6
明確な説明に感謝し、別の質問に答えるのに役立ちました。
legends2k 2014

leaの名前に「ロード」があると混乱します。メモリの場所を計算するための入力はすべて即時値またはレジスタのいずれかであるため、計算されたアドレスをレジスタに「ロード」すると人々は言います。AFAICT leaは計算のみを実行し、何もロードしません。
ジョセフガービン2017年

2
@josephGarvin IIRCフェッチという用語はその側面に適用されます。ロードは、レジスタの値をゼロから何かに置き換える方法です。例LAHFFLAGSをAHレジスタにロードします。CLRのCIL(より高いレベルのスタックベースの抽象マシン)では、ロードという用語は、想定上のスタックに値を置くことを指し、通常l...であり、s...と同等のものが逆になります)。これらのメモ:cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html)は、区別が当てはまるアーキテクチャが実際にあることを示しています。
Ruben Bartelink 2017年

それはすべてのことを思い出すslideshare.net/pirhilton/... ;)
ルーベンBartelink

45

NASM構文では:

mov eax, var       == lea eax, [var]   ; i.e. mov r32, imm32
lea eax, [var+16]  == mov eax, var+16
lea eax, [eax*4]   == shl eax, 2        ; but without setting flags

MASM構文でOFFSET varは、ロードの代わりにmov-immediateを取得するために使用します。


3
NASM構文のみ。MASM構文でmov eax, varは、はと同じロードであり、ラベルを即値定数として使用するmov eax, [var]ためmov eax, OFFSET varに使用する必要があります。
Peter Cordes

1
明確でシンプルで、私が確認しようとしていたことを示しています。ありがとう。
JayArby 2017年

1
これらすべての例で、leaRIP相対アドレッシングの64ビットモードを除いて、がより良い選択であることに注意してください。 mov r32, imm32より多くのポートで実行されます。 lea eax, [edx*4]それ以外の場合は1つの命令で実行できないコピーアンドシフトですが、同じレジスタ内でLEA [eax*4]disp32=0。(ただし、シフトとは異なるポートで実行されます。)agner.org/optimizeおよびstackoverflow.com/tags/x86/infoを参照してください。
Peter Cordes

29

命令MOV reg、addrは、アドレスaddrに格納されている変数をレジスタregに読み込むことを意味します。命令LEA reg、addrは、アドレス(アドレスに格納されている変数ではない)をレジスタregに読み込むことを意味します。

MOV命令の別の形式は、MOV reg、immdataです。これは、即時データ(つまり、定数)immdataをレジスタregに読み込むことを意味します。LEA reg、addrのaddrが単なる定数(つまり、固定オフセット)の場合、そのLEA命令は、基本的に、即値データと同じ定数をロードする同等のMOV reg、immdata命令とまったく同じです。


11

リテラルのみを指定する場合、違いはありません。ただし、LEAにはより多くの機能があり、それらについては以下で読むことができます。

http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136


たぶん、GNUアセンブラでは.bssセグメントのラベルに関しては真実ではないのでしょうか?AFAIRあなたはleal TextLabel, LabelFromBssSegmentあなたがsmthを得たとき本当にすることはできません。のように.bss .lcomm LabelFromBssSegment, 4、あなたはそうしなければならmovl $TextLabel, LabelFromBssSegmentないでしょうね?
JSmyth 2013

@JSmyth:これleaは、レジスタの宛先が必要なだけの理由ですmovが、imm32ソースとメモリの宛先を持つことができます。もちろん、この制限はGNUアセンブラに固有のものではありません。
Peter Cordes

1
また、質問はMOV AX, [TABLE-ADDR]負荷であるについて尋ねているため、この答えは基本的に間違っています。したがって、大きな違いがあります。同等の指示はmov ax, OFFSET table_addr
Peter Cordes

10

使用するアセンブラーに依存します。

mov ax,table_addr

MASMでは

mov ax,word ptr[table_addr]

したがって、からtable_addrのオフセットではなく、からの最初のバイトをロードしますtable_addr。代わりに使用する必要があります

mov ax,offset table_addr

または

lea ax,table_addr

同じように動作します。

leatable_addrローカル変数の場合、バージョンも正常に機能します。

some_procedure proc

local table_addr[64]:word

lea ax,table_addr

どうもありがとう、それだけで複数の回答をマークすることはできません:(
naveen

5
x86命令のMOVとLEAの違いは、アセンブラーにほとんど依存しません。
IJケネディ

4

これまでの答えはどれも私の混乱の底に達していなかったので、自分の答えを追加したいと思います。

私が欠けていたのは、lea操作が括弧の使用をどのように扱うかとmovは異なるということです。

Cについて考えてみましょう。たとえば、私がlong呼び出す配列があるとしますarray。これで、式array[i]は逆参照を実行し、アドレスarray + i * sizeof(long)[1]のメモリから値をロードします。

一方、式について考えてみます&array[i]。これにはまだサブ式が含まれていますarray[i]が、逆参照は実行されません!の意味array[i]が変わりました。遅延を実行することを意味するのではなく、一種の仕様として機能し、&探しているメモリアドレスを通知します。必要に&応じて、を逆参照の「キャンセル」と考えることもできます。

2つのユースケースは多くの点で類似しているため、構文array[i]は共有されますが、&構文の解釈方法は変更の有無によって変わります。なしでは&、これは逆参照であり、実際には配列から読み取ります。では&、そうではありません。値array + i * sizeof(long)は引き続き計算されますが、逆参照されません。

状況はmovおよびと非常に似ていleaます。を使用するmovと、で発生しない逆参照が発生しleaます。これは、両方で発生する括弧の使用にもかかわらずです。たとえば、movq (%r8), %r9およびleaq (%r8), %r9。ではmov、これらの括弧は「逆参照」を意味します。leaそうではありません。これはarray[i]、がない場合にのみ「逆参照」を意味する方法に似ています&

例は正しいです。

コードを検討する

movq (%rdi, %rsi, 8), %rbp

これにより、メモリ位置の値%rdi + %rsi * 8がレジスタにロードされ%rbpます。つまり、レジスタ%rdiの値とレジスタの値を取得します%rsi。後者に8を掛けてから、前者に追加します。この場所で値を見つけ、それをレジスターに入れ%rbpます。

このコードは、Cのラインに対応するx = array[i];arrayとなる%rdiiなる%rsixなります%rbp8アレイに含まれるデータタイプの長さです。

次に、以下を使用する同様のコードを検討しますlea

leaq (%rdi, %rsi, 8), %rbp

movq逆参照に対応するの使用と同様に、leaqここでの使用は逆参照しないことに対応します。この組立ラインはCラインに対応していx = &array[i];ます。&の意味がarray[i]逆参照から場所の指定に変わったことを思い出してください。同様に、を使用するとleaq、の意味が(%rdi, %rsi, 8)逆参照から場所の指定に変わります。

このコード行のセマンティクスは次のとおりです。%rdiレジスターの値とレジスターの値を取得します%rsi。後者に8を掛けてから、前者に追加します。この値をレジスターに入れ%rbpます。メモリからの負荷は含まれず、算術演算のみが含まれます[2]。

注私の記述の唯一の違いということleaqと、movqつまりは、movq間接参照を行い、そしてleaqません。実際、leaq説明を書くために、私は基本的にの説明をコピーして貼り付け、movq「この場所の値を見つける」を削除しました。

まとめると、movqvs leaqは括弧の使用を(%rsi)とのよう(%rdi, %rsi, 8)に異なる方法で扱うため、扱いが難しいです。でmovq(とを除く他のすべての命令leaで一方)、これらの括弧は、本物の間接参照を表すleaqそうではないと純粋に便利な構文です。


[1] arrayがの配列であるlong場合、式array[i]はアドレスから値をロードすると述べましたarray + i * sizeof(long)。これは本当ですが、対処すべき微妙な点があります。Cコードを書けば

long x = array[5];

これはタイピングと同じではありません

long x = *(array + 5 * sizeof(long));

それは私の以前の発言に基づいているはずですが、そうでありません。

何が起こっているのかというと、Cポインターの追加にはトリックがあります。pタイプの値を指すポインタがあるとしますT。この表現p + iは、「プラスバイトの位置」を意味するものではありませ。代わりに、式は実際には「プラスバイトの位置」を意味します。pip + i pi * sizeof(T)

これの利便性は、「次の値」を取得するためp + 1に、の代わりに記述する必要があることですp + 1 * sizeof(T)

つまり、Cコードlong x = array[5];は実際には

long x = *(array + 5)

Cは自動的にを乗算する5からsizeof(long)です。

このStackOverflowの質問のコンテキストでは、これはどのように関連していますか?これは、「アドレスarray + i * sizeof(long)」と言って、「array + i * sizeof(long)」がCの式として解釈されることを意味するものではありません。私はsizeof(long)自分の答えをより明確にするために自分で乗算を行っていますが、そのため、この式をCとして読み取るべきではないことを理解してください。C構文を使用する通常の数学と同じように。

[2]補足:すべてのlea演算は算術演算であるため、その引数は実際に有効なアドレスを参照する必要はありません。このため、逆参照することを意図していない可能性のある値に対して純粋な演算を実行するためによく使用されます。例えば、cc-O2最適化の並進

long f(long x) {
  return x * 5;
}

次のようになります(無関係な行は削除されます):

f:
  leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
  ret

1
うん、良い説明、他の答えよりも詳細、そしてはい、Cの&演算子は良いアナロジーです。LEAが特殊なケースであることを指摘する価値があるかもしれませんが、MOVはメモリまたはレジスタオペランドを取得できる他のすべての命令と同じです。たとえばadd (%rdi), %eax、MOVと同じように、アドレス指定モードを使用してメモリをアドレス指定します。また関連:アドレス/ポインターではない値でのLEAの使用?LEAは、CPUのHWサポートを使用してアドレス計算を行い、任意の計算を行う方法です。
Peter Cordes

「価値を得る%rdi」-これは不思議な言葉です。つまり、レジスター の値rdi使用する必要があります。「で」の使用は、何もないところにメモリの逆参照を意味するようです。
ecm

@PeterCordesありがとうございます!特別なケースであるという点を回答に追加しました。
ケルクレフ

1
@ecm良い点。気づかなかった。私は今それを変えました、ありがとう!:)
ケルケルフ

FYI、修正は問題のECMが指摘していること短いフレーズが含まれています:「値 %rdiか」値「での %rdi」。「レジスター内の値%rdi」は長くても問題ありませんが、レジスターとメモリーを理解するのに苦労している人を助けるかもしれません。
Peter Cordes

2

基本的に...「REGに移動して...計算した後で...」他の目的にも適しているようです:)

値がポインタであることを忘れた場合は、コードの最適化/最小化に使用できます...

MOV EBX , 1
MOV ECX , 2

;//with 1 instruction you got result of 2 registers in 3rd one ...
LEA EAX , [EBX+ECX+5]

EAX = 8

もともとそれは:

MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5

そう、ハードウェアはModR / M + SIB + disp0 / 8/32をデコードする方法をすでに知っているので、メモリオペランドのマシンエンコーディングと構文を使用leaするシフトアンドアド命令です。
Peter Cordes

1

他の回答で述べたように:

  • MOVつかむだろうでデータを括弧内のアドレスと場所そのデータの宛先へのオペランド。
  • LEA括弧内のアドレスの計算を実行し、その計算されたアドレスを宛先オペランドに配置します。これは、実際にメモリに移動してデータを取得することなく行われます。によって行われた作業LEAは、「実効アドレス」の計算です。

メモリはいくつかの異なる方法でアドレス指定できるため(以下の例を参照)、LEA明示的ADDまたはMUL命令(または同等のもの)を使用せずにレジスタを加算または乗算するために使用されることがあります。

誰もがIntel構文の例を示しているので、AT&T構文の一部を次に示します。

MOVL 16(%ebp), %eax       /* put long  at  ebp+16  into eax */
LEAL 16(%ebp), %eax       /* add 16 to ebp and store in eax */

MOVQ (%rdx,%rcx,8), %rax  /* put qword at  rcx*8 + rdx  into rax */
LEAQ (%rdx,%rcx,8), %rax  /* put value of "rcx*8 + rdx" into rax */

MOVW 5(%bp,%si), %ax      /* put word  at  si + bp + 5  into ax */
LEAW 5(%bp,%si), %ax      /* put value of "si + bp + 5" into ax */

MOVQ 16(%rip), %rax       /* put qword at rip + 16 into rax                 */
LEAQ 16(%rip), %rax       /* add 16 to instruction pointer and store in rax */

MOVL label(,1), %eax      /* put long at label into eax            */
LEAL label(,1), %eax      /* put the address of the label into eax */

lea label, %eax絶対[disp32]アドレス指定モードは必要ありません。mov $label, %eax代わりに使用してください。はい、機能しますが、効率が低下します(マシンコードが大きくなり、実行ユニットが少なくなります)。AT&Tについて言及しているので、アドレス/ポインタではない値にLEAを使用していますか?AT&Tを使用していますが、私の回答には他にもAT&Tの例がいくつかあります。
Peter Cordes

1

例でこれを理解しましょう。

mov eax、[ebx]および

lea eax、[ebx] ebxの値が0x400000であるとします。次に、movはアドレス0x400000に移動し、存在する4バイトのデータをeaxレジスタにコピーします。leaはアドレス0x400000をeaxにコピーします。そのため、各ケースでeaxの各命令値の実行後は次のようになります(メモリ0x400000の包含が30であると仮定)。

eax = 30(movの場合)eax = 0x400000(leaの場合)定義movの場合、rm32から宛先(mov dest rm32)にデータをコピーし、lea(load実効アドレス)は宛先(mov dest rm32)にアドレスをコピーします)。


0

LEA(実効アドレスのロード)は、シフトアンドアド命令です。ハードウェアがアドレス指定モードをデコードおよび計算するために存在するため、8086に追加されました。


0

MOVはLEA [ラベル]と同じことを実行できますが、MOV命令には、命令自体の内部に実効アドレスが直接定数として含まれています(アセンブラによって事前に計算されます)。LEAは、PC相対を使用して、命令の実行中に実効アドレスを計算します。


これは、64ビットモード(PC相対アドレス指定が新しい場所)にのみ当てはまります。他のモードでlea [labelは、よりコンパクトなバイトと比較して、無駄なバイトの無駄があるmovため、話している条件を指定する必要があります。また、一部のアセンブラー[label]は、RIP相対アドレス指定モードの正しい構文ではありません。しかし、はい、それは正確です。 関数またはラベルのアドレスをGNUアセンブラーのレジスターにロードする方法について詳しく説明します。
Peter Cordes

-1

違いは微妙ですが重要です。MOV命令は、実質的に「MOVe」であり、TABLE-ADDRラベルが表すアドレスのコピーです。LEA命令は、間接的な命令である「ロード有効アドレス」です。つまり、TABLE-ADDRは、ロードするアドレスが見つかったメモリ位置を指します。

LEAを効果的に使用することは、Cなどの言語でポインターを使用することと同等であり、強力な命令です。


7
この答えはよくても混乱を招くと思います。「LEA命令は、間接的な命令である「ロード有効アドレス」です。つまり、TABLE-ADDRは、ロードするアドレスが見つかったメモリ位置を指します。」実際には、LEAはアドレスの内容ではなくアドレスをロードします。実際には、質問者はMOVとLEAが重複し、状況によってはまったく同じことを実行できることを安心させる必要があると思います
Bill Forster
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.