<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Miura Hideki</title>
    <description>The latest articles on DEV Community by Miura Hideki (@miura1729).</description>
    <link>https://dev.to/miura1729</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F44622%2F606190b6-05cf-46d5-b40d-a99501994849.jpeg</url>
      <title>DEV Community: Miura Hideki</title>
      <link>https://dev.to/miura1729</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/miura1729"/>
    <language>en</language>
    <item>
      <title>私が書いているものは型推論器ではなくなんなのか</title>
      <dc:creator>Miura Hideki</dc:creator>
      <pubDate>Sun, 03 Jun 2018 06:20:54 +0000</pubDate>
      <link>https://dev.to/miura1729/-2376</link>
      <guid>https://dev.to/miura1729/-2376</guid>
      <description>&lt;h1&gt;
  
  
  はじめに
&lt;/h1&gt;

&lt;p&gt;現在、mruby用のコンパイラを書こうとしていてその一環として、&lt;a href="https://github.com/miura1729/mruby-meta-circular" rel="noopener noreferrer"&gt;こんなもの&lt;/a&gt;を作っています。永らく型推論器だと思っていたのですが、どうも型推論器には当たらないことが分かってきました。そこでここでは今作っているプログラムについて説明します。&lt;br&gt;
基本的にやっていることはプログラム全体を走査して型情報を集めて表示する(またはそれを使ってコンパイルする)ということです。一言で行ってしまうと簡単ですが実際に実装しようとするとなかなか大変です。&lt;/p&gt;

&lt;p&gt;まず型情報を集めると言ってもどこから元を集めればいいのでしょうか？命令列は簡単に得られますが、そのオペランドになっているレジスタに何が入っているかは自明ではありません。明らかにレジスタの内容が分かる定数のレジスタを元にレジスタの参照関係が扱いやすく整理されている必要があります。さらに、mrubyのVMではデータはレジスタにだけあるのではありません。インスタンス変数、グローバル変数、定数などもあります。こういったものの型も集めなければ全体の型情報は得られません。そのため、型情報を集めるための中間コードの設計がとても大事になります。この辺の話は中間コードの章で説明します。&lt;/p&gt;

&lt;p&gt;集める型をどのような構造に入れるかも自明ではありません。たとえばRubyのカオスな型システムだとある時点でFixnumだと思ってたものが別の所だとStringだったとしても不思議ではありません。いろんな変な状況が現れても大丈夫な構造を用意する必要があります。&lt;br&gt;
また、Rubyではメソッドの引数についてオーバーロードをゆるすというよりオーバーロードと言う言葉すら無意味なほど &lt;del&gt;ガバガバ&lt;/del&gt; 自由度の高い言語なのでそのあたりを何とかする必要があります。このプログラムではTUP(Tuppleから)という概念を使ってそれを実現しています。これも重要な概念でしょう。&lt;/p&gt;

&lt;p&gt;ここまで説明すると枠組みが出来上がりますので、ここの命令毎にルールを説明します。命令によっては色々ヒューリスティクなことをしているのでまあ説明する必要があるでしょう。&lt;/p&gt;

&lt;p&gt;VMの命令のルールを説明して終わりなわけがありません。mrubyにはC(死んだはずなんだけどね)で書かれたメソッドがありこれら毎に型のルールを指定しないといけません。多くは自明なものですが中には複雑なルールもあります。&lt;/p&gt;

&lt;p&gt;次に条件分岐の扱いです。普通は条件分岐は辿れるとこ全部を辿らればいいのですが、例えば、&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
     &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;みたいなコードでblockがnilではcallは呼べないよ　というエラーは誰もが受け取りたくないでしょう。そういうわけである程度条件分岐の条件を見て適切な制御をしています。その説明も章を設けて行います。&lt;/p&gt;

&lt;p&gt;とりあえずこんな感じの文章が出来るはずです。&lt;/p&gt;

&lt;h1&gt;
  
  
  中間コード
&lt;/h1&gt;

&lt;p&gt;このプログラムではmrubyのバイトコード命令毎に入力から出力に型を伝搬していくのが主な動作になります。この時、入力と出力というのは自明ではありません。そういうとレジスタには番号が付いているから簡単では？と思われるかもしれませんが、同じレジスタの番号でも上書きされていれば別のレジスタとして扱わなければなりません。&lt;br&gt;
&lt;a href="https://github.com/miura1729/ytljit/" rel="noopener noreferrer"&gt;昔作ったコンパイラの経験&lt;/a&gt;から条件分岐の処理は特に面倒になるため基本ブロックにきっちり分けて管理する必要性を感じました。&lt;br&gt;
また、&lt;a href="https://github.com/miura1729/mruby" rel="noopener noreferrer"&gt;mrubyのJIT&lt;/a&gt;の経験からレジスタ割り当てをおこなう場合はレジスタの生存期間が簡単にわかるようになっている必要があるという知見を得ました。&lt;/p&gt;

