コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

「インラインアセンブラ」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
編集の要約なし
Cewbot (会話 | 投稿記録)
m Bot作業依頼: sourceタグをsyntaxhighlightタグに置換 (Category:非推奨のsourceタグを使用しているページ) - log
11行目: 11行目:
[[x86]]の[[FPU]]を利用して変数xのタンジェントを計算する[[D言語]]で記述されたインラインアセンブラの例。x86系プロセッサで利用可能な[[円周率]]の近似値を得るための<code>fldpi</code>命令を利用でき、コンパイラが浮動小数点を用いた場合より高速である。
[[x86]]の[[FPU]]を利用して変数xのタンジェントを計算する[[D言語]]で記述されたインラインアセンブラの例。x86系プロセッサで利用可能な[[円周率]]の近似値を得るための<code>fldpi</code>命令を利用でき、コンパイラが浮動小数点を用いた場合より高速である。


<source lang="d">
<syntaxhighlight lang="d">
// 変数xのタンジェントを計算する
// 変数xのタンジェントを計算する
real tan(real x)
real tan(real x)
43行目: 43行目:
;
;
}
}
</syntaxhighlight>
</source>


== システムコールの例 ==
== システムコールの例 ==
52行目: 52行目:
基本的なインラインアセンブラの形式は非常に単純である。基本形は下記のとおり([[GNUコンパイラコレクション|GCC]]のみ、Visual Studio では __asm{ }の形式に直す必要がある)。
基本的なインラインアセンブラの形式は非常に単純である。基本形は下記のとおり([[GNUコンパイラコレクション|GCC]]のみ、Visual Studio では __asm{ }の形式に直す必要がある)。


<source lang="c">asm("アセンブリコード");</source>
<syntaxhighlight lang="c">asm("アセンブリコード");</syntaxhighlight>


例:
例:


<source lang="c">asm("movl %ecx, %eax"); /* ecxの中身をeaxに移動する */</source>
<syntaxhighlight lang="c">asm("movl %ecx, %eax"); /* ecxの中身をeaxに移動する */</syntaxhighlight>


または
または


<source lang="c">__asm__("movb %bh, (%eax)"); /* bhから1バイトをeaxが指し示すメモリに移動する */</source>
<syntaxhighlight lang="c">__asm__("movb %bh, (%eax)"); /* bhから1バイトをeaxが指し示すメモリに移動する */</syntaxhighlight>


<code>asm</code>と<code>__asm__</code>はいずれも正しい。もし<code>asm</code>というキーワードがソースコード中のほかのキーワードと重複している場合は<code>__asm__</code>を利用してもよい。
<code>asm</code>と<code>__asm__</code>はいずれも正しい。もし<code>asm</code>というキーワードがソースコード中のほかのキーワードと重複している場合は<code>__asm__</code>を利用してもよい。


<source lang="c">
<syntaxhighlight lang="c">
extern int errno;
extern int errno;


87行目: 87行目:
return res;
return res;
}
}
</syntaxhighlight>
</source>


==注==
==注==

2020年7月5日 (日) 23:02時点における版

インラインアセンブラ: Inline assembler)は、高水準言語の処理系中に埋込まれているアセンブラ、ないし、そのような言語でソースコード中(インライン)にアセンブリ言語によるコードを埋込むことができる、という機能である。たとえば以下のような利用法がある。

最適化
アルゴリズムで最も性能に影響する部分をアセンブリ言語に置き換える。これによりプログラマはコンパイラの制約を受けることなく自由に細工を施すことができる。
プロセッサ固有の特殊な命令の利用
コンペア・アンド・スワップテスト・アンド・セットのような、セマフォやロックを実装するための命令があるプロセッサがあるが、それらの機能を言語拡張などではなく[1]、インラインアセンブラにより直接簡便に利用できる。他には、SIMD拡張命令など具体的にはSPARCVISインテルMMXSSEモトローラAltivecといった命令はコンパイラからの有効的利用が難しく(研究はさかんに行われているが)、インラインアセンブラを利用してC言語中から直接利用することで高い性能を実現できることがある。
システムコール
システムコールのAPIは、現代では通常はC言語のライブラリとして定義されているが、上述の特殊な命令と同様にSVC命令などを直接利用して呼び出すためにアセンブリ言語が利用される。

最適化の例とプロセッサ固有命令の例

x86FPUを利用して変数xのタンジェントを計算するD言語で記述されたインラインアセンブラの例。x86系プロセッサで利用可能な円周率の近似値を得るためのfldpi命令を利用でき、コンパイラが浮動小数点を用いた場合より高速である。

// 変数xのタンジェントを計算する
real tan(real x)
{
   asm
   {
       fld     x[EBP]                  ; // xをロード
       fxam                            ; // 不正な値の検査
       fstsw   AX                      ;
       sahf                            ;
       jc      trigerr                 ; // xはNAN(非数)、無限、または空
                                         // 387は非正規化数を扱えない
SC18:  fptan                           ;
       fstp    ST(0)                   ; // dump X, which is always 1
       fstsw   AX                      ;
       sahf                            ;
       jnp     Lret                    ; // C2 = 1 (xは範囲外)
       // argument reductionしてxを有効範囲内に収める
       fldpi                           ;
       fxch                            ;
SC17:  fprem1                          ;
       fstsw   AX                      ;
       sahf                            ;
       jp      SC17                    ;
       fstp    ST(1)                   ; // piをスタックから除去
       jmp     SC18                    ;
   }
trigerr:
   return real.nan;
Lret:
   ;
}

システムコールの例

メモリが保護されている環境でOSの機能を直接呼び出すことは一般的に不可能である。OSはユーザーモードより上位の特権モード(カーネルモード)で動作しており、OSにリクエストするためには(ソフトウェア)割り込みを利用する。これを行う機能をもつ高級言語はほとんどなく、システムコールのためのラッパー関数はインラインアセンブラを用いて記述されている。

下記のC言語によるサンプルにはシステムコールのラッパーも含まれる。一般的にはマクロと組み合わせて記述するが、ここでは説明のためにあえてマクロを利用していない。

基本的なインラインアセンブラの形式は非常に単純である。基本形は下記のとおり(GCCのみ、Visual Studio では __asm{ }の形式に直す必要がある)。

asm("アセンブリコード");

例:

asm("movl %ecx, %eax"); /* ecxの中身をeaxに移動する */

または

__asm__("movb %bh, (%eax)"); /* bhから1バイトをeaxが指し示すメモリに移動する */

asm__asm__はいずれも正しい。もしasmというキーワードがソースコード中のほかのキーワードと重複している場合は__asm__を利用してもよい。

extern int errno;

int funcname(int arg1, int *arg2, int arg3)
{
  int res;
  __asm__ volatile(
    "int $0x80"        /* OSに命令を発行する */
    : "=a" (res)       /* eaxの中身("a")を結果として返す */
      "+b" (arg1),     /* arg1をebxに渡す ("b") */
      "+c" (arg2),     /* arg2をecxに渡す ("c") */
      "+d" (arg3)      /* arg3をedxに渡す ("d") */
    : "a"  (128)       /* システムコール番号をeaxに渡す ("a") */
    : "memory", "cc"); /* メモリと条件レジスタが修正されたことをコンパイラに通知する */

  /* エラーが発生した場合、OSは負数を返す。
   * エラーが発生するとラッパー関数は-1を返し、グローバル変数errnoに値をセットする */
  if (-125 <= res && res < 0) {
    errno = -res;
    res   = -1;
  }  
  return res;
}

  1. ^ GCCはそれらをサポートする組込み関数を持っている( https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html

外部リンク