その時々

その時々で違うんです。特に決まっていないんです。

インラインアセンブラ

次はインラインアセンブラについて勉強です。

書式

次はインラインアセンブラについて勉強です。

インラインアセンブラは次の書式で書く。

asm("命令 source, dest");

なお、これはAT&T形式でIntel形式では

asm{命令 dest source};

となるが、GCCコンパイルするのでAT&T形式で書くこととする。

asmが他の語と競合するときは__asm__と書く。
ANSI C互換の場合も__asm__と書くようだ。

GCCの最適化によってasmの位置が大きく変わらないようにするにはvolatileを付ける。主に繰り返し文の中で使うときに付ける。

ここまでをまとめると

asm("命令 source, dest");
__asm__("命令 souce, dest");
asm volatile("命令 source, dest");
__asm__ volatile("命令 source, dest");
__asm__ __vplatile("命令 source, dest");
のいずれかで書くということだ。

複数行書く

asm("movb $0x02, %ah\n\t"
    "testb $0x03, %al\n\t");

または

asm("movb $0x02, %ah;"
    "testb $0x03, %al;");

グローバル変数

グローバル変数インラインアセンブラの中で直接使用出来る。

#include <stdio.h>

int i;
int j;

main()
{
  i=10;
  asm("movl i,%eax\n\t"
      "add $10,%eax\n\t"
      "movl %eax, j\n\t");

  printf("%d %d\n",i,j);
}

ローカル変数

ローカル変数はebpレジスタを介して使用する。
下の例ではebpレジスタのオフセット値-4にa、-8にb、-12にcの値がセットされる。

#include <stdio.h>

void foo (void)
{
   int a,b,c;
 
   asm ("movl $1,-4(%ebp)");
   asm ("movl $2,-8(%ebp)");
   asm ("movl $3,-12(%ebp)");

   printf ("%d %d %d\n", a,b,c);
   return;
}

int main ()
{
   foo();
   return 0;
}

同じようにしてもmain関数の中ではオフセット値が変わってくる。
原因はよくわからない。

#include <stdio.h>

main()
{
  int a,b,c;

  asm("movl $1, -12(%ebp)");
  asm("movl $2, -16(%ebp)");
  asm("movl $3, -20(%ebp)");

  printf("%d %d %d\n", a, b, c);
}

ちなみにインラインアセンブラ内からCの関数を呼びだすことも出来る。

int foo(){
  puts("OK");
  return 0;
}

main(){
  asm("call foo \n\t");
}

拡張アセンブラ

__asm__ ( アセンブリテンプレート
        : 出力オペランド                    /* オプション */
        : 入力オペランド                    /* オプション */
        : 破壊されるレジスタのリスト        /* オプション */
);	  	

破壊されるレジスタとは
レジスタの値が破壊されるので退避してほしいときに使用する。

int in1=10,in2=5,out1;
__asm__ ("movl %1, %%eax;            /* アセンブリテンプレート */
          addl %2, %%eax;
          movl %%eax, %0;"
        :"=r"(out1)                  /* 出力 */
        :"r"(in1),"r"(in2)           /* 入力 */
        :"%eax"                      /* 破壊されるレジスタ */
     );	
制約子
"a" %eax
"b" %ebx
"c" %ecx
"d" %edx
"s" %esi
"D" %edi
"q" レジスタの自動割り当て (eax,ebx,ecx,edxの中から)
"r" レジスタの自動割り当て (eax,ebx,ecx,edx,esi,ediの中から)
"g" レジスタの自動割り当て (eax,ebx,ecx,edx,esi,edi,メモリの中から)
"m" メモリ
"t" 浮動小数レジスタの先頭
"u" 浮動小数レジスタの2番目
"f" 浮動小数レジスタの自動割り当て

退避するレジスタは"%eax","%ebx"の様に指定します。
項目は , で区切り複数指定できます。

修飾文字

出力先には"=r"というように修飾文字 '='を付ける

= 書込み専用にする
+ 読み書き
& 早期破壊オペランド
% このオペランドと次のオペランドが交換可能であることを指示する。
# 次に続く文字からコンマまでの全ての文字を制約としては使わないことを示す。それらの文字は、レジスタ選択でのみ意味を持つ。
次に続く文字がレジスタ選択の際には無視されることを示す。
int hoge(int i,int j){
	int k;

	asm ("add %1,%2" :	"=r"(k) : "r" (i) ,"r" (j));
	return k;
}

アセンブラコード内での何番目のオペランドか指定するには%0, %1, %2...と書きます。
上のサンプルでは
%0はk、%1はi、%2はjとなります。
[名前]とオペランドの前に指定してやることにより
アセンブラコード内で%[名前]で参照出来るようになります。

asm("movl %[hoge], %%eax" : "=r"(i) : [hoge] "r"(j))

予測不可能なメモリ破壊がある場合は、退避するレジスタのところに"memory"を書く。
入出力オペランドとして使われていないメモリの予測不可能な破壊がある場合、asmではなくasm volatileを使い、退避するレジスタに"memory"を追加する。

アセンブリ文が短い場合は#defineでマクロを使ったほうが効率的。

参考URL
http://caspar.hazymoon.jp/OpenBSD/annex/gcc_inline_asm.html
http://sci10.org/on_gcc_asm.html
http://d.hatena.ne.jp/wocota/20090628/1246188338
http://7ujm.net/linux/asm.html
http://7ujm.net/linux/asm.html
http://sci10.org/on_gcc_asm.html#K_KOUBUN
http://d.hatena.ne.jp/kikairoya/20100220/1266668559
http://www.issei.org/diary/_20030224/d200212c.html
http://www.ibm.com/developerworks/jp/linux/library/l-ia/
http://marunomaruno.web.fc2.com/gcc-inline-asm01.html?c=appendix