# HG changeset patch # User Takahiro SHIMIZU # Date 1530617577 -32400 # Node ID bc8b0482c14f04e7a727808d304b844d2f2c94cd # Parent 32e35be2ce712777813b547f5f5bc269fef3d2a5 auto-Update generated slides by script diff -r 32e35be2ce71 -r bc8b0482c14f slides/2018/07/02/slide.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/slides/2018/07/02/slide.md Tue Jul 03 20:32:57 2018 +0900 @@ -0,0 +1,21 @@ +title: CbCによるMoarVMの改良 +author: Takahiro Shimizu +profile: +lang: Japanese + + +# 研究目的 +- Perl5の後継言語として開発されているPerl6はMoarVMと呼ばれるVMを搭載している. +- Perl6はMoarVM,JVM,JavaScript上で動くRakudoと呼ばれる実装と,コンパイラ開発者用のサブセットであるNQPが主な実装となっている. +- 現在Perl6及びMoarVMは全体的な速度がPerl5と比較し低下しており,実務として利用できるレベルに達していない. +- さらにPerl6の実装自体巨大なcase-switch文など見通しが悪くなっている. +- この問題を解決するために現在当研究室で開発している継続を中心にしたContinuation based Cを用いて改良を行う +- CbCの設計理念からVMの実装と親和性が高い事も推測できる為,実際にCbCを用いてどのようにVMが実装できるかを検証する + +# 今週の進捗 + +|現代的な|方法| +|について|hoge| + +# 来週の予定 + diff -r 32e35be2ce71 -r bc8b0482c14f slides/2018/07/03/memo.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/slides/2018/07/03/memo.txt Tue Jul 03 20:32:57 2018 +0900 @@ -0,0 +1,18 @@ + +LLVMのプロファイラ + +とりあえず測定する + +llvm true profiler +perl6にLLVMを直接つけてしまうという手もありそう +--> VMの中から直接読んでいる所 + +GC--> stack上のオブジェクトを全県探索しないといけない +Cでプログラムすると関数呼び出しはstackに乗るけれど変更になるのでは + +通常のallockルーチンのベースでmmapされている + +ログ解析ツールをPerl5/Perl6で書いてみる +* Rakudoで直接CbCを吐く + +llvm IRを吐き出す diff -r 32e35be2ce71 -r bc8b0482c14f slides/2018/07/03/slide.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/slides/2018/07/03/slide.md Tue Jul 03 20:32:57 2018 +0900 @@ -0,0 +1,371 @@ +title: CbCによるMoarVMの改良 +author: Takahiro Shimizu +profile: +lang: Japanese + + +# 研究目的 +- Perl5の後継言語として開発されているPerl6はMoarVMと呼ばれるVMを搭載している. +- Perl6はMoarVM,JVM,JavaScript上で動くRakudoと呼ばれる実装と,コンパイラ開発者用のサブセットであるNQPが主な実装となっている. +- 現在Perl6及びMoarVMは全体的な速度がPerl5と比較し低下しており,実務として利用できるレベルに達していない. +- さらにPerl6の実装自体巨大なcase-switch文など見通しが悪くなっている. +- この問題を解決するために現在当研究室で開発している継続を中心にしたContinuation based Cを用いて改良を行う +- CbCの設計理念からVMの実装と親和性が高い事も推測できる為,実際にCbCを用いてどのようにVMが実装できるかを検証する + +# 今週の進捗 +* 油断していたら一週間経ってました +* DynAsmに手がかりが無いかなと思って読んでいます + +# DynAsm + +* minilua経由で`dynasm.lua`などが呼ばれている模様 +* `dasm_init`でbreak pointを書けたら止まった + +``` +(lldb) bt +* thread #2, stop reason = breakpoint 1.1 + * frame #0: 0x000000010029982f libmoar.dylib`dasm_init(compiler=0x00007000010bbc60, maxsection=2) at dasm_x86.h:93 + frame #1: 0x0000000100287d6a libmoar.dylib`MVM_jit_compiler_init(tc=0x0000000100a4b8f0, cl=0x00007000010bbc60, jg=0x000000010183ac7a) at compile.c:19 + frame #2: 0x0000000100287ff5 libmoar.dylib`MVM_jit_compile_graph(tc=0x0000000100a4b8f0, jg=0x000000010183ac7a) at compile.c:52 + frame #3: 0x00000001001b50da libmoar.dylib`MVM_spesh_candidate_add(tc=0x0000000100a4b8f0, p=0x0000000100a4c480) at candidate.c:101 + frame #4: 0x00000001001cf991 libmoar.dylib`worker(tc=0x0000000100a4b8f0, callsite=0x00000001006c9150, args=0x0000000000000000) at worker.c:16 + frame #5: 0x000000010014e8e2 libmoar.dylib`invoke_handler(tc=0x0000000100a4b8f0, invokee=0x000000010181ae40, callsite=0x00000001006c9150, args=0x0000000000000000) at MVMCFunction.c:9 + frame #6: 0x00000001000e8494 libmoar.dylib`thread_initial_invoke(tc=0x0000000100a4b8f0, data=0x0000000100a4c070) at threads.c:59 + frame #7: 0x00000001000aefee libmoar.dylib`MVM_interp_run(tc=0x0000000100a4b8f0, initial_invoke=(libmoar.dylib`thread_initial_invoke at threads.c:50), invoke_data=0x0000000100a4c070) at interp.c:93 + frame #8: 0x00000001000e7a35 libmoar.dylib`start_thread(data=0x0000000100a4c070) at threads.c:87 + frame #9: 0x00007fff7abf8661 libsystem_pthread.dylib`_pthread_body + 340 + frame #10: 0x00007fff7abf850d libsystem_pthread.dylib`_pthread_start + 377 + frame #11: 0x00007fff7abf7bf9 libsystem_pthread.dylib`thread_start + 13 +``` + +# dasm_free + +* freeしている箇所で一度止めてupしていく + +``` +(lldb) b dasm_free +Breakpoint 2: where = libmoar.dylib`dasm_free + 12 at dasm_x86.h:118, address = 0x0000000100299a0c +(lldb) c +Process 42568 resuming +Process 42568 stopped +* thread #2, stop reason = breakpoint 2.1 + frame #0: 0x0000000100299a0c libmoar.dylib`dasm_free(compiler=0x00007000010bbc60) at dasm_x86.h:118 + 115 void + 116 dasm_free (Dst_DECL) + 117 { +-> 118 dasm_State *D = Dst_REF; + 119 int i; + 120 for (i = 0; i < D->maxsection; i++) + 121 if (D->sections[i].buf) +Target 0: (moar) stopped. +(lldb) bt +* thread #2, stop reason = breakpoint 2.1 + * frame #0: 0x0000000100299a0c libmoar.dylib`dasm_free(compiler=0x00007000010bbc60) at dasm_x86.h:118 + frame #1: 0x0000000100287f39 libmoar.dylib`MVM_jit_compiler_deinit(tc=0x0000000100a4b8f0, cl=0x00007000010bbc60) at compile.c:40 + frame #2: 0x00000001002881d7 libmoar.dylib`MVM_jit_compile_graph(tc=0x0000000100a4b8f0, jg=0x000000010183ac7a) at compile.c:99 + frame #3: 0x00000001001b50da libmoar.dylib`MVM_spesh_candidate_add(tc=0x0000000100a4b8f0, p=0x0000000100a4c480) at candidate.c:101 + frame #4: 0x00000001001cf991 libmoar.dylib`worker(tc=0x0000000100a4b8f0, callsite=0x00000001006c9150, args=0x0000000000000000) at worker.c:16 + frame #5: 0x000000010014e8e2 libmoar.dylib`invoke_handler(tc=0x0000000100a4b8f0, invokee=0x000000010181ae40, callsite=0x00000001006c9150, args=0x0000000000000000) at MVMCFunction.c:9 + frame #6: 0x00000001000e8494 libmoar.dylib`thread_initial_invoke(tc=0x0000000100a4b8f0, data=0x0000000100a4c070) at threads.c:59 + frame #7: 0x00000001000aefee libmoar.dylib`MVM_interp_run(tc=0x0000000100a4b8f0, initial_invoke=(libmoar.dylib`thread_initial_invoke at threads.c:50), invoke_data=0x0000000100a4c070) at interp.c:93 + frame #8: 0x00000001000e7a35 libmoar.dylib`start_thread(data=0x0000000100a4c070) at threads.c:87 + frame #9: 0x00007fff7abf8661 libsystem_pthread.dylib`_pthread_body + 340 + frame #10: 0x00007fff7abf850d libsystem_pthread.dylib`_pthread_start + 377 + frame #11: 0x00007fff7abf7bf9 libsystem_pthread.dylib`thread_start + 13 +``` + +# MBM_jit__compile_graph + +``` +(lldb) f +frame #2: 0x00000001002881d7 libmoar.dylib`MVM_jit_compile_graph(tc=0x0000000100a4b8f0, jg=0x000000010183ac7a) at compile.c:99 + 96 code = MVM_jit_compiler_assemble(tc, &cl, jg); + 97 + 98 /* Clear up the compiler */ +-> 99 MVM_jit_compiler_deinit(tc, &cl); + 100 + 101 /* Logging for insight */ + 102 if (tc->instance->jit_bytecode_dir) { +(lldb) +``` + +``` +(lldb) p code +(MVMJitCode *) $2 = 0x00000001047196a0 +(lldb) p *code +(MVMJitCode) $3 = { + func_ptr = 0x00000001007ff000 + size = 1723 + bytecode = 0x0000000100560cf0 "R\x03" + sf = 0x0000000103248300 + local_types = 0x0000000104719920 + num_locals = 18 + num_labels = 7 + labels = 0x0000000102902530 + num_deopts = 0 + num_inlines = 0 + num_handlers = 0 + deopts = 0x0000000000000000 + inlines = 0x0000000000000000 + handlers = 0x0000000000000000 + spill_size = 1 + seq_nr = 0 +} +``` + +# disassしてみる + +* `MAGIC_BYTECODE`という謎のbytecodeを吐いている + +``` +(lldb) disass -a code->bytecode +libmoar.dylib`MAGIC_BYTECODE: + 0x100560cf0 <+0>: pushq %rdx + 0x100560cf1 <+1>: addl (%rax), %eax + 0x100560cf3 <+3>: addb %al, (%rax) + 0x100560cf5 <+5>: addb %al, (%rax) + 0x100560cf7 <+7>: addb %al, (%rax) + 0x100560cf9 <+9>: addb %al, (%rax) + 0x100560cfb <+11>: addb %al, (%rax) + 0x100560cfd <+13>: addb %al, (%rax) + 0x100560cff <+15>: addb %al, (%rbx) + 0x100560d01 <+17>: addb %al, (%rax) + 0x100560d03 <+19>: addb %al, (%rax) + 0x100560d05 <+21>: addb %al, (%rax) + 0x100560d07 <+23>: addb %al, (%rax) + 0x100560d09 <+25>: addb %al, (%rax) + 0x100560d0b <+27>: addb %al, (%rax) + 0x100560d0d <+29>: addb %al, (%rax) + 0x100560d0f <+31>: addb %al, (%rax) + 0x100560d11 <+33>: addb %al, (%rax) + 0x100560d13 <+35>: addb %al, (%rax) + 0x100560d15 <+37>: addb %al, (%rax) + 0x100560d17 <+39>: addb %al, (%rax) + 0x100560d19 <+41>: addb %al, (%rax) + 0x100560d1b <+43>: addb %al, (%rax) + 0x100560d1d <+45>: addb %al, (%rax) +``` + +# 作っていそう + +``` + 45 MVMJitCode * MVM_jit_compile_graph(MVMThreadContext *tc, MVMJitGraph *jg) { + 46 MVMJitCompiler cl; + 47 MVMJitCode *code; + 48 MVMJitNode *node = jg->first_node; + 49 + 50 MVM_jit_log(tc, "Starting compilation\n"); +(lldb) + 51 /* initialation */ + 52 MVM_jit_compiler_init(tc, &cl, jg); + 53 /* generate code */ + 54 MVM_jit_emit_prologue(tc, &cl, jg); + 55 while (node) { + 56 switch(node->type) { + 57 case MVM_JIT_NODE_LABEL: + 58 MVM_jit_emit_label(tc, &cl, jg, node->u.label.name); + 59 break; + 60 case MVM_JIT_NODE_PRIMITIVE: + 61 MVM_jit_emit_primitive(tc, &cl, jg, &node->u.prim); +(lldb) + 62 break; + 63 case MVM_JIT_NODE_BRANCH: + 64 MVM_jit_emit_block_branch(tc, &cl, jg, &node->u.branch); + 65 break; + 66 case MVM_JIT_NODE_CALL_C: + 67 MVM_jit_emit_call_c(tc, &cl, jg, &node->u.call); + 68 break; + 69 case MVM_JIT_NODE_GUARD: + 70 MVM_jit_emit_guard(tc, &cl, jg, &node->u.guard); + 71 break; + 72 case MVM_JIT_NODE_INVOKE: +(lldb) + 73 MVM_jit_emit_invoke(tc, &cl, jg, &node->u.invoke); + 74 break; + 75 case MVM_JIT_NODE_JUMPLIST: + 76 MVM_jit_emit_jumplist(tc, &cl, jg, &node->u.jumplist); + 77 break; + 78 case MVM_JIT_NODE_CONTROL: + 79 MVM_jit_emit_control(tc, &cl, &node->u.control, NULL); + 80 break; + 81 case MVM_JIT_NODE_EXPR_TREE: + 82 MVM_jit_compile_expr_tree(tc, &cl, jg, node->u.tree); + 83 break; +(lldb) + 84 case MVM_JIT_NODE_DATA: + 85 MVM_jit_emit_data(tc, &cl, &node->u.data); + 86 break; + 87 case MVM_JIT_NODE_SAVE_RV: + 88 MVM_jit_emit_save_rv(tc, &cl, node->u.stack.slot); + 89 break; + 90 } + 91 node = node->next; + 92 } + 93 MVM_jit_emit_epilogue(tc, &cl, jg); + 94 +(lldb) + 95 /* Generate code */ + 96 code = MVM_jit_compiler_assemble(tc, &cl, jg); + 97 + 98 /* Clear up the compiler */ + 99 MVM_jit_compiler_deinit(tc, &cl); + 100 + 101 /* Logging for insight */ + 102 if (tc->instance->jit_bytecode_dir) { + 103 MVM_jit_log_bytecode(tc, code); + 104 } + 105 if (tc->instance->jit_log_fh) +(lldb) + 106 fflush(tc->instance->jit_log_fh); + 107 return code; + 108 } + 109 +``` + +# 実際にアセンブルしている箇所 + +``` +110 MVMJitCode * MVM_jit_compiler_assemble(MVMThreadContext *tc, MVMJitCompiler *cl, MVMJitGraph *jg) { +111 MVMJitCode * code; +112 MVMint32 i; +113 char * memory; +114 size_t codesize; +115 +116 MVMint32 dasm_error = 0; +117 +118 /* compile the function */ +119 if ((dasm_error = dasm_link(cl, &codesize)) != 0) { +120 MVM_jit_log(tc, "DynASM could not link, error: %d\n", dasm_error); +121 return NULL; +122 } +123 +124 memory = MVM_platform_alloc_pages(codesize, MVM_PAGE_READ|MVM_PAGE_WRITE); +125 if ((dasm_error = dasm_encode(cl, memory)) != 0) { +126 MVM_jit_log(tc, "DynASM could not encode, error: %d\n", dasm_error); +127 return NULL; +128 } +129 +130 /* set memory readable + executable */ +131 if (!MVM_platform_set_page_mode(memory, codesize, MVM_PAGE_READ|MVM_PAGE_EXEC)) { +132 MVM_jit_log(tc, "Setting jit page executable failed or was denied. deactivating jit.\n"); +133 /* our caller allocated the compiler and our caller must clean it up */ +134 tc->instance->jit_enabled = 0; +135 return NULL; +136 } +137 +138 MVM_jit_log(tc, "Bytecode size: %"MVM_PRSz"\n", codesize); +139 +140 /* Create code segment */ +141 code = MVM_malloc(sizeof(MVMJitCode)); +142 code->func_ptr = (void (*)(MVMThreadContext*,MVMCompUnit*,void*)) memory; +143 code->size = codesize; +144 code->bytecode = (MVMuint8*)MAGIC_BYTECODE; +145 code->sf = jg->sg->sf; +146 code->spill_size = cl->spills_num; +147 if (cl->spills_num > 0) { +148 MVMint32 sg_num_locals = jg->sg->num_locals; +149 code->num_locals = sg_num_locals + cl->spills_num; +150 code->local_types = MVM_malloc(code->num_locals * sizeof(MVMuint16)); +151 if (jg->sg->local_types != NULL) { +152 memcpy(code->local_types, jg->sg->local_types, sizeof(MVMuint16)*sg_num_locals); +153 } else { +154 memcpy(code->local_types, code->sf->body.local_types, sizeof(MVMuint16)*sg_num_locals); +155 } +156 for (i = 0; i < cl->spills_num; i++) { +157 code->local_types[sg_num_locals + i] = cl->spills[i].reg_type; +158 } +159 } else { +160 code->local_types = NULL; +161 code->num_locals = 0; +162 } +``` + + +# メモリのプロテクトを解除 + +* `memory = MVM_platform_alloc_pages(codesize, MVM_PAGE_READ|MVM_PAGE_WRITE);`で呼び出している +* `platform/posix/mmap.c`で主に定義されている + +``` + 42 void *MVM_platform_alloc_pages(size_t size, int page_mode) + 43 { + 44 int prot_mode = page_mode_to_prot_mode(page_mode); + 45 void *block = mmap(NULL, size, prot_mode, MVM_MAP_ANON | MAP_PRIVATE, -1, 0); + 46 + 47 if (block == MAP_FAILED) + 48 MVM_panic(1, "MVM_platform_alloc_pages failed: %d", errno); + 49 + 50 return block; + 51 } +``` + +* mmapを変数をラップする用に使用している + +``` + 21 static int page_mode_to_prot_mode(int page_mode) { + 22 switch (page_mode) { + 23 case MVM_PAGE_READ: + 24 return PROT_READ; + 25 case MVM_PAGE_WRITE: + 26 return PROT_WRITE; + 27 case (MVM_PAGE_READ|MVM_PAGE_WRITE): + 28 return PROT_READ|PROT_WRITE; + 29 case MVM_PAGE_EXEC: + 30 return PROT_EXEC; + 31 case (MVM_PAGE_READ|MVM_PAGE_EXEC): + 32 return PROT_READ|PROT_EXEC; + 33 case (MVM_PAGE_WRITE|MVM_PAGE_EXEC): + 34 return PROT_WRITE|PROT_EXEC; + 35 case (MVM_PAGE_READ|MVM_PAGE_WRITE|MVM_PAGE_EXEC): + 36 return PROT_READ|PROT_WRITE|PROT_EXEC; + 37 default: + 38 return PROT_NONE; + 39 } + 40 } +``` + + +# JITに突入する箇所 + +* ` MVM_jit_enter_code`が怪しいが複数宣言されている +* `src/jit/compile.c` + +``` +324 /* Enter the JIT code segment. The label is a continuation point where control +325 * is resumed after the frame is properly setup. */ +326 void MVM_jit_enter_code(MVMThreadContext *tc, MVMCompUnit *cu, +327 MVMJitCode *code) { +328 void *label = tc->cur_frame->jit_entry_label; +329 MVMint32 ofs = (char*)label - (char*)code->func_ptr; +330 if (ofs < 0 || ofs >= code->size) +331 MVM_oops(tc, "JIT entry label out of range for code!\n" +332 "(label %p, func_ptr %p, code size %lui, offset %li, frame_nr %i, seq nr %i)", +333 label, code->func_ptr, code->size, ((char*)label) - ((char*)code->func_ptr), +334 tc->cur_frame->sequence_nr, code->seq_nr); +335 code->func_ptr(tc, cu, label); +336 } +``` + +# コード実行箇所 + +* `code->func_ptr`でgrepした +* src/core/nativecall.cがそれっぽい + +``` +1149 void MVM_nativecall_invoke_jit(MVMThreadContext *tc, MVMObject *site) { +1150 MVMNativeCallBody *body = MVM_nativecall_get_nc_body(tc, site); +1151 MVMJitCode * const jitcode = body->jitcode; +1152 +1153 jitcode->func_ptr(tc, *tc->interp_cu, jitcode->labels[0]); +1154 } +``` + +* ただしサンプルコードでは止まらない + * JITされてない? +* 呼ばれている箇所を探索 + * jit/graph.c + * op_to_funcで呼ばれているが巨大なcase文 + * opcodeによって判定している + * これ自体は`consume_reprop`で呼ばれているが謎 +* 多分`MVM_interp_run`をちゃんと読む必要がありそう +