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

4.8 KiB
Raw Blame History

date
2024-06-08 22:40

#memo #mimium

まあいつものサンプルを考える

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 の評価に相当するやつなのでは

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)
}