LLVM이란 무엇입니까? Swift, Rust, Clang 등의 힘

새로운 언어와 기존 언어의 개선이 개발 환경 전반에 걸쳐 급증하고 있습니다. Mozilla의 Rust, Apple의 Swift, Jetbrains의 Kotlin 및 기타 여러 언어는 개발자에게 속도, 안전, 편의성, 휴대 성 및 성능에 대한 새로운 선택 범위를 제공합니다.

왜 지금? 한 가지 큰 이유는 언어 구축을위한 새로운 도구, 특히 컴파일러입니다. 그중 최고는 일리노이 대학의 연구 프로젝트로 Swift 언어 제작자 인 Chris Lattner가 처음 개발 한 오픈 소스 프로젝트 인 LLVM입니다.

LLVM을 사용하면 새 언어를 쉽게 만들 수있을뿐만 아니라 기존 언어의 개발을 향상시킬 수 있습니다. 컴파일러 생성, 출력 된 코드를 여러 플랫폼 및 아키텍처로 이식, 벡터화와 같은 아키텍처 별 최적화 생성, 다음과 같은 공통 언어 은유를 처리하기위한 코드 작성 등 언어 생성 작업의 가장 감사 할 수없는 많은 부분을 자동화하기위한 도구를 제공합니다. 예외. 자유 라이선스는 소프트웨어 구성 요소로 자유롭게 재사용하거나 서비스로 배포 할 수 있음을 의미합니다.

LLVM을 사용하는 언어 목록에는 친숙한 이름이 많이 있습니다. Apple의 Swift 언어는 LLVM을 컴파일러 프레임 워크로 사용하고 Rust는 LLVM을 도구 체인의 핵심 구성 요소로 사용합니다. 또한 많은 컴파일러에는 LLVM과 밀접하게 관련된 프로젝트 인 C / C ++ 컴파일러 (이 이름은 "C-lang") 인 Clang과 같은 LLVM 버전이 있습니다. .NET 구현 인 Mono에는 LLVM 백엔드를 사용하여 네이티브 코드로 컴파일하는 옵션이 있습니다. 그리고 명목상 JVM 언어 인 Kotlin은 LLVM을 사용하여 머신 네이티브 코드로 컴파일하는 Kotlin Native라는 언어 버전을 개발하고 있습니다.

LLVM 정의

그 중심에있는 LLVM은 프로그래밍 방식으로 머신 네이티브 코드를 생성하기위한 라이브러리입니다. 개발자는 API를 사용하여 중간 표현 또는 IR 이라는 형식으로 지침을 생성합니다 . 그런 다음 LLVM은 IR을 독립 실행 형 바이너리로 컴파일하거나 코드에서 JIT (just-in-time) 컴파일을 수행하여 언어 용 인터프리터 또는 런타임과 같은 다른 프로그램의 컨텍스트에서 실행할 수 있습니다.

LLVM의 API는 프로그래밍 언어에서 발견되는 많은 공통 구조 및 패턴을 개발하기위한 기본 요소를 제공합니다. 예를 들어 거의 모든 언어에는 함수와 전역 변수의 개념이 있으며 많은 언어에는 코 루틴과 C 외부 함수 인터페이스가 있습니다. LLVM은 IR의 표준 요소로 함수와 전역 변수를 가지고 있으며 코 루틴을 생성하고 C 라이브러리와 인터페이스하기위한 은유를 가지고 있습니다.

특정 바퀴를 재발 명하는 데 시간과 에너지를 소비하는 대신 LLVM의 구현을 사용하고주의가 필요한 언어 부분에 집중할 수 있습니다.

Go, Kotlin, Python, Rust에 대해 자세히 알아보기 

가다:

  • Google의 Go 언어의 힘을 활용하세요
  • 최고의 Go 언어 IDE 및 편집기

Kotlin :

  • Kotlin이란 무엇입니까? Java 대안 설명
  • Kotlin 프레임 워크 : JVM 개발 도구 설문 조사

파이썬 :

  • 파이썬이란 무엇입니까? 당신이 알아야 할 모든 것
  • 자습서 : Python을 시작하는 방법
  • 모든 Python 개발자를위한 6 가지 필수 라이브러리

