Skip to content

Commit

Permalink
readme: add release history and roadmap and extend usage
Browse files Browse the repository at this point in the history
This is in preparation for the v0.2 release. The installation
instructions will be accurate once v0.2 is released.

Updates #20.
  • Loading branch information
mewmew committed Jun 14, 2017
1 parent ab9426a commit 04be81a
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 13 deletions.
265 changes: 257 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,270 @@ The aim of this project is to provide a pure Go library for interacting with [LL

## Installation

```bash
go get github.com/llir/llvm/...
```

## Hacking

Anyone wishing to hack on the project may need to download the [Gocc](https://github.com/goccmack/gocc) tool, which is used to generate the LLVM IR assembly lexer and parser from a [BNF grammar](https://github.com/llir/llvm/blob/master/asm/internal/ll.bnf).

```bash
go get github.com/goccmack/gocc
go get -d github.com/llir/llvm
make -C $GOPATH/src/github.com/llir/llvm/asm/internal
go get -t github.com/llir/llvm/...
```

## Status
## Usage

### Output LLVM IR assembly

[Example usage in GoDoc](https://godoc.org/github.com/llir/llvm/ir#example-package).

```go
// This example produces LLVM IR code equivalent to the following C code,
// which implements a pseudo-random number generator.
//
// int abs(int x);
//
// int seed = 0;
//
// // ref: https://en.wikipedia.org/wiki/Linear_congruential_generator
// // a = 0x15A4E35
// // c = 1
// int rand(void) {
// seed = seed*0x15A4E35 + 1;
// return abs(seed);
// }

// Create convenience types and constants.
i32 := types.I32
zero := constant.NewInt(0, i32)
a := constant.NewInt(0x15A4E35, i32) // multiplier of the PRNG.
c := constant.NewInt(1, i32) // increment of the PRNG.

// Create a new LLVM IR module.
m := ir.NewModule()

// Create an external function declaration and append it to the module.
//
// int abs(int x);
abs := m.NewFunction("abs", i32, ir.NewParam("x", i32))

// Create a global variable definition and append it to the module.
//
// int seed = 0;
seed := m.NewGlobalDef("seed", zero)

// Create a function definition and append it to the module.
//
// int rand(void) { ... }
rand := m.NewFunction("rand", i32)

// Create an unnamed entry basic block and append it to the `rand` function.
entry := rand.NewBlock("")

// Create instructions and append them to the entry basic block.
tmp1 := entry.NewLoad(seed)
tmp2 := entry.NewMul(tmp1, a)
tmp3 := entry.NewAdd(tmp2, c)
entry.NewStore(tmp3, seed)
tmp4 := entry.NewCall(abs, tmp3)
entry.NewRet(tmp4)

// Print the LLVM IR assembly of the module.
fmt.Println(m)
```

### Parse LLVM IR assembly

[Example usage in GoDoc](https://godoc.org/github.com/llir/llvm/asm#example-package).

```go
// Parse the LLVM IR assembly file `rand.ll`.
m, err := asm.ParseFile("internal/testdata/rand.ll")
if err != nil {
log.Fatal(err)
}
// Pretty-print the data types of the parsed LLVM IR module.
pretty.Println(m)
```

### Process LLVM IR

[Example usage in GoDoc](https://godoc.org/github.com/llir/llvm/ir#example-package--Evaluator).

```go
package main

import (
"fmt"
"log"

"github.com/llir/llvm/asm"
"github.com/llir/llvm/ir"
"github.com/llir/llvm/ir/constant"
"github.com/llir/llvm/ir/types"
"github.com/llir/llvm/ir/value"
)

func main() {
// Parse the LLVM IR assembly file `eval.ll`.
m, err := asm.ParseFile("testdata/eval.ll")
if err != nil {
log.Fatal(err)
}
// Evalute and print the return value of the `main` function.
for _, f := range m.Funcs {
if f.Name == "main" {
e := newEvaluator(f)
fmt.Println("result:", e.eval())
break
}
}

}

// evaluator is a function evaluator.
type evaluator struct {
// Function.
f *ir.Function
// Function arguments.
args []value.Value
}

// newEvaluator returns a new function evaluator, for evaluating the result of
// invoking f with args.
func newEvaluator(f *ir.Function, args ...value.Value) *evaluator {
return &evaluator{f: f, args: args}
}

// eval evalutes f and returns the corresponding 64-bit integer.
func (e *evaluator) eval() uint32 {
f := e.f
if !types.Equal(f.Sig.Ret, types.I32) {
panic(fmt.Errorf("support for function return type %v not yet implemented", f.Sig.Ret))
}
for _, block := range f.Blocks {
switch term := block.Term.(type) {
case *ir.TermRet:
// NOTE: support for functions with more than one RET terminator not
// yet implemented.
if term.X != nil {
// Evaluate the result of the first return value of a function is
// evaluated.
return e.evalValue(term.X)
}
}
}
panic(fmt.Errorf("unable to locate RET terminator in function %q", f.Name))
}

// evalInst evaluates inst and returns the corresponding 64-bit integer.
func (e *evaluator) evalInst(inst ir.Instruction) uint32 {
switch inst := inst.(type) {
// Binary instructions.
case *ir.InstAdd:
return e.evalValue(inst.X) + e.evalValue(inst.Y)
case *ir.InstSub:
return e.evalValue(inst.X) - e.evalValue(inst.Y)
case *ir.InstMul:
return e.evalValue(inst.X) * e.evalValue(inst.Y)
case *ir.InstUDiv:
return e.evalValue(inst.X) / e.evalValue(inst.Y)
case *ir.InstSDiv:
return e.evalValue(inst.X) / e.evalValue(inst.Y)
case *ir.InstURem:
return e.evalValue(inst.X) % e.evalValue(inst.Y)
case *ir.InstSRem:
return e.evalValue(inst.X) % e.evalValue(inst.Y)
// Bitwise instructions.
case *ir.InstShl:
return e.evalValue(inst.X) << e.evalValue(inst.Y)
case *ir.InstLShr:
return e.evalValue(inst.X) >> e.evalValue(inst.Y)
case *ir.InstAShr:
x, y := e.evalValue(inst.X), e.evalValue(inst.Y)
result := x >> y
if x&0x80000000 != 0 {
for i := uint32(31); i >= 0; i-- {
mask := uint32(1 << i)
if result&mask != 0 {
break
}
result |= mask
}
}
return result
case *ir.InstAnd:
return e.evalValue(inst.X) & e.evalValue(inst.Y)
case *ir.InstOr:
return e.evalValue(inst.X) | e.evalValue(inst.Y)
case *ir.InstXor:
return e.evalValue(inst.X) ^ e.evalValue(inst.Y)
// Other instructions.
case *ir.InstCall:
callee, ok := inst.Callee.(*ir.Function)
if !ok {
panic(fmt.Errorf("support for callee of type %T not yet implemented", inst.Callee))
}
ee := newEvaluator(callee, inst.Args...)
return ee.eval()
default:
panic(fmt.Errorf("support for instruction type %T not yet implemented", inst))
}
}

// evalValue evalutes v and returns the corresponding 64-bit integer.
func (e *evaluator) evalValue(v value.Value) uint32 {
switch v := v.(type) {
case ir.Instruction:
return e.evalInst(v)
case *constant.Int:
return uint32(v.X.Int64())
case *types.Param:
if len(v.Name) == 0 {
panic("support for unnamed parameters not yet implemented")
}
f := e.f
for i, param := range f.Sig.Params {
if v.Name == param.Name {
return e.evalValue(e.args[i])
}
}
panic(fmt.Errorf("unable to locate paramater %q of function %q", v.Name, f.Name))
default:
panic(fmt.Errorf("support for value type %T not yet implemented", v))
}
}
```

## Release history

### Version 0.1 (2015-04-19)

Initial release.

Preliminary work on the `llvm/ir` package which provides an in-memory representation of LLVM IR in pure Go.

Hand-written lexer and preliminary work on a recursive descent parser for LLVM IR assembly.

## Roadmap

### Version 0.2 (to be released)

Primary focus of version 0.2: *read and write support of LLVM IR assembly*.

Lexers and parsers for LLVM IR assembly are automatically generated from a [BNF grammar](https://github.com/llir/llvm/blob/master/asm/internal/ll.bnf) using [Gocc](https://github.com/goccmack/gocc).

A high-level API for parsing LLVM IR assembly is provided by [llvm/asm](https://godoc.org/github.com/llir/llvm/asm).

The [llvm/ir](https://godoc.org/github.com/llir/llvm/ir) package supports all instructions of LLVM IR.

### Version 0.3 (to be released)

Updated: 2017-01-02
Primary focus of version 0.3: *data flow analysis*.

- [x] Write support of LLVM IR assembly files.
- [Example usage in GoDoc](https://godoc.org/github.com/llir/llvm/ir#example-package).
- [x] Read support of LLVM IR assembly files (see issue [#15](https://github.com/llir/llvm/issues/15)).
- [Example usage in GoDoc](https://godoc.org/github.com/llir/llvm/asm#example-package).
Introduce API for use-def chains.

## Public domain

Expand Down
4 changes: 1 addition & 3 deletions ir/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ package ir_test

import (
"fmt"
"go/constant"
"log"

"github.com/llir/llvm/asm"
"github.com/llir/llvm/ir"
"github.com/llir/llvm/ir/constant"
"github.com/llir/llvm/ir/types"
"github.com/llir/llvm/ir/value"
)

// === [ Evaluator example ] ===================================================

func Example_evaluator() {
// Parse the LLVM IR assembly file `eval.ll`.
m, err := asm.ParseFile("testdata/eval.ll")
Expand Down
2 changes: 0 additions & 2 deletions ir/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"github.com/llir/llvm/ir/types"
)

// === [ Pseudo-random number generator example ] ==============================

func Example() {
// This example produces LLVM IR code equivalent to the following C code,
// which implements a pseudo-random number generator.
Expand Down

0 comments on commit 04be81a

Please sign in to comment.