V6でJIT・部分適用・継続

      七誌
JIT
• add(a, b) { return a + b; }
  – 0000: 016600 000002     mov 2(sp), r0
  – 0004: 066600 000004     add 4(sp), r0
  – 0008: 000207            rts pc

 main() {
     int buf[5], (*p)();
     buf[0] = 0016600; buf[1] = 2;
     buf[2] = 0066600; buf[3] = 4;
     buf[4] = 0000207; p = buf;
     printf("%d¥n", (*p)(2, 3));
 }
部分適用 (1)
• スタックに部分適用の値を割り込ませて jmp

SP→    戻り先                              戻り先
         2            戻り先         挿入→    2
         3               3               3


      add(2, 3)       add2(3)

                   let add2 = add 2
部分適用 (2)
• スタックに部分適用の値を割り込ませて jmp
.globl _add, _add2    main() {
_add:                     printf("%d¥n", add(2, 3));
    mov 2(sp), r0         printf("%d¥n", add2(3));
    add 4(sp), r0     }
    rts pc

_add2:                                     戻り先
    mov   (sp), r0
    mov   $2, (sp)      戻り先        挿入→        2
    mov   r0, -(sp)
    jmp   _add             3                  3
部分適用 (3)
                thunk[] {
• サンクに              0011600,    /* mov (sp), r0 */
  部分適用の             0012716, 0, /* mov $0, (sp) */
  関数を生成             0010046,    /* mov r0, -(sp) */
                    0000137, 0, /* jmp *$0 */ };
• 部分適用          partapp(func, arg) {
  partial           thunk[2] = arg;
                    thunk[5] = func;
  application       return thunk; }
                sub(a, b) { return a - b; }
                main() {
                    int (*p)();
                    p = partapp(sub, 5);
                    printf("%d¥n", (*p)(4)); }
setjmp/longjmp
• レジスタと戻り先(@スタック)を保存・復帰
_setjmp:             _longjmp:
  mov (sp)+, r1        mov 4(sp), r0
  mov (sp), r0         mov 2(sp), r1
  mov r2, (r0)+        mov (r1)+, r2
  mov r3, (r0)+        mov (r1)+, r3
  mov r4, (r0)+        mov (r1)+, r4
  mov r5, (r0)+        mov (r1)+, r5
  mov sp, (r0)+        mov (r1)+, sp
  mov r1, (r0)         mov (r1), r1
  clr r0               jmp (r1)
  jmp (r1)
コルーチン
                int caller[6], callee[6], i;
• ローカル変数が       test() {
  使えない            for (i = 1; i <= 5; i++)
                    if (setjmp(callee) == 0)
• setjmp()の戻り         longjmp(caller, i);
  値で0と-1を使        longjmp(caller, -1); }
                main() {
  用しているため、
                  int v;
  それ以外の値          v = setjmp(caller);
  しか戻せない          if (v == 0) test();
                  else if (v > 0) {
                    printf("%d¥n", v);
                    longjmp(callee, 1); }}
改良版setjmp/longjmp
_setjmp2:         _longjmp2:
  mov (sp)+, r1     mov 4(sp), r0
                    mov 2(sp), r1
  mov (sp), r0      mov 10(r1), sp
  mov r2, (r0)+     mov sp, r2
  mov r3, (r0)+     mov 14(r1), r3
  mov r4, (r0)+     mov 16(r1), r4
  mov r5, (r0)+     br 1f
                  0:mov (r3)+, (r2)+
  mov sp, (r0)+     sub $2, r4
  mov r1, (r0)+   1:tst r4
  clr (r0)+         bgt 0b
  clr (r0)          mov (r1)+, r2
  clr r0            mov (r1)+, r3
                    mov (r1)+, r4
  jmp (r1)          mov (r1), r5
                    jmp *4(r1)
継続
• コルーチン   test() {
  サポート関数を   int i;
            for (i = 0; i < 5; i++)
  いくつか追加
              yield(i);
• ローカル変数を }
  保持して実行!
               main() {
                 initcr(test);
                 while (callcr())
                   printf("%d¥n", value);
               }
宣伝
    – 10月末にJITでサンクを作って部分適用したり、
      継続の実装過程を扱う勉強会を開催します!

