Skip to content

Update README #80

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 28, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 161 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,185 @@


# LLVMSwift
[![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)

LLVMSwift is a set of Swifty API wrappers for the LLVM C API.
It makes compiler development feel great from Swift!
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.

## Introduction

## Usage
### LLVM IR

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

```swift
let module = Module(name: "main")
let builder = IRBuilder(module: module)
```

Once you do that, you can start adding functions, global variables, and generating instructions!
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.

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

```swift
let main = builder.addFunction(name: "main",
type: FunctionType(argTypes: [],
returnType: VoidType()))
let builder = IRBuilder(module: module)

let main = builder.addFunction(
name: "main",
type: FunctionType(argTypes: [],
returnType: VoidType())
)
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:

builder.buildRetVoid()
```swift
let constant = IntType.int64.constant(21)
let sum = builder.buildAdd(constant, constant)
builder.buildRet(sum)
```

This simple program generates the following IR:

module.dump()
```llvm
// module.dump()

define void @main() {
entry:
ret i64 42
}
```

The IRBuilder class has methods for almost all functions from the LLVM C API, like:
### Types

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](http://llvm.org/docs/LangRef.html#conversion-operations)).
LLVMSwift represents this with values conforming to the `IRType` protocol and defines
the following types:

- `builder.buildAdd`
- `builder.buildSub`
- `builder.buildMul`
- `builder.buildCondBr`
- `builder.addSwitch`
|**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 |

and so many more.

Plus, it provides common wrappers around oft-used types like `Function`, `Global`, `Switch`, and `PhiNode`.
### 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](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:

```swift
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:

```swift
let function = builder.addFunction(
"calculateFibs",
type: FunctionType(argTypes: [IntType.int1],
returnType: FloatType.double)
)
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(), .notEqual)

// 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)
builder.buildStore(thenVal, to: local)
// Branch to the merge block
builder.buildBr(mergeBB)

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

// MARK: Merge Block
builder.positionAtEnd(of: mergeBB)
let phi = builder.buildPhi(FloatType.double, name: "phi_example")
phi.addIncoming([
(thenVal, thenBB),
(elseVal, elseBB),
])
builder.buildRet(phi)
```

This program generates the following IR:

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

then: ; preds = %entry
store double 0x3F8702E05C0B8170, double* %local
br label %merge

else: ; preds = %entry
store double 0x3F82C9FB4D812CA0, double* %local
br label %merge

merge: ; preds = %else, %then
%phi_example = phi double [ 0x3F8702E05C0B8170, %then ], [ 0x3F82C9FB4D812CA0, %else ]
ret double %phi_example
}
```

### JIT

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

```swift
// Setup the JIT
let jit = try! JIT(module: module, machine: TargetMachine())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure the machine is defaulted, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not.

typealias FnPtr = @convention(c) (Bool) -> Double
// Retrieve a handle to the function we're going to invoke
let fnAddr = jit.addressOfFunction(name: "calculateFibs")
let fn = unsafeBitCast(fnAddr, to: FnPtr.self)
// Call the function!
print(fn(true)) // 0.00917431192660551...
print(fn(false)) // 0.0112359550561798...
```

## Installation

Expand Down Expand Up @@ -86,4 +225,3 @@ all its code generation.

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