녹:

  • Rust는 무엇입니까? 안전하고 빠르고 쉬운 소프트웨어 개발 방법
  • Rust를 시작하는 방법 알아보기 

LLVM : 이식성을 고려한 설계

LLVM을 이해하려면 C 프로그래밍 언어에 대한 비유를 고려하는 것이 도움이 될 수 있습니다. C는 시스템 하드웨어에 가깝게 매핑 할 수있는 구조를 가지고 있고 거의 모든 시스템 아키텍처. 그러나 C는 어느 정도까지는 이식 가능한 어셈블리 언어로 유용합니다. 특정 목적을 위해 설계되지 않았습니다.

반대로 LLVM의 IR은 처음부터 휴대용 어셈블리로 설계되었습니다. 이 이식성을 달성하는 한 가지 방법은 특정 머신 아키텍처와 독립적 인 기본 요소를 제공하는 것입니다. 예를 들어 정수 유형은 기본 하드웨어의 최대 비트 너비 (예 : 32 또는 64 비트)로 제한되지 않습니다. 128 비트 정수와 같이 필요한만큼의 비트를 사용하여 기본 정수 유형을 만들 수 있습니다. 또한 특정 프로세서의 명령어 세트와 일치하도록 출력을 만드는 것에 대해 걱정할 필요가 없습니다. LLVM은 당신을 위해 그것을 처리합니다.

LLVM의 아키텍처 중립적 설계를 통해 현재와 미래의 모든 종류의 하드웨어를 쉽게 지원할 수 있습니다. 예를 들어, IBM은 최근 z / OS, Linux on Power (IBM의 MASS 벡터화 라이브러리 지원 포함) 및 LLVM의 C, C ++ 및 Fortran 프로젝트를위한 AIX 아키텍처를 지원하는 코드를 제공했습니다. 

LLVM IR의 라이브 예제를 보려면 ELLCC 프로젝트 웹 사이트로 이동하여 브라우저에서 바로 C 코드를 LLVM IR로 변환하는 라이브 데모를 사용해보십시오.

프로그래밍 언어에서 LLVM을 사용하는 방법

LLVM의 가장 일반적인 사용 사례는 언어에 대한 AOT (Ahead-of-Time) 컴파일러입니다. 예를 들어 Clang 프로젝트는 사전에 C 및 C ++를 네이티브 바이너리로 컴파일합니다. 그러나 LLVM은 다른 것도 가능하게합니다.

LLVM을 사용한 Just-in-time 컴파일

일부 상황에서는 코드를 미리 컴파일하는 대신 런타임에 즉시 생성해야합니다. 예를 들어 Julia 언어는 REPL (read-eval-print loop) 또는 대화 형 프롬프트를 통해 빠르게 실행되고 사용자와 상호 작용해야하기 때문에 JIT에서 코드를 컴파일합니다. 

Python 용 수학 가속 패키지 인 Numba는 선택한 Python 함수를 기계 코드로 JIT 컴파일합니다. 또한 Numba로 장식 된 코드를 미리 컴파일 할 수 있지만 (Julia와 마찬가지로) Python은 해석 언어로 빠른 개발을 제공합니다. JIT 컴파일을 사용하여 이러한 코드를 생성하면 사전 컴파일보다 Python의 대화 형 워크 플로가 더 잘 보완됩니다.

다른 사람들은 PostgreSQL 쿼리 컴파일과 같이 LLVM을 JIT로 사용하는 새로운 방법을 실험하여 성능을 최대 5 배 향상시킵니다.

LLVM을 사용한 자동 코드 최적화

LLVM은 IR을 원시 기계 코드로 컴파일하지 않습니다. 또한 연결 프로세스 전체에 걸쳐 고도로 세분화 된 코드를 최적화하도록 프로그래밍 방식으로 지시 할 수도 있습니다. 최적화는 함수 인라인, 데드 코드 제거 (사용하지 않는 유형 선언 및 함수 인수 포함), 루프 풀기 등 매우 공격적 일 수 있습니다.

