CbCによるPerl6処理系 |
Takahiro Shimizu, Shinji Kono
琉球大学
|
__code
と書く事で宣言出来る.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);
}
src/core/interp.c
で定義しており, この中の関数 MVM_interp_run
でバイトコードに応じた処理を実行する
OP
で宣言されたブロックがそれぞれバイトコードに対応する処理となっている.GET_REG
などのマクロを用いてMoarVMのレジスタにアクセスする.const_i64
に対応する処理で呼び出されている MVM_BC_get_I64
は, バイトコードにオフセットを足した箇所で指定されている数値を64gitの整数値として取り出す関数となっているcur_op
は次のバイトコード列が登録されており, マクロ NEXT
で決められた方法で次のバイトコードに対応した処理に遷移する.DISPATCH(NEXT_OP) {
OP(const_i64):
GET_REG(cur_op, 0).i64 = MVM_BC_get_I64(cur_op, 2);
cur_op += 10;
goto NEXT;
}
DISPATCH(NEXT_OP) {
OP(const_i64):
DISPATCH
及び OP
は次の様に定義している #define OP(name) OP_ ## name
#define NEXT *LABELS[NEXT_OP]
DISPATCH
は, ラベルgotoが利用できる場合は無視されるOP
が, 対応するバイトコード命令を, ラベル列に変換する OP_const_i16:
OP_const_i32:
MVM_exception_throw_adhoc(tc, "const_iX NYI");
OP_const_i64:
GET_REG
があり, 次のような使い方をする OP(const_i64):
GET_REG(cur_op, 0).i64 = MVM_BC_get_I64(cur_op, 2);
cur_op += 10;
GET_REG
はバイトコードに埋められた数値を利用して, レジスタ情報を取得/設定などをするGET_REG
は次の様に展開される reg_base[*((MVMuint16 *)(cur_op + 0))].i64 = MVM_BC_get_I64(cur_op, 2);
reg_base
はMoarVMの現在のフレームのレジスタ情報が保存されたポインタであり, MVM_interp_runではローカル変数として利用しているNEXT
は, ラベルgotoが使用可能な場合次の様に記述されているNEXT
自体はラベルテーブルにアクセスし, ラベルを取り出すNEXT_OP
というマクロが担っている#define NEXT_OP (op = *(MVMuint16 *)(cur_op), cur_op += 2, op)
#define NEXT *LABELS[NEXT_OP]
NEXT
は次の様に展開されるgoto *LABELS[(op = *(MVMuint16 *)(cur_op), cur_op += 2, op)];
LABELS
にアクセスし, ラベル情報を取得する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,
NEXT
が担当していた, 次のバイトコードへの移動は, NEXT相当のCodeGear cbc_next
で処理を行う__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);
}
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;
__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,
:=
を利用した束縛で行う, ++
演算子が使用できないなどの違いがある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));
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)
cbc_next
というCodeGearで行う(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
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
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
131 : 131
139 : 139
140 : 140
144 : 144
558 : 558
391 : 391
749 : 749
53 : 53
*54 : 8
--cbc
を与えることによりCbCで動き, そうでない場合は通常のCで記述された箇所で実行される--cbc
オプションをシェルスクリプト内に書き加えることで, Perl6, NQPがそれぞれCbCで起動する#!/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 "$@"
#! 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));
__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){
goto cbc_unless_i(i,cbc_osrpoint_01);
}