Apa perbedaan antara MOV dan LEA?


144

Saya ingin tahu apa perbedaan antara instruksi ini:

MOV AX, [TABLE-ADDR]

dan

LEA AX, [TABLE-ADDR]


10
terima kasih nick. Pertama-tama, saya tidak akan menemukan jawaban untuk pertanyaan ini dengan melihat tautan itu. Di sini saya mencari info khusus, pembahasan di tautan yang Anda berikan lebih bersifat umum.
naveen

3
Saya menaikkan suara dup @ Nick beberapa tahun yang lalu tetapi baru saja meng-vtc. Pada renungan, saya terlalu terburu-buru dan sekarang dengan naveen bahwa a) pertanyaan lain tidak menjawab "apa bedanya" dan b) ini adalah pertanyaan yang berguna. Permintaan maaf kepada naveen atas kesalahan saya - seandainya saja saya bisa membatalkan vtc ...
Ruben Bartelink


Terkait: Menggunakan LEA pada nilai yang bukan merupakan alamat / penunjuk? berbicara tentang penggunaan LEA lainnya, untuk matematika arbitrer.
Peter Cordes

Jawaban:


176
  • LEA berarti Muat Alamat Efektif
  • MOV berarti Nilai Beban

Singkatnya, LEAmuat penunjuk ke item yang Anda tangani sedangkan MOV memuat nilai sebenarnya di alamat itu.

Tujuannya LEAadalah 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.


6
+1 terima kasih atas penjelasannya yang jelas, membantu saya menjawab pertanyaan lain.
legends2k

Ini membingungkan saya bahwa lea memiliki "memuat" dalam nama dan orang mengatakan itu "memuat" alamat yang dihitung ke dalam register, karena semua input untuk menghitung lokasi memori adalah nilai langsung atau register. AFAICT lea hanya melakukan komputasi, tidak memuat apa pun, di mana memuat berarti menyentuh memori?
Joseph Garvin

2
@josephGarvin IIRC istilah pengambilan akan diterapkan ke aspek itu; Beban hanyalah bagaimana Anda mengganti nilai dalam register dengan sesuatu dari awal. misalnya 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.
Ruben Bartelink

1
semuanya mengingatkan saya pada slideshare.net/pirhilton/… ;)
Ruben Bartelink

47

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 varuntuk mendapatkan mov-segera alih-alih memuat.


4
dalam sintaks NASM saja. Dalam sintaks MASM, mov eax, varis a load, sama dengan mov eax, [var], dan Anda harus mov eax, OFFSET varmenggunakan label sebagai konstanta langsung.
Peter Cordes

1
Jelas, sederhana, dan menunjukkan apa yang saya coba konfirmasi. Terima kasih.
JayArby

1
Perhatikan bahwa dalam semua contoh ini, leaadalah pilihan yang lebih buruk kecuali dalam mode 64-bit untuk pengalamatan relatif RIP. mov r32, imm32berjalan 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 .
Peter Cordes

31

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.


11

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


Saya kira, dengan pengecualian bahwa di GNU assembler itu tidak benar ketika datang ke label di segmen .bss? AFAIR Anda tidak bisa benar-benar leal TextLabel, LabelFromBssSegmentketika Anda mendapatkan sesuatu. seperti .bss .lcomm LabelFromBssSegment, 4, Anda harus movl $TextLabel, LabelFromBssSegment, bukan?
JSmyth

@JSmyth: Itu hanya karena leamemerlukan tujuan register, tetapi movdapat memiliki imm32sumber dan tujuan memori. Batasan ini tentu saja tidak spesifik untuk perakit GNU.
Peter Cordes

1
Juga, jawaban ini pada dasarnya salah karena pertanyaannya adalah tentang MOV AX, [TABLE-ADDR], yaitu beban. Jadi ada perbedaan besar. Instruksi yang setara adalahmov ax, OFFSET table_addr
Peter Cordes

Tautannya sudah mati.
Ryan1729

10

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_addrdan BUKAN offset ke table_addr. Anda harus menggunakan sebagai gantinya

mov ax,offset table_addr

atau

lea ax,table_addr

yang berfungsi sama.

leaversi juga berfungsi dengan baik jika table_addrvariabel lokal misalnya

some_procedure proc

local table_addr[64]:word

lea ax,table_addr

