2017-01-27 9 views
1

私は関数をcからアセンブリに書き直そうとしています。これは、これをより効率的にするのではなく、Cでアセンブリを書くことの練習です。トラブルCのASMブロックを結合する

問題は、私は3つのasm()ブロックで作業していますが、それらを組み合わせることはできません。私はそれらを組み合わせるときに私が紛失しているものがなければならないと思う。

これは、現在動作するコードです:

​​

コードの仕事の上の部分の両方が期待通り:

137   __asm__ __volatile__ (
138    "mov R0, #1\n\t" 
139    "mov R1, %[bb]\n\t" 
140    "and R0, R1\n\t" 
141    "cmp R0, #1\n\t" // (b & 1) == 1 
142    "bne aftereor\n\t" 
143    "eor %[pp], %[pp], %[aa]\n\t" 
144    "aftereor:\n\t" 
145    "mov %[hbs], %[aa]\n\t" 
146    "mov R0, #128 \n\t" 
147    "and %[hbs], R0 \n\t" 
148    "lsl %[aa], %[aa], #1\n\t" 
149    : [pp]"+l" (p),[aa]"+l" (a),[hbs]"=l" (hi_bit_set) 
150    : [bb]"l" (b) 
151    : 
152  ); 
153   __asm__ __volatile__ (
154    "cmp %[hbs], #128 \n\t" 
155    "bne brancha \n\t" 
156    "mov R2, #0x1b\n\t" 
157    "eor %[aa], %[aa], R2\n\t" 
158    "brancha:\n\t" 
159    : [aa]"+l" (a) 
160    : [hbs]"l" (hi_bit_set) 
161    : 
162   ); 
163   __asm__ __volatile__ (
164    "lsr %[bb], %[bb], #1" 
165    : [bb]"+l" (b) 
166    : 
167    : 
168  ); 

これは私がアセンブリに書き換えしようとしていますCコードです。しかし、私の問題は、アセンブリの3つのブロックを1つにまとめるときです。たとえば、次のコードは何らかの理由で期待どおりに動作しません。

137   __asm__ __volatile__ (
138    "mov R0, #1\n\t" 
139    "mov R1, %[bb]\n\t" 
140    "and R0, R1\n\t" 
141    "cmp R0, #1\n\t" // (b & 1) == 1 
142    "bne aftereor\n\t" 
143    "eor %[pp], %[pp], %[aa]\n\t" 
144    "aftereor:\n\t" 
145    "mov %[hbs], %[aa]\n\t" 
146    "mov R0, #128 \n\t" 
147    "and %[hbs], R0 \n\t" 
148    "lsl %[aa], %[aa], #1\n\t" 
149    "cmp %[hbs], #128 \n\t" 
150    "bne brancha \n\t" 
151    "mov R2, #0x1b\n\t" 
152    "eor %[aa], %[aa], R2\n\t" 
153    "brancha:\n\t" 
154    "lsr %[bb], %[bb], #1" 
155    : [pp]"+l" (p),[aa]"+l" (a),[hbs]"+l" (hi_bit_set),[bb]"+l" (b) 
156    : 
157    : 
158  ); 

Iが行われた変更のみが読み取りおよび書き込みする両方の変数「hi_bit_set」と「B」を変更、最初に2番目と3番目のブロックを結合しました。私の理解には、これは私にとってはうまくいくようです。しかし、これは正しい結果を生んでいないので、私は何かを見逃していると推測しています。

ご協力いただきありがとうございます。

答えて

2

'early clobber'を見ましたか?コンパイラは入力と出力に同じレジスタを割り当てますが、いくつかのレジスタを長時間保持し、それらを別々にする必要があります。

また、明示的なレジスタとして 'R0'、 'R1'、および 'R2'を使用することはありません。 'tmp1'変数を作成し、それを入力として渡す必要があります。それを 'R0'と呼び、レジスタasmを使用して実際にそれを割り当てることができます(またはclobbersとしてリストすることもできます)。

コードには多くの潜在的な最適化が含まれており、おそらく50%のサイズになる可能性があります。しかし、私はあなたのオリジナルに忠実に留まらず、動作させるためにレジスタを指定します。

