Cからアセンブリへ


16

avr-8bit用の次のCコードがあると仮定します。

int v1=1;
int v2=2;
v2=v2+v1;

次の分解が予想されました

ldi r18, 1;
ldi r19, 2;
add r19, r18;

しかし、私が走った後:

avr-gcc -mmcu=atmega2560 Test.c -o Test.elf

そして

avr-objdump -S Test.elf > Test.lss

私は次のものを分解しました

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0
    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01
    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03
    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02
    add r24, r18
    adc r25, r19
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

逆アセンブラの結果を理解するのに役立つ人はいますか?

編集:charを使用すると、アセンブリは次のようになります。

ldi r24, 0x01
std Y+1, r24
ldi r24, 0x02
std Y+2, r24
ldd r25, Y+2
ldd r24, Y+1
add r24, r25
std Y+2, r24

std命令はいつありますか?

回答:


20

簡単な答え:レジスタは8ビットで、値は16ビットです。したがって、それらを2つの部分に分けて処理します。

長い答え:

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0

16ビット値1を8ビットレジスタr24、r25に格納します。

    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01

スタック位置Y + 1、Y + 2に保管します。

    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0

16ビット値2を8ビットレジスタr24、r25に格納します。

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

スタック位置Y + 3、Y + 4に保管します。

    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02

それらをスタックから(r18、r19)および(r24、r25)にコピーして戻します

    add r24, r18
    adc r25, r19

(r18、r19)を(r24、r25)に追加します。2番目の追加のキャリーを含みます

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

スタックに戻します。

元のアセンブリを取得するには、2つのことを試してください。

  • 「char」変数を使用する
  • 「-O2」コンパイラオプションを使用

編集:コンパイラが変数をレジスタに保存するのではなくスタックに保存するのは、変数がデフォルトの「自動」ストレージタイプで保存されるためです。それはありますが、ストレージクラスを「登録」でそれらを宣言しても、レジスタにそれらを最適化するが、それはする必要はありません。

これは言語の厳密な要件ではありませんが、通常のコンパイラの動作です。ある時点でv1のアドレスを取得した場合、そのアドレスに保存場所を割り当て、「v1」の値が変更されるたびにそこに保存する必要があります。したがって、v1をレジスターに保存するかスタックに保存するかを記録するために、v1をスタックに保持し、コードの各行を個別に扱います。


ありがとうございました!より明確になりました!質問で私の編集を見つけてください。
DarkCoffee

1
編集をご覧ください。-O2も試してください。-O3かもしれませんが、壊れたコードを生成する可能性があります。
pjc50

3
私が扱う多くの埋め込みコードは、サイズに固有の追加の型を定義しています。たとえば、符号なし整数の場合は「uint8、uint16、uint32」などです。そうすれば、あなたが扱っている変数の種類を常に正確に知ることができます。特に、小さな組み込み、符号付き、フロートでは、未定義のサイズ/符号付きの「int」はすべて最高でCPUサイクルを消費し、最悪の場合深刻なバグを引き起こします。
ジョンU

実際のコンパイラは、約10〜15年前にこのような動作を停止しました。レジスタ割り当ての問題はほとんど解決されており、コンパイラーはそれを上手く使っています。彼らは、変数がいつスタックに置かれなければならないか、いつレジスターに入れられるか、それを移動する努力の価値があるかどうか、そしていつそうするかを正確に知っています。ブックキーピングはコンパイル時に行われ、コンパイラー自体はギガバイトのメモリーを持っています。明らかな理由から、大きな例外はデバッグモードですが、すべてがスタック上にあります。
-MSalters

@ pjc50 -O3は壊れたコードを生成する可能性がありますか?[要出典](いいえ、未定義の動作を呼び出してから最適化設定で
中断する

4

いくつかのサンプルコードを見つけたので、コメントを回答にします。他の人は既に問題を説明しています。

私が扱う多くの埋め込みコードは、サイズに固有の追加の型を定義しています。たとえば、符号なし整数の場合は「uint8、uint16、uint32」などです。そうすれば、あなたが扱っている変数の種類を常に正確に知ることができます。特に、小さな組み込み、符号付き、フロートでは、未定義のサイズ/符号付きの「int」はすべて最高でCPUサイクルを消費し、最悪の場合深刻なバグを引き起こします。

現在の#definesは次のとおりです。

/*
 * Example - the basic data types from our embedded code
 */
typedef unsigned char       uint8;  /*  8 bits */
typedef unsigned short int  uint16; /* 16 bits */
typedef unsigned long int   uint32; /* 32 bits */

typedef char                int8;   /*  8 bits */
typedef short int           int16;  /* 16 bits */
typedef int                 int32;  /* 32 bits */

typedef volatile int8       vint8;  /*  8 bits */
typedef volatile int16      vint16; /* 16 bits */
typedef volatile int32      vint32; /* 32 bits */

typedef volatile uint8      vuint8;  /*  8 bits */
typedef volatile uint16     vuint16; /* 16 bits */
typedef volatile uint32     vuint32; /* 32 bits */

3
良いアイデア; uint8_tとその友人は現在、標準の一部です:stackoverflow.com/questions/16937459/…
pjc50

なんて便利!私たちはそれらをC89というプロジェクトで継承しているので、公式バージョンがあることを知っておくとよいでしょう。
ジョンU

2

Cコードは16ビット整数変数(int)を使用します。コンパイラはあなたの心を読むことができないので、ソースファイルにあるものを正確にコンパイルします。したがって、8ビット変数が必要な場合は、それぞれの型を使用する必要があります。

その結果、メモリに値を保存することになります(ただし、より単純です)。私はCではあまり得意ではありませんが、私見では、変数をRAMの代わりにレジスタに入れたい場合は、変数をレジスタに割り当てるいくつかのオプションがあります。何かのようなもの:

register unsigned char VARNAME asm("r3");

すべてのレジスタがこのようなトリックに使用できるわけではないことに注意してください。

結論は?アセンブリでプログラムを作成します。これらは常に小さく、高速で、読み取り/サポートが容易です。


アセンブリはCよりも読みやすいですか?
dext0rb

@ dext0rb-はい。もちろん、両方を十分に知っていれば。Cのみを知っている場合、アセンブリおよび他の言語は読みにくくなります。
johnfound

最後の点に同意する必要があります。アセンブラーで書かれたプログラムは読みにくいです。上記のソースコードを比較するだけです。Cコードは、その意図だけでなく、はるかに明確で短くなっています。この違いは、構造体が使用されるにつれて大きくなります。
-soandos

@soandos-Cコードは短くなります、はい。クリア?私はわかりません。もしそうなら、上記の質問はまったく聞かれる必要はないでしょう。実際、「短さ」の価格は、細部の「ぼけ」です。
johnfound

もちろん、「私はCがあまり得意ではありません」と言う人は、純粋なアセンブリの美徳を宣言するでしょう。:D
dext0rb
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.