Skip to content

Commit 4e05e28

Browse files
committed
Update README
1 parent d60cc80 commit 4e05e28

File tree

1 file changed

+140
-23
lines changed

1 file changed

+140
-23
lines changed

README.md

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

45163
## Installation
46164

@@ -86,4 +204,3 @@ all its code generation.
86204

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

0 commit comments

Comments
 (0)