0

バックグラウンド:

組み立ては初めてです。私がプログラミングを学んでいたとき、私は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 | 78691922gdb

プログラム受信信号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番目のパラメータをに移動しますedxeax次に、左に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%edx10だけ左にシフトしor、2番目のパラメーターを使用してsします。次に、左に2シフト%edxすることで、実際には%edx4を掛けます。次に、それを%eax(配列のベースアドレス)に追加します。つまり、基本的にはこれを実行しただけです:( [edx*4+0x47e018]Intel構文)または0x47e018(,%edx,4)AT&T。入った要素の値を移動し、に%eax入れint answerます。この方法はより「拡張」されていますが、私の手書きのアセンブリと同じことを行います。では、なぜSIGSEGVコンパイラが正常に動作しているのにしばらく時間がかかるのでしょうか。

4

2 に答える 2

2

これは、Mats Peterssonの回答への追加を目的としています-OPの逆アセンブリ分析(彼のアセンブリとコンパイラが生成したものは同等である)が間違っている理由がすぐにはわからなかったので、私はそれを書きました。

Mats Petersson が説明しているように、問題はtables実際には配列へのポインターであるため、要素にアクセスするには、2 回逆参照する必要があります。私には、これがコンパイラによって生成されたコードのどこで発生するのか、すぐにはわかりませんでした。犯人は、この無害に見える行です。

a1 18 e0 47 00          mov    0x47e018,%eax

素人の目 (私の目も含む) には、これは値 0x47e018が に移動したように見えるかもしれませんが、eax実際にはそうではありません。同じオペコードの Intel 構文表現から、次のような手がかりが得られます。

a1 18 e0 47 00          mov    eax,ds:0x47e018

ああ - ds:- 実際には値ではなく、アドレスです!

今疑問に思っている人のために、値 0x47e018をに移動するためのオペコードと ATT 構文アセンブリは次のとおりeaxです。

b8 18 e0 47 00          mov    $0x47e018,%eax
于 2013-03-13T15:42:17.020 に答える
2

tables配列自体ではなく、配列へのポインターであるに違いありません(逆アセンブリから) 。

したがって、次のものが必要です。

 asm volatile ( "shll $10, %1;"
        movl  _tables,%%eax
       "orl %1, %2;"
       "movl (%%eax,%2,4)",
       : "=r" (answer) : "r" (factor1), "r" (factor2) : "eax" )   

(最後の行の余分な clobber を忘れないでください)。

もちろんバリエーションはありますが、コードがループしている場合はより効率的です。

 asm volatile ( "shll $10, %1;"
       "orl %1, %2;"
       "movl (%3,%2,4)",
       : "=r" (answer) : "r" (factor1), "r" (factor2), "r"(tables) )   
于 2013-03-13T14:45:38.553 に答える