https://github.com/leejaymin/llvm8-tutorials-jemin/tree/master?tab=readme-ov-file
해당 주소에 있는 튜토리얼로 진행하였다.
해당 실습에서는 LLVM의 ModulePass를 활용하여, 새로운 함수와 명령어를 생성한다.
모듈패스란, LLVM의 분석 단위이다. 프로그램 전체 즉 여러 함수와 글로벌 변수를 한꺼번에 다루어 효율적으로 처리한다. 클래스에서 runOnModule이라는 메서드를 구현하여 실제 작업을 수행한다.
간단한 함수를 삽입하고, 해당 함수에 필요한 Basic block과 명령어를 추가하는 코드를 작성하여 보자.
목표는 int inc(int n) { return n+1; } 함수를 LLM 모듈에 삽입하는 패스를 작성하는 것이다.
작성해야 하는 파일은 다음과 같다.
InsertInc.cpp
InsertInc.h
Makefile
test.c
test.ll
//===- Hello.cpp - Example code from "Writing an LLVM Pass" ---------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// docs/WritingAnLLVMPass.html 문서의 Hello World 패스 예제 코드(2가지 버전 구현)
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/raw_ostream.h" // LLVM용 출력 지원
#include "llvm/IR/IRBuilder.h" // IR 명령어 생성용 빌더
#include "llvm/IR/DerivedTypes.h" // 함수 타입 등 파생 타입
#include "llvm/IR/Instructions.h" // IR 명령어 관련
#include "InsertInc.h" // 본인 패스 선언부(헤더)
#include "llvm/IR/Value.h" // Value 타입 관련
#define DEBUG_TYPE "hello" // 디버그용(디버그 메시지 필터 등)
// InsertInc 패스 본체 (레거시 패스 방식)
bool InsertInc::runOnModule(Module &M) {
LLVMContext &Context =M.getContext(); // LLVM 컨텍스트(타입, 상수 등 생성에 사용)
// "inc"라는 함수(시그니처: int inc(int))를 모듈에 삽입 또는 이미 있으면 반환
Constant *newF = M.getOrInsertFunction("inc", \
Type::getInt32Ty(Context), // 리턴 타입: int32
Type::getInt32Ty(Context)); // 파라미터 타입: int32
// inc 함수의 "entry" 기본 블록 생성, 함수에 연결
BasicBlock *BB = BasicBlock::Create(Context, "entry",
dyn_cast<Function>(newF));
// 함수의 첫 번째(유일한) 인자 포인터 획득 (int n)
Argument *Arg = dyn_cast<Function>(newF)->arg_begin();
// 1이라는 상수 생성(int32)
Constant *One = ConstantInt::get(Type::getInt32Ty(Context), 1);
// IR 명령어를 BB에 삽입할 빌더 초기화(BB 끝에 명령어 자동 추가)
IRBuilder<> Builder(BB);
// n + 1 (Add 명령어) 삽입, 결과를 result 변수에 저장
Value *result = Builder.CreateAdd(Arg, One);
// result를 반환하는 Return 명령어 삽입
Builder.CreateRet(result);
// 모듈을 수정했으니 true 반환(새 함수와 명령어를 추가)
return true;
}
// 다른 분석 패스 결과를 전혀 깨뜨리지 않겠다고 명시(패스 실행 전후로 변화 없음)
void InsertInc::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
}
// 레거시 패스 매니저용 ID(필수, 글로벌 변수)
char InsertInc::ID = 0;
// 패스 등록 (opt 등에서 "InsertInc"로 사용, "InsertInc Pass" 설명 표시)
static RegisterPass<InsertInc> X("InsertInc", "InsertInc Pass ");
Github에서 해당 프로젝트의 소스코드를 보면, Legacy Pass Manager 기반으로 작성되어 있는 것을 확인할 수 있다. 하지만 최신 LLVM에서는 New Pass Manager을 사용하므로 해당 API에 맞게 코드를 수정해야 한다.
패스 등록 방식
패스 실행 구조
결과 반환
코드 사용 및 관리
#ifndef LLVM_TUTORIAL_OPTIMIZATION_INSERTINCFUN_INSERTINC_H
#define LLVM_TUTORIAL_OPTIMIZATION_INSERTINCFUN_INSERTINC_H
// 여러 번 포함되어도 문제 없도록 인클루드 가드 설정
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/Passes/PassPlugin.h"
// 다른 파일과 이름 충돌 막기 위해 익명 네임스페이스 사용
namespace {
class InsertInc : public llvm::PassInfoMixin<InsertInc> {
public:
// New Pass Manager에서 패스 로직이 들어가는 함수 선언
// M: 전체 LLVM 모듈(프로그램 전체)
// ModuleAnalysisManager &: 분석 도구 제공
// 반환값: 어떤 분석 결과가 보존되는지 나타내는 PreservedAnalyses
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &);
};
} // end anonymous namespace
#endif // LLVM_TUTORIAL_OPTIMIZATION_INSERTINCFUN_INSERTINC_H
#include <stdio.h>
#define MATSIZE 1024
int add(int x, int y);
int main(){
int x = 1;
int y = 2;
int i, j, k;
int a[MATSIZE][MATSIZE];
int b[MATSIZE][MATSIZE];
int c[MATSIZE][MATSIZE];
printf("Hello world!\n");
printf("%d + %d = %d \n",x,y,add(x,y));
for (i = 0; i < MATSIZE; i++) {
for (j = 0; j < MATSIZE; j++) {
a[i][j] = i;
b[i][j] = j;
}
}
// TODO: Optimize this loop
for (i = 0; i < MATSIZE; i++) {
for (k = 0; k < MATSIZE; k++) {
for (j = 0; j < MATSIZE; j++) {
c[i][j] += a[i][k] * b[k][j];
}
}
}
printf("%d",c[i][j]);
return 0;
}
int add(int x, int y){
return x+y;
}
빌드 후,
test.ll 파일을 읽어 패스를 적용 한 뒤, test.out.ll 에 저장.

위에서 정의했던 inc 함수 정의가 삽입되어 있다.
빌드
test.ll 파일 준비
패스 적용
결과 저장(test.out.ll)