ฟังก์ชั่นรหัสเครื่อง x86-64, 40 ไบต์
หรือ 37 ไบต์หากอนุญาตให้ใช้ 0 กับไม่ใช่ศูนย์เป็น "ความจริง" เช่น strcmp
ขอบคุณสำหรับคำตอบ C คาร์ล Napf สำหรับความคิดบิตแมปซึ่ง x86 สามารถทำได้อย่างมีประสิทธิภาพมากกับบีทีเอส
ฟังก์ชั่นลายเซ็น: _Bool cube_digits_same(uint64_t n);
ใช้ x86-64 System V ABI ( n
ใน RDI, ค่าบูลีนส่งคืน (0 หรือ 1) ใน AL)
_Bool
จะถูกกำหนดโดย ISO C11 และมักจะถูกใช้โดย#include <stdbool.h>
การกำหนดbool
ที่มีความหมายเช่นเดียวกับภาษา C bool
++
การประหยัดที่มีศักยภาพ:
- 3 ไบต์: การกลับเงื่อนไขผกผัน (ไม่ใช่ศูนย์หากมีความแตกต่าง) หรือจาก inline asm: ส่งคืนเงื่อนไขแฟล็ก (ซึ่งเป็นไปได้กับ gcc6)
- 1 ไบต์: ถ้าเราสามารถปิดกั้น EBX (การทำเช่นนั้นจะทำให้ฟังก์ชั่นนี้เป็นแบบแผนการโทรที่ไม่ได้มาตรฐาน) (สามารถทำได้จาก inline asm)
- 1 ไบต์: คำสั่ง RET (จาก inline asm)
ทั้งหมดเหล่านี้เป็นไปได้ที่ว่านี้เป็นส่วนอินไลน์ asm แทนของฟังก์ชั่นซึ่งจะทำให้มัน35 ไบต์สำหรับอินไลน์
0000000000000000 <cube_digits_same>:
0: 89 f8 mov eax,edi
2: 48 f7 e7 mul rdi # can't avoid a REX prefix: 2642245^2 doesn't fit in 32 bits
5: 48 f7 e7 mul rdi # rax = n^3, rdx=0
8: 44 8d 52 0a lea r10d,[rdx+0xa] # EBX would save a REX prefix, but it's call-preserved in this ABI.
c: 8d 4a 02 lea ecx,[rdx+0x2]
000000000000000f <cube_digits_same.repeat>:
f: 31 f6 xor esi,esi
0000000000000011 <cube_digits_same.cube_digits>:
11: 31 d2 xor edx,edx
13: 49 f7 f2 div r10 ; rax = quotient. rdx=LSB digit
16: 0f ab d6 bts esi,edx ; esi |= 1<<edx
19: 48 85 c0 test rax,rax ; Can't skip the REX: (2^16 * 10)^3 / 10 has all-zero in the low 32.
1c: 75 f3 jne 11 <cube_digits_same.cube_digits>
; 1st iter: 2nd iter: both:
1e: 96 xchg esi,eax ; eax=n^3 bitmap eax=n bitmap esi=0
1f: 97 xchg edi,eax ; edi=n^3 bitmap, eax=n edi=n bmp, eax=n^3 bmp
20: e2 ed loop f <cube_digits_same.repeat>
22: 39 f8 cmp eax,edi
24: 0f 94 d0 sete al
;; The ABI says it's legal to leave garbage in the high bytes of RAX for narrow return values
;; so leaving the high 2 bits of the bitmap in AH is fine.
27: c3 ret
0x28: end of function.
LOOP ดูเหมือนจะเป็นวิธีที่เล็กที่สุดที่จะทำซ้ำเพียงครั้งเดียว ฉันยังดูที่การวนซ้ำวนซ้ำ (โดยไม่มีคำนำหน้า REX และการลงทะเบียนบิตแมปอื่น) แต่นั่นก็ใหญ่กว่าเล็กน้อย ฉันยังลองใช้ PUSH RSI และการใช้test spl, 0xf
/ jz
to loop หนึ่งครั้ง (เนื่องจาก ABI ต้องการให้ RSP อยู่ในแนว 16B ก่อน CALL ดังนั้นการกดหนึ่งครั้งจะจัดแนวและอีกแนวหนึ่งอีกครั้ง) ไม่มีการtest r32, imm8
เข้ารหัสดังนั้นวิธีที่เล็กที่สุดคือพร้อมกับคำสั่ง 4B TEST (รวมถึงคำนำหน้า REX) เพื่อทดสอบไบต์ที่ต่ำของ RSP เทียบกับ imm8 ขนาดเดียวกับ LEA + LOOP แต่ต้องใช้คำสั่ง PUSH / POP เพิ่มเติม
ทดสอบสำหรับ n ทั้งหมดในช่วงทดสอบเทียบกับการใช้งาน C ของ steadybox (เนื่องจากใช้อัลกอริทึมที่แตกต่างกัน) ในสองกรณีของผลลัพธ์ที่แตกต่างกันที่ฉันดูรหัสของฉันถูกต้องและกล่องเก็บของผิด ฉันคิดว่ารหัสของฉันถูกต้องสำหรับทุกคน
_Bool cube_digits_same(unsigned long long n);
#include <stdio.h>
#include <stdbool.h>
int main()
{
for(unsigned n=0 ; n<= 2642245 ; n++) {
bool c = f(n);
bool asm_result = cube_digits_same(n);
if (c!=asm_result)
printf("%u problem: c=%d asm=%d\n", n, (int)c, (int)asm_result);
}
}
บรรทัดเดียวที่พิมพ์มี c = 1 asm = 0: false-positives สำหรับอัลกอริทึม C
ทดสอบกับuint64_t
เวอร์ชันของการใช้ C ของ Karl ในอัลกอริทึมเดียวกันและผลลัพธ์ตรงกับอินพุตทั้งหมด