Skip to content

Commit f93e30f

Browse files
committed
Update README
1 parent f8b4743 commit f93e30f

File tree

1 file changed

+137
-24
lines changed

1 file changed

+137
-24
lines changed

README.md

Lines changed: 137 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,167 @@
1-
2-
31
# LLVMSwift
42
[![Build Status](https://travis-ci.org/trill-lang/LLVMSwift.svg?branch=master)](https://travis-ci.org/trill-lang/LLVMSwift) [![Documentation](https://cdn.rawgit.com/trill-lang/LLVMSwift/master/docs/badge.svg)](https://trill-lang.github.io/LLVMSwift) [![Slack Invite](https://llvmswift-slack.herokuapp.com/badge.svg)](https://llvmswift-slack.herokuapp.com)
53

6-
LLVMSwift is a set of Swifty API wrappers for the LLVM C API.
7-
It makes compiler development feel great from Swift!
4+
LLVMSwift is a pure Swift interface to the [LLVM](http://llvm.org) API and its associated libraries. It provides native, easy-to-use components to make compiler development fun.
5+
6+
## Introduction
87

9-
## Usage
8+
### LLVM IR
109

11-
To start emitting IR, you'll want to create a `Module` object, with an optional `Context` parameter,
12-
and an `IRBuilder` that will build instructions for that module.
10+
The root unit of organization of an LLVM IR program is a `Module`
1311

1412
```swift
1513
let module = Module(name: "main")
16-
let builder = IRBuilder(module: module)
1714
```
1815

19-
Once you do that, you can start adding functions, global variables, and generating instructions!
16+
LLVM IR is construction is done with an `IRBuilder` object. An `IRBuilder` is a cursor pointed inside a context, and as such has ways of extending that context and moving around inside of it.
17+
18+
Defining a simple function and moving the cursor to a point where we can begin inserting instructions is done like so:
2019

2120
```swift
22-
let main = builder.addFunction(name: "main",
23-
type: FunctionType(argTypes: [],
24-
returnType: VoidType()))
21+
let builder = IRBuilder(module: module)
22+
23+
let main = builder.addFunction(
24+
name: "main",
25+
type: FunctionType(argTypes: [],
26+
returnType: VoidType())
27+
)
2528
let entry = main.appendBasicBlock(named: "entry")
2629
builder.positionAtEnd(of: entry)
30+
```
2731

28-
builder.buildRetVoid()
32+
Inserting instructions creates native `IRValue` placeholder objects that allow us to structure LLVM IR programs just like Swift programs:
2933

30-
module.dump()
34+
```swift
35+
let constant = IntType.int64.constant(21)
36+
let sum = builder.buildAdd(constant, constant)
37+
builder.buildRet(sum)
3138
```
3239

33-
The IRBuilder class has methods for almost all functions from the LLVM C API, like:
40+
This simple program generates the following IR:
3441

35-
- `builder.buildAdd`
36-
- `builder.buildSub`
37-
- `builder.buildMul`
38-
- `builder.buildCondBr`
39-
- `builder.addSwitch`
42+
```llvm
43+
// module.dump()
4044
41-
and so many more.
45+
define void @main() {
46+
entry:
47+
ret i64 42
48+
}
49+
```
50+
51+
52+
### Control Flow
53+
54+
Control flow is changed through the unconditional and conditional `br` instruction.
55+
56+
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:
57+
58+
```swift
59+
func calculateFibs(_ backward : Bool) -> Double {
60+
let retVal : Double
61+
if !backward {
62+
// the fibonacci series (sort of)
63+
retVal = 1/89
64+
} else {
65+
// the fibonacci series (sort of) backwards
66+
retVal = 1/109
67+
}
68+
return retVal
69+
}
70+
```
4271

43-
Plus, it provides common wrappers around oft-used types like `Function`, `Global`, `Switch`, and `PhiNode`.
72+
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:
73+
74+
```swift
75+
let function = builder.addFunction("calculateFibs", type: FunctionType(argTypes: [IntType.int1], returnType: FloatType.double))
76+
let entryBB = function.appendBasicBlock(named: "entry")
77+
builder.positionAtEnd(of: entryBB)
78+
79+
// allocate space for a local value
80+
let local = builder.buildAlloca(type: FloatType.double, name: "local")
81+
82+
// Compare to the condition
83+
let test = builder.buildICmp(function.parameters[0], IntType.int1.zero(), .notEqual)
84+
85+
// Create basic blocks for "then", "else", and "merge"
86+
let thenBB = function.appendBasicBlock(named: "then")
87+
let elseBB = function.appendBasicBlock(named: "else")
88+
let mergeBB = function.appendBasicBlock(named: "merge")
89+
90+
builder.buildCondBr(condition: test, then: thenBB, else: elseBB)
91+
92+
// MARK: Then Block
93+
builder.positionAtEnd(of: thenBB)
94+
// local = 1/89, the fibonacci series (sort of)
95+
let thenVal = FloatType.double.constant(1/89)
96+
builder.buildStore(thenVal, to: local)
97+
// Branch to the merge block
98+
builder.buildBr(mergeBB)
99+
100+
// MARK: Else Block
101+
builder.positionAtEnd(of: elseBB)
102+
// local = 1/109, the fibonacci series (sort of) backwards
103+
let elseVal = FloatType.double.constant(1/109)
104+
builder.buildStore(elseVal, to: local)
105+
// Branch to the merge block
106+
builder.buildBr(mergeBB)
107+
108+
// MARK: Merge Block
109+
builder.positionAtEnd(of: mergeBB)
110+
let phi = builder.buildPhi(FloatType.double, name: "phi_example")
111+
phi.addIncoming([
112+
(thenVal, thenBB),
113+
(elseVal, elseBB),
114+
])
115+
builder.buildRet(phi)
116+
```
117+
118+
This program generates the following IR:
119+
120+
```llvm
121+
define double @calculateFibs(i1) {
122+
entry:
123+
%local = alloca double
124+
%1 = icmp ne i1 %0, false
125+
br i1 %1, label %then, label %else
126+
127+
then: ; preds = %entry
128+
store double 0x3F8702E05C0B8170, double* %local
129+
br label %merge
130+
131+
else: ; preds = %entry
132+
store double 0x3F82C9FB4D812CA0, double* %local
133+
br label %merge
134+
135+
merge: ; preds = %else, %then
136+
%phi_example = phi double [ 0x3F8702E05C0B8170, %then ], [ 0x3F82C9FB4D812CA0, %else ]
137+
ret double %phi_example
138+
}
139+
```
140+
141+
### JIT
142+
143+
LLVMSwift provides a JIT abstraction to make executing code in LLVM modules quick and easy. Let's execute the PHI node example from before:
144+
145+
```swift
146+
// Setup the JIT
147+
let jit = try! JIT(module: module, machine: TargetMachine())
148+
typealias FnPtr = @convention(c) (Bool) -> Double
149+
// Retrieve a handle to the function we're going to invoke
150+
let fnAddr = jit.addressOfFunction(name: "calculateFibs")
151+
let fn = unsafeBitCast(fnAddr, to: FnPtr.self)
152+
// Call the function!
153+
print(fn(true)) // 0.00917431192660551...
154+
print(fn(false)) // 0.0112359550561798...
155+
```
156+
157+
### Platform Management
44158

45159
## Installation
46160

47161
There are a couple, annoying steps you need to get it working before it'll
48162
build.
49163

50-
- Install LLVM 3.9 using your favorite package manager. For example:
164+
- Install LLVM 4.0 using your favorite package manager. For example:
51165
- `brew install llvm`
52166
- Ensure `llvm-config` is in your `PATH`
53167
- That will reside in the `/bin` folder wherever your package manager
@@ -86,4 +200,3 @@ all its code generation.
86200

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

0 commit comments

Comments
 (0)