1

更新: Eric Postpischil の回答を参照してください。彼は正しいと思います。

インライン アセンブリ コードを調べているときに、このページで見つけました。

static inline char * strcpy(char * dest,const char *src)
{
    int d0, d1, d2;
    __asm__ __volatile__(  "1:\n\t"
                           "lodsb\n\t"
                           "stosb\n\t"
                           "testb %%al,%%al\n\t"
                           "jne 1b"
                           : "=&S" (d0), "=&D" (d1), "=&a" (d2)
                           : "0" (src),"1" (dest) 
                           : "memory");
    return dest;
}

なぜ3つの一時変数が必要なのか混乱していますか? 私はそれらを使用せずに実装しようとします。

static inline char * strcpy(char * dest,const char *src)
{
    __asm__ __volatile__(  "1:\n\t"
                           "lodsb\n\t"
                           "stosb\n\t"
                           "testb %%al,%%al\n\t"
                           "jne 1b"
                           /* : "=&S" (d0), "=&D" (d1), "=&a" (d2) */
                           :
                           : "S" (src),"D" (dest) 
                           : "memory", "esi", "edi", "al");
    return dest;
}

しかし、gccでコンパイルするとエラーが発生しました。

inline.c: In function ‘strcpy’:
inline.c:6:9: error: can't find a register in class ‘SIREG’ while reloading ‘asm’
inline.c:6:9: error: ‘asm’ operand has impossible constraints
4

2 に答える 2

2

lodsb元のコードは、およびstosb命令がレジスタ%ESI、%EDI、および%ALを変更するという事実を処理しようとしています。これを行うために、、、を定義d0d1d2、アセンブリコードからの出力として宣言できるようにしました。これらの値は実際には出力として必要ないため、アセンブリ後には使用されません。ただし、これらは出力であるため、コンパイラーはそれらがアセンブリー・コードによって変更されることを認識します。したがって、コンパイラーは、アセンブリー・コードを通じて変更されないままにしておきたい値をレジスターに保持しないことを認識します。

これを別の方法で行うことも可能であり、コードはセマンティクスをより適切に表現します。つまり、入力は%ESIと%EDIで提供され、メモリ、%ESI、%EDI、および%ALが変更されます。元のコードは、%ESI、%EDI、および%ALが出力であると主張していますが、これは真の意図ではありません。それらは不要な副産物であり、望ましい出力ではありません。

ただし、GCCのバージョンは私のものとは異なります(Apple / clang-418.0.60)。私はあなたが書いたコードをエラーなしで受け入れます。あなたのGCCは、%ESIが入力レジスタ("S"2番目のコロンの後)とクローバーされたもの("esi"3番目のコロンの後)の両方として指定されているという事実によって混乱していると思います。おそらくこれは後で修正されたGCCの欠点であり、元のコードはその欠点を回避していました。

于 2013-01-25T15:18:43.070 に答える
2

物事は本当に簡単です。S制約がesiafor al、およびregisterDを表すことを思い出してください。ediそれらの人が破壊されることをコンパイラに通知するときは、それらをどこにスピルするかを明示的に指定する必要があります(は、、、のd0一時ストレージです)。esid1edid2al

次に、レジスタ割り当てのプロセスで、コンパイラは実際にスピルする必要があるかどうかを判断し、使用可能なレジスタを探します。たとえば、私のコンパイラ (gcc 4.7.2) は次のようにします。

movq  %rdi, %rdx
1:
lodsb
stosb
testb %al,%al
jne 1b
movq  %rdx, %rax
ret

d1に割り当てられrdx、過剰として排除されたd0ことがわかります。d2

その情報をコンパイラに提供しない場合、コンパイラは推測できないため、エラーを出力します。

于 2013-01-25T09:35:39.090 に答える