はじめに
きつねさんの本を用いてLLVMの勉強を始めたところ、LLVMのVersion違いのために、CFGの生成に失敗してしまいました。同じ問題に直面する方がいるかと思いましたので、この記事では、LLVMのVersion 14.0.6(2022年8月時点の最新)で確認したCFGの生成方法を紹介します。尚、今回生成するCFGは単純な分岐処理を行うプログラムを対象としたもので、最終的に以下のように表示されます。
対象読者
LLVMに関する学習を開始した方
目標
CFGの生成ができるようになること
目次
- LLVMとは
- 環境構築
- CFGの生成
- まとめ
本文
1. LLVMとは
1.1 概要
LLVMは、プログラミング言語開発における主にミドルエンドとバックエンドの処理に必要な機能(コンパイル時、リンク時、実行時の最適化などの複雑な処理に関する機能)をAPIとして提供することで、プログラミング言語の開発を支援します。
また、下記の図に示すように、LLVMを用いたプログラミング言語開発では、ミドルエンドが抽象化されているために、複数のフロントエンド、複数のバックエンドに対応することができます。
1.2 CFG (Control Flow Graph)
LLVMで解析する対象のプログラムはBasic Blockと呼ばれる単位に分解されます。
Basic Blockとは、1つの入口と1つの出口を備え、内部に命令を含むコードのセクションです。
CFGは、1つ以上のBasic Blockの入口と出口を矢印でつないだグラフであり、プログラムの制御フローを可視化したものです。
2. 環境構築
2.1 事前準備
今回の環境を示します。
-
OS
Ubuntu 20.04.3 LTS -
Kernel
5.15.0-46-generic -
Tool
clang 10.0.0-4ubuntu1
cmake 3.16.3
gcc 9.4.0
python 3.8.10
make 4.2.1
2.2 LLVMビルド
LLVMをビルドします。
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" ../llvm
make
make check-clang
2.3 環境変数の設定
buildしたbinのパスを通します。
また、includeへのパスを変数として定義しておきます。
# llvm binのパス : $HOME/llvm-project/build/bin
export LLVM_BIN_PATH = "$HOME/llvm-project/build/bin"
export PATH=$PATH:$LLVM_BIN_PATH
# llvm includeのパス : $HOME/llvm-project/llvm/include
export LLVM_INC_PATH = "$HOME/llvm-project/llvm/include"
3. CFGの生成
確認用に、分岐のあるソースコードを準備します。
#include <stdio.h>
int main(int argc, char ** argv) {
int i;
if(argc == 0){
i = 1;
}else{
i=2;
}
return 0;
}
以下のコマンドを実行し、中間表現のコード(LLVM IR)を生成します。
clang++ -S -emit-llvm -I ${LLVM_INC_PATH} -o main.ll main.cpp
以下のコマンドを実行し、CFGの情報を隠しファイル(.main.dot)として生成します。
opt -dot-cfg main.ll -disable-output -enable-new-pm=0
これまでに生成されたファイルを確認します。
ls -a
main.cpp .main.dot main.ll
以下のコマンドを実行し、png形式の画像を生成します。
dot -Tpng .main.dot -o main.png
以下のコマンドを実行し、png形式に変換した画像を表示します。
eog main.png
冒頭にも示しましたが、無事に表示されることが確認できました。
4. まとめ
LLVMにおけるCFGの生成方法について紹介しました。
参考文献
LLVM Documentation
LLVM Control Flow Graph
きつねさんでもわかるLLVM ~コンパイラを自作するためのガイドブック