[R8C][M16C] インライン関数とインラインアセンブラとテンプレートの関係

NC30のC++には、2種類のインライン関数があります。_inlineとinlineです。Cの場合には、これらは単に名前の違いだけで機能的な差異はありません。しかし、C++では全く別のものなので注意が必要です。

_inlineはCでも使える拡張機能で、ごく特殊なケースを除けば、確実にインライン置換されます。その代り、さまざまな制約があります。例えば、_inlineを使ったインライン関数内で静的オブジェクトを宣言すると、警告が出た上、翻訳単位ごとに別の実体が生成されます。また、再帰呼び出しもできなければ、関数へのポインタも取得できません。

inlineを使ったインライン関数はC++の仕様に基づきますが、デフォルトではインライン置換されません。インライン置換させるためには、-Ostatic_to_inlineおよび-Oforward_function_to_inlineオプションを付けてコンパイルする必要があります。もちろん、これらのオプションを付けたからといって、必ずインライン置換されるわけではありません。

さて、上記を踏まえた上で、インライン関数内でインラインアセンブラを使う方法を考えてみたいと思います。調査したところ、_inlineを使ってもinlineを使っても、インライン関数の中でインラインアセンブラを使うことはできました。ただ、残念なことに、inlineを使ったインライン関数内でインラインアセンブラを使うと、インライン置換されないことがわかりました。したがって、ごく小さな処理をインラインアセンブラで記述し、それをインライン関数にしたい場合には、_inlineを使う必要があります。あるいはマクロを使うかです。

インラインアセンブラを含むインライン関数をテンプレート化したいこともあります。例えば、指定した回数だけ nop 命令を挿入するような場合です。次のようにできれば理想です。

template <unsigned N>
inline nops()
{
  asm(".mrepeat $$", N);
  asm("nop");
  asm(".endr");
}

ところが、この方法はうまくいきません。$$はレジスタであれメモリであれ、どこかに記憶域を持たないと使えないからで、テンプレート引数をインラインアセンブラに渡すことはできないのです。しかたがないので、次のような方法を考えてみます。

template <unsigned N>
inline void nops()
{
  asm("nop");
  nops<N - 1>();
}
 
template <>
static void nops<0>()
{
}

これなら何とかコンパイルできました。ところが、コンパイル結果を見てみると、インライン置換されていません。当然です。前述したように、inlineを使ったインライン関数内でインラインアセンブラを使うとインライン置換されないからです。

それでは、_inlineを使えばどうか? 再帰呼び出しは行っているものの、実際に呼び出しているのは別の関数なので、上手くいく可能性はあります。しかし、再帰の問題はともかく、それ以外に二重の意味で_inlineは使えませんでした。

_inlineを使う上で最大の問題は、テンプレートが使えないという点です。さらに、前方参照を行うことができません。これではまるでダメです。

結局、指定回数のnopを挿入するには、次のようにマクロで実現するしかなさそうです。

#define nops(n)  asm(".mrepeat " #n "\nnop\n.endr")

さらにもう一工夫すれば、引数にマクロを指定することもできるようになります。

#define nops(n)   nops_(n)
#define nops_(n)  asm(".mrepeat " #n "\nnop\n.endr")

C++では、マクロの使用は極力控えるべきですが、ほかに実現方法がないのであれば致し方ありません。

この記事のトラックバックURL:

http://www.kijineko.co.jp/trackback/939
このエントリーを含むはてなブックマーク