What is 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. The name “LLVM” itself is not an acronym; it is the full name of the project.
How to compile LLVM for Swift
Checkout Source Code
It’s better to checkout the LLVM source code forked by Apple.
git clone https://github.com/apple/llvm-project
CMake
I recommend that you use Ninja to build in place of Xcode. Building through Ninja is faster.
cd llvm-project mkdir build cd build cmake -G -DCMAKE_OSX_ARCHITECTURES= \ -DCMAKE_BUILD_TYPE=Release \ -DLLVM_BUILD_RUNTIME=Off \ -DLLVM_INCLUDE_TESTS=Off \ -DLLVM_INCLUDE_EXAMPLES=Off \ -DLLVM_TARGETS_TO_BUILD= \ -DLLVM_BUILD_LLVM_DYLIB=true \ ../llvm # for iOS device cmake -G -DCMAKE_OSX_ARCHITECTURES= \ -DCMAKE_TOOLCHAIN_FILE=../llvm/cmake/platforms/iOS.cmake \ -DCMAKE_BUILD_TYPE=Release \ -DLLVM_BUILD_RUNTIME=Off \ -DLLVM_INCLUDE_TESTS=Off \ -DLLVM_INCLUDE_EXAMPLES=Off \ -DLLVM_ENABLE_BACKTRACES=Off \ -DLLVM_TARGETS_TO_BUILD= \ -DLLVM_BUILD_LLVM_DYLIB=true \ ../llvm
Build
It’s easy to build libLLVM.dylib, just one line. After the Ninja build success, we can find libLLVM.dylib in lib/libLLVM.dylib
path.
ninja libLLVM.dylib
How to use LLVM API in Swift
Here we create a new project named HelloLLVM.
Import LLVM Header & libLLVM.dylib
First, we need to create a header for import LLVM API.
cd HelloLLVM/HelloLLVM
mkdir LLVM && cd LLVM && mkdir Header && mkdir Frameworks
cp -rf YOUR_LLVM_PROJECT_PATH/_llvm_project/llvm/include/llvm-c Header
cp -rf YOUR_LLVM_PROJECT_PATH/llvm_project/build/include/llvm Header
cp YOUR_LLVM_PROJECT_PATH/llvm_project/build/lib/libLLVM.dylib Frameworks
Add the previous Header Path
to Header Search Paths
.
Add the libLLVM.dylib
to the project.
If using libLLVM.dylib in the iOS platform, we need to close bitcode
before building HelloLLVM. Otherwise, we will face the link problem.
ld: 'LLVM/Frameworks/libLLVM.dylib' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. file 'LLVM/Frameworks/libLLVM.dylib' for architecture arm64
Finally, click the build button. The project will run successfully if the previous operation is correct.
Run a simple demo
import Foundation
import LLVM_C
let module = LLVMModuleCreateWithName("Hello LLVM")
defer { LLVMDisposeModule(module) }
let int32 = LLVMInt32Type()
let returnType = int32
let paramTypes = UnsafeMutablePointer<LLVMTypeRef?>.allocate(capacity: 2)
paramTypes.initialize(from: [int32, int32], count: 2)
defer { paramTypes.deallocate() }
let functionType = LLVMFunctionType(returnType, paramTypes, 2, 0)
let sumFunction = LLVMAddFunction(module, "sum", functionType)
let entryBlock = LLVMAppendBasicBlock(sumFunction, "entry")
let builder = LLVMCreateBuilder()
LLVMPositionBuilderAtEnd(builder, entryBlock)
let a = LLVMGetParam(sumFunction, 0)
let b = LLVMGetParam(sumFunction, 1)
let temp = LLVMBuildAdd(builder, a, b, "temp")
LLVMBuildRet(builder, temp)
var executionEngine: LLVMExecutionEngineRef?
var outputMsg: UnsafeMutablePointer<Int8>?
LLVMLinkInInterpreter()
LLVMCreateInterpreterForModule(&executionEngine, module, &outputMsg)
if let outputMsg = outputMsg {
print("can't initialize engine: \(String(cString: outputMsg))")
exit(1)
}
let x: UInt64 = 10
let y: UInt64 = 24
let parameters = UnsafeMutablePointer<LLVMGenericValueRef?>.allocate(capacity: 2)
parameters.initialize(from: [LLVMCreateGenericValueOfInt(int32, x, 0), LLVMCreateGenericValueOfInt(int32, y, 1)], count: 2)
defer { parameters.deallocate() }
let result = LLVMRunFunction(executionEngine, sumFunction, 2, parameters)
LLVMDumpModule(module)
print("===============================")
print("\(x) + \(y) = \(LLVMGenericValueToInt(result, 0))")
Yep! That’s all! (Above code just converted from the below article 😎)