comparison Paper/anatofuz.tex @ 81:1b1089b0aef5

update
author Takahiro SHIMIZU <anatofuz@cr.ie.u-ryukyu.ac.jp>
date Fri, 30 Nov 2018 19:22:26 +0900
parents dec42afa6aaa
children 0c012cbdf385
comparison
equal deleted inserted replaced
80:dec42afa6aaa 81:1b1089b0aef5
44 \author{河野 真治}{Shinji KONO}{IERYUKYU}[kono@ie.u-ryukyu.ac.jp] 44 \author{河野 真治}{Shinji KONO}{IERYUKYU}[kono@ie.u-ryukyu.ac.jp]
45 45
46 %概要 46 %概要
47 \begin{abstract} 47 \begin{abstract}
48 スクリプト言語であるPerl5の後継言語としてPerl6が現在開発されている. 48 スクリプト言語であるPerl5の後継言語としてPerl6が現在開発されている.
49 Perl6は設計と実装が区分されており様々な処理系が開発されている.現在主流なPerl6はRakudoと言われるプロジェクトである. 49 Perl6は設計と実装が区分されており様々な処理系が開発されている.
50 現在主流なPerl6はRakudoと言われるプロジェクトである.
50 RakudoではPerl6自体をNQP(NotQuitPerl)と言われるPerl6のサブセットで記述し, NQPをVMが解釈するという処理の流れになっている. 51 RakudoではPerl6自体をNQP(NotQuitPerl)と言われるPerl6のサブセットで記述し, NQPをVMが解釈するという処理の流れになっている.
51 このVMは任意のVMが選択できるようになっており,主に利用されているVMにCで書かれたMoarVMが存在する. 52 このVMは任意のVMが選択できるようになっており,主に利用されているVMにCで書かれたMoarVMが存在する.
52 MoarVMはJITコンパイルなどをサポートしているが, 全体的な起動時間及び処理速度がPerl5と比較し非常に低速である. 53 MoarVMはJITコンパイルなどをサポートしているが, 全体的な起動時間及び処理速度がPerl5と比較し非常に低速である.
53 この問題を解決するためにContinuation based C (CbC)という言語を一部用いてMoarVMの書き換えを行う. 54 この問題を解決するためにContinuation based C (CbC)という言語を一部用いてMoarVMの書き換えを行う.
54 CbCはCよりも細かな単位で記述が可能である為, 言語処理系の実装に適していると考えられる. 55 CbCはCよりも細かな単位で記述が可能である為, 言語処理系の実装に適していると考えられる.
85 86
86 \section{CbC} 87 \section{CbC}
87 \subsection{CbCの概要} 88 \subsection{CbCの概要}
88 CbCは当研究室で開発しているプログラミング言語である. 89 CbCは当研究室で開発しているプログラミング言語である.
89 Cレベルでのプログラミングを行う場合, 本来プログラマが行いたい処理の他にmallocなどを利用したメモリのアロケートやエラーハンドリングなどを記述する必要がある. 90 Cレベルでのプログラミングを行う場合, 本来プログラマが行いたい処理の他にmallocなどを利用したメモリのアロケートやエラーハンドリングなどを記述する必要がある.
90 これらの処理をmeta computationと呼ぶ.これらmeta computationと通常の処理を分離することでバグの原因がmeta computation側にあるか処理側にあるかの分離などが可能となる. 91 これらの処理をmeta computationと呼ぶ.
92 これらmeta computationと通常の処理を分離することでバグの原因がmeta computation側にあるか処理側にあるかの分離などが可能となる.
91 しかしC言語などを用いたプログラミングでmeta computationの分離を行おうとすると, それぞれ事細かに関数やクラスを分割せねばならず容易ではない. 93 しかしC言語などを用いたプログラミングでmeta computationの分離を行おうとすると, それぞれ事細かに関数やクラスを分割せねばならず容易ではない.
92 CbCでは関数よりmeta computationを細かく記述する為にCodeGearという単位を導入した. 94 CbCでは関数よりmeta computationを細かく記述する為にCodeGearという単位を導入した.
93 またCodeGearの実行に必要なデータをDataGearという単位で受け渡す. 95 またCodeGearの実行に必要なデータをDataGearという単位で受け渡す.
94 CbCではCodeGear, DataGearを基本単位として記述するプログラミングスタイルを取る. 96 CbCではCodeGear, DataGearを基本単位として記述するプログラミングスタイルを取る.
95 97
117 119
118 \subsection{現在の実装} 120 \subsection{現在の実装}
119 CbCは現在主要なCコンパイラであるgcc及びllvmをバックエンドとしたclang上の2種類の実装が存在する. 121 CbCは現在主要なCコンパイラであるgcc及びllvmをバックエンドとしたclang上の2種類の実装が存在する.
120 gccはバージョン9.0.0に, clangは7.0.0に対応している. 122 gccはバージョン9.0.0に, clangは7.0.0に対応している.
121 123
122 \subsection{CbCコンパイラのバグ} 124 %\subsection{CbCコンパイラのバグ}
123 % 局所変数のポインタを握ったままgotoするとtail callにならない 125 %% 局所変数のポインタを握ったままgotoするとtail callにならない
124 CbCコンパイラは現在も開発中であり幾つかのバグが発見されている. 126 %CbCコンパイラは現在も開発中であり幾つかのバグが発見されている.
125 まずCodeGear内で宣言した局所変数のポインタを大域変数の配列などに保存した状態でgotoしてしまうとtail call最適化が無効となる. 127 %まずCodeGear内で宣言した局所変数のポインタを大域変数の配列などに保存した状態でgotoしてしまうとtail call最適化が無効となる.
126 これはただの関数呼び出しになってしまう為, 直接的な被害はないもののCbCとしての利点が損なわれてしまう. 128 %これはただの関数呼び出しになってしまう為, 直接的な被害はないもののCbCとしての利点が損なわれてしまう.
127 %また本来は操作しないはずのスタック領域の操作が入ってしまうため, プログラマの意図と反したスタックポインタなのど操作が行われてしまいバグが発生する可能性が存在する. 129 %%また本来は操作しないはずのスタック領域の操作が入ってしまうため, プログラマの意図と反したスタックポインタなのど操作が行われてしまいバグが発生する可能性が存在する.
128 また, CbCの挙動としてCodeGearへの遷移時には軽量継続を行う為スタック領域の操作は行われないはずである. 130 %また, CbCの挙動としてCodeGearへの遷移時には軽量継続を行う為スタック領域の操作は行われないはずである.
129 しかし, 現状は配列にCodeGearのアドレスを代入し, 間接的に軽量継続を行おうとすると, スタック領域の操作が通常の関数呼び出しの様に行われてしまう. 131 %しかし, 現状は配列にCodeGearのアドレスを代入し, 間接的に軽量継続を行おうとすると, スタック領域の操作が通常の関数呼び出しの様に行われてしまう.
130 %これは一部CbCコンパイラが利用しているgcc側のref-zoneという機能が影響していたが 132 %%これは一部CbCコンパイラが利用しているgcc側のref-zoneという機能が影響していたが
131 133 %
132 \subsection{CbCとCの互換性} 134 \subsection{CbCとCの互換性}
133 CbCコンパイラはコンパイル対象のソースコードがCbCであるかどうかを判断する. 135 CbCコンパイラはコンパイル対象のソースコードがCbCであるかどうかを判断する.
134 この際にCodeGearを利用していない場合は通常のCプログラムとして動作する. 136 この際にCodeGearを利用していない場合は通常のCプログラムとして動作する.
135 その為今回検証するMoarVMのビルドにおいてもCbCで書き換えたソースコードがあるMoarVMと, 手を加えていないオリジナルのMoarVMの2種類を同一のCbCコンパイラでビルドする事が可能である. 137 その為今回検証するMoarVMのビルドにおいてもCbCで書き換えたソースコードがあるMoarVMと, 手を加えていないオリジナルのMoarVMの2種類を同一のCbCコンパイラでビルドする事が可能である.
136 138
137 またCからCbCへの遷移時に, 再びCの関数に戻るように実装したい場合がある. 139 またCからCbCへの遷移時に, 再びCの関数に戻るように実装したい場合がある.
138 その際は環境付きgotoと呼ばれる手法を取る.これは\_CbC\_return及び\_CbC\_environmentという変数を使用する. 140 その際は環境付きgotoと呼ばれる手法を取る.
141 これは\_CbC\_return及び\_CbC\_environmentという変数を使用する.
139 この変数は\_CbC\_returnが元の環境に戻る際に利用するCodeGearを指し, \_CbC\_environmentは復帰時に戻す元の環境である. 142 この変数は\_CbC\_returnが元の環境に戻る際に利用するCodeGearを指し, \_CbC\_environmentは復帰時に戻す元の環境である.
140 復帰する場合, 呼び出した位置には帰らず, 呼び出した関数の終了する位置に帰る. 143 復帰する場合, 呼び出した位置には帰らず, 呼び出した関数の終了する位置に帰る.
141 \lstinputlisting[label=cbcreturn, caption=環境付き継続の例]{./src/return.cbc} 144 \lstinputlisting[label=cbcreturn, caption=環境付き継続の例]{./src/return.cbc}
142 Code\ref{cbcreturn}に示す例ではc\_funcから環境付き継続でcgに継続している. 145 Code\ref{cbcreturn}に示す例ではc\_funcから環境付き継続でcgに継続している.
143 通常c\_funcの返り値は-1であるが, cgから環境付き継続でmainに帰る為にcgから渡される1がtestの値となる. 146 通常c\_funcの返り値は-1であるが, cgから環境付き継続でmainに帰る為にcgから渡される1がtestの値となる.
144 147
145 148
146 \subsection{言語処理系におけるCbCの応用} 149 \subsection{言語処理系におけるCbCの応用}
147 CbCを言語処理系, 特にスクリプト言語に応用すると幾つかの箇所に置いて利点があると推測される. 150 CbCを言語処理系, 特にスクリプト言語に応用すると幾つかの箇所に置いて利点がある.
148 CbCにおけるCodeGearはコンパイラの基本ブロックに相当する. 151 CodeGearはコンパイラの基本ブロックに相当する.
149 その為従来のスクリプト言語では主にcase文で記述していた命令コードディスパッチの箇所をCodeGearの遷移として記述する事が可能である. 152 その為従来のスクリプト言語では主にcase文で記述していた命令コードディスパッチの箇所をCodeGearの遷移として記述する事が可能である.
150 通常の言語処理系では命令コードディスパッチ部分は巨大なcase文となり, この部分を実装したCファイルが巨大化してしまう. 153 通常の言語処理系では命令コードディスパッチ部分は巨大なcase文となり, この部分を実装したCファイルが巨大化してしまう.
151 CodeGearを導入することで巨大なcase文をCodeGearとして分割する事が可能となり, 処理のモジュール化が可能となる. 154 CodeGearを導入することで巨大なcase文をCodeGearとして分割する事が可能となり, 処理のモジュール化が可能となる.
152 また, CodeGearとCodeGear間の遷移は軽量継続で行われる為, レジスタレベルでの最適化も可能となる. 155 また, CodeGearとCodeGear間の遷移は軽量継続で行われる為, レジスタレベルでの最適化も可能となる.
153 156
161 Perl6は2002年にLarryWallがPerlを置き換える言語として設計を開始した. 164 Perl6は2002年にLarryWallがPerlを置き換える言語として設計を開始した.
162 Perl5の言語的な問題点であるオブジェクト指向機能の強力なサポートなどを取り入れた言語として設計された. 165 Perl5の言語的な問題点であるオブジェクト指向機能の強力なサポートなどを取り入れた言語として設計された.
163 Perl5は設計と実装が同一であり, Larryらによって書かれたC実装のみだった. 166 Perl5は設計と実装が同一であり, Larryらによって書かれたC実装のみだった.
164 Perl6は設計と実装が分離している. 167 Perl6は設計と実装が分離している.
165 言語的な特徴としては, 独自にPerl6の文法を拡張可能なGrammar, Perl5と比較した場合のオブジェクト指向言語としての進化も見られる. 168 言語的な特徴としては, 独自にPerl6の文法を拡張可能なGrammar, Perl5と比較した場合のオブジェクト指向言語としての進化も見られる.
166 またPerl6は漸進的型付け言語である. 169 またPerl6は漸進的型付け言語である.
167 従来のPerlの様に変数に代入する対象の型や, 文脈に応じて型を変更する動的型言語としての側面を持ちつつ, 独自に定義した型を始めとする様々な型に, 静的に変数の型を設定する事が可能である. 170 従来のPerlの様に変数に代入する対象の型や, 文脈に応じて型を変更する動的型言語としての側面を持ちつつ, 独自に定義した型を始めとする様々な型に, 静的に変数の型を設定する事が可能である.
168 171
169 Perl6は言語仕様及び処理実装がPerl5と大幅に異なっており, 言語的な互換性が存在しない. 172 Perl6は言語仕様及び処理実装がPerl5と大幅に異なっており, 言語的な互換性が存在しない.
170 従って現在ではPerl6とPerl5は別言語としての開発方針になっている. 173 従って現在ではPerl6とPerl5は別言語としての開発方針になっている.
171 Perl6は現在有力な処理系であるRakudoから名前を取りRakuという別名がつけられている. 174 Perl6は現在有力な処理系であるRakudoから名前を取りRakuという別名がつけられている.
172 175
173 \subsection{Rakudo以前の処理系} 176 %\subsection{Rakudo以前の処理系}
174 Perl6の実装は様々なものが実装された. 177 %Perl6の実装は様々なものが実装された.
175 まず2005年に唐鳳によってHaskellで実装されたPugs\cite{pugs}が登場した. 178 %まず2005年に唐鳳によってHaskellで実装されたPugs\cite{pugs}が登場した.
176 179 %
177 その後Pythonとの共同動作環境としてParrot\cite{parrot}が実装された. 180 %その後Pythonとの共同動作環境としてParrot\cite{parrot}が実装された.
178 ParrotはPASMと呼ばれるバイトコードを解釈可能なレジスタマシンである. 181 %ParrotはPASMと呼ばれるバイトコードを解釈可能なレジスタマシンである.
179 ParrotでのPerl6の実装はNQP(NotQuitPerl)と呼ばれるPerl6のサブセットでPerl6を記述するというアイディアの基実装された. 182 %ParrotでのPerl6の実装はNQP(NotQuitPerl)と呼ばれるPerl6のサブセットでPerl6を記述するというアイディアの基実装された.
180 ParrotVMは2006年のversion8.1.0が最後のリリースである. 183 %ParrotVMは2006年のversion8.1.0が最後のリリースである.
181 こちらもPugsと同様に現在のPerl6プロジェクトでは歴史的な実装とされている. 184 %こちらもPugsと同様に現在のPerl6プロジェクトでは歴史的な実装とされている.
182 185
183 186
184 \subsection{Rakudo} 187 \subsection{Rakudo}
185 188
186 RakudoとはParrotで構想に上がったNQP, NQPに基づくPerl6を基にしたプロジェクトである. 189
190 RakudoとはNQP, NQPに基づくPerl6を基にしたプロジェクトである.
191 NQPとは, 以前のPerl6処理系であるParrot\cite{parrot}で, 構想に上がったPerl6のサブセットである.
187 RakudoがPerl6のコンパイラかつインタプリタであると考えても良い. 192 RakudoがPerl6のコンパイラかつインタプリタであると考えても良い.
188 Rakudoは図\ref{fig:perl6construction}に示す構成になっている. 193 Rakudoは図\ref{fig:perl6construction}に示す構成になっている.
189 Rakudoにおけるコンパイラとは厳密には2種類存在する. 194 Rakudoにおけるコンパイラとは厳密には2種類存在する.
190 まず第1のものがPerl6, もしくはNQPをMoarVM, JVMのバイトコードに変換するNQPコンパイラである. 195 まず第1のものがPerl6, もしくはNQPをMoarVM, JVMのバイトコードに変換するNQPコンパイラである.
191 次にそのNQPが出力したバイトコードをネイティブコードに変換するVMの2種類である. 196 次にそのNQPが出力したバイトコードをネイティブコードに変換するVMの2種類である.
192 このVMは現在MoarVM, JavaVM, JavaScriptを選択可能である. 197 このVMは現在MoarVM, JavaVMを選択可能である.
193 Rakudo及びNQP projectではこのNQPコンパイラの部分をフロントエンド, VMの部分をバックエンド\cite{rani1}と呼称している. 198 Rakudo及びNQP projectではこのNQPコンパイラの部分をフロントエンド, VMの部分をバックエンド\cite{rani1}と呼称している.
194 NQPで主に書かれ, MoarVMなどNQPが動作する環境で動くPerl6のことをRakudoと呼ぶ. 199 NQPで主に書かれ, MoarVMなどNQPが動作する環境で動くPerl6のことをRakudoと呼ぶ.
195 Perl6はNQP以外にもNQPを拡張したPerl6自身で書かれている箇所が存在し, これはNQPコンパイラ側でMoarVMが解釈可能な形へ変換を行う. 200 Perl6はNQP以外にもNQPを拡張したPerl6自身で書かれている箇所が存在し, これはNQPコンパイラ側でMoarVMが解釈可能な形へ変換を行う.
196 201
197 \begin{figure}[ht] 202 \begin{figure}[ht]
200 \end{center} 205 \end{center}
201 \caption{Rakudoの構成} 206 \caption{Rakudoの構成}
202 \label{fig:perl6construction} 207 \label{fig:perl6construction}
203 \end{figure} 208 \end{figure}
204 209
210 \subsection{MoarVM}
211 MoarVMとはRakudoで主に開発が進められているVMである.
212 Perl6及びNQPの専用処理系であり, レジスタマシンである.
213 MoarVMはNQPから与えられたMoarVMのバイトコードを実行する.
214
215 MoarVMのバイトコードインタプリタはsrc/core/interp.cで定義されている.
216 この中の関数MVM\_interp\_runで命令に応じた処理を実行する.
217 関数内では命令列が保存されているcur\_op, 現在と次の命令を指し示すop, Threadの環境が保存されているThreadcontextなどの変数を利用する.
218 命令実行は大きく二種類の動作があり, Cのgotoが利用できる場合はCode\ref{orig_macro}に示すMVM\_CGOTOフラグが立ちラベル遷移を利用する.
219 goto文が利用できない場合は巨大なcase文として命令を実行する.
220
221 ラベル遷移を利用する場合はCode\ref{oplabelsh}に示すラベルテーブルLABELSにアクセスし, テーブルに登録されているアドレスを取得し, マクロNEXTで遷移する.
222 Code\ref{dispatch_c}に示すno\_opは何もせず次の命令に移動する為, goto NEXT;のみ記述されている.
223
224 \lstinputlisting[label=orig_macro, caption=interp.cのマクロ部分]{./src/orig_macro.c}
225 \lstinputlisting[label=oplabelsh, caption=ラベルテーブルの一部分]{./src/oplabels.h}
226
227 \lstinputlisting[label=dispatch_c, caption=オリジナル版MoarVMのバイトコードディスパッチ]{./src/dispatch.c}
228
229 %interp.cでは命令コードのディスパッチはマクロを利用したcur\_opの計算及びラベルの遷移, もしくはマクロDISPATCHが展開するswitch文で行われていた.
230 この為MoarVM内の命令コードに対応する処理は, 命令ディスパッチが書かれているCソースファイルの, 特定の場所のみに記述せざるを得ない.
231 その為命令コードのモジュール化などが行えず, 1ファイル辺りの記述量が膨大になってしまう.
232 また各命令コードに対応する処理は, ラベルジャンプもしくはswitch文に展開されてしまう為, Threaded Codeの実装を考えた場合, 大幅なコードの改修が要求される.
233 デバッグ時には, Cレベルでのデバッグ時にはアドレスと実際に呼ばれる箇所を確認する事に手間がかかる.
234
205 \subsection{NQP} 235 \subsection{NQP}
206 236
207 RakudoにおけるNQP\cite{nqp}は現在MoarVM, JVM上で動作し, MoarVMを一部利用することでNodeJSからも動作させる事が可能である. 237 RakudoにおけるNQP\cite{nqp}は現在MoarVM, JVM上で動作する.
208 NQPはPerl6のサブセットであるため, 主な文法などはPerl6に準拠しているが幾つか異なる点が存在する. 238 NQPはPerl6のサブセットであるため, 主な文法などはPerl6に準拠しているが幾つか異なる点が存在する.
209 NQPは最終的にはNQP自身でブートストラップする言語であるが, ビルドの最初にはすでに書かれたMoarVMのバイトコードを必要とする. 239 NQPは最終的にはNQP自身でブートストラップする言語であるが, ビルドの最初にはすでに書かれたMoarVMのバイトコードを必要とする.
210 このMoarVMのバイトコードの状態をStage0と言う.%い, バイトコードが付随するソースディレクトリ内のディレクトリ名として設定されている. 240 このMoarVMのバイトコードの状態をStage0と言う.%い, バイトコードが付随するソースディレクトリ内のディレクトリ名として設定されている.
211 Perl6の一部はNQPを拡張したもので書かれている為, Rakudoを動作させる為にはMoarVMなどのVM, VMに対応させる様にビルドしたNQPがそれぞれ必要となる. 241 Perl6の一部はNQPを拡張したもので書かれている為, Rakudoを動作させる為にはMoarVMなどのVM, VMに対応させる様にビルドしたNQPがそれぞれ必要となる.
212 現在のNQPではMoarVM, JVMに対応するStage0はそれぞれMoarVMのバイトコード, jarファイルが用意されており, JavaScriptではバイトコードの代わりにランタイム独自のModuleLoaderなどが設計されている. 242 現在のNQPではMoarVM, JVMに対応するStage0はそれぞれMoarVMのバイトコード, jarファイルが用意されている.
213 MoarVMのModuleLoaderはStage0にあるMoarVMのバイトコードで書かれた一連のファイルが該当する. 243 MoarVMのModuleLoaderはStage0にあるMoarVMのバイトコードで書かれた一連のファイルが該当する.
214 244
215 245
216 Stage0にあるファイルをMoarVMに与えることで, NQPのインタプリタが実行される様になっている. 246 Stage0にあるファイルをMoarVMに与えることで, NQPのインタプリタが実行される様になっている.
217 これはStage0の一連のファイルは, MoarVMのバイトコードなどで記述されたNQPコンパイラのモジュールである為である. 247 これはStage0の一連のファイルは, MoarVMのバイトコードなどで記述されたNQPコンパイラのモジュールである為である.
220 %NQPは6modelと呼ばれるオブジェクトモデルを採用としている.%が, これを構築する為に必要なNQPCORE,正規表現系のQRegex,MoarVMのModuleLoaderなどがMoarVMBytecodeで記述されている.これらMoarVMBytecodeの拡張子は.MoarVMである. 250 %NQPは6modelと呼ばれるオブジェクトモデルを採用としている.%が, これを構築する為に必要なNQPCORE,正規表現系のQRegex,MoarVMのModuleLoaderなどがMoarVMBytecodeで記述されている.これらMoarVMBytecodeの拡張子は.MoarVMである.
221 %MoarVMに対してStage0のディレクトリにライブラリパスを設定し, nqp.MoarVMを実行させることでnqpの対話型環境が起動する. 251 %MoarVMに対してStage0のディレクトリにライブラリパスを設定し, nqp.MoarVMを実行させることでnqpの対話型環境が起動する.
222 252
223 \begin{figure}[ht] 253 \begin{figure}[ht]
224 \begin{center} 254 \begin{center}
225 \includegraphics[width=70mm]{fig/stagenqp.pdf} 255 \includegraphics[width=70mm]{fig/tgraph.pdf}
226 \end{center} 256 \end{center}
227 \caption{NQPのビルドフロー} 257 \caption{NQPのビルドフロー}
228 \label{fig:nqpbuild} 258 \label{fig:nqpbuild}
229 \end{figure} 259 \end{figure}
230 260
231 NQPのビルドフローを図\ref{fig:nqpbuild}に示す. 261 NQPのビルドフローを図\ref{fig:nqpbuild}に示す.
232 RakudoによるPerl6に処理系はNQPにおけるnqpと同様に, moarにライブラリパスなどを設定したperl6というシェルスクリプトである. 262 RakudoによるPerl6に処理系はNQPにおけるnqpと同様に, moarにライブラリパスなどを設定したperl6というシェルスクリプトである.
233 このperl6を動かすためにはself buildしたNQPコンパイラが必要となる.その為にStage0を利用してStage1をビルドしNQPコンパイラを作成する. 263 このperl6を動かすためにはself buildしたNQPコンパイラが必要となる.
264 その為にStage0を利用してStage1をビルドしNQPコンパイラを作成する.
234 Stage1は中間的な出力であり, 生成されたNQPファイルはStage2と同一であるが, MoarVMのバイトコードが異なる. 265 Stage1は中間的な出力であり, 生成されたNQPファイルはStage2と同一であるが, MoarVMのバイトコードが異なる.
235 Perl6では完全なセルフコンパイルを実行したNQPが要求される為, Stage1を利用してもう一度ビルドを行いStage2を作成する. 266 Perl6では完全なセルフコンパイルを実行したNQPが要求される為, Stage1を利用してもう一度ビルドを行いStage2を作成する.
236 267
237 Perl6のテストスイートであるRoast\cite{roast}やドキュメントなどによって設計が定まっているPerl6とは異なりNQP自身の設計は今後も変更になる可能性が開発者から公表されている. 268 Perl6のテストスイートであるRoast\cite{roast}やドキュメントなどによって設計が定まっているPerl6とは異なりNQP自身の設計は今後も変更になる可能性が開発者から公表されている.
238 現在の公表されているNQPのオペコードはNQPのリポジトリ\cite{nqpopcode}に記述されているものである. 269 現在の公表されているNQPのオペコードはNQPのリポジトリ\cite{nqpopcode}に記述されているものである.
239 270
240 271
241 \subsection{Rakudo Perl6} 272 %\subsection{Rakudo Perl6}
242 Rakudo実装上におけるPerl6はRakudo Perl6と呼ばれているGitリポジトリで管理されているプログラムのことである. 273 %Rakudo実装上におけるPerl6はRakudo Perl6と呼ばれているGitリポジトリで管理されているプログラムのことである.
243 前述した通りRakudo Perl6はPerl6のサブセットであるNQPを用いて記述されている. 274 %前述した通りRakudo Perl6はPerl6のサブセットであるNQPを用いて記述されている.
244 従ってyaccやlexと言ったPerl5の文字解析, 構文解析に利用していたプログラムは利用せず, NQP側で構文定義などを行っている. 275 %従ってyaccやlexと言ったPerl5の文字解析, 構文解析に利用していたプログラムは利用せず, NQP側で構文定義などを行っている.
245 NQPはNQP自身でBootstrappingされている為, Rakudo Perl6のbuild時にはNQPの実行環境として要したVM, それに基づいてbuildしたNQPがそれぞれ必要となる. 276 %NQPはNQP自身でBootstrappingされている為, Rakudo Perl6のbuild時にはNQPの実行環境として要したVM, それに基づいてbuildしたNQPがそれぞれ必要となる.
246 277 %
247 278 %
248 279 %
249 \subsection{現在のPerl6} 280 %\subsection{現在のPerl6}
250 Perl6の言語仕様\cite{perl6design}とその時点での実装状況をまとめた公式ドキュメント\cite{perl6doc}は分離している. 281 %Perl6の言語仕様\cite{perl6design}とその時点での実装状況をまとめた公式ドキュメント\cite{perl6doc}は分離している.
251 従来は言語仕様は自然言語の仕様書であったが, 現在はテストスイートであるRoast\cite{roast}そのものと定義されている. 282 %従来は言語仕様は自然言語の仕様書であったが, 現在はテストスイートであるRoast\cite{roast}そのものと定義されている.
252 現在のPerl6の仕様を読む場合Roastを確認し, 現在Rakudo上に実装されている機能を見る場合は公式ドキュメントを確認する必要がある. 283 %現在のPerl6の仕様を読む場合Roastを確認し, 現在Rakudo上に実装されている機能を見る場合は公式ドキュメントを確認する必要がある.
253 284 %
254 \subsection{処理速度} 285 \subsection{処理速度}
255 現在のPerl6が他のプログラミング言語と比較した場合どのような違いがでるのか計測した. 286 現在のPerl6が他のプログラミング言語と比較した場合どのような違いがでるのか計測した.
256 macOSの/var/log/system.logファイルから正規表現でログ中のプログラムが書き込んだ回数を個別に数え上げるというものである. 287 macOSの/var/log/system.logファイルから正規表現でログ中のプログラムが書き込んだ回数を個別に数え上げるというものである.
257 今回はファイルを231Kと3GBの二種類用意し, どの様な違いが出るのか測定した. 288 今回はファイルを231Kと3GBの二種類用意し, どの様な違いが出るのか測定した.
258 289
266 \item Perl6 (JVM) 2018.06-163-g612d071b8 built on JVM 297 \item Perl6 (JVM) 2018.06-163-g612d071b8 built on JVM
267 \item Java 10 298 \item Java 10
268 \item Perl5 299 \item Perl5
269 \end{itemize} 300 \end{itemize}
270 301
271 測定した結果を以下に示す.測定結果の単位は秒である. 302 測定した結果を表\ref{tbl001}に示す. 測定結果の単位は秒である.
272 303
273 \begin{table}[htb] 304 \begin{table}[htb]
274 \begin{tabular}{|l|c|c|c|c|c|} \hline 305 \begin{tabular}{|l|c|c|c|c|c|} \hline
275 FileSize & MoarVM & Perl6 on JVM & Java & Perl5\\ \hline 306 FileSize & MoarVM & Perl6 on JVM & Java & Perl5\\ \hline
276 231K & 0.86 & 21.48 & 0.27 & 0.04 \\ 307 231K & 0.86 & 21.48 & 0.27 & 0.04 \\
277 3G & 2331.08 & 1665.56 & 48.85 & 41.35\\ \hline 308 3G & 2331.08 & 1665.56 & 48.85 & 41.35\\ \hline
278 \end{tabular} 309 \end{tabular}
310 \caption{ログファイル処理時間の計測結果}\label{tbl001}
279 \end{table} 311 \end{table}
280 312
281 計測結果からファイルサイズが小さい場合はMoarVMよりJVMに乗せたPerl6が低速であるが, ファイルサイズが大きい場合はJavaのJITが働くためMoarVMより高速に動いていると推測できる. 313 計測結果からファイルサイズが小さい場合はMoarVMよりJVMに乗せたPerl6が低速であるが, ファイルサイズが大きい場合はJavaのJITが働くためMoarVMより高速に動いていると推測できる.
282 314
283 315
314 346
315 \section{CbCによるMoarVM} 347 \section{CbCによるMoarVM}
316 この章では改良を行ったPerl6処理系であるMoarVMについて述べる. 348 この章では改良を行ったPerl6処理系であるMoarVMについて述べる.
317 今回改良を行ったMoarVMは2018.04.01であり, 利用したNQPは2018.04-3-g45ab6e3バージョンである. 349 今回改良を行ったMoarVMは2018.04.01であり, 利用したNQPは2018.04-3-g45ab6e3バージョンである.
318 \subsection{方針} 350 \subsection{方針}
319 Rakudo処理系の処理速度はNQPコンパイラ側の処理速度, 実行環境であるMoarVMなどのVM環境の主に2種類が影響していると考えられる. 351 %Rakudo処理系の処理速度はNQPコンパイラ側の処理速度, 実行環境であるMoarVMなどのVM環境の主に2種類が影響していると考えられる.
320 NQP側はNQPで書かれている為, NQPの記述を修正すれば高速化することも考えられるが, 今回はVM側の処理系に着目した. 352 %NQP側はNQPで書かれている為, NQPの記述を修正すれば高速化することも考えられるが, 今回はVM側の処理系に着目した.
321 Rakudoが動作するVMは複数存在するが, MoarVMが現在主流の処理系である. 353 %Rakudoが動作するVMは複数存在するが, MoarVMが現在主流の処理系である.
322 MoarVM自身はCで記述されているため, CbCを導入する事が可能である. 354 %MoarVM自身はCで記述されているため, CbCを導入する事が可能である.
323 CbCの持つCodeGearや軽量継続を導入する事で, 通常のMoarVMと比較してよりきめ細やかな実装が出来ると推測される. 355 %CbCの持つCodeGearや軽量継続を導入する事で, 通常のMoarVMと比較してよりきめ細やかな実装が出来ると推測される.
324 CodeGearをThreadedCodeなどに応用する事で, MoarVMの高速化が見込める. 356 %CodeGearをThreadedCodeなどに応用する事で, MoarVMの高速化が見込める.
325 また, CbCに関する今までの研究においては, CbCを言語処理系に応用した例が少ない為, CbCを言語処理系に適応した場合の挙動などを確認したい. 357 %また, CbCに関する今までの研究においては, CbCを言語処理系に応用した例が少ない為, CbCを言語処理系に適応した場合の挙動などを確認したい.
326 その為, 本稿ではCbCをMoarVMに導入したCbCMoarVMを実装する. 358 %その為, 本稿ではCbCをMoarVMに導入したCbCMoarVMを実装する.
327 359 %
328 360
329 MoarVMそのものをCbCで書き換えることも考えられるがMoarVM自体既に巨大なプロジェクトである為すべてを書き換える事は困難である. 361 MoarVMの中心は, バイトコードを解釈する, バイトコードインタプリタ部分である.
330 その為まず比較的CbCで書き換えることが容易な箇所を修正する. 362 その為CbCを用いて, MoarVMのバイトコードインタプリタ部分を記述し直し, CbCMoarVMとして実装する.
331 前章までに述べた通りCbCのCodeGearはコンパイラの基本ブロックに該当する. 363 CbCのCodeGearはコンパイラの基本ブロックに該当する.
332 従ってMoarVMにおける基本ブロックの箇所をCodeGearに書き換える事が可能である. 364 従ってMoarVMにおける基本ブロックの箇所をCodeGearに書き換える事が可能である.
333 365
334 \subsection{MoarVMのバイトコードのディスパッチ} 366 \subsection{MoarVMのバイトコードのディスパッチ}
335 MoarVMのバイトコードインタプリタはsrc/core/interp.cで定義されている. 367 %MoarVMのバイトコードインタプリタはsrc/core/interp.cで定義されている.
336 この中の関数MVM\_interp\_runで命令に応じた処理を実行する. 368 %この中の関数MVM\_interp\_runで命令に応じた処理を実行する.
337 関数内では命令列が保存されているcur\_op, 現在と次の命令を指し示すop, Threadの環境が保存されているThreadcontextなどの変数を利用する. 369 %関数内では命令列が保存されているcur\_op, 現在と次の命令を指し示すop, Threadの環境が保存されているThreadcontextなどの変数を利用する.
338 命令実行は大きく二種類の動作があり, Cのgotoが利用できる場合はCode\ref{orig_macro}に示すMVM\_CGOTOフラグが立ちラベル遷移を利用する. 370 %命令実行は大きく二種類の動作があり, Cのgotoが利用できる場合はCode\ref{orig_macro}に示すMVM\_CGOTOフラグが立ちラベル遷移を利用する.
339 それ以外の場合は巨大なcase文として命令を実行する. 371 %それ以外の場合は巨大なcase文として命令を実行する.
340 372 %
341 ラベル遷移を利用する場合はCode\ref{oplabelsh}に示すラベルテーブルLABELSにアクセスし, テーブルに登録されているアドレスを取得し, マクロNEXTで遷移する. 373 %ラベル遷移を利用する場合はCode\ref{oplabelsh}に示すラベルテーブルLABELSにアクセスし, テーブルに登録されているアドレスを取得し, マクロNEXTで遷移する.
342 Code\ref{dispatch_c}に示すno\_opは何もせず次の命令に移動する為, goto NEXT;のみ記述されている. 374 %Code\ref{dispatch_c}に示すno\_opは何もせず次の命令に移動する為, goto NEXT;のみ記述されている.
343 375
344 %このラベルテーブルの中身はラベルが変換されたアドレスであるため, 実際に呼ばれている命令コードの名前はデバッガレベルでは確認できない. 376 %このラベルテーブルの中身はラベルが変換されたアドレスであるため, 実際に呼ばれている命令コードの名前はデバッガレベルでは確認できない.
345 %巨大なcase文として実行された場合, 実行時間が遅いだけでなく, ラベル遷移と共存させて記述を行っている為Cのソースコードにおける可読性も低下する. 377 %巨大なcase文として実行された場合, 実行時間が遅いだけでなく, ラベル遷移と共存させて記述を行っている為Cのソースコードにおける可読性も低下する.
346 378
347 379
348 \lstinputlisting[label=orig_macro, caption=interp.cのマクロ部分]{./src/orig_macro.c} 380 %\lstinputlisting[label=orig_macro, caption=interp.cのマクロ部分]{./src/orig_macro.c}
349 \lstinputlisting[label=oplabelsh, caption=ラベルテーブルの一部分]{./src/oplabels.h} 381 %\lstinputlisting[label=oplabelsh, caption=ラベルテーブルの一部分]{./src/oplabels.h}
350 382 %
351 \lstinputlisting[label=dispatch_c, caption=オリジナル版MoarVMのバイトコードディスパッチ]{./src/dispatch.c} 383 %\lstinputlisting[label=dispatch_c, caption=オリジナル版MoarVMのバイトコードディスパッチ]{./src/dispatch.c}
352 384
353 interp.cでは命令コードのディスパッチはマクロを利用したcur\_opの計算及びラベルの遷移, もしくはマクロDISPATCHが展開するswitch文で行われていた. 385 interp.cでは命令コードのディスパッチはマクロを利用したcur\_opの計算及びラベルの遷移, もしくはマクロDISPATCHが展開するswitch文で行われていた.
354 この為MoarVM内の命令コードに対応する処理は, 命令ディスパッチが書かれているCソースファイルの, 特定の場所のみに記述せざるを得ない. 386 このディスパッチ方法では, ラベルジャンプや巨大なcase文として記述する必要があり, ファイルが冗長になるなどの問題が生じる.
355 その為命令コードのモジュール化などが行えず, 1ファイル辺りの記述量が膨大になってしまう.
356 また各命令コードに対応する処理は, ラベルジャンプもしくはswitch文に展開されてしまう為, Threaded Codeの実装を考えた場合, 大幅なコードの改修が要求される.
357 デバッグ時には, Cレベルでのデバッグ時にはアドレスと実際に呼ばれる箇所を確認する事に手間がかかる.
358 387
359 CbCMoarVMではこの問題を解決するために, それぞれの命令に対応するCodeGearを作成し, 各CodeGearの名前を要素として持つCbCのCodeGearのテーブルを作成した. 388 CbCMoarVMではこの問題を解決するために, それぞれの命令に対応するCodeGearを作成し, 各CodeGearの名前を要素として持つCbCのCodeGearのテーブルを作成した.
360 このCodeGearのテーブルを参照するCodeGearはcbc\_nextであり, この中のマクロNEXTはinterp.cのマクロNEXTをCbC用に書き直したものである. 389 このCodeGearのテーブルを参照するCodeGearはcbc\_nextであり, この中のマクロNEXTはinterp.cのマクロNEXTをCbC用に書き直したものである.
361 390
362 \lstinputlisting[label=cbc_dispatch_c, caption=CbCMoarVMのバイトコードディスパッチ]{./src/cbc-interp-next.cbc} 391 \lstinputlisting[label=cbc_dispatch_c, caption=CbCMoarVMのバイトコードディスパッチ]{./src/cbc-interp-next.cbc}
479 しかし, MoarVMのバイナリmoarはMoarVMのバイトコードを読み込むことはNQPをセルフビルドしなくとも可能である. 508 しかし, MoarVMのバイナリmoarはMoarVMのバイトコードを読み込むことはNQPをセルフビルドしなくとも可能である.
480 509
481 その為, 正常に動作しているMoarVMのバイナリmoarとnqpを用意し, このnqp側からMoarVMのバイトコードにNQPで記述されたテストを変換する. 510 その為, 正常に動作しているMoarVMのバイナリmoarとnqpを用意し, このnqp側からMoarVMのバイトコードにNQPで記述されたテストを変換する.
482 変換されたMoarVMのバイトコードはバイナリmoarに渡す事で実行可能であり, テストを行う事が出来る. 511 変換されたMoarVMのバイトコードはバイナリmoarに渡す事で実行可能であり, テストを行う事が出来る.
483 512
484 \subsection{CbCコンパイラによるバグ} 513 %\subsection{CbCコンパイラによるバグ}
485 これまでのCbCに関する研究においては, 複数個の入出力をCodeGearに与えるユースケースで利用していた. 514 %これまでのCbCに関する研究においては, 複数個の入出力をCodeGearに与えるユースケースで利用していた.
486 CbCコンパイラ自身はそれぞれ用意したテストスイートを通化するものの, MoarVMの様な巨大なプロジェクトのCodeGearをコンパイルを実行する場合, 予期せぬバグが発生した. 515 %CbCコンパイラ自身はそれぞれ用意したテストスイートを通化するものの, MoarVMの様な巨大なプロジェクトのCodeGearをコンパイルを実行する場合, 予期せぬバグが発生した.
487 主にCodeGear間のgotoにおけるtail callフラグの除去や, DataGearとして渡している構造体の変数のアドレスがスタックポインタの値より上位に来てしまい, 通常のCの関数をcallした際にローカル変数の領域がDataGearのアドレスの周辺を利用してしまう. 516 %主にCodeGear間のgotoにおけるtail callフラグの除去や, DataGearとして渡している構造体の変数のアドレスがスタックポインタの値より上位に来てしまい, 通常のCの関数をcallした際にローカル変数の領域がDataGearのアドレスの周辺を利用してしまう.
488 その為DataGearの構造体の値が書き換わり, CからDataGearにreturnした際にDataGearの構造体が破壊されるバグである. 517 %その為DataGearの構造体の値が書き換わり, CからDataGearにreturnした際にDataGearの構造体が破壊されるバグである.
489 このバグは先程の並列デバッグを行いながらプログラムカウンタや変数の動きをトレースする事などで発見することが出来る. 518 %このバグは先程の並列デバッグを行いながらプログラムカウンタや変数の動きをトレースする事などで発見することが出来る.
490 現状ではCbCコンパイラがプログラマの意図と反する挙動を取るためCbCコンパイラのバグを回避するプログラミングが要求されている. 519 %現状ではCbCコンパイラがプログラマの意図と反する挙動を取るためCbCコンパイラのバグを回避するプログラミングが要求されている.
491 本来コンパイラ側のバグを回避するプログラミングをプログラマに要求する事は好ましくない. 520 %本来コンパイラ側のバグを回避するプログラミングをプログラマに要求する事は好ましくない.
492 従ってCbCコンパイラ自身の信頼性を向上させる事も今後の課題となっている. 521 %従ってCbCコンパイラ自身の信頼性を向上させる事も今後の課題となっている.
493 522 %
494 523
495 524
496 \section{CbCMoarVMの利点と欠点} 525 \section{CbCMoarVMの利点と欠点}
497 MoarVMの様な巨大なスクリプト言語処理系にCbCを適応した所現在までに複数の利点と欠点が発見された. 526 MoarVMの様な巨大なスクリプト言語処理系にCbCを適応した所現在までに複数の利点と欠点が発見された.
498 本章ではまず利点を述べ, 次に現段階でのCbCを適応した場合の欠点について考察する. 527 本章ではまず利点を述べ, 次に現段階でのCbCを適応した場合の欠点について考察する.
546 % 100000000 & 3.258 & 124.69 \\ \hline 575 % 100000000 & 3.258 & 124.69 \\ \hline
547 % \end{tabular} 576 % \end{tabular}
548 % \end{table} 577 % \end{table}
549 しかし, 言語処理系は広く使われる為に著名なOSSなどを利用して開発するのが望ましいが, CbCプロジェクトの認知度が低いという現状がある. 578 しかし, 言語処理系は広く使われる為に著名なOSSなどを利用して開発するのが望ましいが, CbCプロジェクトの認知度が低いという現状がある.
550 579
551 また, 前章までに複数述べた通りCbCコンパイラが現在非常にバグを発生させやすい状態になっている. 580 また, CbCコンパイラが現在非常にバグを発生させやすい状態になっている.
552 CbCコンパイラはgccとllvm/clang上に実装している為, これらのアップデートに追従する必要がある. 581 CbCコンパイラはgccとllvm/clang上に実装している為, これらのアップデートに追従する必要がある.
553 しかしコンパイラのバージョンに応じてCbCで利用するコンパイラ内のAPIが異なる場合が多く, APIの変更に伴う修正作業などを行う必要がある. 582 しかしコンパイラのバージョンに応じてCbCで利用するコンパイラ内のAPIが異なる場合が多く, APIの変更に伴う修正作業などを行う必要がある.
554 583
555 CbCMoarVMではCからCodeGearへ, CodeGearからCへの遷移などが複数回繰り返されているが, この処理中のCodeGearでのtail callの強制が非常に難関である. 584 CbCMoarVMではCからCodeGearへ, CodeGearからCへの遷移などが複数回繰り返されているが, この処理中のCodeGearでのtail callの強制が非常に難関である.
556 tail callの強制には関数定義の箇所や引数, スタック領域のサイズ修正などを行う必要がある. 585 tail callの強制には関数定義の箇所や引数, スタック領域のサイズ修正などを行う必要がある.
557 現在のバグではCodeGear内部での不要なスタック操作命令を完全に排除しきれていない. 586 現在のCbCコンパイラのバグではCodeGear内部での不要なスタック操作命令を完全に排除しきれていない.
558 587
559 またCodeGearからCに帰る場合, 環境付き継続を行う必要がある. 588 またCodeGearからCに帰る場合, 環境付き継続を行う必要がある.
560 Cの関数の末尾でCodeGearを呼び出している場合など環境付き継続を使用しなくても良いケースは存在するが, 頻繁にCとCbCを行き来する場合記述が冗長になる可能性はある. 589 Cの関数の末尾でCodeGearを呼び出している場合など環境付き継続を使用しなくても良いケースは存在するが, 頻繁にCとCbCを行き来する場合記述が冗長になる可能性はある.
561 590
562 \section{Threaded Code} 591 \section{Threaded Code}
609 \section{まとめ} 638 \section{まとめ}
610 本稿ではCbCによってPerl6の処理系であるMoarVMインタプリタの一部改良とその手法を示した. 639 本稿ではCbCによってPerl6の処理系であるMoarVMインタプリタの一部改良とその手法を示した.
611 CbCMoarVMではオリジナルのMoarVMと比較して以下の様な利点が見られた. 640 CbCMoarVMではオリジナルのMoarVMと比較して以下の様な利点が見られた.
612 641
613 \begin{itemize} 642 \begin{itemize}
614 \item CodeGear単位で命令処理を記述する事が可能となり, モジュール化が可能となった. 643 \item CodeGear単位で命令処理を記述する事が可能となり, モジュール化が可能となった.
615 \item ThreadedCodeを実装する際に効率的に実装ができる見込みが立った. 644 \item ThreadedCodeを実装する際に効率的に実装ができる見込みが立った.
616 \item CodeGearを導入した命令単位での最適化が可能となった. 645 \item CodeGearを導入した命令単位での最適化が可能となった.
617 \item break pointを命令の処理単位でかける事が可能となった. 646 \item break pointを命令の処理単位でかける事が可能となった.
618 \item 現在は命令処理部分をCodeGearに書き換えたのみである為, ラベルを利用した場合と比較して速度としては同等である. 647 \item 現在は命令処理部分をCodeGearに書き換えたのみである為, ラベルを利用した場合と比較して速度としては同等である.
619 \end{itemize} 648 \end{itemize}
620 649
621 今後CbCでの開発をより深く行っていくにあたり, CbCコンパイラそのものの信頼性を向上させる必要がある. 650 今後CbCでの開発をより深く行っていくにあたり, CbCコンパイラそのものの信頼性を向上させる必要がある.
622 MoarVMの開発を行うにあたり新たに発見された複数のバグを修正し, より安定するコンパイラにする為に改良を行う. 651 MoarVMの開発を行うにあたり新たに発見された複数のバグを修正し, より安定するコンパイラにする為に改良を行う.
623 652
624 現在CbCMoarVMで直接バイトコードを入力した場合のNQPのテストはJVM, JavaScriptのテストを除く中で80\%パスする. 653 現在CbCMoarVMで直接バイトコードを入力した場合のNQPのテストはJVMなどのテストを除く中で80\%パスする.
625 また数値の計算と出力などの簡単なNQPの例題を作成し, オリジナルのNQP,MoarVMでバイトコード化したものを入力した際も正常に動作している. 654 また数値の計算と出力などの簡単なNQPの例題を作成し, オリジナルのNQP,MoarVMでバイトコード化したものを入力した際も正常に動作している.
626 しかしNQPのセルフビルドは現在オブジェクトの生成に一部失敗している為成功していない. 655 しかしNQPのセルフビルドは現在オブジェクトの生成に一部失敗している為成功していない.
627 今後はさらに複雑な例題やNQPのセルフビルド, Perl6の動作を行っていく. 656 今後はさらに複雑な例題やNQPのセルフビルド, Perl6の動作を行っていく.
628 657
629 MoarVMではGCからオブジェクトを守る為にMVMROOTというマクロを利用し, 局所変数のポインタをスタックに登録する処理を行っている. 658 MoarVMではGCからオブジェクトを守る為にMVMROOTというマクロを利用し, 局所変数のポインタをスタックに登録する処理を行っている.