2019年04月22日

GCC/Clangで複数命令を含むインラインアセンブラを記述する際の注意点

とある開発中のプロジェクトで、GCC/Clang のインラインアセンブラが意図しない不正コードを生成し、バグではないか?と調査しました。

その結果、これはどうやら正しい仕様らしい、しかし非常に間違いやすく混乱しやすいケースだと思ったのでメモしておきます。

解決策だけを先に言いますと、GCC/Clang のインラインアセンブラを、可能ならば常に 1 文に 1 命令のスタイルで記述するようにすれば、この類の混乱は避けられそうです。



以下が、問題となった AArch64 のコード片を少し修正したものです。シンプルさを優先しているので型等が実際とは異なります。(asm1.c)
int SetPriorityMask(int threshold)
{
    long pmr, mask;
    pmr = threshold;
    asm volatile ("MRS %0, S3_0_C4_C6_0\n\t" /* ICC_PMR_EL1 */
                         "MSR S3_0_C4_C6_0, %1\n\t" /* ICC_PMR_EL1 */
                         "ISB"
                         : "=r"(mask) : "r"(pmr)); 
    return (int)mask;
}
このコードをコンパイルすると、以下のような明らかにおかしい(ローカル変数 pmr と mask が同じレジスタにアロケーションされてしまっている)コードが生成されます。GCC でも Clang でも同じようなコードが出るので、単にコンパイラのバグというわけでも無さそうです。
>gcc -v
...
gcc version 6.4.0 (GCC)
>gcc -S asm1.c -o gcc_asm1.s
>type gcc_asm1.s
...
// 5 "asm1.c" 1
        MRS x0, S3_0_C4_C6_0
        MSR S3_0_C4_C6_0, x0
        ISB
// 0 "" 2
...
>clang -v
clang version 6.0.0 (tags/RELEASE_600/final)
>clang -S asm1.c -o clang_asm1.s
>type clang_asm1.s
...
        //APP
        mrs     x8, ICC_PMR_EL1
        msr     ICC_PMR_EL1, x8
        isb
        //NO_APP
...
構文的には何も問題が無く、情報も少ないので非常に悩みました。

解決の手掛かりになったのは以下のバグ報告です。

GNU Arm Embedded Toolchain Incorrect register allocation in inline assembly code

どうも GCC/Clang のインラインアセンブラは、入力オペランドを全て使用してから、出力オペランドに書き込む、と仮定しているようです。

6.47.2 Extended Asm - Assembler Instructions with C Expression Operands

GCC may allocate the output operand in the same register as an unrelated input operand, on the assumption that the assembler code consumes its inputs before producing outputs. This assumption may be false if the assembler code actually consists of more than one instruction. (GCC は、(拡張)アセンブラコードは出力を生成する前に入力オペランドは消費済みであると仮定し、無関係な入力オペランドと同じレジスタを出力オペランドに割り当てるかもしれない。この仮定は(拡張)アセンブラコードが実際には複数命令で構成される場合に偽になるかもしれない。)


今回のパターンのように、入力オペランドを使用する命令の前に実行される命令に含まれる出力オペランドを earlyclobber オペランドと言うそうで、この問題を回避するためには & を付けてその意図を明示する必要があるそうです。earlyclobber 出力オペランドと入力オペランドはオーバーラップしないことが保証されます。

6.47.3.3 Constraint Modifier Characters

以下のように修正し(asm2.c)
...
		  : "=&r"(mask) : "r"(pmr)); 
...
以下のように期待通りの結果が得られました。
>type gcc_asm2.s
...
// 5 "asm2.c" 1
        MRS x0, S3_0_C4_C6_0
        MSR S3_0_C4_C6_0, x1
        ISB
// 0 "" 2
...
>type clang_asm2.s
...
        //APP
        mrs     x9, ICC_PMR_EL1
        msr     ICC_PMR_EL1, x8
        isb
        //NO_APP
...


kmckk at 11:17コメント(0)GCC | 若槻 

コメントする

名前
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード