FPGA開発日記

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

llcの生成するDAGグラフ生成の出力ファイル名を指定できるようにしたい

LLVMのバックエンドコンパイラであるllcは、SelectionDAGの変換プロセスの実行中に変換状態を確認するためにDAG(Directed Acyclic Graph)が生成できるようになっている。

$ ./bin/llc --march=myriscvx32 --debug --view-isel-dags sample.bc

llvm.org

-view-dag-combine1-dags displays the DAG after being built, before the first optimization pass.
-view-legalize-dags displays the DAG before Legalization.
-view-dag-combine2-dags displays the DAG before the second optimization pass.
-view-isel-dags displays the DAG before the Select phase.
-view-sched-dags displays the DAG before Scheduling.

生成されるのはGraphviz用のdotファイルで、これをgraphvizのdotコマンドに流し込むことでグラフを画像などに変換することができる。非常に便利な機能だ。

$ dot -Tpng /tmp/dag._Z11zero_returnv-4503a6.dot > zero_return.sched-dag.png

しかし面倒なのは、この機能は生成するdotファイルのファイル名を指定することができず、/tmp/ディレクトリに適当な名前で制止されてしまう。ログファイルを見ながらどのdotファイル化を確認してdotコマンドに流さなければならないのでちょっと面倒くさいのだ。

そこで、オプションを追加して生成されるdotファイルを指定することはできないだろうかと考えた。 具体的には、以下のようにオプションを指定して出力するファイル名を決めたい。

$ ./bin/llc --march=myriscvx32 --debug --view-isel-dags sample.bc --output-dag-filename=log.dot sample.bc

ただしこれにはいろいろ問題があって、まず複数ステージのDAGファイルを出力する場合ファイル名をどのように指定すべきなのか、そして複数のブロックに対してDAGを生成する場合にもファイル名をどのようにすべきなのかという問題があるが、とりあえずはそれを無視して指定したファイル名でdotファイルを生成できれば良いものとする。

まず解析の結果、上記の--view-isel-dagsなどのオプションはllvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cppで取得していることが分かった。

...
static cl::opt<std::string>
FilterDAGBasicBlockName("filter-view-dags", cl::Hidden,
                        cl::desc("Only display the basic block whose name "
                                 "matches this for all view-*-dags options"));
static cl::opt<bool>
ViewDAGCombine1("view-dag-combine1-dags", cl::Hidden,
          cl::desc("Pop up a window to show dags before the first "
                   "dag combine pass"));
static cl::opt<bool>
ViewLegalizeTypesDAGs("view-legalize-types-dags", cl::Hidden,
          cl::desc("Pop up a window to show dags before legalize types"));
static cl::opt<bool>
    ViewDAGCombineLT("view-dag-combine-lt-dags", cl::Hidden,
                     cl::desc("Pop up a window to show dags before the post "
                              "legalize types dag combine pass"));
...

そしてオプションに応じてDAGを生成する記述は以下の部分になる。

  if (ViewDAGCombine1 && MatchFilterBB)
    CurDAG->viewGraph("dag-combine1 input for " + BlockName);
...
  if (ViewLegalizeTypesDAGs && MatchFilterBB)
    CurDAG->viewGraph("legalize-types input for " + BlockName);
...
    if (ViewDAGCombineLT && MatchFilterBB)
      CurDAG->viewGraph("dag-combine-lt input for " + BlockName);
...
    if (ViewDAGCombineLT && MatchFilterBB)
      CurDAG->viewGraph("dag-combine-lv input for " + BlockName);
...
  if (ViewLegalizeDAGs && MatchFilterBB)
    CurDAG->viewGraph("legalize input for " + BlockName);
...
  if (ViewDAGCombine2 && MatchFilterBB)
    CurDAG->viewGraph("dag-combine2 input for " + BlockName);
...
  if (ViewISelDAGs && MatchFilterBB)
    CurDAG->viewGraph("isel input for " + BlockName);
...
  if (ViewSchedDAGs && MatchFilterBB)
    CurDAG->viewGraph("scheduler input for " + BlockName);
...

とにかくたくさんあるが、viewGraphという関数に飛んでいる。

  • llvm/lib/CodeGen/SelectionDAG/SelectionDAGPrinter.cpp
/// viewGraph - Pop up a ghostview window with the reachable parts of the DAG
/// rendered using 'dot'.
///
void SelectionDAG::viewGraph(const std::string &Title) {
// This code is only for debugging!
#ifndef NDEBUG
  ViewGraph(this, "dag." + getMachineFunction().getName(),
            false, Title);
#else
  errs() << "SelectionDAG::viewGraph is only available in debug builds on "
         << "systems with Graphviz or gv!\n";
#endif  // NDEBUG
}

さらにViewGraphというグローバルに定義してある関数にジャンプする。

  • llvm/include/llvm/Support/GraphWriter.h
/// ViewGraph - Emit a dot graph, run 'dot', run gv on the postscript file,
/// then cleanup.  For use from the debugger.
///
template<typename GraphType>
void ViewGraph(const GraphType &G, const Twine &Name,
               bool ShortNames = false, const Twine &Title = "",
               GraphProgram::Name Program = GraphProgram::DOT) {
  std::string Filename = llvm::WriteGraph(G, Name, ShortNames, Title);

  if (Filename.empty())
    return;

  DisplayGraph(Filename, false, Program);
}

このあたりで分かる通り、llvm::WriteGraph()でファイル名がランダムに付けられることが分かる。そして驚くべきことに、WriteGraphにはファイル名を指定することができる引数が用意されていた!

