FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

MLIRの勉強 (3. 自作言語のMLIR変換とダンプ)

MLIRについて勉強している。

独自言語を作成し、その中間表現をMLIRを使って表現してみることに挑戦する。

ここで想定する流れは以下の通りだ。

  1. 独自言語(MYSV : System Verilogオリジナル改造バージョン) を読み込んで、独自形式でASTを作成する
  2. MYSVのASTをMLIRのツリーに変換する
  3. MLIRのツリーをダンプする

次はMLIRに変換してダンプできるようになろう。

MLIRの変換およびMLIRのダンプには、MLIRGen.cppおよびMLIRGen.hというファイルを作成する。

MLIRGen.hのファイル自体は一番トップレベルのmlirGen(mlir::MLIRContext, ModuleAST)を宣言する。最もトップレベルでの変換を行うための関数だ。ここから階層的にMLIRに変換する関数を実装していく。

namespace mysv {
   class ModuleAST;

   /// Emit IR for the given Mysv moduleAST, returns a newly created MLIR module
   /// or nullptr on failure.
   mlir::OwningOpRef<mlir::ModuleOp> mlirGen(mlir::MLIRContext &context,
                                             ModuleAST &moduleAST);
 } // namespace mysv

ModuleASTの変換から始めていこう。

// The public API for codegen.
 mlir::OwningOpRef<mlir::ModuleOp> mlirGen(mlir::MLIRContext &context,
                                           ModuleAST &moduleAST) {
   return MLIRGenImpl(context).mlirGen(moduleAST);
 }

ModuleASTは、Assign文の集合で構成されるので、内部のAssignExprASTを繰り返しMLIRに変換する処理で構成されている。

   /// Public API: convert the AST for a Mysv module (source file) to an MLIR
   /// Module operation.
   mlir::ModuleOp mlirGen(ModuleAST &moduleAST) {
     // Create a scope in the symbol table to hold variable declarations.
     ScopedHashTableScope<llvm::StringRef, mlir::Value> varScope(symbolTable);

     // We create an empty MLIR module and codegen functions one at a time and
     // add them to the module.
     theModule = mlir::ModuleOp::create(builder.getUnknownLoc());

     for (AssignExprAST &a : moduleAST)
       mlirGen(a);

このsymbolTableというのはクラス内で宣言されているメンバ変数で、変数の名前とMLIRのmlir::Valueをマップするためのテーブルらしい。

   /// The symbol table maps a variable name to a value in the current scope.
   /// Entering a function creates a new scope, and the function arguments are
   /// added to the mapping. When the processing of a function is terminated, the
   /// scope is destroyed and the mappings created in this scope are dropped.
   llvm::ScopedHashTable<StringRef, mlir::Value> symbolTable;

AssignExprASTの変換により、変換用の固定値(数値)のMLIRツリーを作り上げていく。

   /// Emit a new function and add it to the MLIR module.
   mlir::Value mlirGen(AssignExprAST &assignAST) {
     // Create a scope in the symbol table to hold variable declarations.
     ScopedHashTableScope<llvm::StringRef, mlir::Value> varScope(symbolTable);

     // Create an MLIR function for the given prototype.
     builder.setInsertionPointToEnd(theModule.getBody());

     auto *init = assignAST.getInitVal();
     if (!init) {
       emitError(loc(assignAST.loc()),
                 "missing initializer in variable declaration");
       return nullptr;
     }

     auto assign = mlirGen(*init);
     if (!assign)
       return nullptr;

これにより、とりあえずMLIRを生成してダンプすることができるようになった。

$ ./bin/mysv --emit=mlir ../mlir/examples/mysv/test/assign.sv

module {
  %0 = "mysv.constant"() {value = 0 : si64} : () -> i64
  %1 = "mysv.constant"() {value = 2 : si64} : () -> i64
}