Bu talimatlar arasındaki farkın ne olduğunu bilmek istiyorum:
MOV AX, [TABLE-ADDR]
ve
LEA AX, [TABLE-ADDR]
Bu talimatlar arasındaki farkın ne olduğunu bilmek istiyorum:
MOV AX, [TABLE-ADDR]
ve
LEA AX, [TABLE-ADDR]
Yanıtlar:
LEA
Yük Etkili Adres anlamına gelirMOV
Yük Değeri anlamına gelirKısacası, LEA
adreslediğ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.
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.
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 var
bir yük yerine anında bir komut almak için kullanın.
mov eax, var
bir yüktür, aynıdır mov eax, [var]
ve mov eax, OFFSET var
bir etiketi hemen sabit olarak kullanmak için kullanmanız gerekir .
lea
RIP'e dayalı adresleme için 64 bit modu dışında daha kötü bir seçim olduğunu unutmayın . mov r32, imm32
daha 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 .
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.
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
leal TextLabel, LabelFromBssSegment
olduğunda gerçekten yapamazsın . gibi .bss .lcomm LabelFromBssSegment, 4
, gerekecek movl $TextLabel, LabelFromBssSegment
, değil mi?
lea
bir kayıt hedefi gerektirmesidir, ancak mov
bir imm32
kaynağı ve bir hafıza hedefi olabilir. Bu sınırlama elbette GNU montajcısına özgü değildir.
MOV AX, [TABLE-ADDR]
, ki bu bir yük. Yani büyük bir fark var. Eşdeğer talimatmov ax, OFFSET table_addr
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.
lea
sürümü table_addr
yerel bir değişken ise de iyi çalışıyor örn.
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
Ö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, lea
iş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 long
ben ç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 mov
ve 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), %r9
ve leaq (%r8), %r9
. İle mov
, bu parantez "dereference" anlamına gelir; ile lea
değ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 * 8
kayıt defterine yükler %rbp
. Yani: kayıttaki %rdi
değ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];
, array
olur %rdi
ve i
olur %rsi
ve x
olur C çizgisine karşılık gelir %rbp
. 8
Dizisi 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 movq
dereferencing karşılık, kullanımı leaq
burada 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, leaq
değ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 %rdi
değ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 leaq
ve movq
olmasıdır movq
bir dereference yapar ve leaq
yapmaz. Aslında, leaq
açıklamayı yazmak için , temelde açıklamasını kopyalayıp yapıştırdım movq
ve "Bu konumda değeri bul" u kaldırdım.
Özetlemek gerekirse: movq
vs. 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ç lea
oysa) bu parantezler, gerçek bir dereference belirtmek leaq
onlar değil ve tamamen uygun sözdizimi vardır.
[1] array
Bir dizi olduğunda long
ifadenin 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 p
tip değerlere işaret eden bir işaretçi var T
. İfadesi p + i
yapar değil ortalama "adresinde konum p
artı i
bayt". Bunun yerine, ifade p + i
aslında " p
artı i * sizeof(T)
bayttaki konum" anlamına gelir .
Bunun kolaylığı, "bir sonraki değeri" elde etmek için sadece yazmak p + 1
yerinep + 1 * sizeof(T)
.
Bu, C kodunun long x = array[5];
aslında
long x = *(array + 5)
C otomatik çarpma nedeniyle 5
tarafı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, cc
ile -O2
optimizasyon ç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
&
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.
%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.
%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.
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
lea
bir 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.
Diğer cevaplarda belirtildiği gibi:
MOV
çekecek veri parantez içindeki adres ve yerinde olduğunu veri hedefe içine işlenen.LEA
parantez 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), LEA
bazen açık ADD
veya MUL
talimat (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 */
lea label, %eax
mutlak bir [disp32]
adresleme modu istemezsiniz . mov $label, %eax
Bunun 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.
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) ).
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.
lea [label
ise 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.
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.