kode mesin x86-64, 12 byte untuk int64_t
input
6 byte untuk double
input
Membutuhkan popcnt
ekstensi ISA ( CPUID.01H:ECX.POPCNT [Bit 23] = 1
).
(Atau 13 byte jika memodifikasi arg di tempat membutuhkan penulisan semua 64-bit, daripada meninggalkan sampah di atas 32. Saya pikir masuk akal untuk berargumen bahwa penelepon mungkin hanya ingin memuat 32b yang rendah, dan x86 nol -memperluas dari 32 ke 64 secara implisit dengan setiap operasi 32-bit. Namun, itu menghentikan penelepon untuk melakukan add rbx, [rdi]
atau sesuatu.)
Instruksi x87 lebih pendek daripada SSE2 cvtsi2sd
/ yang lebih jelas movq
(digunakan dalam jawaban @ ceilingcat ), dan [reg]
mode pengalamatan berukuran sama dengan reg
: hanya mod / byte byte.
Kuncinya adalah menemukan cara agar nilai yang dilewatkan dalam memori, tanpa perlu terlalu banyak byte untuk menangani mode. (mis. meneruskan pada stack tidak terlalu bagus.) Untungnya, aturan memperbolehkan read / write args, atau memisahkan output args , jadi saya bisa membuat penelepon memberikan saya sebuah pointer ke memori yang saya boleh tulis.
Dipanggil dari C dengan tanda tangan: void popc_double(int64_t *in_out);
Hanya 32b rendah dari hasilnya yang valid, yang mungkin aneh untuk C tetapi wajar untuk asm. (Memperbaiki ini membutuhkan awalan REX di toko akhir ( mov [rdi], rax
), jadi satu byte lagi.) Di Windows, ubah rdi
ke rdx
, karena Windows tidak menggunakan Sistem V ABI x86-64.
Daftar NASM. TIO link memiliki kode sumber tanpa pembongkaran.
1 addr machine global popcnt_double_outarg
2 code popcnt_double_outarg:
3 ;; normal x86-64 ABI, or x32: void pcd(int64_t *in_out)
4 00000000 DF2F fild qword [rdi] ; int64_t -> st0
5 00000002 DD1F fstp qword [rdi] ; store binary64, using retval as scratch space.
6 00000004 F3480FB807 popcnt rax, [rdi]
7 00000009 8907 mov [rdi], eax ; update only the low 32b of the in/out arg
8 0000000B C3 ret
# ends at 0x0C = 12 bytes
Cobalah online! Termasuk_start
program pengujian yang memberikan nilai dan keluar dengan status keluar = nilai balik popcnt. (Buka tab "debug" untuk melihatnya.)
Melewati pointer input / output yang terpisah juga akan berfungsi (rdi dan rsi di System86 ABI x86-64), tetapi kemudian kita tidak dapat menghancurkan input 64-bit atau dengan mudah membenarkan memerlukan buffer output 64-bit sementara hanya menulis rendah 32b.
Jika kita ingin berdebat bahwa kita dapat mengambil pointer ke integer input dan menghancurkannya, sambil mengembalikan output rax
, maka cukup hilangkan mov [rdi], eax
dari popcnt_double_outarg
, turunkan menjadi 10 byte.
Alternatif tanpa trik konvensi panggilan yang konyol, 14 byte
gunakan tumpukan sebagai ruang awal, dengan push
untuk mendapatkannya di sana. Gunakan push
/ pop
untuk menyalin register dalam 2 byte, bukan 3 untuk mov rdi, rsp
. ( [rsp]
selalu membutuhkan SIB byte, jadi perlu menghabiskan 2 byte untuk menyalin rsp
sebelum tiga instruksi yang menggunakannya.)
Panggilan dari C dengan tanda tangan ini: int popcnt_double_push(int64_t);
11 global popcnt_double_push
12 popcnt_double_push:
13 00000040 57 push rdi ; put the input arg on the stack (still in binary integer format)
14 00000041 54 push rsp ; pushes the old value (rsp updates after the store).
15 00000042 5A pop rdx ; mov rdx, rsp
16 00000043 DF2A fild qword [rdx]
17 00000045 DD1A fstp qword [rdx]
18 00000047 F3480FB802 popcnt rax, [rdx]
19 0000004C 5F pop rdi ; rebalance the stack
20 0000004D C3 ret
next byte is 0x4E, so size = 14 bytes.
Menerima input dalam double
format
Pertanyaannya hanya mengatakan itu adalah bilangan bulat dalam rentang tertentu, bukan karena itu harus dalam representasi bilangan bulat biner base2. Menerima double
input berarti tidak ada gunanya menggunakan x87 lagi. (Kecuali jika Anda menggunakan konvensi panggilan kustom double
di mana s dilewatkan dalam register x87. Kemudian simpan ke zona merah di bawah tumpukan, dan muncul dari sana.)
11 byte:
57 00000110 66480F7EC0 movq rax, xmm0
58 00000115 F3480FB8C0 popcnt rax, rax
59 0000011A C3 ret
Tetapi kita dapat menggunakan trik pass-by-reference yang sama seperti sebelumnya untuk membuat versi 6-byte: int pcd(const double&d);
58 00000110 F3480FB807 popcnt rax, [rdi]
59 00000115 C3 ret
6 byte .
binary64
format floating-point jika mereka mau? Beberapa orang (termasuk saya sendiri, awalnya) yang menafsirkan pertanyaan sebagai membutuhkan yang berfungsi menerima masukan sebagai tipe integer seperti Clong
. Di C, Anda dapat berargumen bahwa bahasa akan dikonversi untuk Anda, sama seperti ketika Anda meneleponsqrt((int)foo)
. Tetapi ada beberapa jawaban kode mesin x86 asm (seperti codegolf.stackexchange.com/a/136360/30206 dan milik saya) yang sama-sama berasumsi kita harus menerima input integer 64-bit. Menerimabinary64
nilai akan menghemat 5 byte.