x86-64机器代码功能,40字节。
或37个字节(如果允许0到非零)是“真实的”,如strcmp。
多亏了Karl Napf的C回答了位图的思想,该x86可以非常有效地使用BTS进行操作。
功能签名:_Bool cube_digits_same(uint64_t n);
,使用x86-64 System V ABI。(n
在RDI中,布尔返回值(在AL中为0或1))。
_Bool
由ISO C11定义,通常用于#include <stdbool.h>
定义bool
与C ++相同的语义bool
。
潜在的节省:
- 3个字节:返回相反的条件(如果存在差异,则为非零)。或从内联asm:返回标志条件(使用gcc6可能)
- 1个字节:如果我们可以破坏EBX(这样做会使该函数具有非标准的调用约定)。(可以从嵌入式asm做到这一点)
- 1个字节:RET指令(从内联asm)
如果这是一个inline-asm片段而不是一个函数,那么所有这些都是可能的,这会使inline-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
循环一次(因为ABI要求RSP在CALL之前是16B对齐的,因此一按即可将其对齐,而另一按则将其重新对齐)。没有test r32, imm8
编码,因此最小的方法是使用4B TEST指令(包括REX前缀)来针对imm8测试RSP的低字节。与LEA + LOOP相同,但需要额外的PUSH / POP指令。
针对steadybox的C实现对测试范围内的所有n进行了测试(因为它使用了不同的算法)。在我看到的两种不同结果的情况下,我的代码是正确的,而steadybox的代码是错误的。我认为我的代码对所有n都是正确的。
_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:对于C算法为假阳性。
还针对uint64_t
同一算法的Karl C实现版本进行了测试,结果对于所有输入均匹配。