Swiftpack.co - Package - llvm-swift/LLVMSwift


Build Status Documentation Slack Invite

LLVMSwift is a pure Swift interface to the LLVM API and its associated libraries. It provides native, easy-to-use components to make compiler development fun.



The root unit of organization of an LLVM IR program is a Module

let module = Module(name: "main")

LLVM IR construction is handled by IRBuilder objects. An IRBuilder is a cursor pointed inside a context, and as such has ways of extending that context and moving around inside of it.

Defining a function and moving the cursor to a point where we can begin inserting instructions is done like so:

let builder = IRBuilder(module: module)

let main = builder.addFunction("main",
                               type: FunctionType([], IntType.int64))
let entry = main.appendBasicBlock(named: "entry")
builder.positionAtEnd(of: entry)

Inserting instructions creates native IRValue placeholder objects that allow us to structure LLVM IR programs just like Swift programs:

let constant = IntType.int64.constant(21)
let sum = builder.buildAdd(constant, constant)

This simple program generates the following IR:

// module.dump()

define i64 @main() {
  ret i64 42


LLVM IR is a strong, statically typed language. As such, values and functions are tagged with their types, and conversions between them must be explicit (see Conversion Operators). LLVMSwift represents this with values conforming to the IRType protocol and defines the following types:

|Type | Represents | |:---:|:---:| | VoidType | Nothing; Has no size | | IntType | Integer and Boolean values (i1) | | FloatType | Floating-point values | | FunctionType | Function values | | LabelType | Code labels | | TokenType | Values paired with instructions | | MetadataType | Embedded metadata | | X86MMXType | X86 MMX values | | PointerType | Pointer values | | VectorType | SIMD data | | ArrayType | Homogeneous values | | Structure Type | Heterogeneous values |

Control Flow

Control flow is changed through the unconditional and conditional br instruction.

LLVM is also famous for a control-flow specific IR construct called a PHI node. Because all instructions in LLVM IR are in SSA (Single Static Assignment) form, a PHI node is necessary when the value of a variable assignment depends on the path the flow of control takes through the program. For example, let's try to build the following Swift program in IR:

func calculateFibs(_ backward : Bool) -> Double {
  let retVal : Double
  if !backward {
    // the fibonacci series (sort of)
    retVal = 1/89
  } else {
    // the fibonacci series (sort of) backwards
    retVal = 1/109
  return retVal

Notice that the value of retVal depends on the path the flow of control takes through this program, so we must emit a PHI node to properly initialize it:

let function = builder.addFunction("calculateFibs", 
                                   type: FunctionType([IntType.int1], 
let entryBB = function.appendBasicBlock(named: "entry")
builder.positionAtEnd(of: entryBB)

// allocate space for a local value		
let local = builder.buildAlloca(type: FloatType.double, name: "local")

// Compare to the condition
let test = builder.buildICmp(function.parameters[0], IntType.int1.zero(), .equal)

// Create basic blocks for "then", "else", and "merge"
let thenBB = function.appendBasicBlock(named: "then")
let elseBB = function.appendBasicBlock(named: "else")
let mergeBB = function.appendBasicBlock(named: "merge")

builder.buildCondBr(condition: test, then: thenBB, else: elseBB)

// MARK: Then Block
builder.positionAtEnd(of: thenBB)
// local = 1/89, the fibonacci series (sort of)
let thenVal = FloatType.double.constant(1/89)
// Branch to the merge block

// MARK: Else Block
builder.positionAtEnd(of: elseBB)
// local = 1/109, the fibonacci series (sort of) backwards
let elseVal = FloatType.double.constant(1/109)
// Branch to the merge block

// MARK: Merge Block
builder.positionAtEnd(of: mergeBB)
let phi = builder.buildPhi(FloatType.double, name: "phi_example")
  (thenVal, thenBB),
  (elseVal, elseBB),
builder.buildStore(phi, to: local)
let ret = builder.buildLoad(local, type: FloatType.double, name: "ret")

This program generates the following IR:

define double @calculateFibs(i1) {
  %local = alloca double
  %1 = icmp ne i1 %0, false
  br i1 %1, label %then, label %else

then:                                             ; preds = %entry
  br label %merge

else:                                             ; preds = %entry
  br label %merge

merge:                                            ; preds = %else, %then
  %phi_example = phi double [ 0x3F8702E05C0B8170, %then ], [ 0x3F82C9FB4D812CA0, %else ]
  store double %phi_example, double* %local
  %ret = load double, double* %local
  ret double %ret


LLVMSwift provides a JIT abstraction to make executing code in LLVM modules quick and easy. Let's execute the PHI node example from before:

// Setup the JIT
let jit = try JIT(machine: TargetMachine())
typealias FnPtr = @convention(c) (Bool) -> Double
_ = try jit.addEagerlyCompiledIR(module) { (name) -> JIT.TargetAddress in
  return JIT.TargetAddress()
// Retrieve a handle to the function we're going to invoke
let addr = try jit.address(of: "calculateFibs")
let fn = unsafeBitCast(addr, to: FnPtr.self)
// Call the function!
print(fn(true)) // 0.00917431192660551...
print(fn(false)) // 0.0112359550561798...


There are a couple annoying steps you need to accomplish before building LLVMSwift:

  • Install LLVM 8.0+ using your favorite package manager. For example:
    • brew install llvm
  • Ensure llvm-config is in your PATH
    • That will reside in the /bin folder wherever your package manager installed LLVM.
  • Create a pkg-config file for your specific LLVM installation.
    • We have a utility for this: swift utils/make-pkgconfig.swift

Once you do that, you can add LLVMSwift as a dependency for your own Swift compiler projects!

Installation with Swift Package Manager

.package(url: "https://github.com/llvm-swift/LLVMSwift.git", from: "0.4.0")

Installation without Swift Package Manager

We really recommend using SwiftPM with LLVMSwift, but if your project is structured in such a way that makes using SwiftPM impractical or impossible, use the following instructions:

  • Xcode:
    • Add this repository as a git submodule
    • Add the files in Sources/ to your Xcode project.
    • Under Library Search Paths add the output of llvm-config --libdir
    • Under Header Search Paths add the output of llvm-config --includedir
    • Under Link Target with Libraries drag in /path/to/your/llvm/lib/libLLVM.dylib

This project is used by Trill for all its code generation.



This project is released under the MIT license, a copy of which is available in this repo.


Stars: 543
Help us keep the lights on



0.6.0 - May 25, 2019

⚠️ Breaking Changes Ahead ⚠️

  • LLVMSwift now enforces deployment target constraints. In the future, we will continue to update this constraint in line with the LLVM homebrew package.
  • The PassPipeliner is now provided as an alternative to FunctionPassManager, which may crash if used in many common situations. Please migrate accordingly.
  • The FunctionPassManager is now deprecated
  • Documentation for core components continues to improve
  • An analog to LLVM’s infamous target Triple API has been added. APIs that previously dealt with Strings have been updated accordingly
  • The bindings to ObjectFile were not correct and were not nearly feature-rich enough. The API has been completely overhauled
  • The IRInstruction protocol has been added to unify instruction values around a common protocol.
  • Metadata can now be attached to instructions
  • The debug location accessor now supports being reset to nil without crashing
  • DIFile now exposes file metadata, and file metadata now exposes its source text, directory, and name
  • The Metadata Builder facility (MDBuilder) has been ported from LLVM
  • Additional bit-manipulation primitives for setting and clearing bits and getting leading and trailing 1’s counts have been added to APInt
  • The labels on FunctionType's initializer have been removed
  • The internalize pass has been added
  • All SROA passes no longer differentiate between their parameters. They are now deprecated - SROA is available under with the unified scalarReplacementOfAggregates pass

0.5.0 - Mar 22, 2019

LLVMSwift now tracks LLVM 8.0

  • Convenience bindings to common memory intrinsics (memcpy/memset/memmove) and pointer/integral casts have been added
  • TargetData functions that used to take and return integers were deprecated last release. They are now removed. Please update to the corresponding functions using Size and Alignment.
  • More bindings to optimizer passes have been added
  • Support for the JIT on Linux has been restored
  • The value type of a global value is now accessible
  • A new API for retrieving and attaching metadata to global values and instructions has been added.
  • The new APInt type provides arbitrary precision integral arithmetic that is compatible with LLVM values
  • BasicBlock is now Equatable


LLVMSwift finally supports intrinsics! For more information, see Intrinsics.

0.4.1 - Feb 25, 2019

  • Convenience functions to insert the llvm.dbg.declare and llvm.dbg.value intrinsics have been added to the DIBuilder
  • Inline assembly builders have been improved. Notably, the choice of syntax is now customizable.
  • Bindings for inserting and manipulating module-level debug information flags has been added.
  • Bindings for a few missing optimization passes have been added
  • A crash in the setter for a module's link name has been resolved (h/t @matthewseaman)
  • All concrete instances of IRTypes are now equatable. Values of IRType may be compared directly but are not themselves Equatable.

0.4.0 - Nov 8, 2018

LLVMSwift now tracks LLVM 7.0

In addition to breaking a lot of APIs, our upgrade path to LLVM 7.0 coincides with the first removals of deprecated APIs in LLVMSwift. Without further ado

⚠️ Breaking Changes Ahead ⚠️

  • LLVMSwift now supports Swift 4.2 and the Swift 4.2 tooling and package manager
  • cllvm has been internalized to LLVMSwift. The separate repository has been archived and will no longer be maintained.
  • The CallingConvention enumeration has drastically expanded
  • APIs for COMDAT support have been added
  • The IRConstant protocol has been added, and all global values now conform to it.
  • The return type of APIs that return constants have been refined to use IRConstant where possible
  • Constant GEP, sign extension, and bitcasting APIs have been aded
  • Restrictions on function attributes have been completely lifted
  • Documentation for ThreadLocalModels has drastically expanded and is much more accurate
  • UnnamedAddress now supports local unnamed addresses
  • Routines for adding globals have been moved from IRBuilder to Module where they belong
  • StructType now includes accessors for its opacity and whether it was created with bitpacking or not.
  • The Size and Alignment unit values have been added. Like clang's CharUnits, they prevent bugs with mixed-unit arithmetic and make it convenient to work with byte-level calculations.
  • APIs that used to take and return integers now speak in terms of Size and Alignment. Notably, TargetData's functions that used to take and return integers now take and return Size and Alignment. The old forms are now deprecated
  • The StructLayout enum has been ported from LLVM to provide convenient access to the layout of StructTypes.
  • Accessors for many more attributes of TargetMachine have been added.
  • The Targets supported by a TargetMachine are now enumerable.


LLVMSwift has migrated JIT to ORCJIT this release. To do so, we have completely removed and rewritten the old API. The ORCJIT will continue to evolve and stabilize over the next several LLVM releases.

Debug Information

Last but not least, LLVMSwift now includes a nearly feature-complete implementation of LLVM's debug information APIs. It's been a long time coming, but creating and attaching metadata to IRValues is now a reality!

0.3.0 - Apr 16, 2018

⚠️ Breaking Changes Ahead ⚠️

LLVMSwift now requires LLVM 6.0 and Swift 4.1 to build.

In exchange, MetadataType and TokenType now have working initializers.