Mercurial > hg > Papers > 2016 > kaito-master
view slide/blank.html @ 19:1d151a4d03f6
fix slide
author | Kaito Tokumori <e105711@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 15 Feb 2016 22:49:13 +0900 |
parents | bea98599d11f |
children |
line wrap: on
line source
<!DOCTYPE html> <html> <head> <meta charset='utf-8'> LLVM Clang 上の Continuation based C コンパイラの改良 <!-- 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" href="themes/blank/projection.css" media="screen,projection"> <link rel="stylesheet" href="themes/blank/screen.css" media="screen"> <link rel="stylesheet" href="themes/blank/print.css" media="print"> <link rel="stylesheet" href="blank.css" 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="fig/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">LLVM Clang 上の Continuation based C コンパイラの改良</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>code segment を用いるプログラミング言語 CbC</h2> <p>プログラムを記述する際, 本来行いたい処理の他にも記述しなけらばならない処理が存在する. malloc 等によるメモリの管理, スレッドの待ち合わせやネットワークの管理, エラーハンドリング等がこれにあたる.</p> <p>これらの計算は meta computation と呼ばれる.</p> <p>meta computation を柔軟に記述するために計算を細かく分割したい.</p> <p>code segment, data segment という単位を提案する. code segment が処理, data segment がデータの単位であり, meta code segment, meta data segment という meta computation の単位も存在する.</p> <p>Continuation based C (CbC) は code segment を用いるプログラミング言語である. </p> <p>本研究では, LLVM Clang をベースとしたコンパイラの改良を行い, 他の CbC コンパイラが出力するコードとの実行速度の比較, C と Scheme との実行速度の比較を行った</p> </div> <div class='slide'> <h2>Continuation based C(CbC)</h2> <table border='1' align='center' width='80%'> <tr><td width='50%'> <pre class='small_code'> __code cs0(int a, int b){ goto cs1(a+b); } __code cs1(int c){ goto cs2(c); } </pre> </td><td valign='top'> <ul> <li>__code は code segment であることを示す. <li>code segment から次の code segment への移動を軽量継続と呼ぶ. <li>軽量継続は goto のとなりに code segment 名と引数(出力)を書く. <li>軽量継続はフレームポインタ操作によるスタックの状態の保存を行わない. </ul> </td></tr> </table> <div align="center"><img src="fig/codesegment.svg" width="45%"></div> </div> <div class='slide'> <h2>CbC コンパイラ</h2> <ul> <li>Micro-C <li>GCC <li>LLVM clang </ul> </div> <div class='slide'> <h2>LLVM Clang</h2> <ul> <li>LLVM はコンパイラバックエンド <ul> <li>LLVM IR をターゲットのアセンブリコードに変換する <li>最適化が主な処理 </ul> <li>Clang はC/C++/Obj-C のフロントエンド <ul> <li>対象言語の構文解析を行う. <li>構文解析以後は LLVM を利用する. </ul> </ul> </div> <div class='slide'> <h2>LLVM Clang を用いる利点</h2> <ul> <li>ドキュメントが整っており開発者に優しい <li>複数のアーキテクチャに対応 <li>強力な最適化機構を持つ <li>OS X デフォルトのコンパイラでビルドが GCC と比べ容易 <li>GCC に比べ開発が易しい </ul> </div> <div class='slide'> <h2>LLVM と Clang の 基本構造</h2> <p> clang はソースコードを読み込むと Parser を用いて clangAST を生成する。CodeGenがASTを元にLLVM IRを生成し, それが LLVM の入力に対応する。 </p> <p> AST の各ノードが式や文, 値等に対応している. </p> <p> LLVM は中間表現の形を何度も変化させる。最適化をかけながら徐々にターゲットへの依存度が高くし、最終的にアセンブリコードや実行可能ファイルを出力する。最適化を含む全ての処理がパスによって行われる。 </p> <div align="center"><img src="fig/clang_llvm_structure.svg" width="45%"></div> </div> <div class='slide'> <h2>CbC 実装概要</h2> <ul> <li>code segment は __code 型の関数とする <li>軽量継続は tail call elimination の強制によって実現 <li>code segment はプロトタイプ宣言を必要としない <li>LLVM IR に変更を加えると後の最適化全てに手を加えなければならなくなる. そのため LLVM IR は一切変更しない. </ul> </div> <div class='slide'> <h2>プロトタイプ宣言の自動生成</h2> <ul> <li>CbC では code segment を大量に書くことになるがそのたびにプロトタイプ宣言を書くのは手間 <li>軽量継続を解析する時にプロトタイプ宣言の有無を調べ、存在しなかった場合に code segment の定義をもとに自動で生成する <li>自分で書いてもいい </ul> <table border='1' width='80%' align='center'> <tr> <td>元のコード <td>生成される AST に対応するコード </tr> <tr> <td><pre class='small_code'> __code code1(int a, int b) { : goto code2(a,b); } __code code2(int a, int b){ : } </pre> <td><pre class='small_code'> <font color='red'>__code code2(int a, int b);</font> __code code1(int a, int b) { : goto code2(a,b); } __code code2(int a, int b){ : } </pre> </tr> </table> </div> <div class='slide'> <h2>軽量継続のための構文</h2> <ul> <li>code segment の軽量継続ための goto syntax の追加 <li>通常の関数呼び出しと同じように実装し、 tail call elimination を強制することで軽量継続にする <li>AST生成の時点では関数呼び出しの直後に return 文を付加するだけ </ul> <table border='1' width='80%' align='center'> <tr> <td>元のコード <td>生成される AST に対応するコード </tr> <tr> <td><pre class='small_code'> __code code1() { : goto code2(); } </pre> <td><pre class='small_code'> void code1() { : code2(); <font color='red'>return;</font> } </pre> </tr> </table> </div> <div class='slide'> <h2>tail call elimination</h2> <table border='1' align='center' width='80%'> <tr><td width='50%'> <pre class='small_code'> int funcA(int a, int b){ : funcC(a-b); // NOT TAIL CALL : return funcB(a+b); // TAIL CALL } </pre> </td><td valign='top'> <ul> <li>tail call に対する最適化 <li>tail call は関数の最後に位置する関数呼び出し <li>通常関数は call 命令で呼び出されるが tail call elimination によって jmp 命令を用いるようになる </ul> </td></tr> </table> </div> <div class='slide'> <h2>tail call elimination の強制</h2> <ul> <li>常に TailCallElim パスを追加 <li>常に tailcallopt を有効化 <li>最適化レベルにかかわらず code segment に対して処理を行う <li>code segment の呼び出し規約に常に fastcc を付加 <li>全ての code segment の型は void (__code) で統一 </ul> </div> <div class='slide'> <h2>CbC から生成される LLVM IR とアセンブリ</h2> <ul> <li>軽量継続は tail, fastcc 付きの void 型の関数呼び出しに <li>LLVM IR への変更は一切ない </ul> <table border='1' width='80%' align='center'> <tr> <td>元のコード <td>LLVM IR <td>アセンブリ </tr> <tr> <td><pre class='small_code'> __code code1(int a, int b) { goto code2(a+b); } __code code2(int c){ : } </pre> <td><pre class='small_code'> define fastcc void @code1(i32 %a, i32 %b) #0 { entry: %add = add nsw i32 %a, %b <font color='red'>tail call fastcc void</font> @code2() ret void } define fastcc void @code2() #0 { : } </pre> <td><pre class='small_code'> _code1: ## @code1 .cfi_startproc ## BB#0: ## %entry pushq %rbp Ltmp0: .cfi_def_cfa_offset 16 Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbp Ltmp2: .cfi_def_cfa_register %rbp addl %esi, %edi popq %rbp jmp _code2 ## TAILCALL .cfi_endproc </pre> </tr> </table> <ul> <li>code segment code2 への軽量継続が jmp 命令で行われている <li>tail call elimination が正しく働いている <li>万が一 tail call elimination に失敗した場合にはエラーメッセージを出す <li>push, pop 命令によるフレームポインタの操作が残ってしまっている </ul> </div> <div class='slide'> <h2>フレームポインタとスタックポインタ操作の除去</h2> <ul> <li>omit leaf frame pointer という最適化を使用する. <li>omit leaf frame pointer は leaf function 呼び出し時のスタック操作に関わる最適化. <li>フレームポインタ, スタックポインタの操作を除去する. <li>leaf function とは 関数を呼び出さない関数, コールツリーの先端に位置する関数を指す. <li>全ての code segment は leaf function の条件を満たす. </ul> </div> <div class='slide'> <h2>フレームポインタとスタックポインタ操作の除去</h2> <table border='1' width='80%' align='center'> <tr> <td>元のコード <td>omit leaf frame pointer 無し <td>omit leaf frame pointer 有り </tr> <tr> <td><pre class='small_code'> __code code1(int a, int b) { goto code2(a+b); } __code code2(int c){ : } </pre> <td><pre class='small_code'> _code1: ## @code1 .cfi_startproc ## BB#0: ## %entry <span style='background-color:#ffff77'> pushq %rbp Ltmp0: .cfi_def_cfa_offset 16 Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbp</span> Ltmp2: .cfi_def_cfa_register %rbp addl %esi, %edi popq %rbp jmp _code2 ## TAILCALL .cfi_endproc </pre> <td><pre class='small_code'> _code1: ## @code1 .cfi_startproc ## BB#0: ## %entry pushq %rax Ltmp0: .cfi_def_cfa_offset 16 addl %esi, %edi popq %rax jmp _code2 ## TAILCALL .cfi_endproc </pre> </tr> </table> <ul> <li>スタックポインタとフレームポインタの操作が取り除くことができた <li>rax の値を push/pop しているがコレも取り除くことが出来る. </ul> </div> <div class='slide'> <h2>環境付き継続</h2> <ul> <li>code segment から 関数に戻るための継続 <li>環境とはスタックの状態を指す <li>関数呼び出しが行われる際, スタックポインタとフレームポインタの値を変更し, 呼び出す関数用にスタックを割く. その際元のフレームポインタとスタックポインタの値を保持しておき, return の際にもとに戻すことで前の関数に戻る. <li>軽量継続ではスタックポインタとフレームポインタの値は変更されず, 環境を保持しないので前の関数に戻ることが出来ない <li>継続前の環境を __environment, 継続前の環境に戻るための code segment を __return に保存し, これらを利用して関数に戻る </ul> </div> <div class='slide'> <h2>環境付き継続の例</h2> <table width='100%'> <tr><td valign='top'> <ul> <li>__return と __environment を用いる <li>__return は funcB へ戻るための code segment <li>__environment は funcB の環境 <li>この例では funcB が -1 でなく 1 を返す </ul> <td style="border: double;"> <pre class='small_code'><div class='highlight'>__code cs(__code(*ret)(int,void *),void *env){ goto ret(1,env); } int funcB(){ goto cs(<font color='red'>__return</font>, <font color='red'>__environment</font>); /* never reached */ return -1; } int funcA(){ int retval; retval = funcB(); printf("return = %d\n",retval); return 0; } </div></pre> </tr> </table> </div> <div class='slide'> <h2>環境付き継続実装方法</h2> <ul> <li>llvm builtin setjmp/longjmp (LLVM) <li>setjmp/longjmp (LLVM old) <li>nested function (GCC) <li>アセンブリコードを用いた直接的なポインタ, 値の操作 (Micro-C) </ul> </div> <div class='slide'> <h2>LLVM での環境付き継続の実装</h2> <table width='100%'> <tr><td valign='top'> <ul> <li>setjmp.h を自動で include <li>環境を保存するための構造体の生成 <li>C の関数で __builtin_setjmp を生成して環境を保存 <li>__builtin_longjmp を用いて元の環境に戻る code segment を生成 </ul> <td style="border: double;"> <pre class='small_code'><div class='highlight'> #include <setjmp.h> struct CbC_env { void *ret_p,*env; }; __code cs(int retval,__code(*ret)(int,void *),void *env){ goto ret(n, env); } __code func..ret0 (int retval, void* env){ *(int*)((struct CbC_env *)(env))->ret_p = retcal; __builtin_longjmp((int*)(((struct CbC_env *)env)->env),1); } int func (){ __code (*__return)(); struct CbC_env __environment; jmp_buf env; int retval; __environment.ret_p = &retval; __environment.env = &env; __return = func..ret0; if (__builtin_setjmp(__environment.env)){ return retval; } goto code1(30, __return, &__environment); return 0; } </div></pre> </tr> </table> </div> <div class='slide'> <h2>Gears OS</h2> <ul> <li>Gears OS は CbC で記述された並列フレームワーク <li>meta code segment, data segment が存在する <li>現在の CbC だけで記述するのは容易いとは言えない <li>記述を助ける機能, 新しい構文が必要 </ul> </div> <div class='slide'> <h2>CbC での meta computation</h2> <table width='100%'> <tr><td valign='top'> <ul> <li>code segment 間の遷移に meta code segment の処理が入る <li>右のコードは synchronizedQueue のコードの一部. queue の lock が meta computation. <li>data segment へは context からアクセスできる <li>context は data segment, code segmentを管理する meta data segment である. <li>code segment が必要とする data segment の取得は stub で行われる <div align="center"><img src="fig/metaCS.svg" width="90%"></div> </ul> <td style="border: double;"> <pre class='small_code'><div class='highlight'> __code meta_sender(struct Context* context, struct Queue* queue, enum Code next) { // lock pthread_mutex_lock(&queue->mutex); goto (context->code[next])(context); } __code sender(struct Context* context, struct Queue* queue) { goto meta_sender(context, queue, Put); } __code sender_stub(struct Context* context) { goto sender(context, &context->data[Queue]->queue); } __code code4(struct Context* context, long* count, struct Allocate* allocate, struct Element* element) { allocate->after_put = Code3; element->value = (*count)++; goto meta(context, Sender); } </div></pre> </tr> </table> </div> <div class='slide'> <h2>CbC で Gears OS を記述するには</h2> <ul> <li>context は meta data segment なので code segment からは見えない <li>code segment は 見かけ上 code segment に接続するが, 実際には meta code segment を経由する. <li>stub の自動生成 </ul> </div> <div class='slide'> <h2>Gears OS サポート</h2> <ul> <li>右のコードから左のコードが生成される <li>code segment が meta level のものに触れなくなる. <li>stub による data segment の取り出しが自動的に行われる. </ul> <table border='1' width='80%' align='center'> <tr> <td>生成されるコード <td>元のコード </tr> <tr> <td><pre class='small_code'> __code meta_sender(struct Context* context, struct Queue* queue, enum Code next) { // lock pthread_mutex_lock(&queue->mutex); goto (context->code[next])(context); } __code sender(struct Context* context, struct Queue* queue) { goto <font color='red'>meta_sender(context, queue, Put)</font>; } __code sender_stub(struct Context* context) { goto sender(context, &context->data[Queue]->queue); } __code code4(struct Context* context, long* count, struct Allocate* allocate, struct Element* element) { allocate->after_put = Code3; element->value = (*count)++; goto <font color='red'>meta(context, Sender)</font>; } </pre> <td><pre class='small_code'> __code meta_sender(struct Context* context, struct Queue* queue, enum Code next) { // lock pthread_mutex_lock(&queue->mutex); goto (context->code[next])(context); } __code sender(struct Queue* queue) { goto <font color='red'>put(queue)</font>; } __code code4(long* count, struct Allocate* allocate, struct Element* element) { allocate->after_put = Code3; element->value = (*count)++; goto <font color='red'>sender()</font>; } </pre> </tr> </table> </div> <div class='slide'> <h2>評価</h2> <ul> <li>環境付き継続の速度比較 <li>C と CbC との比較 </ul> </div> <div class='slide'> <h2>環境付き継続の速度比較</h2> <ul> <li>環境付き継続を大量に繰り返すプログラムで測定 <li>x86-64 Mac OS X <li>改良前の元の比べて 7 倍近い速度 <li>nested function より高速 <li>最適化を用いることで Micro-C と同等の速度を実現 </ul> <table border="1" align='center' width='40%'> <tbody> <tr> <td style="text-align: center;">コンパイラ名</td> <td style="text-align: center;">実行速度 (秒)</td> </tr> <tr> <td style="text-align: center;">LLVM Clang</td> <td style="text-align: right;">3.35</td> </tr> <tr> <td style="text-align: center;">LLVM Clang -O2</td> <td style="text-align: right;">1.30</td> </tr> <tr> <td style="text-align: center;">LLVM Clang (old)</td> <td style="text-align: right;">23.30</td> </tr> <tr> <td style="text-align: center;">Micro-C</td> <td style="text-align: right;">1.29</td> </tr> <tr> <td style="text-align: center;">GCC</td> <td style="text-align: right;">14.73</td> </tr> <tr> <td style="text-align: center;">GCC -O2</td> <td style="text-align: right;">12.96</td> </tr> </tbody> </table> </div> <div class='slide'> <h2>C, Scheme との速度比較</h2> <ul> <li>関数呼び出しまたは軽量継続, 継続を繰り返し行うプログラム. <li>x86-64 Mac OS X <li>Scheme は chicken コンパイラを使用 <li>関数呼び出しよりも軽量継続の方が高速である <li>スタック操作の処理が重たいことがわかる </ul> <table border="1" align='center' width='40%'> <tbody> <tr> <td style="text-align: center;">言語名</td> <td style="text-align: center;">実行速度 (秒)</td> </tr> <tr> <td style="text-align: center;">CbC</td> <td style="text-align: right;">3.10</td> </tr> <tr> <td style="text-align: center;">C</td> <td style="text-align: right;">4.85</td> </tr> <tr> <td style="text-align: center;">Scheme</td> <td style="text-align: right;">39.24</td> </tr> </tbody> </table> </div> <div class='slide'> <h2>まとめ</h2> <ul> <li>LLVM clang 上に実装した CbC コンパイラの改良を行った <li>omit leaf frame pointer の強制を行い、軽量継続時のフレームポインタ操作を除去した <li>code segment のプロトタイプ宣言を自動で行うようにした. <li>環境付き継続に builtin の関数を用いることで速度が元の 7 倍になった <li>Gears OS の記述をサポートするスクリプトの作成を行い, data segment, meta code segment を用いる CbC の記述量を減らした <li>C 言語との速度比較では軽量継続が関数呼び出しより高速であるという結果が得られた </ul> </div> <div class='slide'> <h2>今後の課題</h2> <ul> <li>環境付き継続においてアセンブリコードを直接生成するbuiltin 関数の実装 <li>meta code segment, meta data segment, data segment を用いる構文を LLVM Clang 上に実装 <li>data segment の signature を利用できる構文の設計・実装 <li>C に依存しない、code segment, data segment を用いる新しいプログラミング言語の開発 </ul> </div> <div class='slide'> <h2>LLVM と Clang のもつ 中間表現</h2> <ul> <li>clangAST : 抽象構文木。各ノードが式や文、値等に対応する。 <li>LLVM IR : LLVM の入力。LLVM 言語とも呼ばれる。メインの中間表現。 <li>SelectionDAG : 各ノードが命令や演算子に対応する有向非巡回グラフ。 <li>Machine Code : アセンブリコードとは異なる。レジスタの割当などはここで行う。 <li>MC Layer : 正確には中間表現を扱う層。一つの命令や演算子に対応するクラスを持ち、様々な出力を同様のAPIを用いて行える。 </ul> <div align="center"><img src="fig/clang_llvm_structure.svg" width="45%"></div> </div> <div class='slide'> <h2>LLVM IR</h2> <ul> <li>LLVM bitcode とも呼ばれる <li>人が読みやすいアセンブリ言語形式、ビットコード、実行時のメモリ上の形式の3つの形式を持つ <li>関数呼び出しに、呼び出し規約等のフラグをもつ </ul> <table width='100%'> <tr> <td style="border: double;"> <pre class='code'> define fastcc void @factorial(i32 %x) #0 { entry: tail call fastcc void @factorial0(i32 1, i32 %x) ret void } </pre> </td> </tr> </table> </div> <div class='slide'> <h2>__code 型</h2> <ul> <li>__code 型の値を返す関数というわけではなく code segment を示すフラグとする <li>code segment は戻り値を持たないので void 型と同様に実装する <li>LLVM IR として出力した際にも void になる </ul> </div> </div> <!-- presentation --> </bodypp> </html>