これらの指示の違いは何ですか?
MOV AX, [TABLE-ADDR]
そして
LEA AX, [TABLE-ADDR]
これらの指示の違いは何ですか?
MOV AX, [TABLE-ADDR]
そして
LEA AX, [TABLE-ADDR]
回答:
LEA
ロード実効アドレスを意味しますMOV
負荷値を意味しますつまり、LEA
MOVはそのアドレスの実際の値をロードするのに対し、アドレス指定している項目へのポインターをロードします。
の目的は、LEA
重要なアドレス計算を実行して結果を保存できるようにすることです[後で使用するため]
LEA ax, [BP+SI+5] ; Compute address of value
MOV ax, [BP+SI+5] ; Load value at that address
関係する定数のみがある場合、MOV
(アセンブラの定数計算により)がの最も単純な使用例と重複するように見えることがありLEA
ます。複数のベースアドレスなどのマルチパート計算がある場合に役立ちます。
LAHF
:FLAGSをAHレジスタにロードします。CLRのCIL(より高いレベルのスタックベースの抽象マシン)では、ロードという用語は、想定上のスタックに値を置くことを指し、通常l
...であり、s
...と同等のものが逆になります)。これらのメモ:cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html)は、区別が当てはまるアーキテクチャが実際にあることを示しています。
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を取得するために使用します。
mov eax, var
は、はと同じロードであり、ラベルを即値定数として使用するmov eax, [var]
ためmov eax, OFFSET var
に使用する必要があります。
lea
RIP相対アドレッシングの64ビットモードを除いて、がより良い選択であることに注意してください。 mov r32, imm32
より多くのポートで実行されます。 lea eax, [edx*4]
それ以外の場合は1つの命令で実行できないコピーアンドシフトですが、同じレジスタ内でLEA [eax*4]
はdisp32=0
。(ただし、シフトとは異なるポートで実行されます。)agner.org/optimizeおよびstackoverflow.com/tags/x86/infoを参照してください。
命令MOV reg、addrは、アドレスaddrに格納されている変数をレジスタregに読み込むことを意味します。命令LEA reg、addrは、アドレス(アドレスに格納されている変数ではない)をレジスタregに読み込むことを意味します。
MOV命令の別の形式は、MOV reg、immdataです。これは、即時データ(つまり、定数)immdataをレジスタregに読み込むことを意味します。LEA reg、addrのaddrが単なる定数(つまり、固定オフセット)の場合、そのLEA命令は、基本的に、即値データと同じ定数をロードする同等のMOV reg、immdata命令とまったく同じです。
リテラルのみを指定する場合、違いはありません。ただし、LEAにはより多くの機能があり、それらについては以下で読むことができます。
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
leal TextLabel, LabelFromBssSegment
あなたがsmthを得たとき本当にすることはできません。のように.bss .lcomm LabelFromBssSegment, 4
、あなたはそうしなければならmovl $TextLabel, LabelFromBssSegment
ないでしょうね?
lea
は、レジスタの宛先が必要なだけの理由ですmov
が、imm32
ソースとメモリの宛先を持つことができます。もちろん、この制限はGNUアセンブラに固有のものではありません。
MOV AX, [TABLE-ADDR]
負荷であるについて尋ねているため、この答えは基本的に間違っています。したがって、大きな違いがあります。同等の指示はmov ax, OFFSET table_addr
使用するアセンブラーに依存します。
mov ax,table_addr
MASMでは
mov ax,word ptr[table_addr]
したがって、からtable_addr
のオフセットではなく、からの最初のバイトをロードしますtable_addr
。代わりに使用する必要があります
mov ax,offset table_addr
または
lea ax,table_addr
同じように動作します。
lea
table_addr
ローカル変数の場合、バージョンも正常に機能します。
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
これまでの答えはどれも私の混乱の底に達していなかったので、自分の答えを追加したいと思います。
私が欠けていたのは、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
となる%rdi
とi
なる%rsi
とx
なります%rbp
。8
アレイに含まれるデータタイプの長さです。
次に、以下を使用する同様のコードを検討します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
「この場所の値を見つける」を削除しました。
まとめると、movq
vs 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
は、「プラスバイトの位置」を意味するものではありません。代わりに、式は実際には「プラスバイトの位置」を意味します。p
i
p + i
p
i * 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
&
演算子は良いアナロジーです。LEAが特殊なケースであることを指摘する価値があるかもしれませんが、MOVはメモリまたはレジスタオペランドを取得できる他のすべての命令と同じです。たとえばadd (%rdi), %eax
、MOVと同じように、アドレス指定モードを使用してメモリをアドレス指定します。また関連:アドレス/ポインターではない値でのLEAの使用?LEAは、CPUのHWサポートを使用してアドレス計算を行い、任意の計算を行う方法です。
%rdi
」-これは不思議な言葉です。つまり、レジスター の値をrdi
使用する必要があります。「で」の使用は、何もないところにメモリの逆参照を意味するようです。
%rdi
か」値「での %rdi
」。「レジスター内の値%rdi
」は長くても問題ありませんが、レジスターとメモリーを理解するのに苦労している人を助けるかもしれません。
基本的に...「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
lea
するシフトアンドアド命令です。
他の回答で述べたように:
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の例がいくつかあります。
例でこれを理解しましょう。
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)にアドレスをコピーします)。
MOVはLEA [ラベル]と同じことを実行できますが、MOV命令には、命令自体の内部に実効アドレスが直接定数として含まれています(アセンブラによって事前に計算されます)。LEAは、PC相対を使用して、命令の実行中に実効アドレスを計算します。
lea [label
は、よりコンパクトなバイトと比較して、無駄なバイトの無駄があるmov
ため、話している条件を指定する必要があります。また、一部のアセンブラー[label]
は、RIP相対アドレス指定モードの正しい構文ではありません。しかし、はい、それは正確です。 関数またはラベルのアドレスをGNUアセンブラーのレジスターにロードする方法について詳しく説明します。
違いは微妙ですが重要です。MOV命令は、実質的に「MOVe」であり、TABLE-ADDRラベルが表すアドレスのコピーです。LEA命令は、間接的な命令である「ロード有効アドレス」です。つまり、TABLE-ADDRは、ロードするアドレスが見つかったメモリ位置を指します。
LEAを効果的に使用することは、Cなどの言語でポインターを使用することと同等であり、強力な命令です。