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实现版本进行了测试,结果对于所有输入均匹配。