LLVM Optimization (3)
실습 2
[실습 2]에서는 LLVM의 기본적인 module 구조를 이해하고, IR 내의 명렬어들을 C++ iterator를 사용하여 순회 및 출력을 한다.
llvm::Module
->llvm::Function
->llvm::BasicBlock
->llvm::Instruction
- LLVM IR에서는 위와 같은 계층 구조로 타겟 프로그램을 관리함
- Module: 일반적으로 하나의 소스 파일
- Function: 함수
- Basic Block: Straight Forward Code Section으로 Branch나 Return 같은 명령어로 끝남
- Instruction: 명령어
- 전역변수를 의미하는
llvm::GlobalVariable
등은llvm::Module
에서 따로 관리 - 각 클래스들의 iterator 또는 기타 메소드를 통해 계층 구조에 접근 가능
- 출력을 위해서 llvm::raw_os_ostream 인스턴스를 사용함
예제
PrintInst.cpp 코드는 Function과 BasicBlock, Instruction을 순회하며 출력하는 코드이다.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <stdlib.h>
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/DiagnosticHandler.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/Support/raw_ostream.h"
std::string input_path;
llvm::LLVMContext* TheContext;
std::unique_ptr<llvm::Module> TheModule;
void ParseIRSource(void);
void TraverseModule(void);
int main(int argc , char** argv)
{
if(argc < 2)
{
std::cout << "Usage: ./PrintInst <ir_file_path>" << std::endl;
return -1;
}
input_path = std::string(argv[1]);
// Read & Parse IR Source
ParseIRSource();
// Traverse TheModule
TraverseModule();
return 0;
}
// Read & Parse IR Sources
// Human-readable assembly(*.ll) or Bitcode(*.bc) format is required
void ParseIRSource(void)
{
llvm::SMDiagnostic err;
// Context
TheContext = new llvm::LLVMContext();
if( ! TheContext )
{
std::cerr << "Failed to allocated llvm::LLVMContext" << std::endl;
exit( -1 );
}
// Module from IR Source
TheModule = llvm::parseIRFile(input_path, err, *TheContext);
if( ! TheModule )
{
std::cerr << "Failed to parse IR File : " << input_path << std::endl;
exit( -1 );
}
}
// Traverse Instructions in TheModule
void TraverseModule(void)
{
llvm::raw_os_ostream raw_cout( std::cout );
// Module::iterator --> Function
for( llvm::Module::iterator ModIter = TheModule->begin(); ModIter != TheModule->end(); ++ModIter )
{
llvm::Function* Func = llvm::cast<llvm::Function>(ModIter);
// Print Function Name
raw_cout << Func->getName() << '\n';
// Function::iterator --> BasicBlock
for( llvm::Function::iterator FuncIter = Func->begin(); FuncIter != Func->end(); ++FuncIter )
{
llvm::BasicBlock* BB = llvm::cast<llvm::BasicBlock>(FuncIter);
raw_cout << BB->getName() << '\n';
// BasicBlock::iterator --> Instruction
for( llvm::BasicBlock::iterator BBIter = BB->begin(); BBIter != BB->end(); ++BBIter )
{
llvm::Instruction* Inst = llvm::cast<llvm::Instruction>(BBIter);
// Print Instruction
raw_cout << '\t';
Inst->print(raw_cout);
raw_cout << '\n';
}
}
}
}
Test.c 코드
#include <stdio.h>
int func1(void)
{
int a = 4;
return a;
}
int main(void)
{
return 0;
}
PrintInst.cpp 컴파일하기
clang++ PrintInst.cpp -o PrintInst $(llvm-config --cxxflags --ldflags --system-libs --libs mcjit irreader)
타켓 어플리케이션을 IR 레벨로 컴파일하기
clang -emit-llvm -S Test.c -o Test.ll
프로그램을 통해 IR 파일을 읽고 명령어 출력하기
./PrintInst Test.ll
실행 결과
위와 같이 Test.ll 파일이 생성되는 것을 확인할 수 있으며 Module, Function, BasicBlock, Instruction 구조를 확인할 수 있다. 위의 Test.c 코드는 BasicBlock이 존재하지 않아 이를 제외하고 출력하였다.
댓글남기기