5

18h をロードしてポート 60h に出力する必要があります (asm("") 内)。

ldi r1, 0x18 ; 0x18 -> r1
sts 0x60, r1 ; output r1 -> 0x60

これにレジスタr1が使用されているか、他のレジスタが使用されているかは気にしません。使用するレジスタをコンパイラに決定させる簡単な方法はありますか?

外部の r/w 変数を使用することもできますが、不要なオーバーヘッドが発生します。

register uint8_t tmp;
asm volatile (
    "ldi %[tmp], 0x18 \n\t"
    "sts 0x60, %[tmp]"
    : [tmp] "=r"(tmp) :);

これは AVR atmega (8 ビット) プロセッサ用です。GCC 4.3.2 の使用

4

2 に答える 2

3

出力オペランドには「=&」制約を使用する必要があると思います。

メカニズムは次のようになります。

  • 入力オペランドのいずれかに対して、コンパイラはいくつかのレジスタを提供し、インライン アセンブリが開始する前にそれらのオペランドの値をロードします。入力オペランドは読み取り専用です。これは、アセンブリ内でレジスタをそのままにしておくことをコンパイラがさらに期待することを意味します。つまり、コンパイラは、後でこれらの値を使用することを決定する可能性があるため、アセンブリ後にレジスタが同じ値を持つことを期待しています。

  • 出力オペランドの場合、コンパイラは一連のレジスタも割り当てます。インライン アセンブリの最後に、これらのレジスタに結果をロードしておく必要があります。警告:出力オペランドに指定されたレジスタは、入力オペランドに指定されたレジスタと一致する場合があります。それでも、これは完全に理にかなっています。たとえば、コンパイラは、アセンブリの前にあるレジスタに入力を提供し、アセンブリのにまったく同じレジスタに結果を収集します。

  • 出力のみの修飾子 ('&') を使用して、まさにこれが起こらないようにすることができます。コンパイラは、出力専用レジスタが入力レジスタを兼ねないことを保証します。

  • 入出力修飾子 ('+') を使用して、オペランドの入出力にまったく同じレジスターを使用するようコンパイラーに明示的に要求できます。これにより、実質的にオペランドへの透過的なアクセスが可能になります。反対のことが成り立たないことを要約します。この修飾子を使用しないことは、コンパイラが同じレジスタを二重に使用することを妨げません。そのためには、出力のみの修飾子を使用します。

したがって、基本的に、選択は簡単です。

  • 読み取り専用の性質とコンパイラの仮定のため、一時変数の入力オペランドを要求しないでください。たとえば、一時変数を作成し、それを何らかの値 (おそらくゼロ) で初期化すると、後でその値 (ゼロ) が必要になるたびに、コンパイラが提供するレジスタを再利用する可能性があります。
  • 一部の入力オペランドと一致する可能性があるため、出力オペランドを要求しないでください。疑わしい一時を使用すると、誤って入力オペランドが衝突する可能性があります。
  • output-only修飾子で出力を使用します。このようにして、コンパイラーは安全にスクラッチできるいくつかのレジスターを割り当てます (そこからの出力が期待されるため)。

入出力を使用すると、安全なレジスタも提供されます。ただし、オペランドは初期化が必要であり (コンパイラーは入力として読み取っていると想定しているため)、インライン アセンブリの複数のスニペットで同じオペランドを使用する場合は、復元が必要です (同じ理由で)。対照的に、出力専用修飾子を使用すると、コンパイラは変数が読み取られないことを簡単に把握できます。

于 2016-10-31T18:32:15.000 に答える
1

以前にオーバーヘッドが発生した理由はわかりませんが、外部一時register変数を使用すると、-O0 (最適化なし) でもオーバーヘッドなしで動作します。だから私は使用しています:

register uint8_t tmp;
asm volatile (
    "ldi %[tmp], 0x18 \n\t"
    "sts 0x60, %[tmp]"
    : [tmp] "=r"(tmp) :);

変数を宣言し、asm ステートメントの出力ブロックに記述する必要がありますが、必要なアセンブリ コードが作成されます (オーバーヘッドはありません)。

于 2011-08-16T20:26:15.930 に答える