8

私は Freescale Kinetis K60 を使用しており、CodeWarrior IDE (コンパイラに GCC を使用していると思われます) を使用しています。

2 つの 32 ビット数 (結果は 64 ビット数) を乗算し、上位 32 ビットのみを保持したいと考えています。

ARM Cortex-M4 の正しいアセンブリ命令は SMMUL 命令だと思います。アセンブリではなく、C コードからこの命令にアクセスしたいと考えています。どうすればいいですか?

コードは理想的には次のようなものになると思います。

int a,b,c;

a = 1073741824;   // 0x40000000 = 0.5 as a D0 fixed point number
b = 1073741824;   // 0x40000000 = 0.5 as a D0 fixed point number

c = ((long long)a*b) >> 31;  // 31 because there are two sign bits after the multiplication
                             // so I can throw away the most significant bit

CodeWarrior でこれを試すと、c の正しい結果が得られます (D0 FP 番号として 536870912 = 0.25)。SMMUL 命令はどこにも表示されず、乗算は 3 つの命令 (UMULL、MLA、および MLA - 符号なし乗算を使用する理由がわかりませんが、それは別の質問です) です。32 の右シフトも試してみました。これは、SMMUL 命令のほうが理にかなっている可能性があるためです。しかし、それは何も変わりません。

4

2 に答える 2

7

そのコードを最適化する際に発生する問題は次のとおりです。

08000328 <mul_test01>:
 8000328:   f04f 5000   mov.w   r0, #536870912  ; 0x20000000
 800032c:   4770        bx  lr
 800032e:   bf00        nop

あなたのコードは実行時に何もしないので、オプティマイザーは最終的な答えを計算するだけです。

これ:

.thumb_func
.globl mul_test02
mul_test02:
    smull r2,r3,r0,r1
    mov r0,r3
    bx lr

これで呼び出されます:

c = mul_test02(0x40000000,0x40000000);

0x10000000 を与える

正の数を使用しているため、UMULL は同じ結果を返します。オペランドと結果はすべて正であるため、符号付き/符号なしの違いにはなりません。

うーん、あなたはこれで私を手に入れました。私はあなたのコードを、コンパイラに乗算を 64 ビットに昇格するように指示していると読みます。smull は、64 ビットの結果を与える 2 つの 32 ビット オペランドです。これは、コードが求めているものではありません....しかし、gcc と clang の両方がとにかく smull を使用していました。コンパイル時にオペランドに 32 を超える有効数字がない場合でも、まだ smull が使用されていました。

シフトが原因だったのかもしれません。

うん、そうだった..

int mul_test04 ( int a, int b )
{
    int c;
    c = ((long long)a*b) >> 31; 
    return(c);
}

与える

gcc と clang の両方 (clang は r2 と r3 を使用する代わりに r0 と r1 をリサイクルします)

08000340 <mul_test04>:
 8000340:   fb81 2300   smull   r2, r3, r1, r0
 8000344:   0fd0        lsrs    r0, r2, #31
 8000346:   ea40 0043   orr.w   r0, r0, r3, lsl #1
 800034a:   4770        bx  lr

でもこれは

int mul_test04 ( int a, int b )
{
    int c;
    c = ((long long)a*b); 
    return(c);
}

これを与える

gcc:

08000340 <mul_test04>:
 8000340:   fb00 f001   mul.w   r0, r0, r1
 8000344:   4770        bx  lr
 8000346:   bf00        nop

クラン:

0800048c <mul_test04>:
 800048c:   4348        muls    r0, r1
 800048e:   4770        bx  lr

したがって、ビットシフトにより、コンパイラは結果の上部のみに関心があることを認識し、オペランドの上部を破棄できます。つまり、smull を使用できます。

これを行う場合:

int mul_test04 ( int a, int b )
{
    int c;
    c = ((long long)a*b) >> 32; 
    return(c);
}

どちらのコンパイラもよりスマートになり、特に clang は次のようになります。

0800048c <mul_test04>:
 800048c:   fb81 1000   smull   r1, r0, r1, r0
 8000490:   4770        bx  lr

gcc:

