ここで素朴な疑問:レジスタは32ビットなのに、どうやって64ビットの演算をするの?
これがわかれば、多倍長計算の開発にもつながるかもしれない!な~んてね。(CV:大亀あすか)
というわけで、さっそく実験。
(ちゃららら)C/C++ to Assembly~~~!(CV:大山のぶ代)
カキカキ
#include
int main(void) {
long long a;
long long b;
long long c;
a=1234567890ll;
b=9876543210ll;
c=a*b;
printf("%lld\n",c);
c=b/a;
printf("%lld\n",c);
c=a+b;
printf("%lld\n",c);
c=b-a;
printf("%lld\n",c);
return 0;
}
・・・
( ゚∀゚)つ【imulq,idivq,addq,subq】
▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂ うわあああああああ
それはチートだろ…というわけでローカルのコンパイラ(gcc4.7.2)で試してみる。
上のコードでは混ざってしまいわかりにくいので、関数に分離したものをコンパイルする。
long long add(long long a,long long b) {
return a+b;
}
long long sub(long long a,long long b) {
return a-b;
}
long long mul(long long a,long long b) {
return a*b;
}
long long div(long long a,long long b) {
return a/b;
}
long long mod(long long a,long long b) {
return a%b;
}
► スポイラーを表示
でも、本番の解析に取り掛かる前に、前提を確認しておきましょう。
まず、関数の引数の格納位置を確認します。
printfの前の処理から、関数の引数は前から順に%esp周辺に格納されそうなことがわかりますが、どの位置に格納されるのでしょうか。
#include
asm(
"_zikken0:\n\t"
"movl (%esp),%eax\n\t"
"ret\n\t"
);
extern int zikken0(long long a);
asm(
"_zikken1:\n\t"
"movl 4(%esp),%eax\n\t"
"ret\n\t"
);
extern int zikken1(long long a);
asm(
"_zikken2:\n\t"
"movl 8(%esp),%eax\n\t"
"ret\n\t"
);
extern int zikken2(long long a);
asm(
"_zikken3:\n\t"
"movl 12(%esp),%eax\n\t"
"ret\n\t"
);
extern int zikken3(long long a);
int main(void) {
printf("%08x\n",zikken0(0x123456789abcdef0ll));
printf("%08x\n",zikken1(0x123456789abcdef0ll));
printf("%08x\n",zikken2(0x123456789abcdef0ll));
printf("%08x\n",zikken3(0x123456789abcdef0ll));
return 0;
}
しかし、関数の先頭でpushlしていることから、%espがずれることが予想できます。
どっち向きにずれるのでしょうか?
#include
asm(
"_pushkensyou:\n\t"
"movl %esp,%eax\n\t"
"pushl %ebp\n\t"
"subl %esp,%eax\n\t" /* eax=eax-esp */
"popl %ebp\n\t"
"ret\n\t"
);
extern int pushkensyou(void);
int main(void) {
printf("%d\n",pushkensyou());
return 0;
}
このことから、先ほどのコードのメモリの配置図はこのようになることがわかります。
+--------+--------+--------+--------+--------+--------+
|[t0] |[t1] |[t2] |[t3] |[t4] |[t5] |
+--------+--------+--------+--------+--------+--------+
-24(%ebp)-20(%ebp)-16(%ebp)-12(%ebp) -8(%ebp) -4(%ebp)
+--------+--------+--------+--------+--------+--------+
| | |[a1] |[a2] |[b1] |[b2] |
+--------+--------+--------+--------+--------+--------+
%ebp 4(%ebp) 8(%ebp) 12(%ebp) 16(%ebp) 20(%ebp)
t0~t5は計算用領域のようです。
まずは、足し算。
movl 8(%ebp), %eax
movl %eax, -16(%ebp) a1→t2
movl 12(%ebp), %eax
movl %eax, -12(%ebp) a2→t3
movl 16(%ebp), %eax
movl %eax, -24(%ebp) b1→t0
movl 20(%ebp), %eax
movl %eax, -20(%ebp) b2→t1
movl -24(%ebp), %eax b1→eax
movl -20(%ebp), %edx b2→edx
movl -16(%ebp), %ecx a1→ecx
movl -12(%ebp), %ebx a2→ebx
addl %ecx, %eax b1+a1→eax
adcl %ebx, %edx b2+a2+(b1+a1の繰り上がり)→edx
また、long long型の戻り値は%eaxに下位32ビット、%edxに上位32ビットが格納されることがわかります。
引き算も、足し算とほとんど同じです。
//略(足し算と共通)
movl -24(%ebp), %ecx b1→ecx
movl -20(%ebp), %ebx b2→ebx
movl -16(%ebp), %eax a1→eax
movl -12(%ebp), %edx a2→edx
subl %ecx, %eax a1-b1→eax
sbbl %ebx, %edx a2-b2-(a1-b1の繰り下がり)→edx
次に、掛け算です。
movl 8(%ebp), %eax
movl %eax, -8(%ebp) a1→t4
movl 12(%ebp), %eax
movl %eax, -4(%ebp) a2→t5
movl 16(%ebp), %eax
movl %eax, -16(%ebp) b1→t2
movl 20(%ebp), %eax
movl %eax, -12(%ebp) b2→t3
movl -4(%ebp), %eax a2→eax
movl %eax, %edx a2→edx
imull -16(%ebp), %edx b1*a2→edx
movl -12(%ebp), %eax b2→eax
imull -8(%ebp), %eax a1*b2→eax
leal (%edx,%eax), %ecx edx+eax→ecx
movl -16(%ebp), %eax b1→eax
mull -8(%ebp) eax*a1→edx,eax
addl %edx, %ecx ecx+edx→ecx
movl %ecx, %edx ecx→edx
(a1+a2*k)*(b1+b2*k)=a1*a2+(a1*b2+b1*a2)*k+(a2*b2)*k^2 ただしk=2^32
k^2の項は、64ビットの範囲を超えるので無視できることがわかります。
では、割り算を見てみましょう。
movl 8(%ebp), %eax
movl %eax, -16(%ebp)
movl 12(%ebp), %eax
movl %eax, -12(%ebp)
movl 16(%ebp), %eax
movl %eax, -24(%ebp)
movl 20(%ebp), %eax
movl %eax, -20(%ebp)
movl -24(%ebp), %eax
movl -20(%ebp), %edx
movl %eax, 8(%esp)
movl %edx, 12(%esp)
movl -16(%ebp), %eax
movl -12(%ebp), %edx
movl %eax, (%esp)
movl %edx, 4(%esp)
call ___divdi3
┗(^o^)┛wwWww┏(^o^)┓ドコドコドコドコwwwWWWwwWwwwwW
剰余も同様にサブルーチンでした。
足し算と引き算の計算方法は多倍長計算に応用できそうですが、掛け算と割り算は無理そうです。
といっても足し算と引き算もインラインアセンブリを書く事になりますが…
ちなみに
コードは省略しますが、ビットAND、OR、XORは上位下位それぞれ演算するだけです。
シフト演算は、ほかのレジスタからシフトインできるshrdl、shldl命令があるようです。
ご清聴ありがとうございました。
【追記】
これが___divdi3の正体のようですね。解析はまたこんど…
http://www.ic.unicamp.br/~islene/2s2008 ... 2/divdi3.c