Mercurial > hg > Papers > 2014 > kaito_sigos
view presen/slide/s6/presen.html @ 30:cfcf8ccd7eb9 default tip
fix
author | Kaito Tokumori <e105711@ie.u-ryukyu.ac.jp> |
---|---|
date | Thu, 15 May 2014 01:45:05 +0900 |
parents | f2b3fa53dce3 |
children |
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>OS 等のシステムプログラム, Cerium のようなフレームワークはユーザーからの API コールを受けて処理を行う. <li>処理終了後 API 呼び出し元に直接戻るのが望ましい. <li>インライン展開の場合は全てのファイルを一度にコンパイルしなければならない. <li>Tail Call Elimination は必ず行われるとは限らない. <li>Tail Call Elimination を保証する言語が求められる. </ul> </div> <div class='slide'> <h2>研究目的</h2> <ul> <li>当研究室では プログラミング言語 CbC を開発している. <ul> <li>CbC は Tail Call Elimination を保証する状態遷移ベースの言語. <li>処理の単位 code segment, データの単位 data segment を用いて書く. <li>メタプログラミングも可能にする. </ul> <li>広く使われるようになってきた LLVM 上で CbC コンパイラを実装する. <li>setjmp/longjmp によるポータブルな環境付き継続の実装. </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'> <div class="highlight"><font color='red'>__code</font> code1(int n,__code(*exit_code)(int,void *),void *exit_env){ printf("code1 : code entry1\n"); <font color='red'>goto exit_code(n,exit_env);</font> }</div> int caller(){ printf("caller : main1 entry\n"); __code (*__ret)(int, void *) = __return; struct __CbC_env *__env = __environment; goto code1(1, __ret, __env); return 0; } int main(){ int n; n = caller(); printf("return = %d\n",n); return 0; } </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 と呼ばれる独自の言語を持つ. <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'>内部表現には手を加えていない. </p> <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'> __code code1(int n,__code(*exit_code)(int,void *),void *exit_env){ printf("code1 : code entry1\n"); goto exit_code(n,exit_env); } </pre> <td><img src="fig/clangAST_char.svg" width="100%"> </tr> </table> <p>CbC の文に対してこの木が正しく生成されるようにソースコードに手を加えていく.</p> </div> <div class='slide'> <h2>clang/LLVM 上での 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> <p>__code 型の追加はパーサーを編集して行う.</p> <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>code segment は戻り値を持たないので void 型と同じように扱う. <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 = <font color='red'>DS.SetTypeSpecType(DeclSpec::TST___code, Loc, PrevSpec, DiagID);</font> break; }</pre> </tr> </table> </div> <div class='slide'> <h2>goto syntax の追加</h2> <p>goto syntax の追加もパーサーを編集して行う.</p> <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内部扱う AST に対応するコード </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> <p>clang の Type が LLVM のものに変わるのは CodeGen の段階.</p> <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>以下が実際に変換を行っている箇所. ABIArgInfo の種類に応じて型の変換をしている. <li>code segment は戻り値を持たないので void 型と同様に扱う. <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> <p>最適化の有効化は CodeGen で行う.</p> <p>TCE pass は SelectionDAGISel で行われる最適化.</p> <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> <p>環境付き継続の実装はパーサーを編集する.</p> <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 valign='top'> <ul> <li>C の関数から code segment に継続した後, 継続元の関数を呼び出した関数に戻るための機能. <li>__return, __environment という二つのキーワードを利用して実現. <li>以下のコードの場合, caller から継続した code1 は環境付き継続を用いて caller の呼び出し元である main に戻る. <li>GCC 上に実装した CbC コンパイラは nested function を使用していたが clang はこの構文を受け付けない. <li>setjmp/longjmp を用いて実装. </ul> <td style="border: double;"> <pre class='small_code'><div class='highlight'>__code code1(int n,__code(*exit_code)(int,void *),void *exit_env){ printf("code1 : code entry1\n"); goto exit_code(n,exit_env); } int caller(){ printf("caller : main1 entry\n"); __code (*__ret)(int, void *) = <font color='red'>__return</font>; struct __CbC_env *__env = <font color='red'>__environment</font>; goto code1(1, __ret, __env); return 0; } int main(){ int n; n = caller(); printf("return = %d\n",n); return 0; } </div></pre> </tr> </table> </div> <div class='slide'> <h2>環境付き継続</h2> <p align='center'>自動生成する AST に対応する擬似コード</p> <table> <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 caller..ret0 (int retval, void* env){ *(int*)((struct CbC_env *)(env))->ret_p = retval; <font color='red'>longjmp</font>((int*)(((struct CbC_env *)env)->env),1); } __code code1(int n, __code (*exit_code)(int, void *), void *exit_env) { printf("code1 : code entry1\n"); { exit_code(n, exit_env); return; } } </pre> <td> <pre class='small_code'> int caller() { printf("caller : main1 entry\n"); __code (*__ret)(int, void *) = ({ __code (*__CbC_return)(int, void *); __CbC_return = caller..ret0; __CbC_return; }); struct __CbC_env *__env = ({ struct __CbC_env __CbC_environment; int retval; jmp_buf env_buf; __CbC_environment.ret_p = &retval; __CbC_environment.env = env_buf; if (<font color='red'>setjmp</font>(__CbC_environment.env)) { return retval; } &__CbC_environment; }); { code1(1, __ret, __env); } return 0; } int main() { int n; n = caller(); printf("return = %d\n", n); return 0; } </pre> </tr> </table> </div> <div class='slide'> <h2>アセンブリコードの確認</h2> <p>Tail Call Elimination が強制されているかどうかをアセンブリコードから判断する.</p> <table width='100%' align='center' border='1'> <tr> <td>コンパイル前 <td>対応するアセンブリ </tr> <tr> <td valign='top'> <pre class='small_code'> __code caller(int x) { goto code1(1, x); } </pre> <td> <pre class='small_code'> _caller: ## @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> _code1 ## TAILCALL .cfi_endproc </pre> </tr> </table> <ul> <li>code1 が jmp 命令で呼び出されている. <li>Tail Call Elimination がきちんと強制されている. <li>jmp になっていない場合には警告が出る. </ul> </div> <div class='slide'> <h2>実行速度</h2> <ul> <li>conv1というプログラムを使用. <ul> <li>通常の関数呼び出しを行うコードを継続で実装したプログラム. </ul> <li>引数に応じて純粋に変換した場合とスタックを削り最適化を行った場合のコードどちらを実行するかを選択. <li>インライン展開は無効化している. </ul> <table width='80%' align='center' border='1'> <tr> <td width='30%'> <td>継続への純粋な変換 <td>最適化を行ったコード A <td>最適化を行ったコード B </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> <table width='80%' align='center' border='0'> <tr><td align='right'>単位 : 秒</tr> </table> <ul> <li>最適化の有無で比較すると, 最適化有りのほうが二倍以上速い. <li>Micro-C と比較すると, 最適化無しでは劣ることもあるが<font color='red'>最適化を有効化すると全ての場合で LLVM/clang の方が速くなる</font>. <li>GCC と比較すると, 速度面では劣るが, <font color='red'>最適化を無効化してもプログラムが正常に動作する</font>という点で優位. </ul> </div> <div class='slide'> <h2>まとめ</h2> <ul> <li>clang/LLVM 上で CbC コンパイラを実装した.</li> <br> <li>GCC 上に実装した CbC コンパイラとの違い. <ul> <li>nested function がないので環境付き継続を setjmp/longjmp を用いて実装した. <li>以後 nested function をサポートしないコンパイラ上に CbC コンパイラを実装する際, 同様にして実装できる. <li>GCC 版では最適化レベルが 2 以上でないと Tail Call Elimination を正しく強制できないが, LLVM/clang 版では最適化レベルが 0 でも正しく強制できる. </ul> </ul> </div> <div class='slide'> <h2>今後の課題</h2> <ul> <li>インラインアセンブリを用いた環境付き継続の実装. <ul> <li>アーキテクチャが限定されるが命令数がかなり少なくなる. <li>対応していないアーキテクチャ用に setjmp/longjmp を残す. </ul> <li>data segment の設計及び実装. <ul> <li>code segment の処理対象となるデータの単位. <li>並列実行時の待ちの記述が処理と別になる. <li>code segment 間のデータのやりとりの記述が容易になる. </ul> <li>CbC によるプログラムの作成 <ul> <li>並列プログラミングフレームワーク Cerium. </ul> </ul> </div> </div> <!-- presentation --> </body> </html>