はじめに
Cでx86とかarmターゲットだとインラインアセンブラを書けるけれども、WebAssemblyターゲットでもインラインアセンブラ書きたいですよね...? (そんな人はこれまでにいなかったと願っておくのです)
各サンプルに、WebAssemblyStudio での実行用プロジェクトへのリンクを追加しています。ソースファイルの編集はPCブラウザでなければ正常にできませんが、スマホでも実行ができると思います。必要に応じて活用してください。
対象読者
- wasi-sdk または emscripten を使って、C言語ソースファイルから WebAssembly を吐き出したことがある
- WebAssembly テキストフォーマットにすこしでも馴染みがある
目標
- C言語ソースファイル中のインラインアセンブラとして、WebAssembly テキストフォーマットを使いこなせるようになる
注意書き
WebAssemblyターゲットのインラインアセンブラに関する情報は、皆無に等しい状態です。そのため、筆者の手元の環境でコンパイル/実行できるサンプルを掲載しています。
検証環境
- emscripten 2.0.3
- clang 12.0.0
レベル1: 加減乗除
シンプルに 12 + 34
を計算し、その値を main 関数の戻り値としています。
オンラインサンプル: WebAssembly Studio#?zaqqdfn25vo
int main() {
__asm (
"i32.const 12\n"
"i32.const 34\n"
"i32.add \n"
"br 0"
);
}
スタックに 12, 34 を追加し、スタック上の2数を足してスタックに戻し、スタック上の数値を関数の戻り値にするという、WebAssemblyスタックマシンでの一連の命令です。
レベル2: 変数を読み書きする
変数 val
にインラインアセンブラで書き込みを行うものです。
オンラインサンプル: WebAssembly Studio#?81by60h3kr4
int main() {
int val;
__asm (
"i32.const 12\n"
"i32.const 34\n"
"i32.add \n"
"local.set %0"
: "=r"(val)
);
return 0;
}
変数 val
はスタック上に存在しますが、"=r"(val)
と指定することで、スタック変数をローカル変数にコピーする処理をコンパイラが追加してくれます。
レベル3: 関数呼び出し
C言語標準関数の puts をインラインアセンブラで呼んでみます。
オンラインサンプル: WebAssembly Studio#?imnemdya3kg
int main(void) {
const char Hello[] = "Hello World\n";
__asm (
"local.get %0\n"
"call %1\n"
"drop"
: : "r"(Hello), "i"(puts)
);
}
call
命令が、スタック上の値を引数の数だけ渡して関数を呼ぶ命令です。
サンプルでの %1
の代わりに関数名を指定することもできます。しかし、C++に持っていった時にマングリング名を指定するか extern "C"
をつけたりするよりは、関数を即値として渡してしまった方が楽だったりします。(コンパイル結果も間接呼び出しではなく直接呼び出しになる)