&lt;p&gt;以上のことから中間コードは&lt;a href="https://ja.wikipedia.org/wiki/%E9%9D%99%E7%9A%84%E5%8D%98%E4%B8%80%E4%BB%A3%E5%85%A5" rel="noopener noreferrer"&gt;SSA&lt;/a&gt;ベースが最適と言う結論になりました。ところが、mrubyのVMのバイトコードをSSAに変換すると言うのはなかなか煩雑なものです。考えた末、SSAと同等のコードが得られる方法を思いついたのでそれを実装しています。この方法ではコードはグラフになるためテキストにエンコードするのが煩雑になるという欠点があります。&lt;/p&gt;

&lt;p&gt;mrubyのバイトコードをSSAに変換するには次のような手順で行います、&lt;br&gt;
　　* バイトコードを分岐を含まない基本ブロックごとに分ける&lt;br&gt;
　　* 同じ名前のレジスタでも代入ごとに別の名前を付けて区別できるようにする(単一代入化)&lt;br&gt;
　　* 基本ブロック間でのレジスタのやり取りはどこのブロックからやってくるのか明示する(phi命令)&lt;/p&gt;
&lt;h2&gt;
  
  
  基本ブロックに分ける
&lt;/h2&gt;

&lt;p&gt;バイトコードを分岐を含まない基本ブロックで分けるためには命令列(irep-&amp;gt;iseq)について切れ目の位置を記録しておいて、その切れ目の位置で分ければよいわけです。切れ目の位置は次のようなところです。&lt;br&gt;
　　* JMP/JMPIF/JMPNOT/RETURN/RESCUE などのpcが単純なインクリメントではない命令&lt;br&gt;
　　* 上記命令の飛び先&lt;br&gt;
　　* 命令列の終わり(最後は必ずRETURNで終わるので実質必要ない)&lt;/p&gt;

&lt;p&gt;命令列を分けた後、命令の実行関係で基本ブロックごとにリンクを貼ります。RETURN/JMPなどは1本ですが、JMPIF/JMPNOTは2本(条件が真偽それぞれの飛び先)あります。&lt;/p&gt;
&lt;h2&gt;
  
  
  単一代入化
&lt;/h2&gt;

&lt;p&gt;おそらく一番めんどくさい話。同じ名前のレジスタであっても途中で代入すると別の名前のレジスタとして扱わないといけないと言う話。SSAを知っている人以外は意味不明だと思うので、SSAの別の資料を見てください。学術的にはレジスタのリネームとか色々やりようがあるだろうけど、ここでは簡単で泥臭い方法を用いてます。&lt;/p&gt;

&lt;p&gt;その方法とはレジスタをオブジェクトとして実体を用意してレジスタの代入のたびに新しいレジスタをアロケートするというものです。つまり、SSAで面倒くさいレジスタの名前付けがオブジェクトのアドレスで勝手にできてしまうわけです。しかも、型推論にはレジスタにいろいろな付加情報を加える必要がある(例えば型)のでレジスタをオブジェクトにするのはとても都合の良いことです。&lt;/p&gt;

&lt;p&gt;中間コードを図にするとこんな感じになります。&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1rmyv0vp10v761alokp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1rmyv0vp10v761alokp.png" alt="中間コード" width="590" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;命令オブジェクトは@OPに命令のオペコードがシンボルで入り、@inregが入力レジスタの配列、@outregが出力レジスターの配列です。ADD R2, 1, :+みたいな命令ではR2は入力レジスタであり、出力レジスタになるわけですが、別のレジスタとして取り扱います。出力レジスタは必ずその場でアロケートした(または、インスタンス変数みたいに別の場所に格納してある)レジスタになります。入力レジスタと出力レジスタは必ず分離することが大切です。前回のytljitでは入力レジスタと出力レジスタを区別しなかったばっかりにうまくいかなかった知見があります。&lt;/p&gt;
&lt;h1&gt;
  
  
  型表現
&lt;/h1&gt;

