Rust言語へのトランスパイル#
mimium v4.1.0の機能として、mimiumのソースコードをRust言語のソースコードへ直接変換するトランスパイル機能が追加されました。
この機能は、mimium-cliでは--emit-rustオプションを指定することで、またオンラインエディタでも利用することができます。
概要#
例えば、サイン波を出力する簡単なコード(exampleのgetnow.mmm)を例にしてみましょう。
このコードをトランスパイルすると、多くのテンプレートコードとともに、次のようなコードを含むRustソースコードが生成されます。
impl<H: MimiumHost> MimiumProgram<H> {
...
fn dsp(&mut self) -> Word {
let mut reg_0: mmmfloat = 0.0 as mmmfloat;
let mut reg_1: mmmfloat = 0.0 as mmmfloat;
let mut reg_2: mmmfloat = 0.0 as mmmfloat;
let mut reg_3: mmmfloat = 0.0 as mmmfloat;
let mut reg_4: mmmfloat = 0.0 as mmmfloat;
let mut reg_5: Word = 0u64;
let mut reg_6: Word = 0u64;
let mut reg_7: mmmfloat = 0.0 as mmmfloat;
let mut reg_8: mmmfloat = 0.0 as mmmfloat;
let mut reg_9: mmmfloat = 0.0 as mmmfloat;
let mut reg_10: Word = 0u64;
let mut reg_11: Word = 0u64;
let mut reg_12: mmmfloat = 0.0 as mmmfloat;
let mut reg_13: mmmfloat = 0.0 as mmmfloat;
let mut reg_14: mmmfloat = 0.0 as mmmfloat;
let mut reg_15: mmmfloat = 0.0 as mmmfloat;
let mut reg_16: Word = 0u64;
let mut stack_alloc_5 = [0u64; 1];
let mut stack_alloc_10 = [0u64; 1];
reg_0 = self.host.current_time();
reg_1 = 440.0 as mmmfloat;
reg_2 = reg_0 * reg_1;
reg_3 = self.host.sample_rate();
reg_4 = reg_2 / reg_3;
stack_alloc_5[0usize] = f64_to_word(reg_4);
reg_7 = word_to_f64(stack_alloc_5[0usize]);
reg_8 = 1.0 as mmmfloat;
reg_9 = reg_7 % reg_8;
stack_alloc_10[0usize] = f64_to_word(reg_9);
reg_12 = word_to_f64(stack_alloc_10[0usize]);
reg_13 = 6.2831853 as mmmfloat;
reg_14 = reg_12 * reg_13;
reg_15 = reg_14.sin();
return f64_to_word(reg_15);
}
...
}このソースコードは、MimiumHostというトレイトを実装することで簡単に利用することができます。MimiumHostの実装には、nowやsamplerateのような外部関数機能を提供する必要があります。
以下は、テストで使用している最小限のホストの実装例です。
struct TestHost {
now: f64,
sample_rate: f64,
}
impl MimiumHost for TestHost {
fn call_ext(
&mut self,
name: &str,
_args: &[Word],
_ret_words: usize,
) -> Result<Vec<Word>, String> {
Err(format!("unexpected external call: {}", name))
}
fn current_time(&mut self) -> f64 {
self.now
}
fn sample_rate(&mut self) -> f64 {
self.sample_rate
}
}
fn main() {
let host = TestHost {
now: 0.0,
sample_rate: 48_000.0,
};
let mut program = MimiumProgram::with_host(host);
// Uncomment when the generated source defines `call_main` and needs global initialization.
// program.call_main().unwrap();
for input in [0.0f64, 0.0, 0.0, 0.0] {
let output = program.call_dsp(&[f64_to_word(input)]).unwrap();
for word in output {
println!("{:.12}", word_to_f64(word));
}
self.now += 1.0;
}
}仕組みと制限#
見ての通り、生成されたRustコードは元のコードほど可読性の高いものではありません。これは、ソースコードのシンタックスツリーを直接マッピングしているのではなく、中間表現(MIR)を通じてRustに変換しているためです。これはFaustやCmajor、Vultといった低級言語への変換機能を持つ他の言語でも似たようなものです。
ただし、Faustと比べると、Faustがほぼ全ての信号処理を一つのdsp関数にインライン化するのに比べて、mimiumでは全ての関数定義はRust上でも関数として分割して定義されます。理論的には、Faustの方が実行スピードとしては優れる一方で、mimiumではバイナリサイズのフットプリントを抑える方向に働くはずです。
実行スピードに関しては、FaustやCmajorと比較すると、mimiumはRustに変換した後でもクロージャや配列を使うコードには小さなランタイムでのヒープメモリ確保を行う形式をとっている都合上多少速度が劣ることが推測されます。ただし、uzulangのサンプルのような、Faustでは実現不可能なコードもRustに直接変換できることはmimiumの利点と言えるでしょう。
現在のところ、ControlやProbeマクロのような入出力のパラメーターを与える機能はまだサポートされていません。これは今後のアップデートで対応予定です。