MLIRについて勉強している。
ちょっとTypeについて詰まってしまったので、ドキュメントを読み直している。
MLIRは完全に拡張可能なインフラストラクチャとして設計されている; 属性(定数メタデータなど)、演算、型などについての閉じた集合が存在しない。MLIRはこの拡張性をDialect
(方言)という考え方によってサポートしている。Dialectはユニークなnamespace
によって抽象化グループである。
MLIRでは、Operations
が抽象化と計算のコアユニットであり、多くのLLVM命令と似ている。Operations
はアプリケーション特有のセマンティックを持つコタができ、LLVMのコアIR: 命令、グローバル(関数やモジュールなど)のすべてを表現することができる。
以下はToyのtranspose操作のMLIRアセンブリである。
%t_tensor = "toy.transpose"(%tensor) {inplace = true} : (tensor<2x3xf64>) -> tensor<3x2xf64> loc("example/file/path":12:1)
%t_tensor
“toy.transpose”
- Operationの名前。これは一意な文字列であることが期待され、"."の前にDialectの名前空間が付く。これは、toy方言での転置操作と読むことができる
(%tensor)
{ inplace = true }
- 0個以上の属性からなる辞書で、常に一定の値を持つ特別なオペランドである。ここでは、'
inplace
'という名前のブール型属性を定義しており、一定の値としてtrueを持つ
- 0個以上の属性からなる辞書で、常に一定の値を持つ特別なオペランドである。ここでは、'
(tensor<2x3xf64>) -> (tensor<3x2xf64)
- これは、関数の形で操作の型を指し、括弧の中に引数の型を、その後に戻り値の型を示す
loc(”example/file/path”:12:1)
- このOperationが発生したソースコード内の位置を示す
Operationはいくつかのコンセプトに基づいてモデル化されている。
- Operationの名前
- SSAオペランドの値
- 属性のリスト (属性、そのOperationに対する固定の値、cmpiの比較の種類など)
- 結果の値の型のリスト
- デバッグ目的のためのソースの位置
- 後続ブロックのリスト(殆どの場合分岐命令のため)
- リージョンのリスト(関数のような構造的な操作のため)
MLIRでは,すべてのOperationにソース位置の指定が必須である。LLVMでは、デバッグ情報の場所はメタデータであり、削除することも可能だが、MLIRではlocationはコア要件であり、APIはそれに依存し操作される。ロケーション情報を削除するという選択は、誤りにより発生することのない明示的な選択であると言える。
例として もし変換がある操作を別の操作に置き換えるなら、その新しい操作には新しいロケーションが必要である。これによって、その操作がどこから来たのか追跡することが可能になる。
コンパイラのパスをテストするためのツールであるmlir-opt
ツールは、デフォルトでは出力にロケーションを含めないことに注意すること。mlir-print-debuginfo
フラグを指定すると、ロケーションを含める。(その他のオプションは mlir-opt --help
を実行すること)。
不透明なAPI
MLIRはOpreation, 属性、型などすべてのIRの要素がカスタマイズ可能になっている。IRの要素は基本的なコンセプトを削減できるようになっている。例えば、Toy言語ではmlir-optによりToyに関連するDialectを登録せずに
func.func @toy_func(%tensor: tensor<2x3xf64>) -> tensor<3x2xf64> { %t_tensor = "toy.transpose"(%tensor) { inplace = true } : (tensor<2x3xf64>) -> tensor<3x2xf64> return %t_tensor : tensor<3x2xf64> }
未登録の属性,Operation,型の場合,MLIRは構造的な制約(例えばdominanceなど)を強制するが、それ以外は完全に不透明となる。例えば,未登録の操作は、特定のデータ型に対して操作可能か,いくつのオペランドを取ることができるか,いくつの結果を生成するかなど,MLIRはほとんど情報を持たない。このような柔軟性は,ブートストラップには有用だが,成熟したシステムでは一般に避けるべきである。未登録の操作は、変換や分析において保守的に扱わなければならず、構築や操作が非常に難しくなる。
この処理は、Toyのために無効なIRを作成し、Verfierを引っかけることなくラウンドトリップすることで確認することができる。
func.func @main() { %0 = "toy.print"() : () -> tensor<2x3xf64> }
上記のプログラムにはいくつかの問題が存在する: toy.print
はターミネータではないし,オペランドを取るべきだし, 値を返すべきでもない。次節では、MLIRに方言と操作を登録し、Verifierと接続し、操作を操作するためのより良いAPIを追加する予定である。