make Block implement LLVMVal

This commit is contained in:
Brian Picciano 2016-08-01 18:08:51 -06:00
parent b9a40be6d8
commit 09573cf98b
6 changed files with 80 additions and 42 deletions

View File

@ -13,13 +13,19 @@ import (
// TODO empty parenthesis
// TODO need to figure out how to test LLVMVal stuff
// TODO once we're a bit more confident, make ActualFunc
// TODO LLVMVal -> LLVMBuild?
type LLVMCtx struct {
B llvm.Builder
M llvm.Module
}
// Actual represents the actual expression in question, and has certain
// properties. It is wrapped by Expr which also holds onto contextual
// information, like the token to which Actual was originally parsed from
type Actual interface {
// Initializes an llvm.Value and returns it.
LLVMVal(*Ctx, llvm.Builder) llvm.Value
LLVMVal(*Ctx, LLVMCtx) llvm.Value
}
// equaler is used to compare two expressions. The comparison should not take
@ -43,9 +49,9 @@ type Expr struct {
// LLVMVal passes its arguments to the underlying Actual instance. It caches the
// result, so if this is called multiple times the underlying one is only called
// the first time.
func (e Expr) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
func (e Expr) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
if e.val == nil {
v := e.Actual.LLVMVal(ctx, builder)
v := e.Actual.LLVMVal(ctx, lctx)
e.val = &v
}
return *e.val
@ -67,7 +73,7 @@ func (e Expr) equal(e2 Expr) bool {
type Bool bool
// LLVMVal implements the Actual interface method
func (b Bool) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
func (b Bool) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
return llvm.Value{}
}
@ -85,9 +91,9 @@ func (b Bool) equal(e equaler) bool {
type Int int64
// LLVMVal implements the Actual interface method
func (i Int) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
v := builder.CreateAlloca(llvm.Int64Type(), "")
builder.CreateStore(llvm.ConstInt(llvm.Int64Type(), uint64(i), false), v)
func (i Int) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
v := lctx.B.CreateAlloca(llvm.Int64Type(), "")
lctx.B.CreateStore(llvm.ConstInt(llvm.Int64Type(), uint64(i), false), v)
return v
}
@ -105,7 +111,7 @@ func (i Int) equal(e equaler) bool {
type String string
// LLVMVal implements the Actual interface method
func (s String) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
func (s String) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
return llvm.Value{}
}
@ -124,7 +130,7 @@ func (s String) equal(e equaler) bool {
type Identifier string
// LLVMVal implements the Actual interface method
func (id Identifier) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
func (id Identifier) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
return llvm.Value{}
}
@ -149,7 +155,7 @@ func (m Macro) String() string {
}
// LLVMVal implements the Actual interface method
func (m Macro) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
func (m Macro) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
panic("Macros have no inherent LLVMVal")
}
@ -176,7 +182,7 @@ func (tup Tuple) String() string {
}
// LLVMVal implements the Actual interface method
func (tup Tuple) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
func (tup Tuple) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
return llvm.Value{}
}
@ -208,7 +214,7 @@ func (s Statement) String() string {
}
// LLVMVal implements the Actual interface method
func (s Statement) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
func (s Statement) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
m, ok := s.To.Actual.(Macro)
if !ok {
// TODO proper error
@ -225,7 +231,7 @@ func (s Statement) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
// TODO proper error
panic(err)
}
return newe.LLVMVal(ctx, builder)
return newe.LLVMVal(ctx, lctx)
}
func (s Statement) equal(e equaler) bool {
@ -238,19 +244,34 @@ func (s Statement) equal(e equaler) bool {
// Block represents a set of statements which share a scope, i.e. If one
// statement binds a variable the rest of the statements in the block can use
// that variable, including sub-blocks within this one.
type Block []Statement
type Block []Expr
func (b Block) String() string {
strs := make([]string, len(b))
for i := range b {
strs[i] = b[i].String()
strs[i] = b[i].Actual.(Statement).String()
}
return fmt.Sprintf("{ %s }", strings.Join(strs, " "))
}
// LLVMVal implements the Actual interface method
func (b Block) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
return llvm.Value{}
func (b Block) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
name := randStr() // TODO make this based on token
// TODO make these based on actual statements
out := llvm.Int64Type()
in := []llvm.Type{}
fn := llvm.AddFunction(lctx.M, name, llvm.FunctionType(out, in, false))
block := llvm.AddBasicBlock(fn, "entry")
lctx.B.SetInsertPoint(block, block.FirstInstruction())
var v llvm.Value
for _, se := range b {
v = se.Actual.LLVMVal(ctx, lctx)
}
// last v is used as return
// TODO empty return
lctx.B.CreateRet(v)
return fn
}
func (b Block) equal(e equaler) bool {

View File

@ -8,11 +8,11 @@ import (
type addActual []Expr
func (aa addActual) LLVMVal(ctx *Ctx, builder llvm.Builder) llvm.Value {
a := builder.CreateLoad(aa[0].LLVMVal(ctx, builder), "")
func (aa addActual) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
a := lctx.B.CreateLoad(aa[0].LLVMVal(ctx, lctx), "")
for i := range aa[1:] {
b := builder.CreateLoad(aa[i+1].LLVMVal(ctx, builder), "")
a = builder.CreateAdd(a, b, "")
b := lctx.B.CreateLoad(aa[i+1].LLVMVal(ctx, lctx), "")
a = lctx.B.CreateAdd(a, b, "")
}
return a
}

View File

@ -291,14 +291,13 @@ func parseBlock(toks []lexer.Token) (Expr, error) {
if expr, toks, err = parse(toks); err != nil {
return Expr{}, err
}
stmt, ok := expr.Actual.(Statement)
if !ok {
if _, ok := expr.Actual.(Statement); !ok {
return Expr{}, exprErr{
reason: "blocks may only contain full statements",
tok: expr.Token,
tokCtx: "non-statement here",
}
}
b = append(b, stmt)
b = append(b, expr)
}
}

View File

@ -131,10 +131,10 @@ func TestParseStatement(t *T) {
}
func TestParseBlock(t *T) {
stmt := func(in, to Expr) Statement {
return Statement{In: in, To: to}
stmt := func(in, to Expr) Expr {
return Expr{Actual: Statement{In: in, To: to}}
}
block := func(stmts ...Statement) Expr {
block := func(stmts ...Expr) Expr {
return Expr{Actual: Block(stmts)}
}

14
expr/util.go Normal file
View File

@ -0,0 +1,14 @@
package expr
import (
"encoding/hex"
"math/rand"
)
func randStr() string {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return hex.EncodeToString(b)
}

34
main.go
View File

@ -22,45 +22,49 @@ func main() {
llvm.InitializeNativeAsmPrinter()
// setup our builder and module
builder := llvm.NewBuilder()
mod := llvm.NewModule("my_module")
// create our function prologue
main := llvm.FunctionType(llvm.Int64Type(), []llvm.Type{}, false)
llvm.AddFunction(mod, "main", main)
block := llvm.AddBasicBlock(mod.NamedFunction("main"), "entry")
builder.SetInsertPoint(block, block.FirstInstruction())
lctx := expr.LLVMCtx{
B: llvm.NewBuilder(),
M: llvm.NewModule("my_module"),
}
// do the work in the function
a := expr.Expr{Actual: expr.Int(1)}
b := expr.Expr{Actual: expr.Int(2)}
c := expr.Expr{Actual: expr.Int(3)}
tup := expr.Expr{Actual: expr.Tuple{a, b, c}}
tup := expr.Expr{Actual: expr.Tuple([]expr.Expr{a, b, c})}
addMacro := expr.Expr{Actual: expr.Macro("add")}
stmt := expr.Expr{Actual: expr.Statement{In: tup, To: addMacro}}
block := expr.Block([]expr.Expr{stmt})
fn := block.LLVMVal(expr.RootCtx, lctx)
result := stmt.LLVMVal(expr.RootCtx, builder)
builder.CreateRet(result)
// create main and call our function
mainFn := llvm.AddFunction(lctx.M, "main", llvm.FunctionType(llvm.Int64Type(), []llvm.Type{}, false))
mainBlock := llvm.AddBasicBlock(mainFn, "entry")
lctx.B.SetInsertPoint(mainBlock, mainBlock.FirstInstruction())
ret := lctx.B.CreateCall(fn, []llvm.Value{}, "")
lctx.B.CreateRet(ret)
// verify it's all good
if err := llvm.VerifyModule(mod, llvm.ReturnStatusAction); err != nil {
if err := llvm.VerifyModule(lctx.M, llvm.ReturnStatusAction); err != nil {
panic(err)
}
fmt.Println("# verified")
// Dump the IR
fmt.Println("# dumping IR")
mod.Dump()
lctx.M.Dump()
fmt.Println("# done dumping IR")
// create our exe engine
fmt.Println("# creating new execution engine")
engine, err := llvm.NewExecutionEngine(mod)
engine, err := llvm.NewExecutionEngine(lctx.M)
if err != nil {
panic(err)
}
// run the function!
fmt.Println("# running the function main")
funcResult := engine.RunFunction(mod.NamedFunction("main"), []llvm.GenericValue{})
funcResult := engine.RunFunction(lctx.M.NamedFunction("main"), []llvm.GenericValue{})
fmt.Printf("%d\n", funcResult.Int(false))
}