LLVM

 

LLVM 공식 사이트

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. Despite its name, LLVM has little to do with traditional virtual machines, though it does provide helpful libraries that can be used to build them. The name "LLVM" itself is not an acronym; it is the full name of the project.

LLVM 프로젝트는 모듈식의 재사용 가능한 컴파일러와 툴체인의 집합입니다. 이 이름에도 불구하고 LLVM은 기존의 가상머신과는 그것을 만드는데 도움이 될 수 있다는 점을 빼고는 거의 관련이 없습니다. "LLVM"이라는 이름은 약자가 아니며 그것이 프로젝트의 풀 네임입니다.

1. 개요
2. 구조
3. LLVM 프로젝트
3.1. LLVM Core
3.2. Clang
3.2.1. Swift-Clang
3.2.2. Flang
3.2.3. Kotlin/Native
3.2.4. 그 외
3.3. LLDB


1. 개요


[image]
오픈 소스 컴파일러 프로젝트 또는 툴체인[1]의 이름.
미국 일리노이 대학교의 크리스 래트너(Chris Lattner)가 2000년에 쓴 석사 논문에서 출발한 프로젝트이며, 이후 2005년에 애플이 크리스 래트너를 스카웃한 뒤 LLVM 프로젝트를 적극적으로 지원하고 있다. 크리스 래트너는 2010년에 iOS프로그래밍 언어 Swift를 만들기도 했다.
원래 LLVM은 저레벨 가상머신(Low-Level Virtual Machine)의 약자였지만, 프로젝트가 확장되며 이 용어는 더 이상 사용하지 않고 LLVM 자체가 프로젝트의 정식 명칭이 된다.

2. 구조


컴파일러는 프론트엔드-미들엔드-백엔드의 단계로 구성되어 있다. 보통 이 세 단계는 하나의 프로그램으로 일괄 처리되는데, 이럴 경우 '언어의 종류 x 아키텍처의 종류'만큼 복수의 컴파일러가 필요하게 된다. 다양한 언어와 다양한 아키텍처에 대응할 수 있는 이식성이 중요한 요즘 이러한 컴파일러 구조는 재사용성을 떨어뜨린다는 문제가 있다. 바로 이것을 해결할 수 있는 컴파일러 구조가 LLVM이다.
위의 사진에서 보이듯, LLVM은 아키텍처별로 분리된 모듈식 미들엔드-백엔드를 중점으로 하고 있다. 프론트엔드가 여러가지 프로그래밍 언어들을 중간 표현 코드로 번역하고, LLVM은 그 중간 표현 코드를 각각의 아키텍처에 맞게 최적화하여 실행이 가능한 형태로 바꾸는 방식이다. 처음에는 GCC를 프론트엔드로 하고 LLVM은 미들엔드-백엔드로 사용하는 LLVM-GCC 프로젝트(DragonEgg)가 있었으나, LLVM의 자체 프론트엔드인 Clang이 등장한 이후 컴파일의 전 과정을 LLVM 툴체인으로 진행할 수 있게 되었다.
여기서 미들엔드와 백엔드는 LLVM Core라는 라이브러리가 담당하게 되는데, 각각의 프론트엔드는 사람이 작성한 소스 코드를 LLVM Core가 인식할 수 있는 중간 코드 LLVM IR(LLVM Intermediate Representation)로 컴파일한다. LLVM IR은 .ll 형식을 가진 LLVM 어셈블리(LLVM Assembly)[2]와 .bc 형식을 가진 LLVM 비트코드(LLVM Bitcode), 그리고 .o 형식을 가진 C++ 목적 코드(C++ Object Code)로 분류된다.
이 중에서 LLVM 어셈블리와 LLVM 비트코드는 둘 다 하드웨어 독립적이고 어셈블리어에 가까운 형태를 취하고 있다. 다만 LLVM 어셈블리는 사람이 비교적 읽기 쉬운 원시 코드와 유사한 반면 LLVM 비트코드는 메모리 주소값의 나열로 구성된, 좀 더 최적화가 이루어진 형태의 코드이다. 이러한 LLVM 어셈블리/비트코드는 미들엔드에서 최적화가 한 번 더 진행된 후 백엔드에서 실제 기계어로 바뀌어 실행되는데, 그 번역 방식에는 기계어 인터프리트와 실행이 함께 이루어지는 JIT 컴파일 방식과 미리 기계어로 다 번역해 놓고 실행하는 AOT 컴파일 방식이 모두 존재한다.
이처럼 각각의 기능들이 분리되면 커다란 장점이 생긴다. 예를 들어 C++ 코드를 이용하여 x86-64ARM을 대상으로 바이너리를 만들어야 한다고 가정할 때, 기존의 컴파일러라면 각 아키텍처에 맞는 프론트엔드-미들엔드-백엔드 인프라를 전부 별도로 사용해야 하니 총 2개의 툴체인이 필요하지만, LLVM 툴체인을 사용할 경우 Clang으로 컴파일만 하면 모든 아키텍처에 대한 빌드가 가능해지므로 상당한 편리함을 누릴 수 있다. 윈도우 환경을 정식으로 지원하지 않는 GCC나, 유닉스 계열 운영 체제를 정식으로 지원하지 않는 VC++와 달리 두 OS를 모두 지원하는 크로스플랫폼 컴파일러라는 것도 장점이다.
LLVM은 컴파일러 개발자에게도 많은 장점이 있는데, 컴파일러를 만드는 쪽에서 프론트엔드부터 백엔드까지 모든 걸 구현해야 했던 예전과는 달리, 프론트엔드 및 LLVM과의 연결부만 구현하면 LLVM이 코드 최적화와 기계어 생성을 담당해 주기 때문이다. 덕분에 현재 다양한 언어에 대응하는 여러 LLVM 프론트엔드 컴파일러들이 만들어지고 있다.
이름에 Virtual Machine이 들어가기는 하지만, 가상머신은 아니다. LLVM Core는 JVM이나 .NET CLR이 수행하는 메모리 핸들링, 런타임 환경 구축, 리소스 접근 등의 화려한 기능들은 제공하지 않는다. 단지 LLVM IR이라는 중간 코드를 만들어 모듈화된 바이너리 생성을 할 수 있다는 점이 가상머신과 유사할 뿐이며, 이에 따라 LLVM에는 가상머신으로 인해 유발되는 속도 저하 이슈도 없다. 위에서 LLVM이 약자가 아닌 프로젝트의 전체 이름이라고 강조한 것도 이러한 혼동을 방지하기 위함인 듯하다.

