Bagi saya, sepertinya MOV yang funky. Apa tujuannya dan kapan saya harus menggunakannya?
Bagi saya, sepertinya MOV yang funky. Apa tujuannya dan kapan saya harus menggunakannya?
Jawaban:
Seperti yang telah ditunjukkan orang lain, LEA (memuat alamat efektif) sering digunakan sebagai "trik" untuk melakukan perhitungan tertentu, tetapi itu bukan tujuan utamanya. Set instruksi x86 dirancang untuk mendukung bahasa tingkat tinggi seperti Pascal dan C, di mana array — terutama array int atau struct kecil — adalah umum. Pertimbangkan, misalnya, struct yang mewakili (x, y) koordinat:
struct Point
{
int xcoord;
int ycoord;
};
Sekarang bayangkan sebuah pernyataan seperti:
int y = points[i].ycoord;
dimana points[]array dari Point. Dengan asumsi basis array sudah dalam EBX, dan variabel idalam EAX, dan xcoorddan ycoordmasing-masing 32 bit (demikian ycoordjuga pada offset 4 byte dalam struct), pernyataan ini dapat dikompilasi ke:
MOV EDX, [EBX + 8*EAX + 4] ; right side is "effective address"
yang akan mendarat ydi EDX. Faktor skala 8 adalah karena masing Point- masing berukuran 8 byte. Sekarang perhatikan ungkapan yang sama yang digunakan dengan operator "alamat" &:
int *p = &points[i].ycoord;
Dalam hal ini, Anda tidak ingin nilai ycoord, tetapi alamatnya. Di situlah LEA(memuat alamat efektif) masuk. Alih-alih MOV, kompiler dapat menghasilkan
LEA ESI, [EBX + 8*EAX + 4]
yang akan memuat alamat di ESI.
movinstruksi dan meninggalkan tanda kurung? MOV EDX, EBX + 8*EAX + 4
MOVdengan sumber tidak langsung, kecuali itu hanya tipuan dan bukan MOV. Sebenarnya tidak membaca dari alamat yang dihitung, hanya menghitungnya.
Dari "Zen Majelis" oleh Abrash:
LEA, satu-satunya instruksi yang melakukan perhitungan pengalamatan memori tetapi tidak benar-benar mengatasi memori.LEAmenerima operan pengalamatan memori standar, tetapi tidak lebih dari menyimpan offset memori yang dihitung dalam register yang ditentukan, yang mungkin merupakan register tujuan umum.Apa yang memberi kita? Dua hal yang
ADDtidak memberikan:
- kemampuan untuk melakukan penambahan dengan dua atau tiga operan, dan
- kemampuan untuk menyimpan hasil dalam register apa pun ; bukan hanya salah satu dari operan sumber.
Dan LEAtidak mengubah bendera.
Contohnya
LEA EAX, [ EAX + EBX + 1234567 ]menghitung EAX + EBX + 1234567(itulah tiga operan)LEA EAX, [ EBX + ECX ]menghitung EBX + ECXtanpa mengesampingkan baik dengan hasilnya.LEA EAX, [ EBX + N * EBX ](N bisa 1,2,4,8).Usecase lain berguna dalam loop: perbedaan antara LEA EAX, [ EAX + 1 ]dan INC EAXadalah bahwa yang terakhir berubah EFLAGStetapi yang pertama tidak; ini mempertahankan CMPkeadaan.
LEA EAX, [ EAX + EBX + 1234567 ]menghitung jumlah EAX, EBXdan 1234567(itulah tiga operan). LEA EAX, [ EBX + ECX ]menghitung EBX + ECX tanpa mengesampingkan baik dengan hasilnya. Hal ketiga yang LEAdigunakan untuk (tidak terdaftar oleh Frank) adalah perkalian dengan konstanta (oleh dua, tiga, lima atau sembilan), jika Anda menggunakannya seperti LEA EAX, [ EBX + N * EBX ]( Nbisa 1,2,4,8). Usecase lain berguna dalam loop: perbedaan antara LEA EAX, [ EAX + 1 ]dan INC EAXadalah bahwa yang terakhir berubah EFLAGStetapi yang pertama tidak; ini mempertahankan CMPnegara
LEAdapat digunakan untuk ... (lihat "LEA (memuat alamat efektif) sering digunakan sebagai" trik "untuk melakukan perhitungan tertentu" dalam jawaban populer IJ Kennedy di atas)
Fitur penting lainnya dari LEAinstruksi adalah bahwa ia tidak mengubah kode kondisi seperti CFdan ZF, saat menghitung alamat dengan instruksi aritmatika suka ADDatau MULtidak. Fitur ini mengurangi tingkat ketergantungan di antara instruksi dan dengan demikian membuat ruang untuk optimasi lebih lanjut oleh kompiler atau penjadwal perangkat keras.
leakadang berguna bagi kompiler (atau pengode manusia) untuk melakukan matematika tanpa mengalahkan hasil flag. Tetapi leatidak lebih cepat dari itu add. Sebagian besar instruksi x86 menulis flag. Implementasi x86 berkinerja tinggi harus mengubah nama EFLAGS atau menghindari bahaya write-after-write agar kode normal berjalan cepat, jadi instruksi yang menghindari flag write tidak lebih baik karena itu. ( hal-hal bendera parsial dapat menimbulkan masalah, lihat instruksi INC vs. ADD 1: Apakah penting? )
Terlepas dari semua penjelasan, LEA adalah operasi aritmatika:
LEA Rt, [Rs1+a*Rs2+b] => Rt = Rs1 + a*Rs2 + b
Hanya saja namanya sangat bodoh untuk operasi shift + add. Alasan untuk itu sudah dijelaskan dalam jawaban berperingkat teratas (yaitu ia dirancang untuk secara langsung memetakan referensi memori tingkat tinggi).
LEApada AGU tetapi pada ALU integer biasa. Kita harus membaca spesifikasi CPU sangat dekat hari ini untuk mencari tahu "di mana hal-hal berjalan" ...
LEAmemberi Anda alamat yang muncul dari mode pengalamatan terkait memori. Ini bukan shift dan operasi tambahan.
Mungkin hanya hal lain tentang instruksi LEA. Anda juga dapat menggunakan LEA untuk register yang berlipat ganda dengan 3, 5 atau 9.
LEA EAX, [EAX * 2 + EAX] ;EAX = EAX * 3
LEA EAX, [EAX * 4 + EAX] ;EAX = EAX * 5
LEA EAX, [EAX * 8 + EAX] ;EAX = EAX * 9
LEA EAX, [EAX*3]?
shlinstruksi shift kiri seperti untuk mengalikan register dengan 2,4,8,16 ... lebih cepat dan lebih pendek. Tetapi untuk mengalikan dengan angka yang berbeda dari kekuatan 2 kita biasanya menggunakan mulinstruksi yang lebih megah dan lebih lambat.
lea eax,[eax*3]akan menerjemahkan ke setara dengan lea eax,[eax+eax*2].
leaadalah singkatan dari "memuat alamat efektif". Itu memuat alamat referensi lokasi oleh operan sumber ke operan tujuan. Misalnya, Anda dapat menggunakannya untuk:
lea ebx, [ebx+eax*8]
untuk memindahkan item ebxpointer eaxlebih lanjut (dalam array 64-bit / elemen) dengan satu instruksi. Pada dasarnya, Anda mendapat manfaat dari mode pengalamatan kompleks yang didukung oleh arsitektur x86 untuk memanipulasi pointer secara efisien.
Alasan terbesar yang Anda gunakan di LEAatas MOVadalah jika Anda perlu melakukan aritmatika pada register yang Anda gunakan untuk menghitung alamat. Secara efektif, Anda dapat melakukan jumlah aritmatika penunjuk pada beberapa register dalam kombinasi secara efektif untuk "gratis."
Apa yang benar-benar membingungkan adalah bahwa Anda biasanya menulis LEAseperti MOVtetapi Anda tidak benar-benar mengingat memori. Dengan kata lain:
MOV EAX, [ESP+4]
Ini akan memindahkan konten ESP+4ke poin mana EAX.
LEA EAX, [EBX*8]
Ini akan memindahkan alamat efektif EBX * 8ke EAX, bukan yang ditemukan di lokasi itu. Seperti yang dapat Anda lihat, juga dimungkinkan untuk memperbanyak dengan dua faktor (penskalaan) sementara a MOVterbatas pada penambahan / pengurangan.
LEAdilakukan.
8086 memiliki keluarga besar instruksi yang menerima operan register dan alamat efektif, melakukan beberapa perhitungan untuk menghitung bagian offset dari alamat efektif itu, dan melakukan beberapa operasi yang melibatkan register dan memori yang dirujuk oleh alamat yang dihitung. Cukup sederhana untuk memiliki salah satu instruksi dalam keluarga itu berperilaku seperti di atas kecuali untuk melewatkan operasi memori yang sebenarnya. Ini, instruksi:
mov ax,[bx+si+5]
lea ax,[bx+si+5]
diimplementasikan hampir identik secara internal. Perbedaannya adalah langkah yang dilompati. Kedua instruksi tersebut bekerja seperti:
temp = fetched immediate operand (5)
temp += bx
temp += si
address_out = temp (skipped for LEA)
trigger 16-bit read (skipped for LEA)
temp = data_in (skipped for LEA)
ax = temp
Mengenai mengapa Intel menganggap instruksi ini layak untuk dimasukkan, saya tidak begitu yakin, tetapi fakta bahwa itu murah untuk diterapkan akan menjadi faktor besar. Faktor lain adalah fakta bahwa assembler Intel memperbolehkan simbol untuk didefinisikan relatif terhadap register BP. Jika fnorddidefinisikan sebagai simbol relatif-BP (mis. BP + 8), bisa dikatakan:
mov ax,fnord ; Equivalent to "mov ax,[BP+8]"
Jika seseorang ingin menggunakan sesuatu seperti stosw untuk menyimpan data ke alamat relatif BP, bisa mengatakannya
mov ax,0 ; Data to store
mov cx,16 ; Number of words
lea di,fnord
rep movs fnord ; Address is ignored EXCEPT to note that it's an SS-relative word ptr
lebih nyaman daripada:
mov ax,0 ; Data to store
mov cx,16 ; Number of words
mov di,bp
add di,offset fnord (i.e. 8)
rep movs fnord ; Address is ignored EXCEPT to note that it's an SS-relative word ptr
Perhatikan bahwa melupakan dunia "offset" akan menyebabkan konten lokasi [BP + 8], bukannya nilai 8, ditambahkan ke DI. Ups.
Seperti jawaban yang ada disebutkan, LEAmemiliki keuntungan melakukan memori pengalamatan aritmatika tanpa mengakses memori, menyimpan hasil aritmatika ke register yang berbeda, bukan bentuk sederhana dari instruksi tambahan. Manfaat kinerja yang mendasari nyata adalah bahwa prosesor modern memiliki unit dan port LEA ALU terpisah untuk pembuatan alamat yang efektif (termasuk LEAdan alamat referensi memori lainnya), ini berarti operasi aritmatika LEAdan operasi aritmatika normal lainnya di ALU dapat dilakukan secara paralel dalam satu inti.
Lihat artikel arsitektur Haswell ini untuk beberapa detail tentang unit LEA: http://www.realworldtech.com/haswell-cpu/4/
Poin penting lain yang tidak disebutkan dalam jawaban lain adalah LEA REG, [MemoryAddress]instruksi adalah PIC (position independent code) yang menyandikan alamat relatif PC dalam instruksi ini sebagai referensi MemoryAddress. Ini berbeda dari MOV REG, MemoryAddressyang mengkodekan alamat virtual relatif dan memerlukan relokasi / patching dalam sistem operasi modern (seperti ASLR adalah fitur umum). Jadi LEAdapat digunakan untuk mengkonversi non PIC ke PIC.
leapada satu atau lebih ALU yang sama yang mengeksekusi instruksi aritmatika lainnya (tetapi umumnya lebih sedikit dari mereka daripada aritmatika lainnya). Misalnya, CPU Haswell yang disebutkan dapat menjalankan addatau subatau sebagian besar operasi aritmatika dasar lainnya pada empat ALU yang berbeda , tetapi hanya dapat mengeksekusi leapada satu (kompleks lea) atau dua (sederhana lea). Lebih penting lagi, dua leaALU yang dapat dilewati hanyalah dua dari empat ALU yang dapat menjalankan instruksi lain, sehingga tidak ada manfaat paralelisme seperti yang diklaim.
Instruksi LEA dapat digunakan untuk menghindari perhitungan yang memakan waktu dari alamat yang efektif oleh CPU. Jika suatu alamat digunakan berulang kali, lebih efektif untuk menyimpannya dalam register daripada menghitung alamat efektif setiap kali digunakan.
[esi]jarang lebih murah daripada mengatakan [esi + 4200]dan hanya jarang lebih murah daripada [esi + ecx*8 + 4200].
[esi]tidak lebih murah dari [esi + ecx*8 + 4200]. Tapi mengapa repot-repot membandingkan? Mereka tidak setara. Jika Anda ingin yang pertama menunjuk lokasi memori yang sama dengan yang kedua, Anda memerlukan instruksi tambahan: Anda harus menambah esinilai ecxdikalikan dengan 8. Eh, multiplikasi akan merusak flag CPU Anda! Kemudian Anda harus menambahkan 4200. Instruksi tambahan ini menambah ukuran kode (mengambil ruang dalam cache instruksi, siklus untuk mengambil).
[esi + 4200]berulang kali dalam urutan instruksi, maka lebih baik untuk memuat alamat efektif ke dalam register dan menggunakannya. Misalnya, daripada menulis add eax, [esi + 4200]; add ebx, [esi + 4200]; add ecx, [esi + 4200], Anda harus memilih lea edi, [esi + 4200]; add eax, [edi]; add ebx, [edi]; add ecx, [edi], yang jarang lebih cepat. Setidaknya itulah interpretasi sederhana dari jawaban ini.
[esi]dan [esi + 4200](atau [esi + ecx*8 + 4200]apakah ini adalah penyederhanaan yang diusulkan OP (seperti yang saya pahami): bahwa instruksi N dengan alamat kompleks yang sama ditransformasikan menjadi instruksi N dengan pengalamatan sederhana (satu reg), ditambah satu lea, karena pengalamatan yang kompleks adalah "memakan waktu". Faktanya, pengalamatan lebih lambat bahkan pada x86 modern, tetapi hanya dari latensi yang tampaknya tidak penting untuk instruksi berurutan dengan alamat yang sama.
leasehingga meningkatkan tekanan dalam kasus itu. Secara umum, menyimpan perantara adalah penyebab tekanan register, bukan solusi untuk itu - tapi saya pikir dalam sebagian besar situasi itu adalah mencuci. @Kaz
Instruksi LEA (Load Effective Address) adalah cara untuk mendapatkan alamat yang muncul dari mode pengalamatan memori prosesor Intel.
Artinya, jika kita memiliki data yang bergerak seperti ini:
MOV EAX, <MEM-OPERAND>
itu memindahkan isi dari lokasi memori yang ditunjuk ke register target.
Jika kita mengganti MOVdengan LEA, maka alamat lokasi memori dihitung dengan cara yang persis sama dengan <MEM-OPERAND>ekspresi pengalamatan. Namun alih-alih isi lokasi memori, kami mendapatkan lokasi itu sendiri ke tujuan.
LEAbukan instruksi aritmatika tertentu; ini adalah cara mencegat alamat efektif yang timbul dari salah satu mode pengalamatan memori prosesor.
Misalnya, kita dapat menggunakan LEAhanya pada alamat langsung yang sederhana. Tidak ada aritmatika yang terlibat sama sekali:
MOV EAX, GLOBALVAR ; fetch the value of GLOBALVAR into EAX
LEA EAX, GLOBALVAR ; fetch the address of GLOBALVAR into EAX.
Ini valid; kita dapat mengujinya di Linux prompt:
$ as
LEA 0, %eax
$ objdump -d a.out
a.out: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 8d 04 25 00 00 00 00 lea 0x0,%eax
Di sini, tidak ada penambahan nilai skala, dan tidak ada offset. Nol dipindahkan ke EAX. Kita bisa melakukannya menggunakan MOV dengan operan langsung juga.
Ini adalah alasan mengapa orang-orang yang berpikir bahwa tanda kurung LEAterlalu berlebihan salah besar; tanda kurung bukan LEAsintaks tetapi merupakan bagian dari mode pengalamatan.
LEA nyata di tingkat perangkat keras. Instruksi yang dihasilkan mengkodekan mode pengalamatan aktual dan prosesor membawanya ke titik penghitungan alamat. Kemudian memindahkan alamat itu ke tujuan alih-alih menghasilkan referensi memori. (Karena perhitungan alamat dari mode pengalamatan dalam instruksi lain tidak berpengaruh pada flag CPU, LEAtidak berpengaruh pada flag CPU.)
Berbeda dengan memuat nilai dari alamat nol:
$ as
movl 0, %eax
$ objdump -d a.out | grep mov
0: 8b 04 25 00 00 00 00 mov 0x0,%eax
Ini encoding yang sangat mirip, lihat? Hanya 8ddari LEAtelah berubah menjadi 8b.
Tentu saja, LEApengkodean ini lebih lama daripada memindahkan nol langsung ke EAX:
$ as
movl $0, %eax
$ objdump -d a.out | grep mov
0: b8 00 00 00 00 mov $0x0,%eax
Tidak ada alasan untuk LEAmengecualikan kemungkinan ini hanya karena ada alternatif yang lebih pendek; itu hanya menggabungkan secara ortogonal dengan mode pengalamatan yang tersedia.
Berikut ini sebuah contoh.
// compute parity of permutation from lexicographic index
int parity (int p)
{
assert (p >= 0);
int r = p, k = 1, d = 2;
while (p >= k) {
p /= d;
d += (k << 2) + 6; // only one lea instruction
k += 2;
r ^= p;
}
return r & 1;
}
Dengan -O (optimisasi) sebagai opsi kompiler, gcc akan menemukan instruksi lea untuk baris kode yang ditunjukkan.
Tampaknya banyak jawaban sudah selesai, saya ingin menambahkan satu lagi contoh kode untuk menunjukkan bagaimana lea dan memindahkan instruksi bekerja secara berbeda ketika mereka memiliki format ekspresi yang sama.
Untuk membuat cerita panjang pendek, instruksi lea dan instruksi mov keduanya dapat digunakan dengan tanda kurung melampirkan operan src dari instruksi. Ketika mereka diapit dengan () , ekspresi dalam () dihitung dengan cara yang sama; namun, dua instruksi akan menginterpretasikan nilai yang dihitung dalam operan src dengan cara yang berbeda.
Apakah ekspresi digunakan dengan lea atau mov, nilai src dihitung seperti di bawah ini.
D (Rb, Ri, S) => (Reg [Rb] + S * Reg [Ri] + D)
Namun, ketika digunakan dengan instruksi mov, ia mencoba mengakses nilai yang ditunjukkan oleh alamat yang dihasilkan oleh ekspresi di atas dan menyimpannya ke tujuan.
Sebaliknya, ketika instruksi lea dieksekusi dengan ekspresi di atas, ia memuat nilai yang dihasilkan sebagaimana ke tujuan.
Kode di bawah ini mengeksekusi instruksi lea dan instruksi mov dengan parameter yang sama. Namun, untuk menangkap perbedaannya, saya menambahkan penangan sinyal level pengguna untuk menangkap kesalahan segmentasi yang disebabkan oleh mengakses alamat yang salah sebagai hasil dari instruksi mov.
Kode contoh
#define _GNU_SOURCE 1 /* To pick up REG_RIP */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
uint32_t
register_handler (uint32_t event, void (*handler)(int, siginfo_t*, void*))
{
uint32_t ret = 0;
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
ret = sigaction(event, &act, NULL);
return ret;
}
void
segfault_handler (int signum, siginfo_t *info, void *priv)
{
ucontext_t *context = (ucontext_t *)(priv);
uint64_t rip = (uint64_t)(context->uc_mcontext.gregs[REG_RIP]);
uint64_t faulty_addr = (uint64_t)(info->si_addr);
printf("inst at 0x%lx tries to access memory at %ld, but failed\n",
rip,faulty_addr);
exit(1);
}
int
main(void)
{
int result_of_lea = 0;
register_handler(SIGSEGV, segfault_handler);
//initialize registers %eax = 1, %ebx = 2
// the compiler will emit something like
// mov $1, %eax
// mov $2, %ebx
// because of the input operands
asm("lea 4(%%rbx, %%rax, 8), %%edx \t\n"
:"=d" (result_of_lea) // output in EDX
: "a"(1), "b"(2) // inputs in EAX and EBX
: // no clobbers
);
//lea 4(rbx, rax, 8),%edx == lea (rbx + 8*rax + 4),%edx == lea(14),%edx
printf("Result of lea instruction: %d\n", result_of_lea);
asm volatile ("mov 4(%%rbx, %%rax, 8), %%edx"
:
: "a"(1), "b"(2)
: "edx" // if it didn't segfault, it would write EDX
);
}
Hasil eksekusi
Result of lea instruction: 14
inst at 0x4007b5 tries to access memory at 14, but failed
=duntuk memberi tahu kompiler bahwa hasilnya ada di EDX, menyimpan a mov. Anda juga meninggalkan deklarasi clobber awal pada output. Ini menunjukkan apa yang Anda coba tunjukkan, tetapi juga merupakan contoh buruk inline asm yang menyesatkan yang akan pecah jika digunakan dalam konteks lain. Itu Hal Buruk untuk jawaban stack overflow.
%%semua nama daftar itu dalam Extended asm, maka gunakan batasan input. seperti asm("lea 4(%%ebx, %%eax, 8), %%edx" : "=d"(result_of_lea) : "a"(1), "b"(2));. Membiarkan register init compiler berarti Anda juga tidak harus mendeklarasikan clobbers. Anda terlalu rumit dengan xor-zeroing sebelum mov-direct menimpa seluruh register juga.
mov 4(%ebx, %eax, 8), %edxtidak valid? Lagi pula, ya, karena movakan masuk akal untuk menulis "a"(1ULL)untuk memberi tahu kompiler Anda memiliki nilai 64-bit, dan dengan demikian perlu memastikan itu diperpanjang untuk mengisi seluruh register. Dalam praktiknya masih akan digunakan mov $1, %eax, karena menulis EAX nol meluas ke RAX, kecuali jika Anda memiliki situasi aneh kode sekitarnya di mana kompiler tahu bahwa RAX = 0xff00000001atau sesuatu. Sebab lea, Anda masih menggunakan ukuran operan 32-bit, sehingga bit tinggi yang tersesat di register input tidak berpengaruh pada hasil 32-bit.
LEA: hanya instruksi "aritmatika" ..
MOV mentransfer data antar operan tetapi lea hanya menghitung
mov eax, offset GLOBALVARsaja. Anda dapat menggunakan LEA, tetapi ukuran kode sedikit lebih besar daripada mov r32, imm32dan berjalan pada lebih sedikit port, karena masih melewati proses perhitungan alamat . lea reg, symbolhanya berguna dalam 64-bit untuk LEA RIP-relatif, ketika Anda membutuhkan PIC dan / atau alamat di luar 32 bit yang rendah. Dalam kode 32 atau 16-bit, tidak ada keuntungan. LEA adalah instruksi aritmatika yang memperlihatkan kemampuan CPU untuk mendekode / menghitung mode pengalamatan.
imul eax, edx, 1tidak menghitung: itu hanya menyalin edx ke eax. Tapi sebenarnya itu menjalankan data Anda melalui pengganda dengan latensi 3 siklus. Atau itu rorx eax, edx, 0hanya salinan (rotasikan oleh nol).
Semua instruksi "kalkulasi" normal seperti menambahkan perkalian, eksklusif atau mengatur tanda status seperti nol, tandatangani. Jika Anda menggunakan alamat yang rumit, AX xor:= mem[0x333 +BX + 8*CX] bendera ditetapkan sesuai dengan operasi xor.
Sekarang Anda mungkin ingin menggunakan alamat tersebut beberapa kali. Memuat addres semacam itu ke dalam register tidak pernah dimaksudkan untuk menetapkan bendera status dan untungnya tidak. Ungkapan "memuat alamat yang efektif" membuat programmer mengetahui hal itu. Dari situlah ekspresi aneh itu berasal.
Jelas bahwa begitu prosesor mampu menggunakan alamat yang rumit untuk memproses kontennya, ia mampu menghitungnya untuk keperluan lain. Memang itu dapat digunakan untuk melakukan transformasi x <- 3*x+1dalam satu instruksi. Ini adalah aturan umum dalam pemrograman rakitan: Gunakan instruksi namun itu menggetarkan perahu Anda.
Satu-satunya hal yang diperhitungkan adalah apakah transformasi yang diwujudkan oleh instruksi bermanfaat bagi Anda.
Intinya
MOV, X| T| AX'| R| BX|
dan
LEA, AX'| [BX]
memiliki efek yang sama pada AX tetapi tidak pada bendera status. (Ini adalah notasi ciasdis .)
call lbl lbl: pop rax"bekerja" secara teknis sebagai cara untuk mendapatkan nilai rip, tetapi Anda akan membuat prediksi cabang sangat tidak bahagia. Gunakan instruksi yang Anda inginkan, tetapi jangan kaget jika Anda melakukan sesuatu yang rumit dan memiliki konsekuensi yang tidak Anda perkirakan
Maafkan saya jika seseorang telah menyebutkannya, tetapi di masa x86 ketika segmentasi memori masih relevan, Anda mungkin tidak mendapatkan hasil yang sama dari dua instruksi ini:
LEA AX, DS:[0x1234]
dan
LEA AX, CS:[0x1234]
seg:offpasangan. LEA tidak terpengaruh oleh basis segmen; kedua instruksi tersebut akan (secara tidak efisien) dimasukkan 0x1234ke dalam AX. x86 sayangnya tidak memiliki cara mudah untuk menghitung alamat linear lengkap (basis + segmen efektif) menjadi register atau pasangan-pasangan.