다시 말하지만, 힘은이 모든 것을 직접 구현할 필요가 없다는 것입니다. LLVM이이를 처리하거나 필요에 따라 토글하도록 지시 할 수 있습니다. 예를 들어 약간의 성능을 희생하면서 더 작은 바이너리를 원하는 경우 컴파일러 프런트 엔드가 LLVM에 루프 언 롤링을 비활성화하도록 지시 할 수 있습니다.

LLVM을 사용한 도메인 특정 언어

LLVM은 많은 범용 언어에 대한 컴파일러를 생성하는 데 사용되었지만 매우 수직적이거나 문제 영역에 배타적 인 언어를 생성하는데도 유용합니다. 어떤면에서 이것은 LLVM이 가장 밝게 빛나는 곳입니다. LLVM은 그러한 언어를 만드는 데 많은 노력을 덜어주고 잘 수행되도록하기 때문입니다.

예를 들어 Emscripten 프로젝트는 LLVM IR 코드를 가져 와서 JavaScript로 변환합니다. 이론적으로는 LLVM 백엔드를 사용하는 모든 언어가 브라우저에서 실행할 수있는 코드를 내보낼 수 있습니다. 장기적인 계획은 WebAssembly를 생성 할 수있는 LLVM 기반 백엔드를 갖는 것이지만 Emscripten은 LLVM이 얼마나 유연한 지 보여주는 좋은 예입니다.

LLVM을 사용할 수있는 또 다른 방법은 기존 언어에 도메인 별 확장을 추가하는 것입니다. Nvidia는 LLVM을 사용하여 Nvidia CUDA 컴파일러를 만들었습니다.이를 통해 언어는 함께 제공되는 (느린) 라이브러리를 통해 호출되는 대신 사용자가 생성하는 네이티브 코드의 일부로 컴파일되는 CUDA에 대한 네이티브 지원을 추가 할 수 있습니다 (더 빠름).

도메인 별 언어에 대한 LLVM의 성공은 LLVM 내에서 발생하는 문제를 해결하기위한 새로운 프로젝트를 촉진했습니다. 가장 큰 문제는 일부 DSL이 프런트 엔드에서 많은 노력 없이는 LLVM IR로 변환하기 어렵다는 것입니다. 작업 중 하나의 솔루션은 Multi-Level Intermediate Representation 또는 MLIR 프로젝트입니다.

MLIR은 복잡한 데이터 구조 및 작업을 나타내는 편리한 방법을 제공하며,이를 LLVM IR로 자동 변환 할 수 있습니다. 예를 들어 TensorFlow 기계 학습 프레임 워크는 MLIR을 사용하여 네이티브 코드로 효율적으로 컴파일 된 복잡한 데이터 흐름 그래프 작업을 많이 포함 할 수 있습니다.

다양한 언어로 LLVM 작업

LLVM을 사용하는 일반적인 방법은 익숙한 언어의 코드를 사용하는 것입니다 (물론 LLVM의 라이브러리를 지원합니다).

두 가지 일반적인 언어 선택은 C와 C ++입니다. 많은 LLVM 개발자는 몇 가지 이유 때문에 기본적으로이 두 가지 중 하나를 사용합니다. 

  • LLVM 자체는 C ++로 작성되었습니다.
  • LLVM의 API는 C 및 C ++ 구현으로 제공됩니다.
  • 많은 언어 개발은 ​​C / C ++를 기반으로하는 경향이 있습니다.

그러나이 두 언어가 유일한 선택은 아닙니다. 많은 언어가 기본적으로 C 라이브러리를 호출 할 수 있으므로 이론적으로 이러한 언어로 LLVM 개발을 수행 할 수 있습니다. 그러나 LLVM의 API를 우아하게 래핑하는 언어로 된 실제 라이브러리가 있으면 도움이됩니다. 다행히 C # /. NET / Mono, Rust, Haskell, OCAML, Node.js, Go 및 Python을 포함한 많은 언어 및 언어 런타임에 이러한 라이브러리가 있습니다.