3. LLVM 프로젝트



3.1. LLVM Core


LLVM 미들엔드/백엔드를 구성하는 라이브러리. LLVM IR은 특정 아키텍처로부터 독립적이며, 어떤 아키텍처에 설치된 LLVM Core로 빌드했느냐에 따라 타겟 플랫폼이 결정된다. LLVM Core에는 LLVM 비트코드를 실행하는 JIT 컴파일러도 포함되어 있어서 에뮬레이터에서 JIT 컴파일을 구현할 때 많이 쓰인다.

3.2. Clang


Clang은 libclang과 그 프론트엔드로 구성된 C, C++, Objective-C용 컴파일러이다. '클랭'이라고 읽으며, 2007년에 첫 등장하였다. LLVM 프로젝트의 메인 프론트엔드라고 할 수 있다. 소스 코드를 LLVM IR로 컴파일하는 역할을 담당한다.
GCC가 GPLv3를 적용한 이후 소스 코드 공개를 꺼리는 기업들이 하나둘씩 LLVM/Clang을 지원하고 있다. LLVM과 Clang은 University of Illinois/NCSA Open Source License(UIUC License)라는 공개 라이선스를 따르며, 이 라이선스는 소스 코드 공개의 의무가 없기 때문이다.[3] FreeBSD는 버전 10에서 GCC를 밀어내고 LLVM/Clang이 디폴트 컴파일러가 되었으며, 애플은 OS X 매버릭스에서 마지막까지 남아있던 LLVM-GCC를 제거한 후 macOS의 기본 컴파일러를 Clang으로 완전히 교체하였다.
윈도우의 경우 두 가지 사용 방법이 존재하는데, 첫번째로 MSVS와 함께 사용하는 방법이다. 이 경우 LLVM 다운로드 페이지에서 Pre-Built Binaries: Clang for Windows를 내려받아 설치하고, clang-cl 컴파일러를 사용하면 된다. clang-cl은 프론트엔드를 Clang으로, 미들엔드-백엔드를 MSVC Build Tool로 활용하는 윈도우용 Clang 구현체이다. 두번째로는 MinGW를 통한 방법이다. MSYS2 mingw-w64에서 pacman -S mingw-w64-x86_64-clang 설치 후 mingw64의 bin을 PATH에 넣고 사용하거나 MSYS Bash를 통해 사용하는 방법이 있다.
첫번째의 경우 런타임 라이브러리는 MSVC 런타임인 msvcp를 사용하게 되며, 후자의 경우 libstdc++ 또는 링커 옵션을 통해 libc++를 사용할 수 있다.
LLVM/Clang은 C++의 새로운 표준인 C++11을 가장 먼저 지원한 컴파일러이다. C++14가 나왔을 때도 마이크로소프트의 MSVC 컴파일러는 C++11조차 제대로 지원하지 않는 등 상대적으로 굼뜬 모습을 보였던 데 비해, Clang은 빠르게 C++14에 대한 Feature Complete를 달성했고 C++17의 기본적인 요소까지 적용했다.
Clang의 일부인 clang-format은 중괄호나 탭과 같은 C++ 소스 코드의 스타일을 일괄적으로 정리하고 유지할 수 있게 해준다.

