MOV ve LEA arasındaki fark nedir?


139

Bu talimatlar arasındaki farkın ne olduğunu bilmek istiyorum:

MOV AX, [TABLE-ADDR]

ve

LEA AX, [TABLE-ADDR]


8
teşekkürler nick. Her şeyden önce, bu bağlantıya bakarak bu soruya bir cevap bulamazdım. Burada belirli bir bilgi arıyordum, sağladığınız bağlantıdaki tartışma doğada daha genel.
naveen

3
@ Nick'in çağlar öncesini iptal ettim ama şimdi vtc'd. Yansıtma üzerine, çok aceleciydim ve şimdi naveen ile a) diğer soru "fark nedir" ve b) bu ​​yararlı bir soru. Benim hatam için naveen için özür - sadece vtc geri alabilir eğer ...
Ruben Bartelink


İlgili: Adres / işaretçi olmayan değerlerde LEA mı kullanıyorsunuz? keyfi matematik için LEA'nın diğer kullanımlarından bahseder.
Peter Cordes

Yanıtlar:


169
  • LEA Yük Etkili Adres anlamına gelir
  • MOV Yük Değeri anlamına gelir

Kısacası, LEAadreslediğiniz öğeye bir işaretçi yüklerken MOV bu adrese gerçek değeri yükler.

Amacı, LEAönemsiz olmayan bir adres hesaplaması yapmak ve sonucu saklamaktır [sonucu daha sonra kullanmak için]

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

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

Sadece sabitler söz konusu olduğunda, MOV(montajcının sabit hesaplamaları yoluyla) bazen en basit kullanım durumlarıyla örtüşebilir LEA. Birden fazla taban adresi vb. İçeren çok parçalı bir hesaplamanız varsa kullanışlıdır.


6
Açık açıklama için +1 teşekkürler, başka bir soruya cevap vermeme yardımcı oldu .
legends2k

Lea adında "load" olduğunu ve insanların bir hesaplanmış adresi bir kayıt defterine "yüklediğini" söyler, çünkü bellek konumunu hesaplamak için tüm girdiler ya anlık değerler ya da kayıtlardır. AFAICT lea sadece bir hesaplama yapar, hiçbir şey yüklemez, burada yükleme belleğe dokunmak demektir?
Joseph Garvin

2
Getirme terimi bu yöne uygulanacaktır; Yük, bir kayıttaki değeri sıfırdan bir şeyle nasıl değiştirdiğinizdir. örn LAHF.: FLAGS'ı AH kaydına yükleyin . CLR'nin CIL'sinde (daha üst düzey bir yığın tabanlı soyut makine olan yük terimi, kavramsal yığın üzerine bir değer koymayı ifade eder ve normalde l... ve s... eşdeğeri tersini yapar). Bu notlar: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ), ayrımınızın geçerli olduğu mimariler olduğunu gösterir.
Ruben Bartelink

hepsi bana slideshare.net/pirhilton/… ;) hatırlatıyor
Ruben Bartelink

45

NASM sözdiziminde:

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 sözdiziminde, OFFSET varbir yük yerine anında bir komut almak için kullanın.


3
yalnızca NASM sözdiziminde. MASM sözdiziminde, mov eax, varbir yüktür, aynıdır mov eax, [var]ve mov eax, OFFSET varbir etiketi hemen sabit olarak kullanmak için kullanmanız gerekir .
Peter Cordes

1
Açık, basit ve neyi doğrulamaya çalıştığımı gösteriyor. Teşekkürler.
JayArby

1
Bu örneklerin tümünde, leaRIP'e dayalı adresleme için 64 bit modu dışında daha kötü bir seçim olduğunu unutmayın . mov r32, imm32daha fazla bağlantı noktasında çalışır. lea eax, [edx*4]aksi takdirde tek bir komutta yapılamayan bir kopyala ve kaydır işlemidir, ancak aynı kayıtta LEA sadece kodlamak için daha fazla bayt alır çünkü bir [eax*4]gerektirir disp32=0. (Ancak vardiyalardan farklı bağlantı noktalarında çalışır.) Bkz. Agner.org/optimize ve stackoverflow.com/tags/x86/info .
Peter Cordes

29

MOV reg, addr komutu, addr adresinde kayıtlı bir değişkeni kayıt reg'e okumak anlamına gelir. LEA reg, addr komutu, adresi kayıt (adrese kaydedilen değişken değil) reg reg'e okumak anlamına gelir.

MOV komutunun başka bir formu da MOV reg, immdata yani anlık verileri (yani sabit) immdata kayıt reg. Eğer LEA reg, addr sadece bir sabit (yani sabit bir ofset) ise, o zaman LEA talimatı esas olarak eşdeğer bir MOV reg, anlık veri ile aynı sabiti yükleyen immdata talimatı ile tamamen aynı olduğunu unutmayın.


