インラインアセンブリ
紹介
この文書では、Wave言語が提供するインラインアセンブリ機能について説明します。 インラインアセンブリは、Waveが提供する機能の一つで、高水準言語の利便性と構造を保ちながらも、低レベルのハードウェア制御に直接アクセスできるように設計された文法です。
通常のWaveコードでは表現しづらいレジスタ操作、メモリアドレス単位のアクセス、特定のCPU命令の実行などを可能にし、性能最適化が重要であるか、ハードウェアやアーキテクチャに強く依存するコードが必要な場合に活用されます。 この機能を通じて、Waveは単なる高水準言語を超えて、システムプログラミングの領域までカバー することができます。
基本文法
インラインアセンブリはasm { ... }ブロックを通して記述します。
このブロック内には、実際に実行されるアセンブリ命令と、Wave変数とCPUレジスタを結びつけるための入出力マッピングが定義されます。
asm {
"アセンブリ命令" // 実際のアセンブリコード (1行に1命令)
...
in("レジスタ") 値 // 入力レジスタのマッピング
out("レジスタ") 変数 // 出力レジスタのマッピング
}
アセンブリ命令は文字列形式で記述され、実際のCPUで実行される低レベルの命令をそのまま記述します。 複数行で記述することが可能であり、各行には1つの命令のみを記述することを原則とします。
例えば、次のような形で使用することができます。
"mov rax, 1"
"syscall"
in("レジスタ")値構文は、Wave変数や式の値を指定されたCPUレジスタにロードするために使用されます。
これにより、アセンブリコードでそのレジスタを入力値として使用することができます。
下の例は、変数sの値をx86-64規約の最初のsyscall引数レジスタであるrdiに渡す場合です。
in("rdi") s
逆に、out("レジスタ")変数構文は指定されたレジスタに含まれる値をWave変数として取り込む役割をします。
アセンブリの実行結果をWaveコードで使用する必要がある場合にこの方法を使用します。
次の例は、syscall呼び出しの後に戻り値が保存されるraxレジスタの値を変数retに保存する場合です。
out("rax") ret
簡単な例
下の例は、Linux x86-64環境でsyscallを利用して文字列を標準出力に出力する簡単なコードです。
fun main() {
var msg_ptr: ptr<i8> = "Hello from syscall!\n";
var ret_val: i64;
asm {
"mov rax, 1"
"syscall"
in("rdi") 1
in("rsi") msg_ptr
in("rdx") 20
out("rax") ret_val
}
}
この例では、Waveコードは文字列のポインタと長さをレジスタに直接設定し、システムコールを呼び出して出力処理を行います。
システムコールの戻り値はraxレジスタを介して渡され、out構文を通してWave変数に再び取り込まれます。