Mercurial > hg > Papers > 2014 > kaito_sigos
view presen/slide/s6/presen.html @ 26:d2398deb3e48
rewrite implementation
author | Kaito Tokumori <e105711@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 12 May 2014 22:58:06 +0900 |
parents | 9c718fe8b81d |
children | 20f6eff0779c |
line wrap: on
line source
<!DOCTYPE html> <html> <head> <meta charset='utf-8'> <title>Presen</title> <!-- Notes on CSS media types used: 1) projection -> slideshow mode (display one slide at-a-time; hide all others) 2) screen -> outline mode (display all slides-at-once on screen) 3) print -> print (and print preview) Note: toggle between projection/screen (that is, slideshow/outline) mode using t-key Questions, comments? - send them along to the mailinglist/forum online @ http://groups.google.com/group/webslideshow --> <!-- style sheet links --> <link rel="stylesheet/less" href="themes/blank/projection.css.less" media="screen,projection"> <link rel="stylesheet/less" href="themes/blank/screen.css.less" media="screen"> <link rel="stylesheet/less" href="themes/blank/print.css.less" media="print"> <link rel="stylesheet/less" href="blank.css.less" media="screen,projection"> <!-- Notes about less css support - all less stylesheets (*.css.less) need to get listed/loaded first (before the less.js script) - find more info about less.js online @ http://lesscss.org ***** NOTE: less.js browser script currently won’t work if you’re using Google Chrome and the path to your page starts with "file:///" due to a known Chrome issue. (In the developer/js console you will see: XMLHttpRequest cannot load file:///../s6/shared/projection.css.less. Cross origin requests are only supported for HTTP.) --> <!-- add js libs (less, jquery) --> <script src="js/less-1.1.4.min.js"></script> <script src="js/jquery-1.7.min.js"></script> <!-- S6 JS --> <script src="js/jquery.slideshow.js"></script> <script src="js/jquery.slideshow.counter.js"></script> <script src="js/jquery.slideshow.controls.js"></script> <script src="js/jquery.slideshow.footer.js"></script> <script src="js/jquery.slideshow.autoplay.js"></script> <script> $(document).ready( function() { Slideshow.init(); // Example 2: Start Off in Outline Mode // Slideshow.init( { mode: 'outline' } ); // Example 3: Use Custom Transition // Slideshow.transition = transitionScrollUp; // Slideshow.init(); // Example 4: Start Off in Autoplay Mode with Custom Transition // Slideshow.transition = transitionScrollUp; // Slideshow.init( { mode: 'autoplay' } ); } ); </script> <!-- Better Browser Banner for Microsoft Internet Explorer (IE) --> <!--[if IE]> <script src="js/jquery.microsoft.js"></script> <![endif]--> </head> <body> <div class="layout"> <div id="header"></div> <div id="footer"> <div align="right"> <img src="images/concurrency.png" width="200"> </div> </div> </div> <div class="presentation"> <!-- add slides here; example --> <div class='slide cover'> <table width="90%" height="90%" border="0" align="center"> <tr> <td><div align="center"> <h1><font color="#808db5">CbC コンパイラの LLVM/clang 3.5 上での実装</font></h1> </div></td> </tr> <tr> <td><div align="left"> Kaito Tokumori <script> var date = new Date(); var year = date.getFullYear(); var month = date.getMonth(); var day = date.getDate(); var monthList = new Array("January","February","March","April","May","June", "July","August","September","October","November","December"); document.write(monthList[month]+" "+day+", "+year); </script> <hr style="color:#ffcc00;background-color:#ffcc00;text-align:left;border:none;width:300%;height:0.2em;"> </div></td> </tr> </table> </div> <div class='slide'> <h2>研究目的</h2> <ul> <li>当研究室では, プログラムを code segment, data segment という単位を用いて書くという手法を提案している. <li>この手法を用いてプログラミングを行う言語として CbC を開発している. <li>CbC は関数呼び出しではなく継続によって処理の移動を行うので状態遷移ベースのプログラミングに適している. <li>OS, 正規表現検査器等が状態遷移ベース. </ul> </div> <!-- h1.hidden => use heading just for table of contents (toc) --> <div class='slide'> <h2>Continuation based C</h2> <ul> <li>code segment 単位での記述と継続を基本としている. <li>継続とは code segment 間の移動のことで, goto を用いる. <li>その他の構文は C と同じ. <li>C の関数をそのまま使用することもできるが, 関数呼び出しを継続に, ループ制御を再帰的な継続に置き換えることで goto のみにすることも可能. <li>code segment は戻り値を持たず, スタックに値を積まない. <li>code segment への継続は call ではなく jmp 命令で行われる. </div> <div class='slide'> <h2>Continuation based C</h2> <table border='1' align='center' width='80%'> <caption><p>階乗を計算する CbC プログラム</p></caption> <tr><td width='50%'> <pre class='small_code'> __code print_factorial(int prod) { printf("factorial = %d\n",prod); exit(0); } __code factorial0(int prod, int x) { if ( x >= 1) { goto factorial0(prod*x, x-1); }else{ goto print_factorial(prod); } } __code factorial(int x) { goto factorial0(1, x); } int main(int argc, char **argv) { int i; i = atoi(argv[1]); goto factorial(i); }</pre> </td><td valign='top'> <ul> <li>code segment は型に __code を使うことで宣言できる. <li>goto の後に code segment 名と引数を並べて記述することで継続できる. <li>戻り値は存在しない. </td></tr> </table> </div> <div class='slide'> <h2>LLVM/clang</h2> <ul> <li>LLVM はコンパイラ, ツールチェーン技術などを開発するプロジェクトの名称. <li>単に LLVM といった場合には LLVM Core を指し, これはコンパイラの基板となるライブラリの集合. <li>LLVM IR, LLVM BitCode と呼ばれる独自の言語を持つ. <li>clang はバックエンドに LLVM を利用する C/C++/Objective-C コンパイラ. </ul> <div align="center"><img src="fig/clang_llvm_structure.svg" width="45%"></div> </div> <div class='slide'> <h2>LLVM/clang でのコンパイルの流れ</h2> <ul> <li>clang はソースコードを解析すると, 一度 Abstract Syntax Tree (AST) という中間表現を介して LLVM IR に変換する. <li>LLVM IR は SelectionDAG Instruction Selection を経由することで Machine Code に変換される. <ul> <li>このとき一度 SelectionDAG という内部表現に変換され, 最適化が行われる. </ul> <li>Machine Code は Machine Code に対する最適化がかけられた後, アセンブリコードに変換される. </ul> <div align="center"><img src="fig/clang_llvm_structure.svg" width="45%"></div> </div> <div class='slide'> <h2>LLVM/clang の内部表現</h2> <table border='1' align='center' width='80%'> <tr><td width='25%'> 内部表現名 </td><td> 概要 </td></tr> <tr><td> AST </td><td> ソースコードの解析結果を保持したツリー. </td></tr> <tr><td> LLVM IR </td><td> LLVM のメインとなる中間表現. LLVM 内部での形式, 人が理解しやすいアセンブリ言語形式, JIT コンパイラ上で実行するための bitcode 形式の三種類の形を持つ. </td></tr> <tr><td> SelectionDAG </td><td> 非巡回有向グラフで, 各ノードが命令とその対象となるオペランドを持つ. </td></tr> <tr><td> Machine Code </td><td> 無限の仮想レジスタを持つ SSA 形式と物理レジスタを持つ non-SSA 形式の二種を持つ中間表現. </td></tr> <tr><td> MC Layer </td><td> 正確には中間表現ではなく, コード生成を抽象化して扱えるようにした層. </td></tr> </table> <br> <p align='center' class='step emphasize'>CbC コンパイラの実装に深く関わる内部表現である AST についてさらに触れる.</p> </div> <div class='slide'> <h2>Abstract Syntax Tree</h2> <ul> <li>'-Xclang -ast-dump' というオプションを付加することで表示可能. <li>各ノードは宣言, 文, 式を表す Decl, Stmt, Expr といったクラスを継承したクラスから成る. </ul> <table border='1'> <tr> <td>ソースコード <td>AST </tr> <tr> <td valign='top' width='20%'> <pre class='small_code'> int main(){ int a; a = func(1,2); return a; } </pre> <td><img src="fig/clangAST_char_mono.svg" width="100%"> </tr> </table> <p>CbC の文に対してこの木が正しく生成されるようにソースコードに手を加えていく.</p> </div> <div class='slide'> <h2>CbC コンパイラの実装</h2> <ul> <li>__code 型の追加 <li>goto syntax の追加 <li>clang/LLVM 間の __code 型の変換 <li>Tail call elimination の強制 <li>環境付き継続 </ul> </div> <div class='slide'> <h2>__code 型の追加</h2> <div align='center'><img src="fig/clang_llvm_slide_parse.svg" width="70%"></div> </div> <div class='slide'> <h2>__code 型の追加</h2> <table width='100%'> <tr><td> <ul> <li>clang/LLVM 内部では code segment は __code 型の関数として扱う. <li>clang と LLVM で型を扱うクラスは別々なので両方に手を加える必要がある. <li>clang 側では keyword, ID そして型情報を管理するクラスである Type を作成する. <li>LLVM 側では 型情報を管理するクラスである Type と ID の作成が必要. <li>以下は clang が __code をパースする箇所. </ul> </tr> <tr> <td style="border: double;"> <pre class='code'> case tok::kw___code: { LangOptions* LOP; LOP = const_cast<LangOptions*>(&getLangOpts()); LOP->HasCodeSegment = 1; isInvalid = DS.SetTypeSpecType(DeclSpec::TST___code, Loc, PrevSpec, DiagID); break; }</pre> </tr> </table> </div> <div class='slide'> <h2>goto syntax の追加</h2> <div align='center'><img src="fig/clang_llvm_slide_parse.svg" width="70%"></div> </div> <div class='slide'> <h2>goto syntax の追加</h2> <table width='100%'> <tr><td> <ul> <li>goto を用いて継続を行う構文に対応させる. <li>ここでは関数呼び出しを生成しておき, 後で Tail Call Elimination によって継続に直す. <li>以下は goto をパースする箇所. C の goto 出ない場合に継続の構文と判断する. </ul> </tr> <tr> <td style="border: double;"> <pre class='code'> case tok::kw_goto: #ifndef noCbC if (!(NextToken().is(tok::identifier) && PP.LookAhead(1).is(tok::semi)) && NextToken().isNot(tok::star)) { SemiError = "goto code segment"; return ParseCbCGotoStatement(Attrs, Stmts); } #endif Res = ParseGotoStatement(); SemiError = "goto"; break;</pre> </tr> </table> </div> <div class='slide'> <h2>goto syntax の追加</h2> <ul> <li>goto の後の構文を解析し, 関数呼び出しの Stmt を生成する. <li>直後に return文を追加する. これは Tail Call Elimination の条件のうちの一つ. </ul> <table border='1' width='80%' align='center'> <tr> <td>実際のコード <td>clnag内部で扱われるコード </tr> <tr> <td><pre class='small_code'> __code code1() { : goto code2(); } </pre> <td><pre class='small_code'> void code1() { : code2(); return; } </pre> </tr> </table> <ul> <li>ここまでが parser による AST の生成. 次に LLVM IR を吐き出す CodeGen に入る. </ul> </div> <div class='slide'> <h2>clang/LLVM 間の __code 型の変換</h2> <div align='center'><img src="fig/clang_llvm_slide_cg.svg" width="70%"></div> </div> <div class='slide'> <h2>clang/LLVM 間の __code 型の変換</h2> <table width='100%'> <tr><td> <ul> <li>clang の Type が LLVM のものに変わるのは CodeGen の段階. <li>以下が実際に変換を行っている箇所. ABIArgInfo の種類に応じて型の変換をしている. <li>Ignore の時 void 型であるので, この時に __code 型かどうかのチェックを加えた. </ul> </tr> <tr> <td style="border: double;"> <pre class='code'> case ABIArgInfo::Ignore: #ifndef noCbC if (FI.getReturnType().getTypePtr()->is__CodeType()) resultType = llvm::Type::get__CodeTy(getLLVMContext()); else resultType = llvm::Type::getVoidTy(getLLVMContext()); #else resultType = llvm::Type::getVoidTy(getLLVMContext()); #endif break;</pre> </tr> </table> </div> <div class='slide'> <h2>code segment の継続の実装</h2> <ul> <li>Tail Call Elimination という最適化によって実現する. <li>この最適化は関数呼び出しに対して行われ, これにより関数への移動が call 命令でなく jmp 命令で行われるようになる. <li>これを code segment に対して強制することで継続が実装される. </ul> </div> <div class='slide'> <h2>Tail Call Elimination の強制</h2> <div align='center'><img src="fig/clang_llvm_slide_cg_DAG.svg" width="70%"></div> </div> <div class='slide'> <h2>Tail Call Elimination の強制</h2> <p>LLVM の入力となる中間言語, LLVM IR には大域 jmp に相当する命令がない. <br>しかし LLVM IR は関数呼び出しにフラグを付けることができるので, それを利用することで code segment の呼び出しだという情報を残すことが可能. </p> <p>以下の条件を満たす必要がある</p> <ul> <li>tail フラグを立てる tail call elimintaion pass の追加. <li>呼び出し元と呼び出す関数の呼び出し規約を fastcc, cc 10, cc 11 のいずれかにする. <li>最適化のオプションである tailcallopt が有効になっている. </ul> <br> <p>それぞれ以下のようにして条件を満たす</p> <ul> <li>最適化のレベルにかかわらず taill call elimintaion pass の追加. <li>呼び出し規約を fastcc に変更. <li>code segment が含まれる際に tailcallopt を自動で有効化. </ul> </div> <div class='slide'> <h2>環境付き継続</h2> <div align='center'><img src="fig/clang_llvm_slide_parse.svg" width="70%"></div> </div> <div class='slide'> <h2>環境付き継続</h2> <table width='100%'> <tr><td> <ul> <li>__return, __environment という二つのキーワードを利用して実現. <li>C の関数から code segment に継続した後, C 元の関数を呼び出した関数に戻るための機能. <li>以下のコードの場合, A から継続した B は環境付き継続を用いて A の呼び出し元である main に戻る. </ul> </tr> <tr> <td style="border: double;"> <pre class='code'> __code B(int retval,__code(*ret)(int,void *),void *env){ goto ret(n, env); } int A(){ goto B(1, __return, __environment); return 0; } int main(){ int retval; retval = A(); // retval should be 1. } </pre> </tr> </table> <ul> <li>GCC 上に実装した CbC コンパイラは nested function を使用していたが clang はこの構文を受け付けない. <li>setjmp/longjmp を用いて実装. </ul> </div> <div class='slide'> <h2>環境付き継続</h2> <p align='center'>自動生成されたコード</p> <table width='80%' align='center'> <tr> <td valign='top'> <pre class='small_code'> #include <setjmp.h> struct CbC_env { void *ret_p,*env; }; __code B(int retval,__code(*ret)(int,void *),void *env){ goto ret(n, env); } __code return1 (int retval, void* env){ *(int*)((struct CbC_env *)(env))->ret_p = retcal; longjmp((int*)(((struct CbC_env *)env)->env),1); } </pre> <td> <pre class='small_code'> int A(){ goto B(1, ({ __code (*__CbC_return)(); __CbC_return = return1; __CbC_return; }), ({ struct CbC_env __CbC_environment; jmp_buf env; int retval; __CbC_environment.ret_p = &retval; __CbC_environment.env = &env; if (setjmp(__CbC_environment.env)){ return retval; } &__CbC_environment; }) ); return 0; } </pre> </tr> </table> </div> <div class='slide'> <h2>評価</h2> <ul> <li>出力するアセンブリコードの確認 <li>Micro-C, GCC, LLVM/clang のそれぞれでコンパイルして得られるプログラムの実行速度の比較 </ul> </div> <div class='slide'> <h2>アセンブリコード</h2> <table width='100%' align='center' border='1'> <tr> <td>コンパイル前 <td>対応するアセンブリ </tr> <tr> <td valign='top'> <pre class='small_code'> __code factorial(int x) { goto factorial0(1, x); } </pre> <td> <pre class='small_code'> _factorial: ## @factorial .cfi_startproc ## BB#0: ## %entry subq $24, %rsp Ltmp5: .cfi_def_cfa_offset 32 movl $1, %eax movl %edi, 20(%rsp) ## 4-byte Spill movl %eax, %edi movl 20(%rsp), %esi ## 4-byte Reload addq $24, %rsp <font color='red'>jmp</font> _factorial0 ## TAILCALL .cfi_endproc </pre> </tr> </table> <ul> <li>factorial0 が jmp 命令で呼び出されている. <li>Tail Call Elimination がきちんと強制されている. </ul> </div> <div class='slide'> <h2>実行速度</h2> <p>conv1というプログラムを使用した. 引数 1 の時には CbC の継続を使用, 引数 2, 3 の時には Micro-C のための最適化が手動で施されたコードを使用するようになっている.</p> <table width='80%' align='center' border='1'> <tr> <td width='30%'> <td>引数 1 <td>引数 2 <td>引数 3 </tr> <tr> <td>Micro-C <td>6.875 <td>2.4562 <td>3.105 </tr> <tr> <td>GCC -O2 <td>2.9438 <td>0.955 <td>1.265 </tr> <tr> <td>LLVM/clang -O0 <td>5.835 <td>4.1887 <td>5.0625 </tr> <tr> <td>LLVM/clang -O2 <td>3.3875 <td>2.29 <td>2.5087 </tr> </table> <ul> <li>最適化の有無で比較すると, 最適化有りのほうが二倍以上速い. <li>Micro-C と比較すると, 最適化無しでは劣ることもあるが最適化を有効化すると全ての場合で LLVM/clang の方が速くなる. <li>GCC と比較すると, 速度面では劣るが, 最適化を無効化してもプログラムが正常に動作するという点では優位. </ul> </div> <div class='slide'> <h2>まとめ</h2> <ul> <li>clang/LLVM 上で CbC コンパイラを実装した. <li>環境付き継続を setjmp/longjmp を用いて実装した. <ul> <li>以後 nested function をサポートしないコンパイラ上に CbC コンパイラを実装する際, 同様にして実装できる. </ul> </ul> <br> <h2>今後の課題</h2> <ul> <li>インラインアセンブリを用いた環境付き継続の実装. <li>data segment の設計及び実装. <li>CbC による例題の作成. </ul> </div> </div> <!-- presentation --> </body> </html>