インラインアセンブリ
紹介
この文書では、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変数に再び取り込まれます。
段落1
インラインアセンブリは、Waveの型安全性と抽象化を意図的に回避する機能です。 誤った命令の使用やレジスタ設定エラーが発生した場合、プログラムが異常終了したり、未定義の動作が発生する可能性があります。
inとoutを通じたレジスタマッピング自体はコンパイル時に検証されますが、アセンブリ命令の意味的有効性や実行結果までは保証されません 。
したがって、インラインアセンブリを使用する際には、対象アーキテクチャと呼び出し規約、そして命令の動作を正確に理解している必要があります。