FPGA開発日記

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

CIRCTの仕組みとMLIRを読み解く (MLIRのTarget Descriptionを読み解く)

前回の解析に基づき、FIRRTLのParseを続けている。

Circuitを構築すると、MLIRで作成したCircitOpを構築するようだ。

 /// file ::= circuit
 /// circuit ::= 'circuit' id ':' info? INDENT module* DEDENT EOF
 ///
 ParseResult FIRCircuitParser::parseCircuit() {
/* ... 中略 ... */
   OpBuilder b(mlirModule.getBodyRegion());

   // Create the top-level circuit op in the MLIR module.
   auto circuit = b.create<CircuitOp>(info.getLoc(), name, annotationVec);

CircitOpOpBuilderを使って構築する。CircuitOpはTarget Descriptionを使って構築しているようだ。

  • circt/include/circt/Dialect/FIRRTL/FIRRTLStructure.td
 def CircuitOp : FIRRTLOp<"circuit",
       [IsolatedFromAbove, SymbolTable,
        SingleBlockImplicitTerminator<"DoneOp">]> {
   let summary = "FIRRTL Circuit";
   let description = [{
     The "firrtl.circuit" operation represents an overall Verilog circuit,
     containing a list of modules.
   }];
/* ... 中略 ... */

これはTarget Descriptionにより、circt/Dialect/FIRRTL/FIRRTL.h.incに変換されている。

 class CircuitOp : public ::mlir::Op<CircuitOp, ::mlir::OpTrait::OneRegion, ::mlir::OpTrait::ZeroResult, ::mlir::OpTrait::ZeroSuccessor, ::mlir::OpTrait::ZeroOperands, ::mlir::OpTrait::IsIsolatedFromAbove, ::mlir::OpTrait::SymbolTable, ::mlir::OpTrait::SingleBlockImplic\
 itTerminator<DoneOp>::Impl> {
 public:
   using Op::Op;
   using Op::print;
   using Adaptor = CircuitOpAdaptor;
   static constexpr ::llvm::StringLiteral getOperationName() {
   return ::llvm::StringLiteral("firrtl.circuit");
 }
   std::pair<unsigned, unsigned> getODSOperandIndexAndLength(unsigned index);
   ::mlir::Operation::operand_range getODSOperands(unsigned index);
   std::pair<unsigned, unsigned> getODSResultIndexAndLength(unsigned index);
   ::mlir::Operation::result_range getODSResults(unsigned index);
   ::mlir::Region &body();
   ::mlir::StringAttr nameAttr();
   ::llvm::StringRef name();
   ::mlir::ArrayAttr annotationsAttr();
   ::mlir::ArrayAttr annotations();
   void nameAttr(::mlir::StringAttr attr);
   void annotationsAttr(::mlir::ArrayAttr attr);
   static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, StringAttr name, ArrayAttr annotations = ArrayAttr());
   static ::mlir::ParseResult parse(::mlir::OpAsmParser &parser, ::mlir::OperationState &result);
   void print(::mlir::OpAsmPrinter &p);
   ::mlir::LogicalResult verify();

     /* ... ここの部分はTarget Descriptionからそのまま引用されてきている ... */
     OpBuilder getBodyBuilder() {
       assert(!body().empty() && "Unexpected empty 'body' region.");
       Block &bodyBlock = body().front();
       return OpBuilder(&bodyBlock, std::prev(bodyBlock.end()));
     }

     /// Return body of this circuit.
     Region &getBodyRegion();
     Block *getBody();

     // Return the main module that is the entry point of the circuit.  This may
     // be either an FModuleOp or an FExtModuleOp.
     Operation *getMainModule();
     /* ... Target Description の引用ここまでで終わり ... */
 };

getBodyRegion(), getBody()は以下で定義されていた。

  • circt/lib/Dialect/FIRRTL/FIRRTLOps.cpp
 Region &CircuitOp::getBodyRegion() { return getOperation()->getRegion(0); }
 Block *CircuitOp::getBody() { return &getBodyRegion().front(); }

Blockの定義は以下のようだ。

  • circt/llvm/mlir/include/mlir/IR/Block.h
 /// `Block` represents an ordered list of `Operation`s.
 class Block : public IRObjectWithUseList<BlockOperand>,
               public llvm::ilist_node_with_parent<Block, Region> {
 public:
   explicit Block() {}
   ~Block();

   void clear() {
     // Drop all references from within this block.
/* ... 中略 ... */