&lt;p&gt;前章で説明した中間コードを辿って言ってレジスタオブジェクトの型を決めていきます。レジスタは2つのレベルで型の情報を持ちます。それぞれ@same, @typeというインスタンス変数に保持します。&lt;br&gt;
&lt;a class="mentioned-user" href="https://dev.to/same"&gt;@same&lt;/a&gt;は自分(レジスタ)と同じ型を持つレジスタの集合を表します。@typeは実際に自分の型を保持します。&lt;/p&gt;

&lt;p&gt;なぜ、&lt;a class="mentioned-user" href="https://dev.to/same"&gt;@same&lt;/a&gt;が必要でしょうか？確かに、後から説明するようにレジスタがもつ型情報を別のレジスタに拡散していく形で型の推定をおこなうわけですが、&lt;a class="mentioned-user" href="https://dev.to/same"&gt;@same&lt;/a&gt;を使わずに@typeに直接入れてもよさそうなのにって思われるかもしれません。しかし、それではうまくいかない場合があります。元になるレジスタも実は型が確定していなくて後から確定する場合もあるからです。そういう場合には、&lt;a class="mentioned-user" href="https://dev.to/same"&gt;@same&lt;/a&gt;に同じ型のレジスタだよって情報だけ覚えておいて、あとから@typeを更新します。&lt;/p&gt;

&lt;p&gt;また、&lt;a class="mentioned-user" href="https://dev.to/same"&gt;@same&lt;/a&gt;は複数のレジスタを入れることが出来るので、必ずしも@sameに入っているレジスタと同じ型になるとは限りません。例えばif文でthen節とelse節が別の型を返した場合、ifの型はthen節とelse節と違うそれらのユニオンになります。&lt;/p&gt;
&lt;h2&gt;
  
  
  型オブジェクト
&lt;/h2&gt;

&lt;p&gt;　型そのものは型オブジェクトという型を表現するオブジェクトを用意します。型オブジェクトが満たすべき条件は、同じ型は同じに違う型は違うと区別できることです。簡単そうですが、非常に難しくおそらく最後まで細かい条件づけの調整が続くと思われます。型オブジェクトは型を表現したものですが、さらに分類できそれぞれサブクラスになっています。&lt;/p&gt;
&lt;h3&gt;
  
  
  BasicType
&lt;/h3&gt;
&lt;h3&gt;
  
  
  PrimitiveType
&lt;/h3&gt;
&lt;h3&gt;
  
  
  LiteralType
&lt;/h3&gt;
&lt;h3&gt;
  
  
  ContainerType
&lt;/h3&gt;
&lt;h3&gt;
  
  
  ProcType
&lt;/h3&gt;
&lt;h2&gt;
  
  
  tup
&lt;/h2&gt;

&lt;p&gt;Rubyは引数に型の制約を付けるという発想が無いため、メソッドの引数は必ず多相性を持ちます。同じメソッドでも別の型のオブジェクトを引数で渡すと全く別の動作をする可能性があります。つまり、違う引数の型で呼び出す場合は同じメソッドであっても違うメソッドとして取り扱うべきです。しかし、メソッドが同じか違うのかを区別する方法が必要になります。ナイーブな方法としてレジスタの型情報を引数の型の組と型のテーブルにするという方法が考えられます。これは、ytljitで使用したのですが、非常に効率が悪いです。そこで、引数の型の組にユニークな整数を割り当てそれをキーにするという方法を採用しました。この整数をTUP (Tupleより)とこのプログラムでは呼んでいます。&lt;/p&gt;
&lt;h2&gt;
  
  
  Array
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Proc
&lt;/h2&gt;

&lt;p&gt;Procはメソッドやブロックのオブジェクトです。irepと環境(mrubyのEnvオブジェクト)をwrapしています。Procオブジェクトで難しいのは2つのProcオブジェクトがあった場合の同一性です。一見、irepだけ見ればよさそうですが、それではうまくいかない場合があります。&lt;br&gt;
例えば、&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;のようなプログラムは {|x| block.call(x)} の部分のProcオブジェクトはirep(プログラムコード)だけだと常に同じになってしまいます。ここで、blockに例えばFixnumを返すProcオブジェクトとFloatを返すProcオブジェクトを渡しても同じ型になってしまうので、たとえばFixnum|Floatという型になってしまいます。&lt;/p&gt;