•   サンクと継続の勉強会
•   10月29日(土)13:00~18:00
•   MS&BB池袋西武横店 7号室
•   http://atnd.org/events/19963
ご清聴ありがとうございました

V6でJIT・部分適用・継続

  • 1.
  • 2.
    JIT • add(a, b){ return a + b; } – 0000: 016600 000002 mov 2(sp), r0 – 0004: 066600 000004 add 4(sp), r0 – 0008: 000207 rts pc main() { int buf[5], (*p)(); buf[0] = 0016600; buf[1] = 2; buf[2] = 0066600; buf[3] = 4; buf[4] = 0000207; p = buf; printf("%d¥n", (*p)(2, 3)); }
  • 3.
    部分適用 (1) • スタックに部分適用の値を割り込ませてjmp SP→ 戻り先 戻り先 2 戻り先 挿入→ 2 3 3 3 add(2, 3) add2(3) let add2 = add 2
  • 4.
    部分適用 (2) • スタックに部分適用の値を割り込ませてjmp .globl _add, _add2 main() { _add: printf("%d¥n", add(2, 3)); mov 2(sp), r0 printf("%d¥n", add2(3)); add 4(sp), r0 } rts pc _add2: 戻り先 mov (sp), r0 mov $2, (sp) 戻り先 挿入→ 2 mov r0, -(sp) jmp _add 3 3
  • 5.
    部分適用 (3) thunk[] { • サンクに 0011600, /* mov (sp), r0 */ 部分適用の 0012716, 0, /* mov $0, (sp) */ 関数を生成 0010046, /* mov r0, -(sp) */ 0000137, 0, /* jmp *$0 */ }; • 部分適用 partapp(func, arg) { partial thunk[2] = arg; thunk[5] = func; application return thunk; } sub(a, b) { return a - b; } main() { int (*p)(); p = partapp(sub, 5); printf("%d¥n", (*p)(4)); }
  • 6.
    setjmp/longjmp • レジスタと戻り先(@スタック)を保存・復帰 _setjmp: _longjmp: mov (sp)+, r1 mov 4(sp), r0 mov (sp), r0 mov 2(sp), r1 mov r2, (r0)+ mov (r1)+, r2 mov r3, (r0)+ mov (r1)+, r3 mov r4, (r0)+ mov (r1)+, r4 mov r5, (r0)+ mov (r1)+, r5 mov sp, (r0)+ mov (r1)+, sp mov r1, (r0) mov (r1), r1 clr r0 jmp (r1) jmp (r1)
  • 7.
    コルーチン int caller[6], callee[6], i; • ローカル変数が test() { 使えない for (i = 1; i <= 5; i++) if (setjmp(callee) == 0) • setjmp()の戻り longjmp(caller, i); 値で0と-1を使 longjmp(caller, -1); } main() { 用しているため、 int v; それ以外の値 v = setjmp(caller); しか戻せない if (v == 0) test(); else if (v > 0) { printf("%d¥n", v); longjmp(callee, 1); }}
  • 8.
    改良版setjmp/longjmp _setjmp2: _longjmp2: mov (sp)+, r1 mov 4(sp), r0 mov 2(sp), r1 mov (sp), r0 mov 10(r1), sp mov r2, (r0)+ mov sp, r2 mov r3, (r0)+ mov 14(r1), r3 mov r4, (r0)+ mov 16(r1), r4 mov r5, (r0)+ br 1f 0:mov (r3)+, (r2)+ mov sp, (r0)+ sub $2, r4 mov r1, (r0)+ 1:tst r4 clr (r0)+ bgt 0b clr (r0) mov (r1)+, r2 clr r0 mov (r1)+, r3 mov (r1)+, r4 jmp (r1) mov (r1), r5 jmp *4(r1)
  • 9.
    継続 • コルーチン test() { サポート関数を int i; for (i = 0; i < 5; i++) いくつか追加 yield(i); • ローカル変数を } 保持して実行! main() { initcr(test); while (callcr()) printf("%d¥n", value); }
  • 10.
    宣伝 – 10月末にJITでサンクを作って部分適用したり、 継続の実装過程を扱う勉強会を開催します! • サンクと継続の勉強会 • 10月29日(土)13:00~18:00 • MS&BB池袋西武横店 7号室 • http://atnd.org/events/19963
  • 11.