Code Reading: Dump and Print Function in llvm::Module Class
LLVM 3.1 のソースコードから、llvm::Module の IR 出力用関数のソースコードを読んだ。
/// Print the module to an output stream with an optional
/// AssemblyAnnotationWriter.
void print(raw_ostream &OS, AssemblyAnnotationWriter *AAW) const;
/// Dump the module to stderr (for debugging).
void dump() const;
実装は lib/VMCore/Module.cpp
ではなく、他の IR 出力用関数とともに lib/VMCore/AsmWriter.cpp
にまとめられている。まずは llvm::Module::print
を見てみよう。
void Module::print(raw_ostream &ROS, AssemblyAnnotationWriter *AAW) const {
SlotTracker SlotTable(this);
formatted_raw_ostream OS(ROS);
AssemblyWriter W(OS, SlotTable, this, AAW);
W.printModule(this);
}
llvm::Module::print
AssemblyWriter
は lib/VMCore/AsmWriter.cpp
の無名ネームスペースに定義されているクラスであり、llvm 内のさまざまな型をストリームに出力する関数を提供している。AssemblyWriter::printModule
のコードをざっと眺めてみれば、やっていることの想像はつくだろう。
void AssemblyWriter::printModule(const Module *M) {
if (!M->getModuleIdentifier().empty() &&
// Don't print the ID if it will start a new line (which would
// require a comment char before it).
M->getModuleIdentifier().find('\n') == std::string::npos)
Out << "; ModuleID = '" << M->getModuleIdentifier() << "'\n";
...
}
llvm::Module::dump
同じく lib/VMCore/AsmWriter.cpp
の最後の方に定義されている llvm::Module::dump
はどうなっているだろうか。
// Module::dump() - Allow printing of Modules from the debugger.
void Module::dump() const { print(dbgs(), 0); }
llvm::Module::print
を呼んでいるだけだ。
- 第一引数の raw_ostream には dbgs()
- 第二引数の AssemblyAnnotationWriter* には 0 (null pointer)
では、第一引数に渡している dbgs 関数の戻り値はなんだろうか?
llvm::dbgs
llvm::dbgs
の宣言は include/llvm/Support/Debug.h
にある。デバッグ用の出力ストリーム (raw_ostream) を返す関数だ。
/// dbgs() - This returns a reference to a raw_ostream for debugging
/// messages. If debugging is disabled it returns errs(). Use it
/// like: dbgs() << "foo" << "bar";
raw_ostream &dbgs();
実装は lib/Support/Debug.cpp
で定義されており、NDEBUG
が宣言されているか (非デバッグ・モード) 宣言されていないか (デバッグ・モード) で実装が切り替わるようになっている。
// All Debug.h functionality is a no-op in NDEBUG mode.
#ifndef NDEBUG
/// dbgs - Return a circular-buffered debug stream.
raw_ostream &llvm::dbgs() {
...
}
...
#else
// Avoid "has no symbols" warning.
namespace llvm {
/// dbgs - Return errs().
raw_ostream &dbgs() {
return errs();
}
}
#endif
非デバッグ・モードのとき dbgs()
は単に標準エラー出力への raw_ostream を返す。
#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif
...
/// errs() - This returns a reference to a raw_ostream for standard error.
/// Use it like: errs() << "foo" << "bar";
raw_ostream &llvm::errs() {
// Set standard error to be unbuffered by default.
static raw_fd_ostream S(STDERR_FILENO, false, true);
return S;
}
デバッグ・モードの場合の実装も見てみよう。
/// dbgs - Return a circular-buffered debug stream.
raw_ostream &llvm::dbgs() {
// Do one-time initialization in a thread-safe way.
static struct dbgstream {
circular_raw_ostream strm;
dbgstream() :
strm(errs(), "*** Debug Log Output ***\n",
(!EnableDebugBuffering || !DebugFlag) ? 0 : DebugBufferSize) {
if (EnableDebugBuffering && DebugFlag && DebugBufferSize != 0)
// TODO: Add a handler for SIGUSER1-type signals so the user can
// force a debug dump.
sys::AddSignalHandler(&debug_user_sig_handler, 0);
// Otherwise we've already set the debug stream buffer size to
// zero, disabling buffering so it will output directly to errs().
}
} thestrm;
return thestrm.strm;
}
デバッグ・モードの場合は、標準エラー出力をラップした circular_raw_ostream を返している。static な構造体のコンストラクタで初期化することで、 シグナル・ハンドラなども設定しているのが興味深い。