08000340 <mul_test04>:
 8000340:   fb81 0100   smull   r0, r1, r1, r0
 8000344:   4608        mov r0, r1
 8000346:   4770        bx  lr

0x40000000 は、小数点以下を追跡している浮動小数点数と見なされ、その場所は固定された場所であることがわかります。0x20000000 は答えとして理にかなっています。その31ビットシフトが普遍的に機能するのか、それともこの1つのケースだけで機能するのかはまだわかりません.

上記に使用される完全な例はこちら

https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/sample01

stm32f4で実行して、動作と結果を確認しました。

編集:

関数内でパラメーターをハードコーディングする代わりに、パラメーターを関数に渡す場合:

int myfun ( int a, int b )
{
     return(a+b);
}

コンパイラは、コンパイル時に答えを最適化するのではなく、ランタイム コードを作成することを余儀なくされます。

ハードコードされた数値を使用して別の関数からその関数を呼び出すと、次のようになります。

...
c=myfun(0x1234,0x5678);
...

この呼び出し関数では、コンパイラは答えを計算し、コンパイル時にそこに配置することを選択できます。myfun() 関数がグローバル (静的として宣言されていない) である場合、コンパイラは、後でリンクされる他のコードがそれを使用するかどうかを認識しないため、このファイルの呼び出しポイントの近くでも応答を最適化し、実際の関数を生成する必要があります。他のファイルの他のコードが呼び出されるようにオブジェクトに残しておくと、コンパイラ/オプティマイザがそのCコードで何をするかを調べることができます。たとえば、プロジェクト全体を (ファイル間で) 最適化できる llvm を使用しない限り、この関数を呼び出す外部コードは、コンパイル時に計算された答えではなく、実際の関数を使用します。

gcc と clang の両方が私が説明していることを実行し、関数の実行時コードをグローバル関数として残しましたが、ファイル内ではコンパイル時に答えを計算し、関数を呼び出す代わりにコードにハードコーディングされた答えを配置しました。

int mul_test04 ( int a, int b )
{
    int c;
    c = ((long long)a*b) >> 31;
    return(c);
}

同じファイル内の別の関数で:

hexstring(mul_test04(0x40000000,0x40000000),1);

関数自体はコードに実装されています。

0800048c <mul_test04>:
 800048c:   fb81 1000   smull   r1, r0, r1, r0
 8000490:   0fc9        lsrs    r1, r1, #31
 8000492:   ea41 0040   orr.w   r0, r1, r0, lsl #1
 8000496:   4770        bx  lr

しかし、それが呼ばれる場所では、必要なすべての情報を持っていたので、答えをハードコーディングしました:

 8000520:   f04f 5000   mov.w   r0, #536870912  ; 0x20000000
 8000524:   2101        movs    r1, #1
 8000526:   f7ff fe73   bl  8000210 <hexstring>

ハードコーディングされた回答が必要ない場合は、同じ最適化パスにない関数を使用する必要があります。

コンパイラーとオプティマイザーの操作は多くの実践に帰着しますが、コンパイラーとオプティマイザーは常に (良くも悪くも) 進化しているため、正確な科学ではありません。
別の方法で問題を引き起こしている関数内の小さなコードを分離することにより、大きな関数はスタック フレームを必要とする可能性が高くなり、変数がレジスタからスタックに追い出されます。小さな関数はそれを行う必要がない可能性があり、オプティマイザは、結果としてコードの実装方法を変更する場合があります。コードフラグメントを 1 つの方法でテストして、コンパイラが何をしているかを確認してから、それをより大きな関数で使用すると、必要な結果が得られません。実装したい正確な命令または一連の命令がある場合....それらをアセンブラで実装します。特定の命令セット/プロセッサの特定の命令セットをターゲットにしている場合は、ゲームを避け、コンピューター/コンパイラ/などを変更するときにコードを変更しないようにし、そのターゲットにはアセンブラを使用してください。

于 2011-12-03T05:26:43.637 に答える
-1

GCC は実際の固定小数点型をサポートしています: http://gcc.gnu.org/onlinedocs/gcc/Fixed_002dPoint.html

どのような命令を使用するかはわかりませんが、生活が楽になるかもしれません。

于 2011-12-03T20:47:23.673 に答える