CbCによるPerl6処理系

Takahiro Shimizu, Shinji Kono 琉球大学

研究目的

Continuation Based C (CbC)

CodeGearとDetaGear

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の現在の実装

言語処理系の応用

Perl6の概要

Rakudo

Rakudo

MoarVM

MVM_interp_run

#define NEXT_OP (op = *(MVMuint16 *)(cur_op), cur_op += 2, op)

#if MVM_CGOTO
#define DISPATCH(op)
#define OP(name) OP_ ## name
#define NEXT *LABELS[NEXT_OP]
#else
#define DISPATCH(op) switch (op)
#define OP(name) case MVM_OP_ ## name
#define NEXT runloop
#endif

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

DISPATCH(NEXT_OP) {
    OP(no_op):
        goto NEXT;
    OP(const_i8):
    OP(const_i16):
    OP(const_i32):
        MVM_exception_throw_adhoc(tc, "const_iX NYI");
    OP(const_i64):
        GET_REG(cur_op, 0).i64 = MVM_BC_get_I64(cur_op, 2);
        cur_op += 10;
        goto NEXT;
    OP(pushcompsc): {
        MVMObject * const sc  = GET_REG(cur_op, 0).o;
        if (REPR(sc)->ID != MVM_REPR_ID_SCRef)
            MVM_exception_throw_adhoc(tc, "Can only push an SCRef with pushcompsc");
        if (MVM_is_null(tc, tc->compiling_scs)) {
            MVMROOT(tc, sc, {
                tc->compiling_scs = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
            });
        }
        MVM_repr_unshift_o(tc, tc->compiling_scs, sc);
        cur_op += 2;
        goto NEXT;
    }
}

MVM_interp_run

NQP

#! nqp

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

my $N := 29;

my $z  := fib($N);

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

CbCによるMoarVM

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 cbc_next(INTERP i){
    goto NEXT(i);
}
__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;
     /* Points to the place in the bytecode
         right after the current opcode. */
     /* See the NEXT_OP macro for making sense
          of this */
    MVMuint8 *cur_op;
     /* The current frame’s bytecode start. */
    MVMuint8 *bytecode_start;
     /* Points to the base of the current
         register set for the frame we
     * are presently in. */
    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_no_op(INTERP i){
    goto cbc_next(i);
}
__code cbc_const_i8(INTERP i){
    goto cbc_const_i16(i);
}
__code cbc_const_i16(INTERP i){
    goto cbc_const_i32(i);
}
__code cbc_const_i32(INTERP i){
    MVM_exception_throw_adhoc(i->tc, "const_iX NYI");
   goto cbc_const_i64(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);
}
__code cbc_pushcompsc(INTERP i){
    MVMObject * sc;
    sc  = GET_REG(i->cur_op, 0,i).o;
    if (REPR(sc)->ID != MVM_REPR_ID_SCRef)
        MVM_exception_throw_adhoc(i->tc, "Can only push an SCRef with pushcompsc");
    if (MVM_is_null(i->tc, i->tc->compiling_scs)) {
        MVMROOT(i->tc, sc, {
            i->tc->compiling_scs = MVM_repr_alloc_init(i->tc, i->tc->instance->boot_types.BOOTArray);
        });
    }
    MVM_repr_unshift_o(i->tc, i->tc->compiling_scs, sc);
    i->cur_op += 2;
    goto cbc_next(i);
}

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

MoarVMのデバッグ

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

現在のCbCMoarVM

CbCMoarVMの利点

CbCMoarVMの欠点

CbCMoarVMと通常のMoarVMの比較

#! nqp
# Example of a while loop

my $i := 0;
while $i < 10 {
    say("i={$i++}");
}
subset Fizz of Int where * %% 3;
subset Buzz of Int where * %% 5;
subset FizzBuzz of Int where Fizz&Buzz;
subset Number of Int where none Fizz|Buzz;

proto sub fizzbuzz ($) { * }
multi sub fizzbuzz (FizzBuzz) { "FuzzBuzz" }
multi sub fizzbuzz (Fizz) { "Fizz" }
multi sub fizzbuzz (Buzz) { "Buzz" }
multi sub fizzbuzz (Number $number) { $number }

fizzbuzz($_).say for 1..15;