x86机器代码,39个字节
;;; Obtain a "reversed" version of the input value.
;;;
;;; To do this, each iteration of a loop, we take the input value modulo 10,
;;; add that to our accumulator (EDI), multiply the accumulator by 10, and
;;; divide the input value by 10. x86's DIV instruction does both modulo and
;;; division as a single operation, with the cost of clobbering two output
;;; registers (EAX and EDX). We clobber the input value throughout the loop
;;; (the way we know we're done is when it becomes 0---that means that we have
;;; pulled all of the digits off of it), so we need to save a copy of it first.
89 C8 mov eax, ecx ; make copy of input
31 FF xor edi, edi ; clear accumulator
6A 0A push 10
5E pop esi ; set ESI to 10
Reverse:
0F AF FE imul edi, esi ; accumulator *= 10
99 cdq ; zero EDX in preparation for division
F7 F6 div esi ; EDX:EAX / 10 (EAX is quot, EDX is rem)
01 D7 add edi, edx ; accumulator += remainder
85 C0 test eax, eax ; was quotient 0?
75 F4 jnz Reverse ; if not, keep looping and extracting digits
;;; At this point, EAX is 0 (clobbered throughout the loop),
;;; ECX still contains a copy of our original input, and
;;; EDI contains the 'reversed' input.
89 C8 mov eax, ecx ; make another copy of the input
F7 E7 mul edi ; multiply input (implicit EAX operand)
; by 'reversed', with result in EDX:EAX
; (note: EDX will be 0)
;;; Compute the greatest common denominator (GCD) of the input and
;;; the 'reversed' values, using a subtraction-based algorithm.
GCD_0:
39 CF cmp edi, ecx ; compare the two values
72 02 jb GCD_1 ; go to GCD_1 if less than
87 F9 xchg ecx, edi ; swap values
GCD_1:
29 F9 sub ecx, edi ; subtract
75 F6 jnz GCD_0 ; if sum != 0, go back to the top
;;; Square the GCD.
0F AF FF imul edi, edi
;;; Divide the product of input and 'reversed' by the square of the GCD.
;;; Remember from above that the product of input and 'reversed' is in
;;; the EAX register, and we can assume EDX is 0, so we don't need to do
;;; a CDQ here in preparation for the division. Using EAX as the implicit
;;; source operand saves us a byte when encoding DIV.
F7 F7 div edi
;;; The DIV instruction placed the quotient in EAX,
;;; which is what we want to return to the caller.
C3 ret
上面的函数计算指定输入参数的“不常见因数”。遵循基于寄存器的__fastcall调用约定,该参数在ECX
寄存器中传递。EAX
与所有x86调用约定一样,结果返回到寄存器中。
在线尝试!
以这种紧凑的形式花费了很长时间,但这是一个有趣的练习。扭曲的地段,以获得最优化的寄存器调度可能,在x86的约束下DIV
指令的隐含操作数,并尝试使用的短编码MUL
,并XCHG
尽可能说明。我很想知道是否有人可以想到另一种进一步缩短它的方法。到最后我的脑袋已经炸透了。谢谢下次看到编译器!(虽然这方法比编译器会产生更好的代码...特别是如果你稍微调整了它没有大小限制,删除之类的东西XCHG
。)