저번주에 C코드 파일에 함수 이름을 적용하는 LLVM pass를 적용시키는 것을 시도했다.
하지만 패스가 작성된 cpp 파일을 컴파일하고 링킹하는데에 문제가 있었다.
해당 문제는 다음과 같았다.
clang++ -shared -o NamePrinter.dylib NamePrinter.o
이 명령어를 수행했을 때
ld: symbol(s) not found for architecture arm64
clang-16: error: linker command failed with exit code 1 (use -v to see invocation)
라는 문구와 함께 에러가 발생한 것을 볼 수 있다.
내 경우 해결방법은 llvm 프로젝트 안에 내장 패스처럼 해당 패스를 추가해주는 것이다.
1. 다운 받았던 llvm 프로젝트에서 llvm/lib/Transforms에 패스를 넣을 폴더(NamePrinter)를 추가하고, 이 폴더(llvm/lib/Transforms/NamePrinter)에 패스가 작성된 cpp 파일과 헤더파일을 넣어준다.
그리고 CMakeLists.txt 를 생성하여 해당 내용을 저장한다.
add_compile_options(-fexceptions)
add_llvm_library(NamePrinter MODULE
NamePrinter.cpp
DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)
패스들 간의 dependency를 설정할 수 있다.
2. llvm/lib/Transform에 있는 CMakeLists.txt에 다음 내용을 추가한다.
add_subdirectory(폴더명)
3. llvm이 수정됐으므로 build 폴더로 이동하여 다시 빌드를 진행한다. (참고 : https://blueee.tistory.com/15)
4. 빌드가 완료되면 build/lib/Transforms/NamePrinter 로 이동해서 make 해준다. 그럼 build/lib 에 NamePrinter.dylib이 생성된 걸 확인할 수 있다.
5. 이제 함수 내용을 출력하는 패스를 적용할 C파일을 생성한다.
나는 간단하게 인자를 전달받아 더해서 리턴하는 add 함수와 main 함수로 이루어진 코드를 작성하였다.
#include <stdio.h>
int add(int x, int y) {
return x + y;
}
int main() {
return add(1, 2);
}
6. 해당 명령어를 통해 bc 파일을 생성한다. 이 명령어는 test.c 파일을 clang 컴파일러를 사용하여 LLVM 비트코드로 컴파일하고 결과를 test.bc에 저장하는 역할을 한다.
clang -c -emit-llvm test.c -o test.bc
6-1. 이때, clang과 llvm 버전이 같아야 한다.
나는 Homebrew를 통해 설치한 clang이 16.0.6 이고, 설치한 llvm은 12.0.0 버전이어서 나중에 패스 적용할 때
error: Invalid value (Producer: 'LLVM16.0.6' Reader: 'LLVM 12.0.0')
이런 에러가 발생했다.
clang 버전을 확인할 수 있는 방법은 다음과 같다.
clang --version
6-2. 버전이 서로 다르다면 맞춰줘야 한다.
clang 버전을 낮추기 위해 해당 명령어를 사용하고, 재부팅한다.
brew link llvm@12
재부팅한 후 clang 버전을 다시 확인해보면 12.0.0으로 맞춰져있는 것을 볼 수 있다.
7. 해당 명령어를 사용하여 패스를 적용해준다.
./build/bin/opt -load ./build/lib/NamePrinter.dylib -NamePrinter test.bc -o out.bc
명령어를 실행하면 함수 이름이 잘 출력되는 것을 확인할 수 있다.
사용한 파일 목록
llvm/lib/Transforms/NamePrinter/NamePrinter.cpp
//===- 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements two versions of the LLVM "Hello World" pass described
// in docs/WritingAnLLVMPass.html
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Debug.h"
#include "NamePrinter.h"
#define DEBUG_TYPE "hello"
bool NamePrinter::runOnFunction(Function &F) {
dbgs() <<"Hello: "<< F.getName()<<"\n";
return false;
}
void NamePrinter::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
}
char NamePrinter::ID = 0;
static RegisterPass<NamePrinter> Y("NamePrinter", "Hello World Pass ");
llvm/lib/Transforms/NamePrinter/NamePrinter.h
#ifndef LLVM_TUTORIAL_OPTIMIZATION_TEMPLATE_HELLO_FUNCTION_H
#define LLVM_TUTORIAL_OPTIMIZATION_TEMPLATE_HELLO_FUNCTION_H
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
using namespace llvm;
namespace {
struct NamePrinter : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
NamePrinter() : FunctionPass(ID) {}
bool runOnFunction(Function &M) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
};
}
#endif
test.c
#include <stdio.h>
int add(int x, int y) {
return x + y;
}
int main() {
return add(1, 2);
}
clang의 버전을 낮추지 않고 llvm 프로젝트 내에 있는 clang 을 사용하여 컴파일하면 가능할 것 같은데
baekyumi@YUM CNUProject % ./build/bin/clang -c -emit-llvm test.c -o test.bc
test.c:1:10: fatal error: 'stdio.h' file not found
#include <stdio.h>
^~~~~~~~~
1 error generated.
헤더파일 에러가 발생한다. 이 부분은 더 찾아봐야 할 것 같다.
'CNUproject > 코드 동일성 검사 도구' 카테고리의 다른 글
18_LLVM Pass 작성 후 적용하기 (with Z3) (2) | 2023.09.13 |
---|---|
17_Python IR에 LLVM Pass 적용하기 (0) | 2023.09.11 |
15_LLVM 플러그인 적용 오류 (0) | 2023.08.31 |
14_LLVM PROJECT 다운로드 및 빌드하기 (0) | 2023.08.29 |
13_LLVM IR 최적화하기 (0) | 2023.08.11 |