CbCによるPerl6処理系

Takahiro Shimizu, Shinji Kono 琉球大学

研究目的

Continuation Based C (CbC)

extern int printf(const char*,...);

int main (){
    int data = 0;
    goto cg1(&data);
}
__code cg1(int *datap){
    (*datap)++;
    goto cg2(datap);
}
__code cg2(int *datap){
    (*datap)++;
    printf("%d\n",*datap);
}

CbCの現在の実装

言語処理系の応用

Rakudo

MoarVM

DISPATCH(NEXT_OP) {
    OP(const_i64):
        GET_REG(cur_op, 0).i64 = MVM_BC_get_I64(cur_op, 2);
        cur_op += 10;
        goto NEXT;
}

MVM_BC_get_I64の実装

 MVM_STATIC_INLINE MVMint64 MVM_BC_get_I64(const MVMuint8 *cur_op, int offset) {
     const MVMuint8 *const where = cur_op + offset;
 #ifdef MVM_CAN_UNALIGNED_INT64
     return *(MVMint64 *)where;
 #else
     MVMint64 temp;
     memmove(&temp, where, sizeof(MVMint64));
     return temp;
 #endif
 }

MVM_interp_runで使用されているマクロ

DISPATCH(NEXT_OP) {
    OP(const_i64):
 #define OP(name) OP_ ## name
 #define NEXT *LABELS[NEXT_OP]
    OP_const_i16:
    OP_const_i32:
        MVM_exception_throw_adhoc(tc, "const_iX NYI");
    OP_const_i64:

MVM_interp_runのマクロ

    OP(const_i64):
        GET_REG(cur_op, 0).i64 = MVM_BC_get_I64(cur_op, 2);
        cur_op += 10;
        reg_base[*((MVMuint16 *)(cur_op + 0))].i64 = MVM_BC_get_I64(cur_op, 2);

MVM_interp_runで使用されているマクロ

#define NEXT_OP (op = *(MVMuint16 *)(cur_op), cur_op += 2, op)
#define NEXT *LABELS[NEXT_OP]

goto *LABELS[(op = *(MVMuint16 *)(cur_op), cur_op += 2, op)];

MVM_interp_runのラベルテーブル

static const void * const LABELS[] = {
    &&OP_no_op,
    &&OP_const_i8,
    &&OP_const_i16,
    &&OP_const_i32,
    &&OP_const_i64,
    &&OP_const_n32,
    &&OP_const_n64,
    &&OP_const_s,
    &&OP_set,
    &&OP_extend_u8,
    &&OP_extend_u16,
    &&OP_extend_u32,
    &&OP_extend_i8,
    &&OP_extend_i16,

MVM_interp_run

CbCMoarVMのバイトコードディスパッチ

__code cbc_next(INTERP i){
    __code (*c)(INTERP)
    c = CODES[(i->op = *(MVMuint16 *)(i->cur_op), i->cur_op += 2, i->op)]; // c = NEXT(i)
    goto c(i);
}

__code cbc_const_i64(INTERP i){
    GET_REG(i->cur_op, 0,i).i64 = MVM_BC_get_I64(i->cur_op, 2);
    i->cur_op += 10;
    goto cbc_next(i);
}

CodeGearの入出力インターフェイス

typedef struct interp {
    MVMuint16 op;
    MVMuint8 *cur_op;
    MVMuint8 *bytecode_start;
    MVMRegister *reg_base;
     /* Points to the current compilation unit
         . */
    MVMCompUnit *cu;
     /* The current call site we’re
         constructing. */
    MVMCallsite *cur_callsite;
    MVMThreadContext *tc;
 } INTER,*INTERP;

CbCMoarVMのCodeGearテーブル

__code (* CODES[])(INTERP) = {
  cbc_no_op,
  cbc_const_i8,
  cbc_const_i16,
  cbc_const_i32,
  cbc_const_i64,
  cbc_const_n32,
  cbc_const_n64,
  cbc_const_s,
  cbc_set,
  cbc_extend_u8,
  cbc_extend_u16,

NQP

sub add_test(int $n) {
    my $sum := 0;
    while nqp::isgt_i($n,1) {
        $sum := nqp::add_i($sum,$n);
        $n := nqp::sub_i($n,1);
    }
    return $sum;
}

say(add_test(10));

NQPのバイトコード

     annotation: hoge.nqp:3
     label_1:
00007      const_i64_16       loc_2_int, 1
00008      gt_i               loc_2_int, loc_0_int, loc_2_int
00009      unless_i           loc_2_int, label_2(00022)
00010      osrpoint
     annotation: hoge.nqp:4
00011      decont             loc_3_obj, loc_1_obj
00012      smrt_numify        loc_4_num, loc_3_obj
00013      coerce_ni          loc_5_int, loc_4_num
00014      add_i              loc_5_int, loc_5_int, loc_0_int
00015      hllboxtype_i       loc_3_obj
00016      box_i              loc_3_obj, loc_5_int, loc_3_obj
00017      set                loc_1_obj, loc_3_obj
     annotation: hoge.nqp:5
00018      const_i64_16       loc_5_int, 1
00019      sub_i              loc_5_int, loc_0_int, loc_5_int
00020      set                loc_0_int, loc_5_int
00021      goto               label_1(00007)

NQPのバイトコードとCbC

CbCMoarVMの利点

CbCMoarVMの欠点

MoarVMのデバッグ手法

MoarVMのデバッグ時のbreak point

(gdb) b cbc_next
Breakpoint 2 at 0x7ffff7560288: file src/core/cbc-interp.cbc, line 61.
(gdb) command 2
Type commands for breakpoint(s) 2, one per line.
End with a line saying just "end".
>p CODES[*(MVMuint16 *)i->cur_op]
>p *(MVMuint16 *)i->cur_op
>c
>end
dalmore gdb --args ../../MoarVM_Original/MoarVM/moar --libpath=src/vm/moar/stage0  gen/moar/stage1/nqp
(gdb) b dummy
Function "dummy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (dummy) pending.
(gdb) command 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>up
>p *(MVMuint16 *)(cur_op)
>c
>end

MoarVMのトレース

Breakpoint 1, dummy () at src/core/interp.c:46
46	}
#1  0x00007ffff75608fe in MVM_interp_run (tc=0x604a20,
    initial_invoke=0x7ffff76c7168 <toplevel_initial_invoke>, invoke_data=0x67ff10)
    at src/core/interp.c:119
119	                goto NEXT;
$1 = 159

Breakpoint 1, dummy () at src/core/interp.c:46
46	}
#1  0x00007ffff75689da in MVM_interp_run (tc=0x604a20,
    initial_invoke=0x7ffff76c7168 <toplevel_initial_invoke>, invoke_data=0x67ff10)
    at src/core/interp.c:1169
