Saya ingin tahu apa perbedaan antara instruksi ini:
MOV AX, [TABLE-ADDR]
dan
LEA AX, [TABLE-ADDR]
Saya ingin tahu apa perbedaan antara instruksi ini:
MOV AX, [TABLE-ADDR]
dan
LEA AX, [TABLE-ADDR]
Jawaban:
LEA
berarti Muat Alamat EfektifMOV
berarti Nilai BebanSingkatnya, LEA
muat penunjuk ke item yang Anda tangani sedangkan MOV memuat nilai sebenarnya di alamat itu.
Tujuannya LEA
adalah untuk memungkinkan seseorang melakukan penghitungan alamat non-sepele dan menyimpan hasilnya [untuk penggunaan nanti]
LEA ax, [BP+SI+5] ; Compute address of value
MOV ax, [BP+SI+5] ; Load value at that address
Jika hanya ada konstanta yang terlibat, MOV
(melalui kalkulasi konstanta assembler) terkadang terlihat tumpang tindih dengan kasus penggunaan yang paling sederhana LEA
. Ini berguna jika Anda memiliki kalkulasi multi-bagian dengan beberapa alamat dasar, dll.
LAHF
: Muat BENDERA ke register AH . Dalam CLR's CIL (yang merupakan mesin abstrak berbasis tumpukan tingkat yang lebih tinggi, istilah beban mengacu pada meletakkan nilai ke tumpukan nosional dan biasanya l
..., dan s
... setara melakukan kebalikannya). Catatan ini: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ) menunjukkan bahwa memang ada arsitektur di mana perbedaan Anda berlaku.
Dalam sintaks 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
Dalam sintaks MASM, gunakan OFFSET var
untuk mendapatkan mov-segera alih-alih memuat.
mov eax, var
is a load, sama dengan mov eax, [var]
, dan Anda harus mov eax, OFFSET var
menggunakan label sebagai konstanta langsung.
lea
adalah pilihan yang lebih buruk kecuali dalam mode 64-bit untuk pengalamatan relatif RIP. mov r32, imm32
berjalan di lebih banyak port. lea eax, [edx*4]
adalah copy-and-shift yang tidak dapat dilakukan dalam satu instruksi sebaliknya, tetapi dalam register yang sama LEA hanya membutuhkan lebih banyak byte untuk dikodekan karena [eax*4]
membutuhkan disp32=0
. (Ini berjalan pada port yang berbeda dari shift.) Lihat agner.org/optimize dan stackoverflow.com/tags/x86/info .
Instruksi MOV reg, addr berarti membaca variabel yang disimpan di address addr ke register reg. Instruksi LEA reg, addr artinya membaca alamat (bukan variabel yang disimpan di alamat) ke register reg.
Bentuk lain dari instruksi MOV adalah MOV reg, immdata yang berarti membaca data langsung (yaitu konstan) immdata ke register reg. Perhatikan bahwa jika addr dalam LEA reg, addr hanyalah sebuah konstanta (yaitu offset tetap) maka instruksi LEA itu pada dasarnya persis sama dengan instruksi MOV reg yang setara, instruksi immdata yang memuat konstanta yang sama dengan data langsung.
Jika Anda hanya menentukan literal, tidak ada perbedaan. LEA memiliki lebih banyak kemampuan, dan Anda dapat membacanya di sini:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
leal TextLabel, LabelFromBssSegment
ketika Anda mendapatkan sesuatu. seperti .bss .lcomm LabelFromBssSegment, 4
, Anda harus movl $TextLabel, LabelFromBssSegment
, bukan?
lea
memerlukan tujuan register, tetapi mov
dapat memiliki imm32
sumber dan tujuan memori. Batasan ini tentu saja tidak spesifik untuk perakit GNU.
MOV AX, [TABLE-ADDR]
, yaitu beban. Jadi ada perbedaan besar. Instruksi yang setara adalahmov ax, OFFSET table_addr
Itu tergantung assembler yang digunakan, karena
mov ax,table_addr
di MASM bekerja sebagai
mov ax,word ptr[table_addr]
Jadi itu memuat byte pertama dari table_addr
dan BUKAN offset ke table_addr
. Anda harus menggunakan sebagai gantinya
mov ax,offset table_addr
atau
lea ax,table_addr
yang berfungsi sama.
lea
versi juga berfungsi dengan baik jika table_addr
variabel lokal misalnya
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
Tak satu pun dari jawaban sebelumnya yang benar-benar membingungkan saya, jadi saya ingin menambahkan jawaban saya sendiri.
Apa yang saya lewatkan adalah lea
operasi memperlakukan penggunaan tanda kurung berbeda dari bagaimana mov
.
Pikirkan C. Katakanlah saya memiliki array long
yang saya sebut array
. Sekarang ekspresi array[i]
melakukan dereferensi, memuat nilai dari memori di alamat array + i * sizeof(long)
[1].
Di sisi lain, pertimbangkan ekspresinya &array[i]
. Ini masih berisi sub-ekspresi array[i]
, tetapi tidak ada dereferensi yang dilakukan! Arti array[i]
telah berubah. Ini tidak lagi berarti melakukan penghormatan tetapi bertindak sebagai semacam spesifikasi , memberi &
tahu alamat memori apa yang kita cari. Jika Anda suka, Anda dapat menganggapnya &
sebagai "membatalkan" dereferensi.
Karena dua kasus penggunaan serupa dalam banyak hal, keduanya berbagi sintaks array[i]
, tetapi ada atau tidaknya &
perubahan cara interpretasi sintaks tersebut. Tanpa &
, itu dereferensi dan benar-benar membaca dari array. Dengan &
, tidak. Nilainya array + i * sizeof(long)
masih dihitung, tetapi tidak dideferensiasi.
Situasinya sangat mirip dengan mov
dan lea
. Dengan mov
, terjadi dereferensi yang tidak terjadi dengan lea
. Ini terlepas dari penggunaan tanda kurung yang terjadi di keduanya. Misalnya, movq (%r8), %r9
dan leaq (%r8), %r9
. Dengan mov
, tanda kurung ini berarti "dereferensi"; dengan lea
, mereka tidak. Ini mirip dengan bagaimana array[i]
hanya berarti "dereferensi" jika tidak ada &
.
Contohnya adalah dalam urutan.
Pertimbangkan kodenya
movq (%rdi, %rsi, 8), %rbp
Ini memuat nilai di lokasi memori %rdi + %rsi * 8
ke dalam register %rbp
. Yaitu: dapatkan nilai di register %rdi
dan nilai di register %rsi
. Kalikan yang terakhir dengan 8, lalu tambahkan ke yang pertama. Temukan nilainya di lokasi ini dan tempatkan ke dalam register %rbp
.
Kode ini sesuai dengan baris C x = array[i];
, di mana array
menjadi %rdi
dan i
menjadi %rsi
dan x
menjadi %rbp
. Ini 8
adalah panjang tipe data yang terdapat dalam larik.
Sekarang pertimbangkan kode serupa yang menggunakan lea
:
leaq (%rdi, %rsi, 8), %rbp
Sama seperti penggunaan movq
terkait dengan dereferensi, penggunaan di leaq
sini terkait dengan tidak dereferensi. Baris ini dari perakitan sesuai dengan jalur C x = &array[i];
. Ingatlah bahwa &
mengubah arti array[i]
dari dereferensi menjadi sekadar menentukan lokasi. Demikian pula penggunaan leaq
mengubah arti (%rdi, %rsi, 8)
dari dereferensi menjadi menentukan lokasi.
Semantik dari baris kode ini adalah sebagai berikut: dapatkan nilai di register %rdi
dan nilai di register %rsi
. Kalikan yang terakhir dengan 8, lalu tambahkan ke yang pertama. Tempatkan nilai ini ke dalam register %rbp
. Tidak ada beban dari memori yang terlibat, hanya operasi aritmatika [2].
Perhatikan bahwa satu-satunya perbedaan antara uraian saya tentang leaq
dan movq
adalah movq
perbedaan pendapat, dan leaq
tidak. Sebenarnya, untuk menulis leaq
deskripsi, saya pada dasarnya menyalin + menempelkan deskripsi movq
, dan kemudian menghapus "Temukan nilai di lokasi ini".
Untuk meringkas: movq
vs. leaq
rumit karena mereka memperlakukan penggunaan tanda kurung, seperti dalam (%rsi)
dan (%rdi, %rsi, 8)
, berbeda. Dalam movq
(dan semua instruksi lain kecuali lea
), tanda kurung ini menunjukkan dereferensi asli, sedangkan di dalamnya leaq
tidak dan merupakan sintaks murni yang nyaman.
[1] Saya telah mengatakan bahwa when array
adalah sebuah array long
, ekspresi array[i]
memuat nilai dari alamatnya array + i * sizeof(long)
. Ini benar, tetapi ada sedikit kehalusan yang harus diatasi. Jika saya menulis kode C.
long x = array[5];
ini tidak sama dengan mengetik
long x = *(array + 5 * sizeof(long));
Tampaknya itu harus didasarkan pada pernyataan saya sebelumnya, tetapi sebenarnya tidak.
Apa yang terjadi adalah bahwa penambahan penunjuk C memiliki trik untuk itu. Katakanlah saya memiliki pointer yang p
menunjuk ke nilai-nilai tipe T
. Ekspresi p + i
tersebut tidak berarti "posisi pada p
plus i
byte". Sebaliknya, ekspresi p + i
sebenarnya berarti "posisi pada p
plus i * sizeof(T)
byte".
Kenyamanan ini adalah bahwa untuk mendapatkan "nilai berikutnya" kita hanya perlu menulis p + 1
bukan p + 1 * sizeof(T)
.
Ini berarti bahwa kode C long x = array[5];
sebenarnya setara dengan
long x = *(array + 5)
karena C secara otomatis akan mengalikan 5
dengan sizeof(long)
.
Jadi dalam konteks pertanyaan StackOverflow ini, bagaimana semua ini relevan? Artinya ketika saya mengatakan "alamat array + i * sizeof(long)
", saya tidak bermaksud untuk " array + i * sizeof(long)
" diartikan sebagai ekspresi C. Saya melakukan perkalian sizeof(long)
sendiri untuk membuat jawaban saya lebih eksplisit, tetapi memahami bahwa karena itu, ungkapan ini tidak boleh dibaca sebagai C. Sama seperti matematika biasa yang menggunakan sintaks C.
[2] Catatan tambahan: karena semua yang lea
dilakukannya adalah operasi aritmatika, argumennya sebenarnya tidak harus merujuk ke alamat yang valid. Untuk alasan ini, ini sering digunakan untuk melakukan aritmatika murni pada nilai yang mungkin tidak dimaksudkan untuk dideferensi. Misalnya, cc
dengan -O2
terjemahan pengoptimalan
long f(long x) {
return x * 5;
}
menjadi berikut (baris yang tidak relevan dihapus):
f:
leaq (%rdi, %rdi, 4), %rax # set %rax to %rdi + %rdi * 4
ret
&
operator C adalah analogi yang bagus. Mungkin perlu ditunjukkan bahwa LEA adalah kasus khusus, sedangkan MOV sama seperti setiap instruksi lain yang dapat mengambil memori atau mendaftarkan operan. misalnya add (%rdi), %eax
hanya menggunakan mode pengalamatan ke memori alamat, sama seperti MOV. Juga terkait: Menggunakan LEA pada nilai yang bukan merupakan alamat / pointer? mengambil penjelasan ini lebih jauh: LEA adalah bagaimana Anda dapat menggunakan dukungan HW CPU untuk matematika alamat untuk melakukan perhitungan sewenang-wenang.
%rdi
" - Ini adalah kata yang aneh. Maksud Anda, nilai dalam register rdi
harus digunakan. Penggunaan "at" oleh Anda tampaknya berarti dereferensi memori yang sebenarnya tidak ada.
%rdi
" atau "nilai di %rdi
". "Nilai dalam register %rdi
" Anda panjang tapi bagus, dan mungkin bisa membantu seseorang yang kesulitan memahami register vs. memori.
Seperti yang dinyatakan dalam jawaban lain:
MOV
akan mengambil data di alamat di dalam tanda kurung dan menempatkan data itu ke dalam operan tujuan.LEA
akan melakukan perhitungan alamat di dalam tanda kurung dan menempatkan alamat yang dihitung ke dalam operan tujuan. Ini terjadi tanpa benar-benar keluar ke memori dan mendapatkan data. Pekerjaan yang dilakukan LEA
adalah dalam menghitung "alamat efektif".Karena memori dapat dialamatkan dengan beberapa cara berbeda (lihat contoh di bawah), LEA
terkadang digunakan untuk menambah atau mengalikan register bersama-sama tanpa menggunakan eksplisit ADD
atau MUL
instruksi (atau setara).
Karena setiap orang menunjukkan contoh dalam sintaks Intel, berikut beberapa dalam sintaks 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
absolut [disp32]
. Gunakan mov $label, %eax
sebagai gantinya. Ya, ini berfungsi, tetapi kurang efisien (kode mesin lebih besar dan berjalan pada unit eksekusi yang lebih sedikit). Karena Anda menyebutkan AT&T, Menggunakan LEA pada nilai yang bukan merupakan alamat / penunjuk? menggunakan AT&T, dan jawaban saya di sana memiliki beberapa contoh AT&T lainnya.
Pada dasarnya ... "Pindah ke REG ... setelah menghitungnya ..." sepertinya bagus untuk tujuan lain juga :)
jika Anda lupa bahwa nilainya adalah penunjuk, Anda dapat menggunakannya untuk pengoptimalan / minimisasi kode ... apa pun ..
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
aslinya akan menjadi:
MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5
lea
adalah instruksi shift-and-add yang menggunakan encoding dan sintaks mesin operan memori, karena hardware sudah mengetahui cara mendekode ModR / M + SIB + disp0 / 8/32.
Mari kita pahami ini dengan sebuah contoh.
mov eax, [ebx] dan
lea eax, [ebx] Misalkan nilai di ebx adalah 0x400000. Kemudian mov akan menuju ke alamat 0x400000 dan menyalin 4 byte data yang ada ke register eax. Sedangkan lea akan menyalin alamat 0x400000 ke eax. Jadi, setelah dieksekusi setiap nilai instruksi eax di tiap kasus akan menjadi (asumsi pada memori 0x400000 berisi adalah 30).
eax = 30 (dalam kasus mov) eax = 0x400000 (dalam kasus lea) Untuk definisi mov salin data dari rm32 ke tujuan (mov tujuan rm32) dan lea (muat alamat efektif) akan menyalin alamat ke tujuan (mov tujuan rm32 ).
MOV dapat melakukan hal yang sama seperti LEA [label], tetapi instruksi MOV berisi alamat efektif di dalam instruksi itu sendiri sebagai konstanta langsung (dihitung sebelumnya oleh assembler). LEA menggunakan PC-relative untuk menghitung alamat efektif selama pelaksanaan instruksi.
lea [label
adalah pemborosan byte vs. yang lebih kompak mov
, jadi Anda harus menentukan kondisi yang Anda bicarakan. Juga, untuk beberapa assembler [label]
bukanlah sintaks yang tepat untuk mode pengalamatan relatif RIP. Tapi ya, itu akurat. Bagaimana cara memuat alamat fungsi atau label ke register di GNU Assembler menjelaskan lebih detail.
Perbedaannya halus tapi penting. Instruksi MOV adalah 'MOVe' yang secara efektif merupakan salinan alamat yang diwakili oleh label TABLE-ADDR. Instruksi LEA adalah 'Load Effective Address' yang merupakan instruksi tak terarah, yang berarti TABLE-ADDR menunjuk ke lokasi memori di mana alamat yang akan dimuat ditemukan.
Secara efektif menggunakan LEA setara dengan menggunakan pointer dalam bahasa seperti C, karena itu adalah instruksi yang ampuh.