- Notifications
You must be signed in to change notification settings - Fork57
A Swift wrapper for the LLVM C API (version 11.0)
License
llvm-swift/LLVMSwift
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
LLVMSwift is a pure Swift interface to theLLVM 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 aModule
letmodule=Module(name:"main")
LLVM IR construction is handled byIRBuilder objects. AnIRBuilder 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:
letbuilder=IRBuilder(module: module)letmain= builder.addFunction("main", type:FunctionType([],IntType.int64))letentry= main.appendBasicBlock(named:"entry")builder.positionAtEnd(of: entry)
Inserting instructions creates nativeIRValue placeholder objects that allow us to structure LLVM IR programs just like Swift programs:
letconstant=IntType.int64.constant(21)letsum= builder.buildAdd(constant, constant)builder.buildRet(sum)
This simple program generates the following IR:
//module.dump()definei64@main() {entry:reti6442}
LLVM IR is a strong, statically typed language. As such, values and functionsare tagged with their types, and conversions between them must be explicit (seeConversion Operators).LLVMSwift represents this with values conforming to theIRType protocol and definesthe 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 is changed through the unconditional and conditionalbr instruction.
LLVM is also famous for a control-flow specific IR construct called aPHI 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{letretVal:Doubleif !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 ofretVal depends on the path the flow of control takes through this program, so we must emit a PHI node to properly initialize it:
letfunction= builder.addFunction("calculateFibs", type:FunctionType([IntType.int1],FloatType.double))letentryBB= function.appendBasicBlock(named:"entry")builder.positionAtEnd(of: entryBB)// allocate space for a local valueletlocal= builder.buildAlloca(type:FloatType.double, name:"local")// Compare to the conditionlettest= builder.buildICmp(function.parameters[0],IntType.int1.zero(),.equal)// Create basic blocks for "then", "else", and "merge"letthenBB= function.appendBasicBlock(named:"then")letelseBB= function.appendBasicBlock(named:"else")letmergeBB= function.appendBasicBlock(named:"merge")builder.buildCondBr(condition: test, then: thenBB, else: elseBB)// MARK: Then Blockbuilder.positionAtEnd(of: thenBB)// local = 1/89, the fibonacci series (sort of)letthenVal=FloatType.double.constant(1/89)// Branch to the merge blockbuilder.buildBr(mergeBB)// MARK: Else Blockbuilder.positionAtEnd(of: elseBB)// local = 1/109, the fibonacci series (sort of) backwardsletelseVal=FloatType.double.constant(1/109)// Branch to the merge blockbuilder.buildBr(mergeBB)// MARK: Merge Blockbuilder.positionAtEnd(of: mergeBB)letphi= builder.buildPhi(FloatType.double, name:"phi_example")phi.addIncoming([(thenVal, thenBB),(elseVal, elseBB),])builder.buildStore(phi, to: local)letret= builder.buildLoad(local, type:FloatType.double, name:"ret")builder.buildRet(ret)
This program generates the following IR:
definedouble@calculateFibs(i1) {entry:%local =allocadouble%1 =icmpnei1%0,falsebri1%1,label%then,label%elsethen:; preds = %entrybrlabel%mergeelse:; preds = %entrybrlabel%mergemerge:; preds = %else, %then%phi_example =phidouble [0x3F8702E05C0B8170,%then ], [0x3F82C9FB4D812CA0,%else ]storedouble%phi_example,double*%local%ret =loaddouble,double*%localretdouble%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 JITletjit=tryJIT(machine:TargetMachine())typealiasFnPtr=@convention(c)(Bool)->Double_=try jit.addEagerlyCompiledIR(module){(name)->JIT.TargetAddressinreturnJIT.TargetAddress()}// Retrieve a handle to the function we're going to invokeletaddr=try jit.address(of:"calculateFibs")letfn=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 buildingLLVMSwift:
- Install LLVM 11.0+ using your favorite package manager. For example:
brew install llvm@11
- Ensure
llvm-configis in yourPATH- That will reside in the
/binfolder wherever your package managerinstalled LLVM.
- That will reside in the
- Create a pkg-config file for your specific LLVM installation.
- We have a utility for this:
swift utils/make-pkgconfig.swift
- We have a utility for this:
Once you do that, you can add LLVMSwift as a dependency for your own Swiftcompiler projects!
.package(url:"https://github.com/llvm-swift/LLVMSwift.git", from:"0.8.0")
We really recommend using SwiftPM with LLVMSwift, but if your project isstructured 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 Pathsadd the output ofllvm-config --libdir - Under
Header Search Pathsadd the output ofllvm-config --includedir - Under
Link Target with Librariesdrag in/path/to/your/llvm/lib/libLLVM.dylib
This project is used byTrill forall its code generation.
- Harlan Haskins (@harlanhaskins)
- Robert Widmann (@CodaFi)
This project is released under the MIT license, a copy of which is availablein this repo.
About
A Swift wrapper for the LLVM C API (version 11.0)
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.