GCCインラインアセンブリを使用して進行中の実験で、ラベルとインラインコードに関する新しい問題に遭遇しました。
次の簡単なジャンプを考えてみましょう:
__asm__
(
"jmp out;"
"out:;"
:
:
);
これはout
ラベルにジャンプする以外は何もしません。このコードはそのままコンパイルできます。しかし、それを関数内に配置し、最適化フラグを使用してコンパイルすると、コンパイラーは「エラー:シンボル 'out'は既に定義されています」と文句を言います。
起こっているように見えるのは、コンパイラが関数をインライン化するたびに、コンパイラがこのアセンブリコードを繰り返していることです。これにより、ラベルout
が重複し、複数のout
ラベルが作成されます。
では、どうすればこれを回避できますか?インラインアセンブリでラベルを使用することは本当に不可能ですか?この GCCインラインアセンブリに関するチュートリアル は、次のように述べています。
したがって、アセンブリをCPPマクロに組み込み、C関数をインライン化できるため、誰でも任意のC関数/マクロとして使用できます。インライン関数はマクロに非常に似ていますが、使用する方がきれいな場合があります。これらすべてのケースでコードが複製されるので、そのasmコードではローカルラベル(1:スタイル)のみを定義する必要があることに注意してください。
これらの「ローカルラベル」に関する詳細情報を検索しようとしましたが、インラインアセンブリに関連するものは何も見つかりません。チュートリアルでは、ローカルラベルは(1:
のように)コロンが後に続く数字であると言っているようです。そのため、そのようなラベルを使用してみました。興味深いことに、コードはコンパイルされましたが、実行時に単にセグメンテーション違反を引き起こしました。うーん...
だから、提案、ヒント、答え...?
ローカルラベルのdeclarationは、実際には、数字とそれに続くコロンです。ただし、ローカルラベルへの参照には、前方または後方を参照するかどうかに応じて、f
またはb
のサフィックスが必要です。つまり、1f
は、順方向の次の1:
ラベルに。
したがって、ラベルを1:
として宣言することは正しいです。しかし、それを参照するには、jmp 1f
と言う必要があります(この場合は前にジャンプしているため)。
さて、この質問は若くなっているわけではありませんが、他に2つの興味深い解決策があります。
1)この例では%=を使用しています。アセンブラテンプレートの%=は、「コンパイル全体で各insnに一意です。これは、特定のinsnで複数回参照されるローカルラベルを作成する場合に役立ちます」。 %=を使用するには、(どうやら)少なくとも1つの入力が必要であることに注意してください(おそらく実際に使用する必要はありません)。
int a = 3;
asm (
"test %0\n\t"
"jnz to_here%=\n\t"
"jz to_there%=\n\t"
"to_here%=:\n\t"
"to_there%=:"
::"r" (a));
これは出力します:
test %eax
jnz to_here14
jz to_there14
to_here14:
to_there14:
あるいは、asm gotoを使用することもできます(v4.5で追加されたと思います)。これにより、実際にはasmラベルだけでなく、cラベルにジャンプできます。
asm goto ("jmp %l0\n"
: /* no output */
: /* no input */
: /* no clobber */
: gofurther);
printf("Didn't jump\n");
// c label:
gofurther:
printf("Jumped\n");