terima kasih banyak, hanya saja saya tidak dapat menandai lebih dari satu sebagai jawaban :(
naveen

5
Perbedaan antara instruksi x86 MOV dan LEA pasti TIDAK tergantung pada assembler.
IJ Kennedy

7

Tak satu pun dari jawaban sebelumnya yang benar-benar membingungkan saya, jadi saya ingin menambahkan jawaban saya sendiri.

Apa yang saya lewatkan adalah leaoperasi memperlakukan penggunaan tanda kurung berbeda dari bagaimana mov.

Pikirkan C. Katakanlah saya memiliki array longyang 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 movdan lea. Dengan mov, terjadi dereferensi yang tidak terjadi dengan lea. Ini terlepas dari penggunaan tanda kurung yang terjadi di keduanya. Misalnya, movq (%r8), %r9dan 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 * 8ke dalam register %rbp. Yaitu: dapatkan nilai di register %rdidan 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 arraymenjadi %rdidan imenjadi %rsidan xmenjadi %rbp. Ini 8adalah panjang tipe data yang terdapat dalam larik.

Sekarang pertimbangkan kode serupa yang menggunakan lea:

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

Sama seperti penggunaan movqterkait dengan dereferensi, penggunaan di leaqsini 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 leaqmengubah arti (%rdi, %rsi, 8)dari dereferensi menjadi menentukan lokasi.

Semantik dari baris kode ini adalah sebagai berikut: dapatkan nilai di register %rdidan 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 leaqdan movqadalah movqperbedaan pendapat, dan leaqtidak. Sebenarnya, untuk menulis leaqdeskripsi, saya pada dasarnya menyalin + menempelkan deskripsi movq, dan kemudian menghapus "Temukan nilai di lokasi ini".

Untuk meringkas: movqvs. leaqrumit 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 leaqtidak dan merupakan sintaks murni yang nyaman.


[1] Saya telah mengatakan bahwa when arrayadalah 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 pmenunjuk ke nilai-nilai tipe T. Ekspresi p + itersebut tidak berarti "posisi pada pplus ibyte". Sebaliknya, ekspresi p + i sebenarnya berarti "posisi pada pplus i * sizeof(T)byte".

Kenyamanan ini adalah bahwa untuk mendapatkan "nilai berikutnya" kita hanya perlu menulis p + 1bukan 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 5dengan 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 leadilakukannya 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, ccdengan -O2terjemahan 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

2
Yup, penjelasannya bagus, lebih detail dari jawaban yang lain, dan ya &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), %eaxhanya 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.
Peter Cordes

"dapatkan nilainya di %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.
ecm

@PeterCordes Terima kasih! Saya telah menambahkan poin tentang itu menjadi kasus khusus untuk jawabannya.
Quelklef

1
@ Poin yang bagus; Saya tidak menyadarinya. Saya sudah mengubahnya sekarang, terima kasih! :)
Quelklef

FYI, ungkapan pendek yang perbaikan masalah ECM menunjukkan meliputi: "nilai dari %rdi " atau "nilai di %rdi ". "Nilai dalam register %rdi" Anda panjang tapi bagus, dan mungkin bisa membantu seseorang yang kesulitan memahami register vs. memori.
Peter Cordes

4

Seperti yang dinyatakan dalam jawaban lain:

  • MOVakan mengambil data di alamat di dalam tanda kurung dan menempatkan data itu ke dalam operan tujuan.
  • LEAakan 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 LEAadalah dalam menghitung "alamat efektif".

Karena memori dapat dialamatkan dengan beberapa cara berbeda (lihat contoh di bawah), LEAterkadang digunakan untuk menambah atau mengalikan register bersama-sama tanpa menggunakan eksplisit ADDatau MULinstruksi (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 */

Anda tidak pernah menginginkan mode pengalamatan lea label, %eaxabsolut [disp32]. Gunakan mov $label, %eaxsebagai 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.
Peter Cordes

2

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

Yup, leaadalah instruksi shift-and-add yang menggunakan encoding dan sintaks mesin operan memori, karena hardware sudah mengetahui cara mendekode ModR / M + SIB + disp0 / 8/32.
Peter Cordes

1

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 ).


1

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.


Itu hanya berlaku untuk mode 64-bit (di mana pengalamatan PC-relative baru); dalam mode lain lea [labeladalah 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.
Peter Cordes

0

LEA (Load Effective Address) adalah instruksi shift-and-add. Itu ditambahkan ke 8086 karena perangkat keras ada di sana untuk memecahkan kode dan menghitung mode alamat.


-1

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.


7
Saya pikir jawaban ini paling membingungkan. "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." Sebenarnya LEA akan memuat alamatnya, bukan isi alamatnya. Saya pikir sebenarnya penanya perlu diyakinkan bahwa MOV dan LEA dapat tumpang tindih, dan melakukan hal yang persis sama, dalam beberapa situasi
Bill Forster
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.