データ構造について自分メモ (ScmCompiledCode)

ScmCompiledCode 型について掘ってみる。
とりあえず Reading Gauche を探してみると

というモノ発見。_コンパイル済みコードパケット_とある。とりあえず今ヒッカカッてる部分としては

Scm_ReportError

report_error_inner

Scm_VMGetStackLite

Scm_VMGetSourceInfo ← ScmCompiledCode 型のポインタを引数でもらう

get_debug_info ← ScmCompiledCode 型の領域を使って実際に何かをする

というあたり。実際に Scm_VMGetSourceInfo() では get_debug_info の戻りから assq で SCM_SYM_SOURCE_INFO なペアを取り出して cdr を戻す処理をしている。
てーコトで get_debug_info で何をしているかを見てみる。まず何かの範囲チェック

static ScmObj get_debug_info(ScmCompiledCode *base, SCM_PCTYPE pc)
{
    int off;
    ScmObj ip;
    if (base == NULL
        || (pc < base->code || pc >= base->code + base->codeSize)) {
        return SCM_FALSE;
    }

上記で SCM_FALSE が戻る条件としては (書いてあるまんまですが

  • base が NULL
  • pc が base->code より小さい
    pc は vm->pc でコメント見てみると base->code はベクタになっててそのインデクスなの??
  • pc が base->code + base->codeSize 以上

ちょっと code が何なのかが見えん。grep かけたら code.c にて操作されているケイスが多いみたい。ソースをヒッカキ回してみる事に。
えーと、例えば Scm_CompileCodeDump() において

  • ScmWord* な p を定義
  • p に ScmCompiledCode 型の領域から code 属性を代入
  • ScmWord insn という変数に p[i] を代入
  • ちなみに i が 0 から codeSize より小さい間繰り返しな処理の中
  • u_int code に SCM_VM_INSN_CODE(insn) の結果を代入
  • Scm_VMInsnName でインストラクションの名前を取り出している

これ、どっかで見たなぁ。code はインデクスになってたんだ。SCM_VM_INSN_CODE マクロは引数と 0x0ff を & している。
以下のマクロ定義を見るに

/* Macros for transition to the packed code vector of NVM.
   In the packed code vector, VM insns are stored untagged.
   It eliminates the shift in the dispatcher. */
#define SCM_VM_INSN_CODE(obj)       (SCM_WORD(obj)&0x0ff)
#define SCM_VM_INSN_ARG(obj)        ((signed long)SCM_WORD(obj) >> 8)
#define SCM_VM_INSN_ARG0(obj)       ((SCM_WORD(obj) >>  8) & 0x03ff)
#define SCM_VM_INSN_ARG1(obj)       ((SCM_WORD(obj) >> 18) & 0x03ff)

#define SCM_VM_INSN(code)           SCM_WORD(code)
#define SCM_VM_INSN1(code, arg)     SCM_WORD((long)((arg)<<8) | (code))
#define SCM_VM_INSN2(code, arg0, arg1)  \
    SCM_WORD((long)((arg1) << 18) | ((arg0) << 8) | (code))

ScmCompiledCode 型の code 属性が指す配列の要素は

-------+-------+-------+-------+
    <--------><--------><------>
       arg1      arg0     code

ってコトで良いのでしょうか。そりゃ良いとして結局のトコロ、base->code はポインタな訳ですが、SCM_PCTYPE pc なソレは一体何だろう。SCM_PCTYPE は以下な定義

#define SCM_PCTYPE ScmWord*

これもポインタだった。なんとなく vm.c を見渡してみるに

  • base->code は配列を指している (要素は上記のソレな形)
  • pc は base->code な配列のある要素のポインタが格納

という前提だとすれば上記の範囲チェックは整合してないとマズい。このチェックが OK であれば

    off = pc - base->code - 1;  /* pc is already incremented, so -1. */
    SCM_FOR_EACH(ip, base->info) {
        ScmObj p = SCM_CAR(ip);
        if (!SCM_PAIRP(p) || !SCM_INTP(SCM_CAR(p))) continue;
        if (SCM_INT_VALUE(SCM_CAR(p)) < off) {
            return SCM_CDR(p);
            break;
        }
    }
    return SCM_FALSE;

という手続きをナニしてるんですが、これも確かにポインタの位置関係が上記の類推で当たってるぽく見えます。あるいは pc は今から実行する命令を指してる、というのも分かるな。
で base->info はリストになってて先頭から順に手繰っていってますが、リストの要素について

  • 要素の car がペアでない
  • 要素の car が int でない

時にはスルーしている。そうでなくって要素の car を int にした値が off より小さい場合に cdr を戻している。その下に break があるのも謎だな。あと off はオフセットってー意味だと思うんですが、

    off = pc - base->code - 1;  /* pc is already incremented, so -1. */

って正しく計算するの??って一瞬思ったんですが上記はポインタ型だから大丈夫なんだ。うーん、本当かどうか微妙だ (何
あと、base->info が指しているリストがどんな順で格納されているか、が不明。とりあえず output はできるかな。