1169	                goto NEXT;
$2 = 162

CbCMoarVMのデバッグ

Breakpoint 2, cbc_next (i=0x7fffffffdc30) at src/core/cbc-interp.cbc:61
61	    goto NEXT(i);
$1 = (void (*)(INTERP)) 0x7ffff7566f53 <cbc_takeclosure>
$2 = 162

Breakpoint 2, cbc_next (i=0x7fffffffdc30) at src/core/cbc-interp.cbc:61
61	    goto NEXT(i);
$3 = (void (*)(INTERP)) 0x7ffff7565f86 <cbc_checkarity>
$4 = 140

Breakpoint 2, cbc_next (i=0x7fffffffdc30) at src/core/cbc-interp.cbc:61
61	    goto NEXT(i);
$5 = (void (*)(INTERP)) 0x7ffff7579d06 <cbc_paramnamesused>
$6 = 558


MoarVMのデバッグ

131 : 131
139 : 139
140 : 140
144 : 144
558 : 558
391 : 391
749 : 749
53 : 53
*54 : 8

現在のCbCMoarVM

#!/bin/sh
exec /mnt/dalmore-home/one/src/Perl6/Optimize/llvm/build_perl6/bin/moar --cbc \
     --libpath=/mnt/dalmore-home/one/src/Perl6/Optimize/llvm/build_perl6/share/nqp/lib \
     /mnt/dalmore-home/one/src/Perl6/Optimize/llvm/build_perl6/share/nqp/lib/nqp.moarvm "$@"

ThreadedCodeの実装

CbCMoarVMと通常のMoarVMの比較

#! nqp

my $count := 100_000_000;

my $i := 0;

while ++$i <= $count {
}
#! nqp

sub fib($n) {
    $n < 2 ?? $n !! fib($n-1) + fib($n - 2);
}

my $N := 30;

my $z  := fib($N);

say("fib($N) = " ~ fib($N));

フィボナッチの例題

単純ループ

基本ブロックとCodeGear

__code cbc_const_i64(INTERP i,__code cbc_next(INTERP i)){
    GET_REG(i->cur_op, 0,i).i64 = MVM_BC_get_I64(i->cur_op, 2);
    i->cur_op += 10;
    goto cbc_next(i);
}

goto cbc_const_i64_16(i,cbc_gt_i_01);

__code cbc_gt_i_01(INTERP i){
	goto cbc_gt_i(i,cbc_unless_i_01);
}

__code cbc_unless_i_01(INTERP i,cbc_osrpoint_01){
	goto cbc_unless_i(i,cbc_osrpoint_01);
}

まとめと今後の課題