/// Writes graph into a provided {@code Filename}.
/// If {@code Filename} is empty, generates a random one.
/// \return The resulting filename, or an empty string if writing
/// failed.
template <typename GraphType>
std::string WriteGraph(const GraphType &G, const Twine &Name,
                       bool ShortNames = false,
                       const Twine &Title = "",
                       std::string Filename = "") {
...

まずはオプションを追加する。--output-dag-filenameオプションを追加してグラフの名前を指定できるようにする。

--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -139,6 +139,9 @@ UseMBPI("use-mbpi",
         cl::init(true), cl::Hidden);

 #ifndef NDEBUG
+cl::opt<std::string>
+ViewDagOutputFileName("output-dag-filename", cl::Hidden,
+                      cl::desc("DAG output filename"));

つぎにSelectionDAGの`viewGraphの呼び出しインタフェースを改造する。引数を1つ追加してファイル名を指定できるようにする。

@@ -806,7 +809,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
              CurDAG->dump());

   if (ViewDAGCombine1 && MatchFilterBB)
-    CurDAG->viewGraph("dag-combine1 input for " + BlockName);
+    CurDAG->viewGraph("dag-combine1 input for " + BlockName, ViewDagOutputFileName);

   // Run the DAG combiner in pre-legalize mode.
   {
@@ -828,7 +831,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
   // Second step, hack on the DAG until it only uses operations and types that
   // the target supports.
   if (ViewLegalizeTypesDAGs && MatchFilterBB)
-    CurDAG->viewGraph("legalize-types input for " + BlockName);
+    CurDAG->viewGraph("legalize-types input for " + BlockName, ViewDagOutputFileName);

   bool Changed;
   {
@@ -852,7 +855,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {

   if (Changed) {
     if (ViewDAGCombineLT && MatchFilterBB)
-      CurDAG->viewGraph("dag-combine-lt input for " + BlockName);
+      CurDAG->viewGraph("dag-combine-lt input for " + BlockName, ViewDagOutputFileName);

     // Run the DAG combiner in post-type-legalize mode.
     {
@@ -896,7 +899,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
                CurDAG->dump());

     if (ViewDAGCombineLT && MatchFilterBB)
-      CurDAG->viewGraph("dag-combine-lv input for " + BlockName);
+      CurDAG->viewGraph("dag-combine-lv input for " + BlockName, ViewDagOutputFileName);

     // Run the DAG combiner in post-type-legalize mode.
     {
@@ -917,7 +920,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
   }

   if (ViewLegalizeDAGs && MatchFilterBB)
-    CurDAG->viewGraph("legalize input for " + BlockName);
+    CurDAG->viewGraph("legalize input for " + BlockName, ViewDagOutputFileName);

   {
     NamedRegionTimer T("legalize", "DAG Legalization", GroupName,
@@ -936,7 +939,7 @@ void SelectionDAGISel::CodeGenAndEmitDAG() {
              CurDAG->dump());

   if (ViewDAGCombine2 && MatchFilterBB)
-    CurDAG->viewGraph("dag-combine2 input for " + BlockName);
+    CurDAG->viewGraph("dag-combine2 input for " + BlockName, ViewDagOutputFileName);

viewGraphの改造だ。1つ引数をつけたし、内部のViewGraph()にも引数を付け足す。

diff --git a/llvm/lib/CodeGen/ScheduleDAGPrinter.cpp b/llvm/lib/CodeGen/ScheduleDAGPrinter.cpp
index 8d04711f07c6..5af446c0532f 100644
--- a/llvm/lib/CodeGen/ScheduleDAGPrinter.cpp
+++ b/llvm/lib/CodeGen/ScheduleDAGPrinter.cpp
@@ -81,10 +81,10 @@ std::string DOTGraphTraits<ScheduleDAG*>::getNodeLabel(const SUnit *SU,
 /// viewGraph - Pop up a ghostview window with the reachable parts of the DAG
 /// rendered using 'dot'.
 ///
-void ScheduleDAG::viewGraph(const Twine &Name, const Twine &Title) {
+void ScheduleDAG::viewGraph(const Twine &Name, const Twine &Title, std::string filename) {
   // This code is only for debugging!
 #ifndef NDEBUG
-  ViewGraph(this, Name, false, Title);
+  ViewGraph(this, Name, false, filename, Title);
 #else
   errs() << "ScheduleDAG::viewGraph is only available in debug builds on "
          << "systems with Graphviz or gv!\n";
--- a/llvm/include/llvm/Support/GraphWriter.h
+++ b/llvm/include/llvm/Support/GraphWriter.h
@@ -364,9 +364,10 @@ std::string WriteGraph(const GraphType &G, const Twine &Name,
 ///
 template<typename GraphType>
 void ViewGraph(const GraphType &G, const Twine &Name,
-               bool ShortNames = false, const Twine &Title = "",
+               bool ShortNames = false, std::string filename = "",
+               const Twine &Title = "",
                GraphProgram::Name Program = GraphProgram::DOT) {
-  std::string Filename = llvm::WriteGraph(G, Name, ShortNames, Title);
+  std::string Filename = llvm::WriteGraph(G, Name, ShortNames, Title, filename);

   if (Filename.empty())
     return;

WriteGraphにはファイル名を付加できる引数が備わっているので、これで接続は完了となる。 ビルドして実行してみる。

$ ./bin/llc --march=myriscvx32 --debug --view-isel-dags sample.bc --output-dag-filename=log.dot sample.bc

指定どおりlog.dotを生成することができた。pngファイルにして確認する。

$ dot -Tpng log.dot > log.png
f:id:msyksphinz:20200410011831p:plain