quartz-research-note/content/mimiumの中間表現を考える.md

138 lines
4.8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
date: 2024-06-08 22:40
---
#memo #mimium
まあいつものサンプルを考える
```rust
fn fbdelay(input:float,fb:float,dtime:float)->float{
return input + delay(self,dtime)*fb
}
fn twodelay (input:float,dtime:float)->float{
return fbdelay(input,dtime,0.7)+fbdelay(input,dtime*2,0.8)
}
fn dsp (input:float)->float{
return twodelay(input,400)+twodelay(input,800)
}
```
コールツリーとしては
```
 dsp
 | |
twodelay(1) twodelay(2)
| | | |
fbdelay(1) fbdelay(2) fbdelay(3) fbdelay(4)
| | | |
delay,feed, delay,feed delay,feed delay,feed
```
- 実行中のVMから見れば、簡約されたあとにfeedの項が置き換えられて出てくるわけではない
- 関数の定義にジャンプしてみて初めてfeedが現れる
- しかし、そのfeedがどこの情報を保持しているかは呼び出し元の情報が必要
- コール元の情報を関数の引数として渡す、というのが前とったやり方
- feedのコールツリーを持つやり方はポインタを辿ってく形になるのであんまやりたくない
- VMがクロージャをsetUpvalue/getUpvalueで解決してるような感じで解決できないか
- callの命令は今call、callCls、callExtの3種類存在
- これはVM上の参照するテーブルが違うので必要
- 例えば、VMマシン側に、`feed_baseptr`みたいな情報を持っておく
- callのオペランドの中にこのbaseptrからのオフセットバイト数を含めるようにする
- いや、コンパイル時、call命令の前にsetFeedAddressみたいな命令を差し込めばそれでいいのか
- で、delay命令が来たときは現在のアドレスから必要な長さをリングバッファとして使用する
- getfeed命令が来たときは現在のアドレスからその型分のバイト数を取り出して、、、どうする
- 即値用の、getfeedfloatと、それ以外のgetfeedptrは分けたほうがいいな
- call命令、delay、getfeed命令が終わるたびにbaseptrの場所は戻される
- いや、最後にsetfeedしなきゃいけないからself用のbaseptrに戻す必要があるのかな
- 場合によってはdelayの結構長いタイム分をオフセットしないといけないわけだから、命令長が足りなくならんかこれ
- オフセットの値を即値でやるのと、レジスタからロードする2パターンの命令持っておけば良い
まあこんな感じで行けそう
```
state_size:
fn fbdelay(input,fb,dtime){
// reg: input,fb,dtime
getfeedfloat // load feed to reg3
pushfeedoffset 8 // shift feed_base by 64bit
delay 3 2 4// write feed and pop head from dtime address offset, push to reg4
mulF 4 2 5// multiplay result of delay(reg4) and fb(reg2)
addF 0 5 6
popfeedoffset 8 // shift feed_base by 64bit
setfeed 6
return 6
}
fn twodelay (input,dtime){
// reg:input,dtime
movConst 'fbdelay' 2
mov 0 3
mov 1 4
movConst '0.7' 5
call 2 3 1 //reg2 is result of fbdelay
mov 1 3
movConst '2.0' 4
mulF 3 4 5
mov 5 3
movConst 'fbdelay' 4
mov 0 5
mov 3 6
movConst '0.8' 7
pushfeedoffset 8
call 4 3 1 //reg4 is result of second fbdelay
popfeedoffset
addF 2 4 5
return 5
}
fn dsp (input){
movConst 'twodelay' 1
mov 0 2
movConst '400' 3
call 1 2 1 //reg1 is result of twodelay
movConst 'twodelay' 2
mov 0 3
movConst '800' 4
pushfeedoffset 8
call 2 2 1
addf 1 2 3
return 3
}
```
delayはどうしておくかというと、最初の8バイトは現在のリングバッファの位置、残りをメモリという扱いにすれば良い。ただメモリのサイズを決定すんのがなあ〜
中間表現では関数呼び出し、即値の読み込みはラベル使ってやるので十分かあ
そうなるとローカルの名前リネームは必要
VM自体のstate_sizeの保存には結局コールツリーを辿る必要が出てくる
pushfeedoffsetでバイトオフセットの値をトレースするためには結局applyの式を辿る必要はある
コールツリーのトラバースをし始めるとワンパスコンパイラとしての旨みがなくなる
feedはlambdaに閉じ込められてletrecにbindされてるのがちょっと面倒
まあでも結局ユーザー層に隠蔽するにはここで静的解析するしかないのか
あれ、でもこの静的解析って結局多段階でいうところのstage 0 の評価に相当するやつなのでは
```rust
fn fbdelay(input:float,fb:float,dtime:float)->float{
return input + delay(self,dtime)*fb
}
fn twodelay (input:float,dtime:float)->float{
return fbdelay(input,dtime,0.7)+fbdelay(input,dtime*2,0.8)
}
fn dsp (input:float)->float{
return twodelay(input,400)+twodelay(input,800)
}
```