&lt;p&gt;そこで、Procオブジェクトは持っている環境に閉じ込められている変数の型も同一性を判定するのに用いるようにしています。こうすると、blockは{|x| block.call(x)}の環境に閉じ込められている変数ですので、blockの型によって別々のProcオブジェクトとして扱うことが出来ます。&lt;/p&gt;

&lt;h1&gt;
  
  
  インストラクションのルール例
&lt;/h1&gt;

&lt;h2&gt;
  
  
  LOADNIL
&lt;/h2&gt;

&lt;h2&gt;
  
  
  MOVE
&lt;/h2&gt;

&lt;h2&gt;
  
  
  SEND
&lt;/h2&gt;

&lt;h1&gt;
  
  
  組み込みメソッドのルール例
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Array#[]
&lt;/h2&gt;

&lt;h1&gt;
  
  
  条件分岐の扱い
&lt;/h1&gt;

</description>
      <category>type</category>
      <category>ruby</category>
      <category>mruby</category>
    </item>
    <item>
      <title>mrubyのJIT ver2　の構想</title>
      <dc:creator>Miura Hideki</dc:creator>
      <pubDate>Wed, 06 Dec 2017 10:13:06 +0000</pubDate>
      <link>https://dev.to/miura1729/mrubyjit-ver2-7fi</link>
      <guid>https://dev.to/miura1729/mrubyjit-ver2-7fi</guid>
      <description>&lt;h1&gt;
  
  
  はじめに
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/miura1729/mruby/" rel="noopener noreferrer"&gt;mrubyのJIT&lt;/a&gt;なるものを作っています。これは、&lt;a href="https://github.com/mruby/mruby" rel="noopener noreferrer"&gt;mruby&lt;/a&gt;にJITコンパイラを加えたものです。&lt;a href="https://qiita.com/miura1729/items/a1828849ec8fec596e74" rel="noopener noreferrer"&gt;一定の成果&lt;/a&gt; は得られたのですが、色々問題も発生してきました。もともと、動くことが第1目標で出来る限り無難な実装を選択したため、これ以上の拡張は厳しい気もします。&lt;/p&gt;

&lt;p&gt;こんな問題があります。このような問題に対して解決するような新たなバージョンを検討します。&lt;/p&gt;

&lt;h2&gt;
  
  
  　マルチスレッド
&lt;/h2&gt;

&lt;p&gt;　当然ながらマルチスレッド化は全く考えていません。さすがにmrubyの設計思想に反して大域変数を使うようなことはしていませんが、生成されたコード領域はグローバルで排他制御していませんのでマルチスレッドには対応していません。そのため、H2Oでの使用は事実上不可能です。&lt;/p&gt;

&lt;h2&gt;
  
  
  メモリ効率
&lt;/h2&gt;

&lt;p&gt;　コンパイル時の型情報やVMに戻る際のレジスタ情報などmrubyのJITで必要なデータを保持するためにメモリを使うのですが、これが何も考えずにad-hocに設計したため非常に無駄の多いものになっています。luajitのコードを読み、もっと効率の良いデータ構造が設計出来ることに気づきました。&lt;/p&gt;

&lt;h2&gt;
  
  
  移植性
&lt;/h2&gt;

&lt;p&gt;もともと、mrubyのJITはx86　32bitのみで動くように開発しました。その後、x64でも動く様変更しましたが、armでの動作などは全く考えていません。&lt;/p&gt;

&lt;h2&gt;
  
  
  コード領域の無駄遣い
&lt;/h2&gt;

&lt;p&gt;　mrubyのJITはメソッドの再定義など動的な性質に対して、コードの自己書き換えで対応している場合があります。ところが自己書き換えで無効になったコード領域はそのまま何も使われずそのままになります。コード領域に対するGCが望まれますが、現状の構造ではそれは不可能です。&lt;/p&gt;

&lt;h1&gt;
  
  
  マルチスレッド
&lt;/h1&gt;

&lt;p&gt;　ここでいうマルチスレッドとは3つの意味があります。１つはmrubyのインスタンスがmrbを複数用意することで複数存在しそれぞれがマルチスレッド動く場合。H2Oはこの状態で動いているためH2Oに対応するためにはこの形での実行が出来ることが必要になります。2つめは、コンパイラ中で並列で動くこと。例えばコンパイルをバッググラウンドで行うとかはこれにあたります。3つめは、コンパイルされたコードがマルチスレッドまたは並列で動くこと。mrubyは標準ではマルチスレッドはサポートしていませんが、マルチスレッドまたは並列に実行するようコンパイルすることは可能であるような表記は存在します。&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