11

Yalnızca bir değişmez değer belirtirseniz, fark yoktur. LEA'nın daha fazla yeteneği var ve bunları burada okuyabilirsiniz:

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


Sanırım, GNU montajcısında .bss segmentindeki etiketler söz konusu olduğunda bu doğru değil mi? AFAIR bir şey leal TextLabel, LabelFromBssSegmentolduğunda gerçekten yapamazsın . gibi .bss .lcomm LabelFromBssSegment, 4, gerekecek movl $TextLabel, LabelFromBssSegment, değil mi?
JSmyth

@JSmyth: Bunun nedeni yalnızca leabir kayıt hedefi gerektirmesidir, ancak movbir imm32kaynağı ve bir hafıza hedefi olabilir. Bu sınırlama elbette GNU montajcısına özgü değildir.
Peter Cordes

1
Ayrıca, bu cevap temelde yanlıştır, çünkü soru soruyor MOV AX, [TABLE-ADDR], ki bu bir yük. Yani büyük bir fark var. Eşdeğer talimatmov ax, OFFSET table_addr
Peter Cordes

10

Kullanılan montajcıya bağlıdır, çünkü

mov ax,table_addr

MASM olarak çalışır

mov ax,word ptr[table_addr]

Bu nedenle table_addr, ofsetten değil, ilk baytları yükler table_addr. Bunun yerine kullanmalısın

mov ax,offset table_addr

veya

lea ax,table_addr

aynı şekilde çalışır.

leasürümü table_addryerel bir değişken ise de iyi çalışıyor örn.

some_procedure proc

local table_addr[64]:word

lea ax,table_addr