한 가지주의 할 점은 LLVM에 대한 일부 언어 바인딩이 다른 것보다 완전하지 않을 수 있다는 것입니다. 예를 들어 Python에는 많은 선택이 있지만 각각의 완성도와 유용성은 다릅니다.

  • Numba를 만든 팀이 개발 한 llvmlite는 Python에서 LLVM을 사용하는 현재 경쟁자로 부상했습니다. Numba 프로젝트의 요구에 따라 LLVM 기능의 하위 집합 만 구현합니다. 그러나이 하위 집합은 LLVM 사용자가 필요로하는 대부분을 제공합니다. (llvmlite는 일반적으로 Python에서 LLVM 작업에 가장 적합한 선택입니다.)
  • LLVM 프로젝트는 LLVM의 C API에 대한 자체 바인딩 세트를 유지하지만 현재는 유지되지 않습니다.
  • LLVM에 대한 최초의 인기있는 Python 바인딩 인 llvmpy는 2015 년에 유지 관리가 중단되었습니다. 모든 소프트웨어 프로젝트에는 좋지 않지만 LLVM의 각 버전에 따라 변경되는 수를 고려할 때 LLVM으로 작업 할 때는 더 나쁩니다.
  • llvmcpy는 C 라이브러리에 대한 Python 바인딩을 최신 상태로 유지하고 자동화 된 방식으로 업데이트하며 Python의 기본 관용구를 사용하여 액세스 할 수 있도록하는 것을 목표로합니다. llvmcpy는 아직 초기 단계에 있지만 이미 LLVM API로 몇 가지 기본적인 작업을 수행 할 수 있습니다.

LLVM 라이브러리를 사용하여 언어를 빌드하는 방법에 대해 궁금한 경우 LLVM 자체 제작자는 C ++ 또는 OCAML을 사용하여 Kaleidoscope라는 간단한 언어를 만드는 과정을 안내하는 자습서를 제공합니다. 이후 다른 언어로 포팅되었습니다.

  • Haskell :  원본 튜토리얼의 직접 이식.
  • Python : 이러한 포트 중 하나는 자습서를 밀접하게 따르고 다른 포트는 대화 형 명령 줄을 사용하여보다 야심적으로 다시 작성합니다. 둘 다 llvmlite를 LLVM에 대한 바인딩으로 사용합니다.
  • Rust  와  Swift : 우리는 LLVM이 존재하도록 도왔던 두 가지 언어로 튜토리얼의 포트를 얻는 것이 불가피 해 보였습니다.

마지막으로 튜토리얼은 인간 언어로 도 제공  됩니다. 원본 C ++ 및 Python을 사용하여 중국어로 번역되었습니다.

LLVM이하지 않는 것

LLVM이 제공하는 모든 기능을 통해 수행하지 않는 작업을 아는 것도 유용합니다.

예를 들어 LLVM은 언어의 문법을 구문 분석하지 않습니다. lex / yacc, flex / bison, Lark 및 ANTLR과 같은 많은 도구가 이미 이러한 작업을 수행합니다. 구문 분석은 어쨌든 컴파일에서 분리되는 것을 의미하므로 LLVM이이 문제를 해결하려고 시도하지 않는 것은 놀라운 일이 아닙니다.

LLVM은 또한 주어진 언어에 대한 더 큰 소프트웨어 문화를 직접적으로 다루지 않습니다. 컴파일러의 바이너리를 설치하고, 설치에서 패키지를 관리하고, 도구 체인을 업그레이드합니다.이 작업은 직접 수행해야합니다.

마지막으로 가장 중요한 것은 LLVM이 프리미티브를 제공하지 않는 언어의 공통 부분이 여전히 있다는 것입니다. 많은 언어는 메모리를 관리하는 주요 방법 또는 RAII (C ++ 및 Rust가 사용하는)와 같은 전략의 부속물로서 가비지 수집 메모리 관리 방식을 가지고 있습니다. LLVM은 가비지 수집기 메커니즘을 제공하지 않지만 가비지 수집기를 더 쉽게 작성할 수 있도록 코드에 메타 데이터를 표시하여 가비지 수집을 구현하는 도구를 제공합니다.

그러나 이것 중 어느 것도 LLVM이 결국 가비지 수집을 구현하기위한 네이티브 메커니즘을 추가 할 가능성을 배제하지 않습니다. LLVM은 6 개월 정도마다 주요 릴리스로 빠르게 개발되고 있습니다. 그리고 개발 속도는 현재 많은 언어가 LLVM을 개발 프로세스의 중심에두고있는 방식 덕분에 증가 할 가능성이 높습니다.