Scm_ApplyRec

このエントリの実装書いて気がついたのですが、Scm_ApplyRec 手続きって apply した結果を戻します。
Scm_ApplyRec 手続きは引数を vm->vals に順にセットして apply_rec 手続きを呼び出します。

static ScmObj apply_rec(ScmVM *vm, ScmObj proc, int nargs)
{
    ScmObj program;
    ScmWord code[2];
    code[0] = SCM_WORD(SCM_VM_INSN1(SCM_VM_VALUES_APPLY, nargs));
    code[1] = SCM_WORD(SCM_VM_INSN(SCM_VM_RET));

    vm->val0 = proc;
    program = vm->base? SCM_OBJ(vm->base) : SCM_OBJ(&internal_apply_compiled_code);
    return user_eval_inner(program, code);
}

手続きオブジェクトを val0 に置いて VALUES_APPLY して RET するナニ。む、VALUES_APPLY って 0.8.x 系には無い vm instruction なのかなぁ。0.9 な vminsn.scm にて以下な定義がある。

;; VALUES_APPLY(nargs) <args>
;;  This instruction only appears in the code generated dynamically
;;  by Scm_Apply(Rec).  This is used to pass the application information
;;  across the boundary frame (see user_eval_inner() in vm.c).
;;  When the VM sees this instruciton, VAL0 contains the procedure to
;;  call, and VAL1... contains the arguments.
;;  If nargs >= SCM_VM_MAX_VALUES-1, args[SCM_VM_MAX_VALUES-1] through
;;  args[nargs-1] are made into a list and stored in VALS[SCM_VM_MAX_VALUES-1]
(define-insn VALUES-APPLY 0 none #f
  (let* ([nargs::int (SCM_VM_INSN_ARG code)])
    (CHECK-STACK (ENV-SIZE nargs))
    (dotimes [i nargs]
      (when (>= i (- SCM_VM_MAX_VALUES 1))
        (for-each (lambda (vv) (PUSH-ARG vv))
                  (aref (-> vm vals) (- SCM_VM_MAX_VALUES 1)))
        (break))
      (PUSH-ARG (aref (-> vm vals) i)))
    ($goto-insn TAIL-CALL)))

なんとなくではありますが、ぱっと見で

  • CHECK_STACK して
  • vm->vals にセットした引数を PUSH_ARG して
  • TAIL_CALL な命令に jmp

なカンジである事が分かります。てか、vminsn.scm 読む、というのもなかなか面白げですねぇ。

話を元に

apply_rec 手続きは末端で user_eval_inner 手続きを呼び出してその戻りを戻す。実はあまり難しく考えてなかったりして、単純に Scm_ApplyRec から手続きをナニした場合には

  • run_loop 手続き呼び出して
  • vm->val0 を戻す

という形なんだろうな、と。これはなかなか凄い。ただ、Scm_Repl 手続きを経由して Scm_ApplyRec が呼ばれるケイスはちょっと事情が異なります。
簡単に言っちゃうと repl_proc 手続きの戻りが戻されるカンジ。詳細は ReadingGauche でそのあたりに到達してからメモを投入するかと思いますが、そこまで行きつくにはまだ結構時間がかかりそげ。