çok teşekkürler, onun sadece ben cevap olarak birden fazla işaretleyemezsiniz :(
naveen

5
X86 MOV ve LEA talimatları arasındaki fark kesinlikle montaja bağlı DEĞİLDİR.
IJ Kennedy

4

Önceki cevapların hiçbiri kendi karışıklığımın dibine ulaşamadı, bu yüzden kendi cevaplarımı eklemek istiyorum.

Eksik olduğum şey, leaişlemlerin parantez kullanımını nasıl yaptığından farklı olmasıdır mov.

Ben bir dizi var C. edelim söz hakkından düşünün longben çağrısı array. Şimdi ifade array[i], adresteki bellekten değeri yükleyerek bir dereference gerçekleştirirarray + i * sizeof(long) [1] .

Öte yandan, ifadeyi düşünün &array[i]. Bu hala alt ifadeyi içerir array[i], ancak silme işlemi gerçekleştirilmez! Anlamı array[i]değişti. Artık bir erteleme yapmak anlamına gelmez, bunun yerine hangi bellek adresini aradığımızı söyleyen bir tür spesifikasyon görevi görür &. İsterseniz, alternatif olarak& olarak dereference "iptal" olarak .

İki kullanım durumu birçok yönden benzer olduğu için sözdizimini paylaşırlar array[i], ancak &sözdiziminin nasıl yorumlandığına dair değişikliklerin varlığı veya yokluğu . Olmadan &, bir dereference ve aslında diziden okur. İle &değil. Değer array + i * sizeof(long)hala hesaplanıyor, ancak kayıttan çıkarılmadı.

Durum ile çok benzer movve lea. İle mov, gerçekleşmeyen bir dereference oluşur lea. Bu, her ikisinde de bulunan parantezlerin kullanılmasına rağmen. Örneğin, movq (%r8), %r9ve leaq (%r8), %r9. İle mov, bu parantez "dereference" anlamına gelir; ile leadeğil. Bu, array[i]sadece yokken sadece "dereference" anlamına gelir &.

Bir örnek sırayla.

Kodu düşünün

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

Bu, bellek konumundaki değeri %rdi + %rsi * 8kayıt defterine yükler %rbp. Yani: kayıttaki %rdideğeri ve kayıttaki değeri alın %rsi. İkincisini 8 ile çarpın ve sonra birincisine ekleyin. Bu konumdaki değeri bulun ve kayıt defterine yerleştirin %rbp.

Bu kod x = array[i];, arrayolur %rdive iolur %rsive xolur C çizgisine karşılık gelir %rbp. 8Dizisi içinde ihtiva edilen veri türü uzunluğudur.

Şimdi aşağıdakileri kullanan benzer kodu düşünün lea:

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

Sadece kullanımı gibi movqdereferencing karşılık, kullanımı leaqburada tekabül değil dereferencing. Bu montaj hattı C hattına karşılık gelir x = &array[i];. Unutmayın ki , kayıt silme işleminin &anlamını array[i]basitçe bir konum belirlemeye değiştirir. Aynı şekilde, leaqdeğişikliklerin kullanımı(%rdi, %rsi, 8) kaldırma bir konum belirlemeye kadar değiştirir.

Bu kod satırının semantiği aşağıdaki gibidir: kayıttaki %rdideğeri ve kayıttaki değeri alın %rsi. İkincisini 8 ile çarpın ve sonra birincisine ekleyin. Bu değeri kayıt defterine yerleştirin%rbp . Bellekten yük yoktur, sadece aritmetik işlemler [2].

Not my açıklamaları arasındaki tek fark o leaqve movqolmasıdır movqbir dereference yapar ve leaqyapmaz. Aslında, leaqaçıklamayı yazmak için , temelde açıklamasını kopyalayıp yapıştırdım movqve "Bu konumda değeri bul" u kaldırdım.

Özetlemek gerekirse: movqvs. leaqçok zordur çünkü parantez kullanımını içeride (%rsi)ve (%rdi, %rsi, 8)farklı şekilde ele alırlar . In movq(ve diğer tüm talimat hariç leaoysa) bu parantezler, gerçek bir dereference belirtmek leaqonlar değil ve tamamen uygun sözdizimi vardır.


[1] arrayBir dizi olduğunda longifadenin array[i]değeri adresten yüklediğini söyledim array + i * sizeof(long). Bu doğrudur, ancak ele alınması gereken bir incelik vardır. C kodunu yazarsam

long x = array[5];

bu yazmakla aynı şey değil

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

Önceki ifadelerime dayanması gerektiği anlaşılıyor , ama değil.

Neler oluyor C işaretçisinin eklenmesinin bir hilesi var. Diyelim ki ptip değerlere işaret eden bir işaretçi var T. İfadesi p + iyapar değil ortalama "adresinde konum partı ibayt". Bunun yerine, ifade p + i aslında " partı i * sizeof(T)bayttaki konum" anlamına gelir .

Bunun kolaylığı, "bir sonraki değeri" elde etmek için sadece yazmak p + 1yerinep + 1 * sizeof(T) .

Bu, C kodunun long x = array[5];aslında

long x = *(array + 5)

C otomatik çarpma nedeniyle 5tarafından sizeof(long).

Peki bu StackOverflow sorusu bağlamında, bunların hepsi nasıl alakalı? Ben "Adres derken demektir array + i * sizeof(long)", ben do not "için demek array + i * sizeof(long)" C ifadesi olarak yorumlanmalıdır. sizeof(long)Cevabımı daha açık hale getirmek için kendi kendime çarpımı yapıyorum , ancak bu nedenle bu ifadenin C olarak okunmaması gerektiğini anlıyorum. Tıpkı C sözdizimini kullanan normal matematik gibi.

[2] Yan not: her leaşey aritmetik işlemler olduğundan, argümanlarının aslında geçerli adresleri ifade etmesi gerekmez. Bu nedenle, genellikle kayıttan çıkarılması amaçlanmayan değerler üzerinde saf aritmetik yapmak için kullanılır. Örneğin, ccile -O2optimizasyon çevirir

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

aşağıdakilere (alakasız çizgiler kaldırıldı):

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

1
Evet, diğer açıklamalardan daha ayrıntılı olarak iyi bir açıklama ve evet C'nin &operatörü iyi bir benzetme. Belki de LEA'nın özel durum olduğunu belirtmeye değer, MOV ise bir bellek alabilir veya işlenen kaydedebilir. örneğin add (%rdi), %eax, MOV ile aynı hafızayı adreslemek için adresleme modunu kullanır. Ayrıca ilgili: Adres / işaretçi olmayan değerlerde LEA mı kullanıyorsunuz? bu açıklamayı daha ileri götürür: LEA, adres hesaplamaları için keyfi hesaplamalar yapmak üzere CPU'nun HW desteğini nasıl kullanabileceğinizdir.
Peter Cordes

"değeri almak %rdi" - Bu garip bir şekilde ifade edilir. Sen değer anlamına kayıt rdi kullanılmalıdır. "At" işlevini kullanmanız, hiçbir yerde olmayan bir bellek dereference anlamına gelir.
ecm

@PeterCordes Teşekkürler! Cevabın özel bir durum olduğu konusunu ekledim.
Quelklef

1
@ecm İyi bir nokta; Fark etmedim. Şimdi değiştirdim, teşekkürler! :)
Quelklef

Bilginize, düzeltmeler sorun ecm olduğuna dikkat çeken kısa cümleleme içerir: "değerini ait %rdi ya da" değer " in %rdi ". "Kayıttaki değeriniz %rdi" uzun ama gayet iyi ve belki de kayıtları ve hafızayı anlamaya çalışan birine yardımcı olabilir.
Peter Cordes

2

Temel olarak ... "REG içine hesaplayın ... hesapladıktan sonra ..." başka amaçlar için de hoş görünüyor :)

değerin bir işaretçi olduğunu unutursanız, kod optimizasyonu / minimizasyonu için kullanabilirsiniz ... ne olursa olsun ..

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