void foo(uint32_t a, uint32_t b, uint32_t p) 
{ 
    register uint32_t tmp1 asm ("r0"); 
    register uint32_t tmp2 asm ("r1"); 
    register uint32_t tmp3 asm ("r2"); 
    uint32_t hi_bit_set; 

    __asm__ __volatile__ (
     "mov R0, #1\n\t" 
     "mov R1, %[bb]\n\t" 
     "and R0, R1\n\t" 
     "cmp R0, #1\n\t" // (b & 1) == 1 
     "bne aftereor\n\t" 
     "eor %[pp], %[pp], %[aa]\n\t" 
     "aftereor:\n\t" 
     "mov %[hbs], %[aa]\n\t" 
     "mov R0, #128 \n\t" 
     "and %[hbs], R0 \n\t" 
     "lsl %[aa], %[aa], #1\n\t" 
     "cmp %[hbs], #128 \n\t" 
     "bne brancha \n\t" 
     "mov R2, #0x1b\n\t" 
     "eor %[aa], %[aa], R2\n\t" 
     "brancha:\n\t" 
     "lsr %[bb], %[bb], #1" 
     : [pp]"+l" (p),[aa]"+l" (a),[hbs]"+l" (hi_bit_set),[bb]"+l" (b) 
     : 
     : "r0", "r1", "r2" /* THIS IS IMPORTANT! */ 
    ); 
} 

このoutput appears good私はクロバーレジスタが含まれていない場合一方、コンパイラは、他の目的のために「R0」などを使用しています。コンパイラの主な仕事は、レジスタを管理することです。スタックにプッシュ/ポップするのは悪い(流出)が、不要なのはMOV命令です。

コンパイルする完全な機能を提供することは、質問するときに常に有効です。私は1つを作ろうとしましたが、あなたはGCCがあなたの関数をどのように翻訳するかを見ることができます。定数の代わりに 'キャリーフラグ'を使用して、設定されたビット情報を抽出することができます。

#include <stdint.h> 

uint32_t foo(uint32_t *A, uint32_t *B, uint32_t p) 
{ 
    uint32_t a = *A; 
    uint32_t b = *B; 

    /* codes starts here... */ 

    if((b & 1) == 1) { 
    p ^= a; 
    } 
    a <<= 1; 
    if(a & 0x100) { 
    a ^= 0x1b; 
    } 
    b >>= 1; 

    /* codes is done. */ 


    *A = a; 
    *B = b; 
    return p; 
} 

ここアセンブラコーダは、上記 '.L2' と' .L3' ラベルのlocal labelsを好むかもしれ

foo(unsigned long*, unsigned long*, unsigned long): 
     push {r4, r5, lr} ; save callee reg 
     ldr  r4, [r1]  ; get 'B' pointer, your [bb] is r4. 
     ldr  r3, [r0]  ; get 'A' pointer, your [aa] is r3. 

     ; codes starts here... 

     lsls r5, r4, #31 
     bpl  .L2 
     eors r2, r3 
.L2: 
     lsls r3, r3, #1 
     lsls r5, r3, #23 
     bpl  .L3 
     movs r5, #27 
     eors r3, r5 
.L3: 
     lsrs r4, r4, #1 

     ; code is done 

     str  r3, [r0]  ; saving [aa] 
     movs r0, r2  ; r0 is 'pp' 
     str  r4, [r1]  ; save [bb] value. 
     pop  {r4, r5, pc} ; restore to callers value. 

、GCCの16ビットThumb出力されます。

+0

お返事ありがとうございます。私は今clobbersを理解する、私はちょうどそのビットを逃したと思う。 答えの2番目の部分については、私は16ビットのサム命令に限定されているので、あなたが提案した条件付き実行の最適化はできません。 –

+0

さらに、 "r0"、 "r1"、 "r2" "をクローバーリストに追加すると、コードを実行する前に" r0 "、" r1 "、" r2 "の値を他のレジスタに移動させるように見えます。コンパイラに特定のレジスタの代わりにレジスタを使用するよう指示する方法はありませんか?確かにこれは、コードブロックの前後でレジスタを移動する必要がないので、より効率的です。 –

+0

親指の指示がある[こちらはコードです](https://godbolt.org/g/Nkv02f)メインコードは9命令です。 16ビットのサムコード。コンパイラが動かないようにする方法があります。たとえば、tmp1、tmp2、tmp3を入出力としてリストし、レジスタの名前を付けません。しかし、サムはビットのステータスを取得するのに 'lsls'を使うことができるので、レジスタに定数をロードする必要はありません。そのため、すべてのtmpX値を持っています。 –