バックグラウンド:
組み立ては初めてです。私がプログラミングを学んでいたとき、私は1000 * 1000までの掛け算の九九を実装するプログラムを作りました。表はそれぞれの答えが線上にあるようにフォーマットされていますfactor1 << 10 | factor2
(私は知っています、私は知っています、それはきれいではありません)。次に、これらのテーブルが配列にロードされますint* tables
。空の行は0で埋められます。テーブルのファイル(7.3 MB)へのリンクは次のとおりです。アセンブリを使用してもこれはそれほど高速化されないことはわかっていますが、楽しみのために(そして少し練習するために)それを実行したかっただけです。
質問:
このコードをインラインアセンブリに変換しようとしています(tables
グローバルです):
int answer;
// ...
answer = tables [factor1 << 10 | factor2];
これは私が思いついたものです:
asm volatile ( "shll $10, %1;"
"orl %1, %2;"
"movl _tables(,%2,4), %0;" : "=r" (answer) : "r" (factor1), "r" (factor2) );
C ++コードは正常に機能しますが、アセンブリが失敗します。私のC++と比較して、私のアセンブリ(特にパーツ)の何が問題になっていますかmovl _tables(,%2,4), %0;
それを解決するために私がしたこと:
私はいくつかの乱数を使用しました:89796asfactor1
およびfactor2
。(は)に要素があることを知っています–これをC++で検証しました。で実行すると、SIGSEGVが発生します。89 << 10 | 786
91922
gdb
プログラム受信信号SIGSEGV、セグメンテーション違反。
この行で:
"movl _tables(,%2,4), %0;" : "=r" (answer) : "r" (factor1), "r" (factor2) );
の周りに2つのメソッドを追加しました。これにより、ブロックが分解asm
のどこにあるかを知ることができます。asm
asm
私のブロックの分解:
からの分解objdump -M att -d
はうまく見えます(私はよくわかりませんが、私が言ったように、私は組み立てに不慣れです):
402096: 8b 45 08 mov 0x8(%ebp),%eax
402099: 8b 55 0c mov 0xc(%ebp),%edx
40209c: c1 e0 0a shl $0xa,%eax
40209f: 09 c2 or %eax,%edx
4020a1: 8b 04 95 18 e0 47 00 mov 0x47e018(,%edx,4),%eax
4020a8: 89 45 ec mov %eax,-0x14(%ebp)
からの分解objdump -M intel -d
:
402096: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]
402099: 8b 55 0c mov edx,DWORD PTR [ebp+0xc]
40209c: c1 e0 0a shl eax,0xa
40209f: 09 c2 or edx,eax
4020a1: 8b 04 95 18 e0 47 00 mov eax,DWORD PTR [edx*4+0x47e018]
4020a8: 89 45 ec mov DWORD PTR [ebp-0x14],eax
私が理解していることから、それは私のvoid calc ( int factor1, int factor2 )
関数の最初のパラメーターをに移動していますeax
。次に、2番目のパラメータをに移動しますedx
。eax
次に、左に10シフトし、。or
を付けedx
ます。32ビット整数は4バイトなので、[edx*4+base_address]
。結果をに移動しeax
てから入れeax
ますint answer
(これ-0x14
はスタック上にあると思います)。あまり問題はありません。
コンパイラの逆アセンブル.exe
:
asm
ブロックをプレーンC++( )に置き換えてanswer = tables [factor1 << 10 | factor2];
分解すると、Intel構文で次のようになります。
402096: a1 18 e0 47 00 mov eax,ds:0x47e018
40209b: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
40209e: c1 e2 0a shl edx,0xa
4020a1: 0b 55 0c or edx,DWORD PTR [ebp+0xc]
4020a4: c1 e2 02 shl edx,0x2
4020a7: 01 d0 add eax,edx
4020a9: 8b 00 mov eax,DWORD PTR [eax]
4020ab: 89 45 ec mov DWORD PTR [ebp-0x14],eax
AT&T構文:
402096: a1 18 e0 47 00 mov 0x47e018,%eax
40209b: 8b 55 08 mov 0x8(%ebp),%edx
40209e: c1 e2 0a shl $0xa,%edx
4020a1: 0b 55 0c or 0xc(%ebp),%edx
4020a4: c1 e2 02 shl $0x2,%edx
4020a7: 01 d0 add %edx,%eax
4020a9: 8b 00 mov (%eax),%eax
4020ab: 89 45 ec mov %eax,-0x14(%ebp)
私はIntelの構文にあまり詳しくないので、AT&Tの構文を理解してみます。
tables
まず、配列のベースアドレスをに移動し%eax
ます。次に、は最初のパラメータをに移動します%edx
。%edx
10だけ左にシフトしor
、2番目のパラメーターを使用してsします。次に、左に2シフト%edx
することで、実際には%edx
4を掛けます。次に、それを%eax
(配列のベースアドレス)に追加します。つまり、基本的にはこれを実行しただけです:( [edx*4+0x47e018]
Intel構文)または0x47e018(,%edx,4)
AT&T。入った要素の値を移動し、に%eax
入れint answer
ます。この方法はより「拡張」されていますが、私の手書きのアセンブリと同じことを行います。では、なぜSIGSEGV
コンパイラが正常に動作しているのにしばらく時間がかかるのでしょうか。