2 분 소요

실습 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 인스턴스를 사용함

image

예제

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  

실행 결과

image

위와 같이 Test.ll 파일이 생성되는 것을 확인할 수 있으며 Module, Function, BasicBlock, Instruction 구조를 확인할 수 있다. 위의 Test.c 코드는 BasicBlock이 존재하지 않아 이를 제외하고 출력하였다.

image

태그:

카테고리:

업데이트:

댓글남기기