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_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のバイトコードディスパッチ

#define NEXT_OP(i) (i->op = *(MVMuint16 *)(i->cur_op), i->cur_op += 2, i->op)
#define DISPATCH(op) {goto (CODES[op])(i);}
#define OP(name) OP_ ## name
#define NEXT(i) CODES[NEXT_OP(i)](i)
static int tracing_enabled = 0;
__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,

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;

DataGearへの変換


__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_next(INTERP i){
    goto NEXT(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);
}
    (i->reg_base[*((MVMuint16 *)(i->cur_op + 0))]).i64 = MVM_BC_get_I64(i->cur_op, 2);
    i->cur_op += 10;
    goto cbc_next(i);

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)

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=0
     x604a20,
    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=0
     x604a20,
    initial_invoke=0x7ffff76c7168 <
        toplevel_initial_invoke>, invoke_data
        =0x67ff10)
    at src/core/interp.c:1169
1169 goto NEXT;
$2 = 162

アレ

100 MVM_STATIC_INLINE MVMint64 MVM_BC_get_I64(const MVMuint8 *cur_op, int offset) {
101     const MVMuint8 *const where = cur_op + offset;
102 #ifdef MVM_CAN_UNALIGNED_INT64
103     return *(MVMint64 *)where;
104 #else
105     MVMint64 temp;
106     memmove(&temp, where, sizeof(MVMint64));
107     return temp;
108 #endif
109 }

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 "$@"

CbCMoarVMの利点

CbCMoarVMの欠点

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 := 29;

my $t0 := nqp::time_n();
my $z  := fib($N);
my $t1 := nqp::time_n();

say("fib($N) = " ~ fib($N));
say("time    = " ~ ($t1-$t0));

フィボナッチの例題

単純ループ

まとめ

まとめと今後の課題