kode mesin x86_64, 4 byte
Instruksi BSF (bit scan forward) melakukan hal ini !
0x0f 0xbc 0xc7 0xc3
Dalam perakitan gcc-style, ini adalah:
.globl f
f:
bsfl %edi, %eax
ret
Input diberikan dalam register EDI dan dikembalikan dalam register EAX sesuai dengan konvensi panggilan c 64-bit standar .
Karena pengkodean biner komplemen dua, ini berfungsi untuk -ve serta + ve nomor.
Juga, terlepas dari dokumentasi yang mengatakan "Jika konten dari operan sumber adalah 0, konten dari operan tujuan tidak ditentukan." , Saya temukan di VM Ubuntu saya bahwa output f(0)
adalah 0.
Instruksi:
- Simpan di atas sebagai
evenness.s
dan kumpulkangcc -c evenness.s -o evenness.o
- Simpan driver tes berikut sebagai
evenness-main.c
dan kompilasi dengan gcc -c evenness-main.c -o evenness-main.o
:
#include <stdio.h>
extern int f(int n);
int main (int argc, char **argv) {
int i;
int testcases[] = { 14, 20, 94208, 7, 0, -4 };
for (i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) {
printf("%d, %d\n", testcases[i], f(testcases[i]));
}
return 0;
}
Kemudian:
- Tautan:
gcc evenness-main.o evenness.o -o evenness
- Lari:
./evenness
@FarazMasroor meminta detail lebih lanjut tentang bagaimana jawaban ini diturunkan.
Saya lebih terbiasa dengan c daripada seluk-beluk perakitan x86, jadi biasanya saya menggunakan kompiler untuk menghasilkan kode perakitan untuk saya. Saya tahu dari pengalaman bahwa ekstensi gcc seperti __builtin_ffs()
, __builtin_ctz()
dan__builtin_popcount()
biasanya mengkompilasi dan merakit ke 1 atau 2 instruksi pada x86. Jadi saya mulai dengan fungsi c seperti:
int f(int n) {
return __builtin_ctz(n);
}
Alih-alih menggunakan kompilasi gcc biasa semua jalan ke kode objek, Anda dapat menggunakan -S
opsi untuk mengkompilasi hanya untuk perakitan - gcc -S -c evenness.c
. Ini memberikan file perakitan evenness.s
seperti ini:
.file "evenness.c"
.text
.globl f
.type f, @function
f:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
rep bsfl %eax, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size f, .-f
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section .note.GNU-stack,"",@progbits
Banyak dari ini bisa dimainkan. Secara khusus kita tahu bahwa konvensi pemanggilan c untuk fungsi dengan tanda tangan bagus dan sederhana - param input dilewatkan dalam register dan nilai balik dikembalikan dalam register. Jadi kita dapat mengambil sebagian besar instruksi - banyak dari mereka yang peduli dengan menyimpan register dan mengatur bingkai stack baru. Kami tidak menggunakan tumpukan di sini dan hanya menggunakan register, jadi tidak perlu khawatir tentang register lain. Ini meninggalkan kode perakitan "golf":int f(int n);
EDI
EAX
EAX
.globl f
f:
bsfl %edi, %eax
ret
Catatan seperti yang ditunjukkan oleh zwol, Anda juga dapat menggunakan kompilasi yang dioptimalkan untuk mencapai hasil yang serupa. Khususnya -Os
menghasilkan instruksi di atas persis (dengan beberapa arahan assembler tambahan yang tidak menghasilkan kode objek tambahan.)
Ini sekarang dirakit dengan gcc -c evenness.s -o evenness.o
, yang kemudian dapat dihubungkan ke program driver tes seperti dijelaskan di atas.
Ada beberapa cara untuk menentukan kode mesin yang sesuai dengan rakitan ini. Favorit saya adalah menggunakan disass
perintah pembongkaran gdb :
$ gdb ./evenness
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
...
Reading symbols from ./evenness...(no debugging symbols found)...done.
(gdb) disass /r f
Dump of assembler code for function f:
0x00000000004005ae <+0>: 0f bc c7 bsf %edi,%eax
0x00000000004005b1 <+3>: c3 retq
0x00000000004005b2 <+4>: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:0x0(%rax,%rax,1)
0x00000000004005bc <+14>: 0f 1f 40 00 nopl 0x0(%rax)
End of assembler dump.
(gdb)
Jadi kita bisa melihat bahwa kode mesin untuk bsf
instruksi 0f bc c7
dan untuk ret
yaitu c3
.