Mercurial > hg > Events > OSC2019
annotate slide.md @ 12:58cd4dd86896
update
author | anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 19 Apr 2019 19:07:07 +0900 |
parents | 4b1eb4d69695 |
children | d3036d998236 |
rev | line source |
---|---|
0
19155754a586
create OSC2019 slide template
anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
parents:
diff
changeset
|
1 title: Perl6の内部表現 |
19155754a586
create OSC2019 slide template
anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
parents:
diff
changeset
|
2 author: Takahiro Shimizu |
19155754a586
create OSC2019 slide template
anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
parents:
diff
changeset
|
3 profile: |
19155754a586
create OSC2019 slide template
anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
parents:
diff
changeset
|
4 lang: Japanese |
19155754a586
create OSC2019 slide template
anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
parents:
diff
changeset
|
5 |
19155754a586
create OSC2019 slide template
anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
parents:
diff
changeset
|
6 ## このセッションの内容 |
19155754a586
create OSC2019 slide template
anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
parents:
diff
changeset
|
7 |
1 | 8 - Perl6の主要な実装であるRakudoの内部構造を探ります |
9 - Rakudoの内部で利用されているVMや, Perl6のサブセットなどについて探索します | |
3 | 10 - スクリプト言語で主に使われているバイトコードインタプリタの気持ちになります |
1 | 11 |
9 | 12 ## 内容 |
13 - Perl6とは? | |
14 - スクリプト言語処理系の動き | |
15 - Perl6の内部構造 | |
16 - NQP | |
17 - MoarVM | |
18 - MoarVMのバイトコード実行 | |
19 - まとめ | |
20 | |
1 | 21 ## Perl6とは |
22 - 当初Perl5の時期バージョンとして開発されていたプログラミング言語 | |
9 | 23 - 現在は別の言語として開発がそれぞれ進んでいる |
1 | 24 - 仕様と実装が分離しており, 現在はテストが仕様となっている |
9 | 25 - 実装は歴史上複数存在しているが,主流な実装はRakudo |
26 - 言語的にはスクリプト言語であり, 漸進的型付き言語 | |
27 - 動作環境は、独自のVMのMoarVM, JVM、一部JavaScript上で動作する | |
1 | 28 |
2 | 29 <img src="2000px-Camelia.svg.png" alt="" style="width: 31%; height: auto;"> |
1 | 30 |
9 | 31 ## 現在のPerl6 |
32 | |
33 - 現在のバージョンは `6.d` | |
34 - [ブラウザ上で実行可能な環境](https://perl6.github.io/6pad/)が存在する | |
35 - [IDE](https://commaide.com/)が開発されている | |
36 - WebApplicationFrameworkなども開発されており、 Perl5のモジュールを移行したものがいくつか存在する | |
37 - 日本では趣味のプロダクト以外社会では使用されていない | |
38 - 海外では実際に使われているケースも存在する | |
39 - 処理速度では一部Perl5に勝っているが、それでも大分遅い | |
40 | |
41 ## [参考]Perl5のソースコード | |
42 | |
43 - Perl5時代 | |
44 − スカラ、配列、ハッシュの3種類 | |
45 - それぞれの変数への参照であるリファレンスが使用可能 | |
46 | |
47 ```perl | |
48 use ustrict; | |
49 use warnings; | |
50 | |
51 my $scalar_value = "hello!"; | |
52 print "$scalar_value\n"; | |
53 | |
54 my @array = (1..10); | |
55 print "$array[0]\n"; | |
56 | |
57 my %hash = ( this_is_key => "this_is_value"); | |
58 print "$hash{this_is_key}\n"; | |
59 | |
60 my $hash_ref = \%hash; | |
61 print "$hash_ref->{this_is_key}\n"; | |
62 ``` | |
63 | |
1 | 64 ## Perl6のソースコード概要 |
65 | |
66 - Perl5の文法とは比較的変更が多い | |
9 | 67 - 雰囲気は似ている |
1 | 68 - 変数がオブジェクトと化した事により, 変数からsayメソッドを呼ぶことが可能 |
69 | |
70 ``` | |
71 my $str_value = 'hello world!'; | |
72 $str_value.say; # hello world! | |
73 ``` | |
74 | |
75 - Perl5と同様に,変数にはデフォルトでは型がないような振る舞いをする | |
0
19155754a586
create OSC2019 slide template
anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
parents:
diff
changeset
|
76 |
1 | 77 ``` |
78 my $sample_value = 'hello world!'; | |
79 $sample_value.say; # hello world! | |
80 | |
81 $sample_value = '31'; | |
82 $sample_value.say; # 31 | |
83 | |
84 say($sample_value * 3); | |
85 ``` | |
86 | |
87 ## Perl6の言語的な特徴 | |
88 | |
89 - 漸進的型付き言語である為, 型を強制することも可能となる | |
90 | |
91 ``` | |
92 my Int $int_value = 31; | |
93 $int_value = "hello"; # Compile error! | |
94 ``` | |
95 | |
7 | 96 ``` |
97 $ perl6 type_invalid.p6 | |
98 Type check failed in assignment to $int_value; expected Int but got Str ("hello") | |
99 in block <unit> at type_invalid.p6 line 4 | |
100 ``` | |
101 | |
4 | 102 ## Perl6の言語的な特徴 |
103 | |
104 - 型を独自に定義することも可能 | |
105 - 入力の型によって実行する関数を変える事などができる | |
106 | |
107 ```perl6 | |
108 my subset Fizz of Int where * %% 3; | |
109 my subset Buzz of Int where * %% 5; | |
110 my subset FizzBuzz of Int where Fizz&Buzz; | |
111 my subset Number of Int where none Fizz|Buzz; | |
112 | |
113 proto sub fizzbuzz ($) { * } | |
114 multi sub fizzbuzz (FizzBuzz) { "FuzzBuzz" } | |
115 multi sub fizzbuzz (Fizz) { "Fizz" } | |
116 multi sub fizzbuzz (Buzz) { "Buzz" } | |
117 multi sub fizzbuzz (Number $number) { $number } | |
118 | |
119 fizzbuzz($_).say for 1..15; | |
120 ``` | |
9 | 121 |
122 - 型を利用したFizzBuzz | |
123 | |
124 ## スクリプト言語 | |
125 - Perl6は現状コンパイルすることはできない | |
126 - スクリプト言語の分類 | |
127 | |
128 - 現在広く使われているスクリプト言語(Perl,Python,Ruby...)などとPerl6の構成は類似している | |
129 - 今回はPerl6の実装を追いながら、最近のスクリプト言語処理系の大まかな実装を理解する | |
130 | |
7 | 131 ## スクリプト言語処理系 |
132 - スクリプト言語は入力として与えられたソースコードを、 直接評価せずにバイトコードにコンパイルする形式が主流となっている | |
133 - その為スクリプト言語の実装は大きく2つで構成されている | |
134 - バイトコードに変換するフロントエンド部分 | |
135 - バイトコードを解釈する仮想機械 | |
136 | |
137 <img src="fig/bytecode_sample_generally_lang.svg" width="80%"> | |
4 | 138 |
9 | 139 |
140 ## Perl6以外のスクリプト言語 | |
141 | |
142 - 現在使われているプロセスVMは言語に組み込まれているものが多い | |
143 - JVMやElixirなどのVMは複数の言語で使用されている | |
144 - Java | |
145 - JVM | |
146 - Ruby | |
147 - YARV | |
148 - Python | |
149 - PythonVM | |
12 | 150 - Erlang |
9 | 151 - Elixir |
152 - BEAM | |
153 | |
1 | 154 ## Perl6の処理系の構成 |
155 | |
9 | 156 - Perl6の処理系で現在主流なものはRakudoと呼ばれる実装である(歴史上複数存在する) |
2 | 157 - Rakudoは3つのレイヤーから構成されている |
158 - Perl6インタプリタ | |
159 - Perl6インタプリタを記述するPerl6のサブセットNQP | |
160 - Perl6のバイトコードを解釈するMoarVM | |
9 | 161 - Perl6/NQPがフロントエンドに相当し、MoarVMがバックエンドに相当する |
162 | |
163 ## Rakudoの構成図 | |
2 | 164 |
9 | 165 ![](fig/Rakudo_System_overview.png) |
7 | 166 |
9 | 167 (http://brrt-to-the-future.blogspot.com/2015/03/advancing-jit-compiler.html) |
7 | 168 |
2 | 169 ## Perl6とNQP |
170 | |
9 | 171 - NQP(NotQuitPerl Perl) |
172 - Perl6のサブセット。Perl6っぽい言語 | |
173 - Perl6、 NQP自体がNQPで記述されている | |
2 | 174 - NQPもNQPで記述されている為、 セルフビルド(自分自身で自分自身をコンパイルする)を行う |
175 - NQPはPerl6の文法をベースにしているが、 制約がいくつか存在する | |
9 | 176 - 元々はPerl6の主力実装がParrotだった時代に登場 |
177 - 文法がアップデートされており、当時の資料は古くなっている | |
178 | |
179 ``` | |
180 my $value := "hello!"; | |
181 say($value); | |
182 ``` | |
183 | |
184 ## NQPスクリプト | |
185 | |
186 - 変数は束縛 `:=` を使う | |
187 − 関数の間に空白を入れてはいけない | |
188 - 再帰呼び出しを使うフィボナッチ数列 | |
2 | 189 |
4 | 190 ``` |
191 #! nqp | |
192 sub fib($n) { | |
193 $n < 2 ?? $n !! fib($n-1) + fib($n - 2); | |
194 } | |
195 | |
196 my $N := 29; | |
197 | |
198 my $z := fib($N); | |
199 | |
200 nqp::say("fib($N) = " ~ fib($N)); | |
9 | 201 ``` |
202 | |
203 ## NQPスクリプト(nまでの整数の和) | |
204 | |
205 ```perl6 | |
206 sub add_test($n){ | |
10 | 207 my $sum := 0; |
9 | 208 while ( $n > 1) { |
209 $sum := $sum + $n; | |
210 --$n; | |
211 } | |
212 return $sum; | |
213 } | |
214 | |
215 say(add_test(10000)); | |
4 | 216 ``` |
217 | |
10 | 218 ## NQP |
9 | 219 |
220 - NQPはPerl6の中で一番レイヤーが低い言語 | |
221 - その為、 実行するVMのオペコード(処理単位)を使用することができる | |
10 | 222 - NQPオペコードは、 Perl6の内部の抽象構文木でも使用されている |
223 - また、 Perl6と同様に型を指定することが可能 | |
9 | 224 |
10 | 225 ```perl6 |
226 sub add_test(int $n){ | |
227 mu $sum := 0; | |
228 while nqp::isgt_i($n,1) { | |
229 $sum := nqp::add_i($sum,$n); | |
230 $n := nqp::sub_i($n,1); | |
231 } | |
232 return $sum; | |
233 } | |
234 ``` | |
7 | 235 |
9 | 236 ## NQPとMoarVM |
237 - NQPそのものは実行することはできない | |
238 - NQPの実行にはMoarVM/JVMが必要となる | |
239 - NQPコンパイラが各VMに対応したバイトコードに変換する | |
240 | |
241 ## Perl6のVM | |
242 - MoarVM, JVM , JavaScriptが選択可能 | |
243 - メインで開発されているのはMoarVMであり、 他のVMは機能が実装されていないものが存在する | |
244 - `rakudo-star` というPerl6のパッケージ環境では、 MoarVMがデフォルトでインストールされる | |
245 | |
246 ## MoarVM | |
247 - C言語で記述されているPerl6専用の仮想機械 | |
7 | 248 - レジスタマシン |
249 - 型情報を持つレジスタに対しての演算として処理される | |
9 | 250 - Rubyなどはスタックマシンとして実装されている |
7 | 251 - LuaJITなどを利用したJITコンパイルなども可能 |
252 - Perl6やNQPは、MoarVMに対してライブラリなどを設定して起動する | |
253 | |
10 | 254 |
255 | |
7 | 256 ## バイトコード |
3 | 257 - Perl6も、Rakudo/NQPはバイトコードに変換され、 バイトコードをVMが実行する |
10 | 258 - Perl6/NQPはバイトコードにコンパイルすることが可能 |
259 - 直接実行することはできない | |
260 | |
261 ``` | |
262 $nqp --target=mbc --output=fib.moarvm fib.nqp | |
263 ``` | |
264 | |
12 | 265 ## バイトコード |
266 - バイナリ形式で表現される為、 VMがどのように読み取るかでバイトコードの意味が異なる | |
267 - スクリプト言語で重要なバイトコード表現は、「仮想機械がどの命令を実行するか」のバイトコード | |
268 - CPUに対するアセンブラの命令に対応する | |
269 - どういった構成なのかは仮想機械によって異なる | |
270 | |
271 | |
10 | 272 ## バイトコードとMoarVM |
273 | |
274 | |
275 - MoarVMバイトコードはMoarVMの実行バイナリ `moar` でディスアセンブルすることが可能 | |
276 | |
277 | |
278 ``` | |
279 annotation: add_test.nqp:1 | |
280 00003 const_i64_16 loc_2_int, 0 | |
281 00004 hllboxtype_i loc_3_obj | |
282 00005 box_i loc_3_obj, loc_2_int, loc_3_obj | |
283 00006 set loc_1_obj, loc_3_obj | |
284 label_1: | |
285 00007 decont loc_3_obj, loc_0_obj | |
286 00008 smrt_numify loc_4_num, loc_3_obj | |
287 00009 const_i64_16 loc_2_int, 1 | |
288 00010 coerce_in loc_5_num, loc_2_int | |
289 00011 gt_n loc_2_int, loc_4_num, loc_5_num | |
290 00012 unless_i loc_2_int, label_2(00031) | |
291 00013 osrpoint | |
292 annotation: add_test.nqp:3 | |
293 00014 decont loc_3_obj, loc_1_obj | |
294 00015 smrt_numify loc_5_num, loc_3_obj | |
295 00016 decont loc_3_obj, loc_0_obj | |
296 00017 smrt_numify loc_4_num, loc_3_obj | |
297 00018 add_n loc_4_num, loc_5_num, loc_4_num | |
298 00019 hllboxtype_n loc_3_obj | |
299 00020 box_n loc_3_obj, loc_4_num, loc_3_obj | |
300 00021 set loc_1_obj, loc_3_obj | |
301 00022 decont loc_3_obj, loc_0_obj | |
302 00023 smrt_numify loc_4_num, loc_3_obj | |
303 00024 coerce_ni loc_6_int, loc_4_num | |
304 00025 const_i64_16 loc_7_int, 1 | |
305 00026 sub_i loc_7_int, loc_6_int, loc_7_int | |
306 00027 hllboxtype_i loc_3_obj | |
307 00028 box_i loc_3_obj, loc_7_int, loc_3_obj | |
308 00029 set loc_0_obj, loc_3_obj | |
309 00030 goto label_1(00007) | |
310 ``` | |
311 | |
312 ## NQPとバイトコードの対応 | |
313 | |
314 ``` | |
315 say(add_test(10000)); | |
316 ``` | |
2 | 317 |
10 | 318 ``` |
319 annotation: add_test.nqp:1 | |
320 label_1: | |
321 00020 getlex_no loc_7_obj, '&say' | |
322 00021 decont loc_7_obj, loc_7_obj | |
323 00022 const_s loc_3_str, '&add_test' | |
324 00023 getlexstatic_o loc_8_obj, loc_3_str | |
325 00024 decont loc_8_obj, loc_8_obj | |
326 00025 const_i64_16 loc_5_int, 10000 | |
327 00026 prepargs Callsite_1 | |
328 00027 arg_i 0, loc_5_int | |
329 00028 invoke_o loc_8_obj, loc_8_obj | |
330 00029 prepargs Callsite_0 | |
331 00030 arg_o 0, loc_8_obj | |
332 00031 invoke_v loc_7_obj | |
333 00032 null loc_7_obj | |
334 00033 return_o loc_7_obj | |
335 ``` | |
336 | |
337 - Perl6の変数は直接実態を参照せず、中身が入っているコンテナを参照するようになっている。 | |
338 - その為 `decont` 命令で、コンテナの中身をレジスタに設定する必要がある | |
339 - `const_i64_16` などは64bitの数という意味で、 `int` 型としてレジスタに登録している | |
340 - `prepargs` で引数の確認を行い, `invoke_o` で実際にサブルーチンに移行する | |
341 | |
342 ## NQPとバイトコードの対応 | |
343 | |
344 ``` | |
345 my $sum := 0; | |
346 ``` | |
347 | |
348 ``` | |
349 annotation: add_test.nqp:1 | |
350 00003 const_i64_16 loc_2_int, 0 | |
351 00004 hllboxtype_i loc_3_obj | |
352 00005 box_i loc_3_obj, loc_2_int, loc_3_obj | |
353 00006 set loc_1_obj, loc_3_obj | |
354 ``` | |
355 | |
356 - まず `loc_2` レジスタをint型の整数0で初期化する | |
357 - 変数 `$sum` はint型の指定がないので、 obj型で登録しなければならない | |
358 - その為, 整数として登録された `loc_2` から、 obj型に一旦キャストし、 `loc_3` レジスタに設定したものを、 `loc_1` レジスタに設定する | |
359 | |
360 ## NQPとバイトコードの対応 | |
361 | |
362 ``` | |
363 while ( $n > 1) { | |
364 ``` | |
365 | |
366 ``` | |
367 label_1: | |
368 00007 decont loc_3_obj, loc_0_obj | |
369 00008 smrt_numify loc_4_num, loc_3_obj | |
370 00009 const_i64_16 loc_2_int, 1 | |
371 00010 coerce_in loc_5_num, loc_2_int | |
372 00011 gt_n loc_2_int, loc_4_num, loc_5_num | |
373 00012 unless_i loc_2_int, label_2(00031) | |
374 00013 osrpoint | |
375 ``` | |
376 | |
377 - 変数 `$n` と 整数 `1` を大小比較する為、 まず `$n` から値を取り出す | |
378 − 比較にもint型の指定がない為、 `num` 型にキャストし、 `num` 型のレジスタでの大小を比較する | |
379 - 比較命令は `gt_n` であり、 結果により `unless_i` 命令で、別のラベルにジャンプする |