1

http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.htmlでインライン アセンブリの例を試してい ます。

  • clobber の動作について clobbering
    は基本的に、指定されたレジスタ/メモリの値を信頼しないように GCC に指示します。

    「ええと、GCC が前後のレジスタで何をしているかを正確に知ることができる場合、最適化するときに本当に役立ちます....(x+1) をレジスタに入れるように指示した場合、GCC はそれを知るのに十分スマートです。 、それを上書きせず、後で C コードが (x+1) を参照し、そのレジスタを解放しておくことができた場合、計算が再利用されます。

この段落は、クロバリングが一般的な部分式の削除を無効にすることを意味しますか?

  • クロバー リストに関するチュートリアルにはいくつかの矛盾があり
    ます。入力/出力リストで指定されたレジスタについては、GCC が認識しているようにクロバー リストに入れる必要はありません。ただし、rep_movsl (または rep_stosl) に関する例では:

    asm ("cld\n\t" "rep\n\t" "stosl" : /* 出力レジスタなし */ : "c" (count), "a" (fill_value), "D" (dest) : " %ecx", "%edi" );

「S、D、c」は出力オペランドにありますが、再び上書きされたものとしてリストされます。Cで簡単なスニペットを試しました:

#include<stdio.h>
int main()
{
  int a[] = {2, 4, 6};
  int b[3];
  int n = 3;
  int v = 12;
  asm ("cld\n\t"
       "rep\n\t"
       "movsl"
       :
       : "S" (a), "D" (b), "c" (n)
       : );
//     : "%ecx", "%esi", "%edi" );
  printf("%d\n", b[1]);
}

コメント付きの clobber リストを使用すると、GCC は次のように文句を言います。

ac:8:3: エラー: 'asm' のリロード中にクラス 'CREG' でレジスタが見つかりません ac:8:3: エラー: 'asm' オペランドに不可能な制約があります

空の clobber リストを使用すると、コンパイルされ、出力は 4 になります。

4

1 に答える 1

6

あなたが引用している文書は、かなり不正確なようです。GCC にとって、asm オペランド制約が実際に意味するものは次のとおりです。

  • 入力: アセンブリ操作は、このオペランドから読み取ります。GCC は、すべての読み取りがアセンブリ操作の最初に同時に発生すると想定しています。
  • 出力: アセンブリ操作はこのオペランドに書き込みます。完了すると、関連付けられた変数に意味のある値が設定されます。(その値が何であるかを GCC に伝える方法はありません。) GCC は、アセンブリ操作の最後にすべての書き込みが同時に行われると想定しています。
  • Clobber: アセンブリ操作は、このオペランドの意味のある値を破棄します。書き込みと同様に、すべてのクロバーは操作の最後に同時に発生すると想定されます。
  • Earlyclobber: 操作の開始時に発生することを除いて、clobber と同じです。

さらに、現在の (GCC 4.7) マニュアルには、次の重要な段落が含まれています。

入力オペランドまたは出力オペランドと重複する方法でクロバー記述を記述してはなりません。たとえば、1 つのメンバーを持つレジスター クラスを記述しているオペランドが、そのレジスターを clobber リストで言及している場合、存在しない可能性があります。特定のレジスター (「明示的な Reg Vars」を参照) に存在するように宣言され、asm の入力または出力オペランドとして使用される変数には、clobber の説明で言及されている部分があってはなりません。入力オペランドを出力オペランドとして指定せずに変更することを指定する方法はありません。指定したすべての出力オペランドがこの目的のためのものである (したがって未使用である) 場合は、GCC が asm ステートメントを未使用として削除するのを防ぐために、以下で説明するように asm 構造に volatile を指定する必要があることに注意してください。

これが、特定のレジスタを入力して上書きしようとすると失敗する理由です。

現在、 rep movsl を挿入することは、最近ではちょっとばかげています-使用memcpyして、GCCにそれを最適な命令シーケンスに置き換えさせてください-しかし、それでもあなたの例を書く正しい方法は

int main()
{
  int a[] = {2, 4, 6};
  int b[3];
  int n = 3;
  int v = 12;

  int *ap = a, *bp = b;

  asm volatile ("rep movsl" : "+S" (ap), "+D" (bp), "+c" (n) : : "memory");
  printf("%d\n", b[1]);
}

配列のアドレスは左辺値ではないため、中間変数apと中間変数が必要であり、出力制約には表示されません。bp「+r」表記は、このレジスターが入力と出力の両方であることを GCC に伝えます。'volatile' が必要なのは、すべての出力オペランドが の後に使用されないasmためです。それ以外の場合、GCC はそれを元気よく削除します (出力オペランドに対して行ったことのためにのみ存在するという理論に基づいて)。clobber リストに「memory」を入れることで、操作によってメモリが変更されたことを GCC に伝えることができます。そして最後に、マイクロ最適化: GCC は決して「std」を発行しないため、「cld」は必要ありません (これは実際には x86 ABI によって保証されています)。

私が行った変更のほとんどは、このような小さなテスト プログラムが正しく動作するかどうかには影響しません。ただし、微妙な最適化エラーを防ぐために、フルサイズのプログラムではすべて不可欠です。たとえば、「メモリ」クロバーを省略した場合、GCCb[1]asm!

于 2012-11-06T20:43:51.843 に答える