comparison Paper/anatofuz.tex @ 17:b2a795a294c4

update Moarvm bytecodes and perlcc
author Takahiro SHIMIZU <anatofuz@cr.ie.u-ryukyu.ac.jp>
date Wed, 07 Nov 2018 17:11:20 +0900
parents ed882dba29f6
children 3e4ffa621ae9
comparison
equal deleted inserted replaced
16:ed882dba29f6 17:b2a795a294c4
50 RakudoではPerl6自体をNQP(NotQuitPerl)と言われるPerl6のサブセットで記述し,NQPをVMが解釈するという処理流れになっている. 50 RakudoではPerl6自体をNQP(NotQuitPerl)と言われるPerl6のサブセットで記述し,NQPをVMが解釈するという処理流れになっている.
51 このVMは任意のVMが選択できるようになっており,現在はMoarVM,JavaVM,Javascriptが動作環境として選択可能である. 51 このVMは任意のVMが選択できるようになっており,現在はMoarVM,JavaVM,Javascriptが動作環境として選択可能である.
52 主に利用されているVMにCで書かれたMoarVMが存在する. 52 主に利用されているVMにCで書かれたMoarVMが存在する.
53 MoarVMはJITコンパイルなどをサポートしているが,全体的な起動時間及び処理速度がPerl5と比較し非常に低速である. 53 MoarVMはJITコンパイルなどをサポートしているが,全体的な起動時間及び処理速度がPerl5と比較し非常に低速である.
54 この問題を解決するためにContinuation based C (CbC)という言語を一部用いる. 54 この問題を解決するためにContinuation based C (CbC)という言語を一部用いる.
55 本論文ではMoarVMの一部をCbCを用いて書き直し,実際にどのようなパフォーマンスが出るかを報告する. 55 本論文ではCbCを用いてMoarVMの一部を書き換える事を検討し,得られた知見についてに述べる.
56 56
57 57
58 \end{abstract} 58 \end{abstract}
59 59
60 \begin{jkeyword} 60 \begin{jkeyword}
90 \subsection{CodeSegmentとDataSegment} 90 \subsection{CodeSegmentとDataSegment}
91 CbCではCの関数の代わりにCodeSegmentを導入する. 91 CbCではCの関数の代わりにCodeSegmentを導入する.
92 CodeSegmentはCの関数宣言の型名の代わりに\_\_codeと書くことで 宣言できる. 92 CodeSegmentはCの関数宣言の型名の代わりに\_\_codeと書くことで 宣言できる.
93 \_\_codeはCbCコンパイラの扱いはvoidと同じ型であるが,CbCプログラミングではCodeSegmentである事を示す識別子としての意味で利用する. 93 \_\_codeはCbCコンパイラの扱いはvoidと同じ型であるが,CbCプログラミングではCodeSegmentである事を示す識別子としての意味で利用する.
94 94
95 \subsection{軽量継続}
96 TBD
95 97
96 \subsection{現在の実装} 98 \subsection{現在の実装}
97 CbCは現在主要なCコンパイラであるgcc及びllvmをバックエンドとしたclang上の2種類の実装が存在する. 99 CbCは現在主要なCコンパイラであるgcc及びllvmをバックエンドとしたclang上の2種類の実装が存在する.
98 gccはバージョン9.0.0に,clangは7.0.0に対応している. 100 gccはバージョン9.0.0に,clangは7.0.0に対応している.
99 101
100 \subsection{CbCコンパイラのバグ} 102 \subsection{CbCコンパイラのバグ}
101 CbCコンパイラ自身はそれぞれ用意したテストスイートを通化するものの,MoarVMの様な巨大なプロジェクトのCSをコンパイルを実行する場合,予期せぬバグが発生した. 103 % 局所変数のポインタを握ったままgotoするとtail callにならない
102 主にCS間のgotoにおけるtail callフラグの除去や,DSとして渡している構造体の変数のアドレスがスタックポインタの値より上位に来てしまい,通常のCの関数をcallした際にローカル変数の領域がDSのアドレスの周辺を利用してしまう. 104 CbCコンパイラは現在も開発中であり幾つかのバグが発見されている.
103 その為DSの構造体の値が書き換わり,CからDSにreturnした際にDSの構造体が破壊されるバグである. 105 まずCodeSegment内で宣言した局所変数のポインタを別の変数などで確保した状態でgotoしてしまうとtail call最適化が切られる.
104 現状ではCbCコンパイラがプログラマの意図と反する挙動を取るためCbCコンパイラのバグを回避するプログラミングが要求されている. 106 これはただの関数呼び出しになってしまう為,直接的な被害はないもののCbCとしての利点が損なわれてしまう.
105 本来コンパイラ側のバグを回避するプログラミングをプログラマに要求するスタイルは好ましくない. 107 また本来は操作しないはずのスタック領域の操作が入ってしまうため,プログラマの意図と反したスタックポインタなのど操作が行われてしまいバグが発生する可能性が存在する.
106 従ってCbCコンパイラ自身の信頼性を向上させる事も今後の課題となっている. 108
107 \subsection{CbCとCの互換性} 109 \subsection{CbCとCの互換性}
108 CbCコンパイラは内部的に与えられているソースコードがCbCであるかどうかを判断する. 110 CbCコンパイラは内部的に与えられているソースコードがCbCであるかどうかを判断する.
109 この際にCodeSegmentを利用していない場合は通常のCプログラムとして動作する. 111 この際にCodeSegmentを利用していない場合は通常のCプログラムとして動作する.
110 これはCbCコンパイラがCコンパイラであるgccとllvm/clang上に実装している為である. 112 これはCbCコンパイラがCコンパイラであるgccとllvm/clang上に実装している為である.
111 その為MoarVMのビルドにおいてもCbCで書き換えたソースコードがあるターゲットと,手を加えていないオリジナルのターゲットの2種類を同一のCbCコンパイラでビルドする事が可能である. 113 その為MoarVMのビルドにおいてもCbCで書き換えたソースコードがあるターゲットと,手を加えていないオリジナルのターゲットの2種類を同一のCbCコンパイラでビルドする事が可能である.
112 114
113 115
114 \subsection{言語処理系におけるCbCの応用} 116 \subsection{言語処理系におけるCbCの応用}
115 CbCを言語処理系,特にスクリプト言語に応用すると幾つかの箇所に置いて利点があると推測される. 117 CbCを言語処理系,特にスクリプト言語に応用すると幾つかの箇所に置いて利点があると推測される.
116 CbCにおけるCSはコンパイラの基本ブロックに相当する. 118 CbCにおけるCSはコンパイラの基本ブロックに相当する.
119 その為従来のスクリプト言語では主にcase文で記述していた命令コードディスパッチの箇所をCodeSegmentの遷移として記述する事が可能である.
120 CbCは状態を単位として記述が可能であるため,命令コードなどにおける状態を利用するスクリプト言語の実装は応用例として適していると考えられる.
117 121
118 122
119 \section{Perl6の概要} 123 \section{Perl6の概要}
120 この章では現在までのPerl6の遍歴及びPerl6の言語的な特徴について記載する. 124 この章では現在までのPerl6の遍歴及びPerl6の言語的な特徴について記載する.
121 \subsection{Perl6の構想と初期の処理系} 125 \subsection{Perl6の構想と初期の処理系}
154 \subsection{NQP} 158 \subsection{NQP}
155 159
156 RakudoにおけるNQP\cite{nqp}は現在MoarVM,JVM上で動作し,MoarVMを一部利用することでNodeJSからも動作させる事が可能である. 160 RakudoにおけるNQP\cite{nqp}は現在MoarVM,JVM上で動作し,MoarVMを一部利用することでNodeJSからも動作させる事が可能である.
157 NQPはPerl6のサブセットであるため主な文法などはPerl6に準拠しているが幾つか異なる点が存在する. 161 NQPはPerl6のサブセットであるため主な文法などはPerl6に準拠しているが幾つか異なる点が存在する.
158 NQP自身はStage0と呼ばれる名前空間上のモジュールのみ動作環境のVMのバイトコードを必要とするが,それ以外はNQPで記述されておりBootstrappingされている言語である. 162 NQP自身はStage0と呼ばれる名前空間上のモジュールのみ動作環境のVMのバイトコードを必要とするが,それ以外はNQPで記述されておりBootstrappingされている言語である.
159 その為Rakudoを動作させる為にはMoarVMなどのVM,VMに対応させる様にビルドしたNQPがそれぞれ必要となる. 163 Perl6の一部はNQPを拡張したもので書かれている為,Rakudoを動作させる為にはMoarVMなどのVM,VMに対応させる様にビルドしたNQPがそれぞれ必要となる.
160 現在のNQPではMoarVM,JVMに対応するStage0はそれぞれMoarVMbytecode,jarファイルが用意されており,Javascriptではバイトコードの代わりにランタイム独自のModuleLoaderなどが設計されている. 164 現在のNQPではMoarVM,JVMに対応するStage0はそれぞれMoarVMbytecode,jarファイルが用意されており,Javascriptではバイトコードの代わりにランタイム独自のModuleLoaderなどが設計されている.
161 MoarVMのModuleLoaderはStage0あるMoarVMbytecodeで書かれた一連のファイルが該当する. 165 MoarVMのModuleLoaderはStage0あるMoarVMbytecodeで書かれた一連のファイルが該当する.
162 166
163 Stage0にあるファイルはNQPのコンパイラの構成要素そのものである. 167 Stage0にあるファイルをmoarvmに与えることでnqpのインタプリタが実行される様になっている.
168 これはStage0の一連のファイルはmoarvm bytecodeなどで記述されたNQPコンパイラのモジュールである為である.
164 NQPは6modelと呼ばれるオブジェクトモデルを採用としているが,これを構築する為に必要なNQPCORE,正規表現系のQRegex,MoarVMのModuleLoaderなどがmoarvmbytecodeで記述されている.これらMoarVMBytecodeの拡張子は.moarvmである. 169 NQPは6modelと呼ばれるオブジェクトモデルを採用としているが,これを構築する為に必要なNQPCORE,正規表現系のQRegex,MoarVMのModuleLoaderなどがmoarvmbytecodeで記述されている.これらMoarVMBytecodeの拡張子は.moarvmである.
165 MoarVMに対してStage0のディレクトリにライブラリパスを設定し,nqp.moarvmを実行させることでnqpの対話型環境が起動する. 170 MoarVMに対してStage0のディレクトリにライブラリパスを設定し,nqp.moarvmを実行させることでnqpの対話型環境が起動する.
166 171
167 実際にperl6を動かすためにはself buildしたNQPコンパイラが必要となる.その為にstage0を利用してStage1をビルドしNQPコンパイラを作成する. 172 実際にperl6を動かすためにはself buildしたNQPコンパイラが必要となる.その為にstage0を利用してStage1をビルドしNQPコンパイラを作成する.
168 173
174 Rakudo実装上におけるPerl6はRakudo Perl6と呼ばれているGitリポジトリで管理されているプログラムのことである. 179 Rakudo実装上におけるPerl6はRakudo Perl6と呼ばれているGitリポジトリで管理されているプログラムのことである.
175 前述した通りRakudo Perl6はPerl6のサブセットであるNQPを用いて記述されている. 180 前述した通りRakudo Perl6はPerl6のサブセットであるNQPを用いて記述されている.
176 従ってyaccやlexと言ったPerl5の文字解析,構文解析に利用していたプログラムは利用せず,NQP側で構文定義などを行っている. 181 従ってyaccやlexと言ったPerl5の文字解析,構文解析に利用していたプログラムは利用せず,NQP側で構文定義などを行っている.
177 NQPはNQP自身でBootstrappingされている為,Rakudo Perl6のbuild時にはNQPの実行環境として要したVM,それに基づいてbuildしたNQPがそれぞれ必要となる. 182 NQPはNQP自身でBootstrappingされている為,Rakudo Perl6のbuild時にはNQPの実行環境として要したVM,それに基づいてbuildしたNQPがそれぞれ必要となる.
178 183
179 言語的な特徴としてはPerl5とは違いアトミックに演算を行う事が可能なatom演算子や,すべてがオブジェクトであるオブジェクト指向言語としての進化も見られる. 184 言語的な特徴としてはPerl5とは違いアトミックに演算を行う事が可能な絵文字で実装されたatom演算子や,すべてがオブジェクトであるオブジェクト指向言語としての進化も見られる.
180 またPerl6は漸進的型付け言語である. 185 またPerl6は漸進的型付け言語である.
181 従来のPerlの様に変数に代入する対象の型や文脈に応じて型を変更する動的型言語としての側面を持ちつつ独自に定義した型を始めとする様々な型に静的に変数の型を設定する事が可能である. 186 従来のPerlの様に変数に代入する対象の型や文脈に応じて型を変更する動的型言語としての側面を持ちつつ独自に定義した型を始めとする様々な型に静的に変数の型を設定する事が可能である.
182 187
183 \subsection{現在のPerl6} 188 \subsection{現在のPerl6}
184 Perl6の言語仕様\cite{perl6design}とその時点での実装状況を纏めた公式ドキュメント\cite{perl6doc}は分離している. 189 Perl6の言語仕様\cite{perl6design}とその時点での実装状況を纏めた公式ドキュメント\cite{perl6doc}は分離している.
185 従来は言語仕様は自然言語の仕様書であったが,現在はテストスイートである「Roast\cite{roast}」そのものと定義されている. 190 従来は言語仕様は自然言語の仕様書であったが,現在はテストスイートである「Roast\cite{roast}」そのものと定義されている.
186 現在のPerl6の仕様はRoastを確認し,現在rakudo上に実装されている機能を見る場合は公式ドキュメントを確認する. 191 現在のPerl6の仕様を読む場合Roastを確認し,現在rakudo上に実装されている機能を見る場合は公式ドキュメントを確認する必要がある.
187 192
188 193
189 \section{CbCによるMoarVM} 194 \section{CbCによるMoarVM}
190 この章では改良を行ったPerl6処理系であるMoarVMについて述べる. 195 この章では改良を行ったPerl6処理系であるMoarVMについて述べる.
191 \subsection{方針} 196 \subsection{方針}
192 MoarVMそのものをCbCで書き換えることも考えられるがMoarVM自体既に巨大なプロジェクトである為すべてを書き換える事は困難である. 197 MoarVMそのものをCbCで書き換えることも考えられるがMoarVM自体既に巨大なプロジェクトである為すべてを書き換える事は困難である.
193 その為まず比較的CbCで書き換えることが容易な箇所を修正する. 198 その為まず比較的CbCで書き換えることが容易な箇所を修正する.
194 前章までに述べた通りCbCのCodeSegmentはコンパイラの基本ブロックに該当する. 199 前章までに述べた通りCbCのCodeSegmentはコンパイラの基本ブロックに該当する.
195 従ってMoarVMにおける基本ブロックの箇所をCodeSegmentに書き換える事が可能である. 200 従ってMoarVMにおける基本ブロックの箇所をCodeSegmentに書き換える事が可能である.
196 MoarVMにおける基本ブロックはインタプリタが実行するバイトコードごとの処理に該当する. 201 MoarVMにおける基本ブロックはインタプリタが実行するバイトコードごとの処理に該当する.
202
203 \subsection{MoarByteCodeのディスパッチ}
204 MoarVMのバイトコードインタプリタはsrc/core/interp.cで定義されている.
205 この中の関数MVM\_interp\_runで命令に応じた処理を実行する.
206 関数内では命令列が保存されているcur\_op,現在と次の命令を指し示すop,Threadの環境が保存されているThreadcontextなどの変数を利用する.
207 命令実行は大きく二種類の動作があり,Cのgotoが利用できる場合はMVM\_CGOTOフラグが立ちラベル遷移を利用する.
208 それ以外の場合は巨大なcase文として命令を実行する.
209
210 ラベル遷移を利用する場合はラベルテーブルにアクセスし,テーブルに登録されているアドレスを取得し,NEXTで遷移する.
211 このラベルテーブルの中身はラベルが変換されたアドレスであるため,Cレベルでのデバッグ時にはアドレスと実際に呼ばれる箇所を確認する事に手間がかかる.
212 巨大なcase文として実行された場合,実行時間が遅いだけでなく,ラベル遷移と共存させて記述を行っている為Cのソースコードにおける可読性も低下する.
213
214 CbCMoarVMではこの問題を解決するために,それぞれの命令に対応するCodeSegmentを作成し,これらへのポインタを持つCbCのCodeSegmentのテーブルを作成した.
215
216 \subsection{命令実行箇所のCodeSegmentへの変換}
217 ラベルテーブルやcase文のswitch相当の命令実行箇所をCbCに変換し,CodeSegmentの遷移として利用する.
218 interp.cは?に示す様なスタイルで記述されている.
219 OP(.*)の.*に該当する箇所はバイトコードの名前である.通常このブロックにはLABELから遷移する為,バイトコードの名前はLABELSの配列の添字に変換されている.
220 そのため対象となるCodeSegmentをLABLESの並びと対応させ,配列CODESに設定すればCodeSegmentの名前は問わない.
221 今回はCodeSegmentである事を示す為にsuffixとしてcbc\_をつける.
222
223 \subsection{MoarVMのBytecodeのデバッグ}
224 moarに対してmoarvm bytecodeをdumpオプションを付けて読み込ませるとmoarvmのbytecodeがアセンブラの様に出力される.
225 しかしこれはmoarvmが実行したbytecodeのトレースではなくmoarvm bytecodeを変換したものに過ぎない.
226 また,明らかに異なる挙動を示す両者のmoarを利用しても同じ結果が返ってきてしまう.
227 そのため今回のMoarVMBytecodeインタプリタの実装のデバッグにはこの方法は適さない.
228 従って実際に実行した命令を確認するにはgdbなどのCデバッガを利用してMoarVMを直接トレースする必要がある.
229
230 \subsection{MoarVMの並列デバッグ手法}
231 しかしMoarVMが実行する命令は膨大な数がある.
232 その為gdbでMoarVMをCbCとオリジナル版での並列デバッグを人間が同時に行うことは困難である.
233 Perlなどのスクリプトを用いて自動的に解析したいため,ログを残す為にscriptコマンドを実行した状態でgdbを起動する.
234 CbC側はcbc\_nextにbreak pointを設定し,オリジナル側は次のオペコードの設定のマクロにダミーの関数を呼び出すように修正し,そこにbreak pointを設定する.
235 CbC側ではCodeSegmentの名前を直接確認できるが,オリジナル版はLABLEの配列の添え字から自分でどのオペコードに対応しているかを探す必要がある.
236 CbCとオリジナルのCODES,LABELの添字は対応している為,ログの解析を行う際はそれぞれの添字を抽出し違いが発生している箇所を探索する.
237
238
197 \subsection{CbCコンパイラによるバグ} 239 \subsection{CbCコンパイラによるバグ}
240 現在までのCbCは複数個の入出力をCodeSegmentに与えるユースケースで利用していた.
241 CbCコンパイラ自身はそれぞれ用意したテストスイートを通化するものの,MoarVMの様な巨大なプロジェクトのCSをコンパイルを実行する場合,予期せぬバグが発生した.
242 主にCS間のgotoにおけるtail callフラグの除去や,DSとして渡している構造体の変数のアドレスがスタックポインタの値より上位に来てしまい,通常のCの関数をcallした際にローカル変数の領域がDSのアドレスの周辺を利用してしまう.
243 その為DSの構造体の値が書き換わり,CからDSにreturnした際にDSの構造体が破壊されるバグである.
244 現状ではCbCコンパイラがプログラマの意図と反する挙動を取るためCbCコンパイラのバグを回避するプログラミングが要求されている.
245 本来コンパイラ側のバグを回避するプログラミングをプログラマに要求するスタイルは好ましくない.
246 従ってCbCコンパイラ自身の信頼性を向上させる事も今後の課題となっている.
198 247
199 \subsection{Threaded Code} 248 \subsection{Threaded Code}
200 CbCはCodeSegmentで末尾最適化(Tail call optimization)を行う. 249 CbCはCodeSegmentで末尾最適化(Tail call optimization)を行う.
201 これはCodeSegmentは必ず関数呼び出しではなくgotoで次の状態に遷移する為にスタック領域の操作が必要とならない為である. 250 これはCodeSegmentは必ず関数呼び出しではなくgotoで次の状態に遷移する為にスタック領域の操作が必要とならない為である.
202 現在のCbCコンパイラの実装ではCodeSegmentからCの関数に戻る場合は末尾最適化を切り,CodeSegment間の遷移では末尾最適化が行われる. 251 現在のCbCコンパイラの実装ではCodeSegmentからCの関数に戻る場合は末尾最適化を切り,CodeSegment間の遷移では末尾最適化が行われる.
205 現在のCbCMoarVMは次の命令セットのディスパッチをcbc\_nextというCodeSegmentで処理している. 254 現在のCbCMoarVMは次の命令セットのディスパッチをcbc\_nextというCodeSegmentで処理している.
206 これは元のMoarVMの命令ディスパッチで行われる現在のオペコードを示すcur\_opと命令列opの操作及び次のラベルに遷移するマクロに該当する. 255 これは元のMoarVMの命令ディスパッチで行われる現在のオペコードを示すcur\_opと命令列opの操作及び次のラベルに遷移するマクロに該当する.
207 CbCMoarVMではラベルに対しての遷移の代わりにMoarVMの命令のCodeSegmentの集合体である配列CODESにアクセスし,その要素であるCodeSegmentに対して遷移する形を取っている. 256 CbCMoarVMではラベルに対しての遷移の代わりにMoarVMの命令のCodeSegmentの集合体である配列CODESにアクセスし,その要素であるCodeSegmentに対して遷移する形を取っている.
208 この一連の処理がオーバーヘッドになる為,今後はcbc\_fixt\_nextというCodeSegmentを導入し直接次の命令に該当するCodeSegmentへgotoする様に実装する予定である. 257 この一連の処理がオーバーヘッドになる為,今後はcbc\_fixt\_nextというCodeSegmentを導入し直接次の命令に該当するCodeSegmentへgotoする様に実装する予定である.
209 258
259 \subsubsection{perlcc}
260 Perl5においてはperlccというモジュールが開発されている.
261 これはPerl5内部で利用しているPerlバイトコードを,PerlのC APIであるXS言語の様なCのソースファイルに埋め込み,それをCコンパイルでコンパイルするというものである.
262 perlccを利用することでPerlインタプリタが無い状況でも可動するバイナリファイルを作成する事が可能である.
263 しかしPerlccはPerlスクリプトが複雑になるほど正確にCに移植を行う事が出来ず,現在ではPerlのコアモジュールから外されている.
264 PerlccはPerlのバイトコードをCに変換しただけであり,Cで実装されているPerl経由で実行した場合と処理速度はほぼ変わらない.
265 またPerlccで生成されたCのソースコードは難解であり,これをデバッグするのが困難でもある.
266 MoarVMでthreaded codeを実現出来た場合,その箇所のみCbCプログラムとして切り出す事が可能である為perlccと似たツールを作成することも可能である.
267 この場合,Perl6を通常動かした際とは異なりバイトコードインタプリタに到達する前の処理が無くなる為多少の高速化が望めると推測できる.
210 268
211 \section{CbCを用いる事についての評価} 269 \section{CbCを用いる事についての評価}
212 Perl6処理系はまずPerl6の豊富な文法に対応する物を作成せねばならず,類似する他のプログラミング言語処理系と比較してもより複雑となっている. 270 Perl6処理系はまずPerl6の豊富な文法に対応する物を作成せねばならず,類似する他のプログラミング言語処理系と比較してもより複雑となっている.
213 実際にPerl5を始めとしたスクリプト言語とPerl6がどのような処理時間の違いが見られるかを実測する. 271 実際にPerl5を始めとしたスクリプト言語とPerl6がどのような処理時間の違いが見られるかを実測する.
214 272