aslında olurdu:

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

Yup, leabir vardiya ve ekleme talimatıdır bellek-işlenen makine kodlama ve sözdizimini kullanan , çünkü donanım ModR / M + SIB + disp0 / 8/32 kodunun nasıl çözüleceğini zaten biliyor.
Peter Cordes

1

Diğer cevaplarda belirtildiği gibi:

  • MOVçekecek veri parantez içindeki adres ve yerinde olduğunu veri hedefe içine işlenen.
  • LEAparantez içindeki adres hesaplamasını gerçekleştirir ve bu hesaplanan adresi hedef işlenene yerleştirir. Bu, aslında belleğe çıkmadan ve verileri elde etmeden olur. Tarafından yapılan çalışma LEA"etkili adres" in hesaplanmasıdır.

Bellek birkaç farklı yolla ele alınabileceğinden (aşağıdaki örneklere bakın), LEAbazen açık ADDveya MULtalimat (veya eşdeğeri) kullanmadan kayıt eklemek veya çoğaltmak için kullanılır .

Herkes Intel sözdiziminde örnekler gösterdiğinden, AT&T sözdiziminde bazıları:

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 */

Asla lea label, %eaxmutlak bir [disp32]adresleme modu istemezsiniz . mov $label, %eaxBunun yerine kullanın . Evet çalışır, ancak daha az verimlidir (daha büyük makine kodu ve daha az yürütme biriminde çalışır). AT&T'den bahsettiğinizden beri adres / işaretçi olmayan değerlerde LEA mı kullanıyorsunuz? AT&T kullanıyor ve cevabımda başka AT&T örnekleri de var.
Peter Cordes

1

Bunu bir örnekle anlayalım.

mov eax, [ebx] ve

lea eax, [ebx] Ebx içindeki değerin 0x400000 olduğunu varsayalım. Daha sonra mov 0x400000 adresine gidecek ve 4 baytlık veriyi eax kaydına kopyalayacak, lea ise 0x400000 adresini eax'a kopyalayacaktır. Bu nedenle, her durumda eax her talimat değerinin yürütülmesinden sonra (0x400000 içeren bellekte 30 varsayalım) varsayalım.

eax = 30 (mov durumunda) eax = 0x400000 (lea durumunda) Tanım mov için tanım rm32'den hedefe (mov dest rm32) ve lea (yük etkin adres) adresi hedefe kopyalayacaktır (mov dest rm32) ).


0

LEA (Etkili Adresi Yükle) bir vardiya ve ekleme talimatıdır. 8086'ya eklendi, çünkü donanım adresleme modlarının kodunu çözmek ve hesaplamak için orada.


0

MOV, LEA [etiket] ile aynı şeyi yapabilir, ancak MOV talimatı talimatın içindeki etkin adresi bir anlık sabit olarak içerir (önceden montajcı tarafından hesaplanır). LEA, talimatın yürütülmesi sırasında etkili adresi hesaplamak için PC'ye göre kullanır.


Bu sadece 64 bitlik mod için geçerlidir (PC'ye bağlı adreslemenin yeni olduğu yerlerde); diğer modlarda lea [labelise daha kompakt olana kıyasla anlamsız bir bayt kaybıdır mov, bu nedenle bahsettiğiniz koşulları belirtmelisiniz. Ayrıca, bazı montajcılar [label]için RIP göreli adresleme modu için doğru sözdizimi yoktur. Ama evet, bu doğru. GNU Assembler'da fonksiyon veya etiket adresinin kayıt defterine nasıl yükleneceği daha ayrıntılı olarak açıklanmaktadır.
Peter Cordes

-1

Fark ince ama önemlidir. MOV talimatı, TABLO-ADDR etiketinin temsil ettiği adresin etkin bir şekilde bir 'MOVe' kopyasıdır. LEA komutu, dolaylı bir talimat olan 'Yük Etkili Adres' dir, yani TABLO-ADDR'nin yüklenecek adresin bulunduğu bir bellek konumuna işaret ettiği anlamına gelir.

LEA'yı etkili bir şekilde kullanmak, güçlü bir talimat olduğu için C gibi dillerde işaretçiler kullanmaya eşdeğerdir.


7
Bence bu cevap en iyi ihtimalle kafa karıştırıcı. "LEA komutu, dolaylı bir talimat olan 'Yük Etkili Adres' dir; bu, TABLO-ADDR'nin yüklenecek adresin bulunduğu bir bellek konumuna işaret ettiği anlamına gelir." Aslında LEA, adresin içeriğini değil adresi yükleyecektir. Aslında soru soran kişinin MOV ve LEA'nın bazı durumlarda üst üste binebileceği ve tam olarak aynı şeyi yapabileceği konusunda güvence altına alınması gerekiyor
Bill Forster
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.