3.2.1. Swift-Clang


애플iOS에서 사용되는 언어 Swift에 대응하는 프론트엔드 컴파일러. 애플이 기존의 LLVM을 포크해서 만든 Swift-LLVM과 결합되어 돌아간다. 기본적인 구조는 Clang과 유사하지만 스위프트 소스 코드와 LLVM IR 사이에 SIL(Swift Intermediate Language)이라는 또다른 중간 코드가 하나 더 있다. 즉 프론트엔드에서 스위프트 코드는 SIL로 컴파일될 때 한 번, LLVM IR로 컴파일될 때 한 번 해서 총 두 번 최적화되는 것이다.
LLVM/Clang과 마찬가지로 UIUC License를 따른다.

3.2.2. Flang


Fortran에 대응하는 프론트엔드 컴파일러이다. Clang을 기반으로 만들어졌으며 엔비디아에서 지원하고 있다. GCC의 Fortran 컴파일러와 LLVM을 결합하는 DragonEgg 프로젝트가 사실상 중단되어 Fortran 사용자들은 Flang으로 넘어가고 있다.

3.2.3. Kotlin/Native


구글에서 안드로이드의 차세대 공식 언어로 지정한 Kotlin을 기계어로 컴파일하기 위한 Clang 기반의 프론트엔드 컴파일러. 원래 Kotlin은 JVM 언어로서 바이트코드를 생성하여 JVM에 의해 실행되지만, LLVM 미들엔드-백엔드 인프라와 결합된 Kotlin/Native 컴파일러를 사용하면 가상머신에 의한 성능 손실을 없애고 네이티브 바이너리로 직접 컴파일하는 것이 가능해진다.

3.2.4. 그 외


Python, Ruby, Scala, Haskell 등의 일부 언어들도 LLVM 기반으로의 바인딩을 지원한다.

3.3. LLDB


LLVM 프론트엔드에 대응하는 디버거이다. GCC의 GDB와 동일한 포지션을 갖고 있으며, 애플 Xcode 5.0 이상의 버전에서 기본 디버거로 사용되고 있다. GDB보다 에러 메시지가 훨씬 간결하면서도 명확하다는 것이 장점. 윈도우 버전은 아직 개발 중이지만, WSL 환경을 이용하면 리눅스용 LLDB를 사용하는 것이 가능하다. 또는 MSYS2 터미널에서 pacman -S mingw-w64-x86_64-lldb 설치를 통해 MinGW 환경에서 사용할 수도 있다.
GDB와 달리 포트란 코드의 디버깅은 지원하지 않는다.

[1] Toolchain. 연쇄적으로 사용되는 소프트웨어 개발 도구들의 집합을 말한다. 컴파일러가 컴파일을 하면 그 결과물인 어셈블리어 파일을 어셈블러가 기계어로 번역하고, 링커가 그렇게 번역된 파일들을 연결하므로 컴파일러-어셈블러-링커는 하나의 툴체인이라고 할 수 있다.[2] 공식 레퍼런스 문서에서는 Human-Readable Assembly라고도 표현하고 있다. #[3] MIT 라이선스BSD 라이선스를 부분적으로 따 와서 합친 형태이다. GPL과도 호환이 가능하다.

분류