SICP 読み (312) 5.5 翻訳系

追記、と書きましたが新たにエントリ投入。現時点では compile-application の

    (preserving '(env continue)
     proc-code
     (preserving '(proc continue)
      (construct-arglist operand-codes)
      (compile-procedure-call target linkage)))))

な部分。ちなみに proc-code が

((env) (proc)
 ((assign proc
	  (op lookup-variable-value)
	  (const =)
	  (reg env))))

あるいは construct-arglist が戻すリストが以下

((env) (val argl)
 ((assign val (op lookup-variable-value) (const n) (reg env))
  (assign argl (op list) (reg val))
  (assign val (const 1))
  (assign argl (op cons) (reg val) (reg argl))))

最後の compile-procedure-call が戻すリストが以下

((proc argl) (env proc val argl continue)
 ((test (op primitive-procedure?) (reg proc))
  (branch (label primitive11))
  compiled-branch12
  (assign continue (label after-call13))
  (assign val (op compiled-procedure-entry)
	  (reg proc))
  (goto (reg val))
  primitive-branch11
  (assign val
	  (op apply-primitive-procedure)
	  (reg proc)
	  (reg env))
  after-call13))

これらを順に preserving していけば compile-application が戻す instruction-seq になるはず。これが compile-if の p-code にセットされるリストになる、と。
順にヤッてみる。まず、最初の preserving では regs は (proc continue) になっていて seq1 の modified が (var argl) になってて seq2 の needed が (proc argl) な模様。このケースでは save/restore は不要という事になる。
そろそろ preserving だの needed だのというソレ達について考えてみた方が良いか。
compile-procedure-call が戻すリスト中心で見てみると、このリストの needed なレジスタは (proc argl) となっている。これは確かにこのブロックの命令列を評価するためには事前に設定が必要。ってか設定が前提になってるな。
あるいは construct-arglist が戻すリストの modified はその中の命令列と整合しているのも分かる。あるいは compile-procedure-call の modified は全てのレジスタが列挙されている訳ですが、val な compiled-procedure に jmp しているという意味では情報として整合している、と言っても良いのだろう、と。
で、何故に preserving では (proc continue) なのか、というとちょっと微妙なんだけど、compile-procedure-call が戻すリストの statements では proc セット前提なので construct-arglist が戻すリストの中でそのレジスタが modify 対象になっていた場合には save/restore しないと駄目、と。でもこの中に continue が入ってる所以が微妙。
ch5-eceval.scm を見ても continue の扱いは微妙かも。
元に戻ります。save/restore 対象外ですので、単純に append されるんですが、単純とは言え needed の扱いに未だ慣れん。
まず seq1 の needed が

(env)

で、seq2 の needed が

(proc argl)

で seq1 の modified が

(val argl)

なので preserving が戻す instruction-seq は

((env proc) (env proc val argl continue)
 ((assign val (op lookup-variable-value) (const n) (reg env))
  (assign argl (op list) (reg val))
  (assign val (const 1))
  (assign argl (op cons) (reg val) (reg argl))
  (test (op primitive-procedure?) (reg proc))
  (branch (label primitive11))
  compiled-branch12
  (assign continue (label after-call13))
  (assign val (op compiled-procedure-entry)
	  (reg proc))
  (goto (reg val))
  primitive-branch11
  (assign val
	  (op apply-primitive-procedure)
	  (reg proc)
	  (reg env))
  after-call13))

となるはず。なんとなく当たってそげ。次は proc-code と上記が (env continue) で preserving される。上記が seq2 にあたるので needs は

(env proc)

で seq1 な modified は

(proc)

です。これも save/restore は発生しない。とりあえず preserving でくっついたリストが seq1 になる時に preserving は save/restore な条件になってくるはず。とりあえずくっつけて戻す体制にする。
ええと append は seq1 の needed

(env)

に seq2 の needed

(env proc)

から seq1 の modified

(proc)

を引いたナニを加えるので (env) 一発。あとは単純加算、なので

((env) (env proc val argl continue)
 ((assign proc
	  (op lookup-variable-value)
	  (const =)
	  (reg env))
  (assign val (op lookup-variable-value) (const n) (reg env))
  (assign argl (op list) (reg val))
  (assign val (const 1))
  (assign argl (op cons) (reg val) (reg argl))
  (test (op primitive-procedure?) (reg proc))
  (branch (label primitive11))
  compiled-branch12
  (assign continue (label after-call13))
  (assign val (op compiled-procedure-entry)
	  (reg proc))
  (goto (reg val))
  primitive-branch11
  (assign val
	  (op apply-primitive-procedure)
	  (reg proc)
	  (reg env))
  after-call13))

おお。どっかで見たことあるぞ。とりあえず上記が compile-if における p-code にセットされる instruction-seq になるはず。
ってか、compile-if からどこに戻るかが忘却の彼方。(を

追記

備忘録まで。
元々は compile-definition の中の get-value-code にセットされる

(compile (definition-value exp) 'val 'next)

ですな。ここから compile-lambda が呼び出され、その中の compile-lambda-body に制御が移って compile-sequence から compile-if に制御が移った最中なのか。帰りも大変だなあ。このシリーズで年を越してしまいそうな予感。

さらに追記

難関は a-code です。その前に c-code にセットされるナニについてはさくっと済ませとく事に。とりあえず compile-if に渡されてるのは target が val で linkage が return な模様。
if-consequent は 1 ですので compile から起動されるのは以下の手続き

(compile-self-evaluateing 1 'val 'return)

これは

(() (val)
 ((assign val (const 1))
  (goto (reg continue))))

で良いのかね。なんか違いそげ。ええと

(preserving '(continue)
	    '(() (val) ((assign val (const 1))))
	    '((continue) () ((goto (reg continue)))))

はどうなるのか、というと append だな。てーコトは以下になるな。

((continue) (val)
 ((assign val (const 1))
  (goto (reg continue))))

これが c-code にセットされるとして、a-code にセットされるのは以下の手続きの戻りになるはず。

(compile '(* (factorial (- n 1)) n) 'val 'return)

かいな。これ大変だぞ。ってかどうなるんだろ。