improve how buildCmds are defined

This commit is contained in:
Brian Picciano 2017-02-11 13:35:02 -07:00
parent 54448fda80
commit 8937201f5c
3 changed files with 198 additions and 140 deletions

67
main.go
View File

@ -24,70 +24,3 @@ func main() {
out, err := mod.Run() out, err := mod.Run()
fmt.Printf("\n\n########\nout: %v %v\n", out, err) fmt.Printf("\n\n########\nout: %v %v\n", out, err)
} }
/*
func main() {
//ee, err := expr.Parse(os.Stdin)
//if err != nil {
// panic(err)
//}
//for _, e := range ee {
// fmt.Println(e)
//}
log.Printf("initializing build context")
ctx := expr.NewCtx()
bctx := expr.NewBuildCtx("my_module")
log.Printf("making program")
add := expr.Macro("add")
//bind := expr.Macro("bind")
op := expr.Macro("op")
in := expr.Macro("in")
incr := expr.NewStatement(op,
expr.NewList(
expr.NewStatement(add, expr.NewTuple(
expr.Int(1),
expr.NewStatement(in, expr.NewTuple()),
)),
),
)
stmts := []expr.Statement{
expr.NewStatement(
incr,
expr.NewStatement(incr, expr.Int(4)),
),
}
log.Printf("creating main function")
mainFn := llvm.AddFunction(bctx.M, "main", llvm.FunctionType(llvm.Int64Type(), []llvm.Type{}, false))
mainBlock := llvm.AddBasicBlock(mainFn, "")
bctx.B.SetInsertPoint(mainBlock, mainBlock.FirstInstruction())
log.Printf("actually processing program")
out := bctx.Build(ctx, stmts...)
bctx.B.CreateRet(out)
//bctx.Build(ctx, stmts...)
//bctx.B.CreateRet(llvm.ConstInt(llvm.Int64Type(), uint64(5), false))
fmt.Println("######## dumping IR")
bctx.M.Dump()
fmt.Println("######## done dumping IR")
log.Printf("verifying")
if err := llvm.VerifyModule(bctx.M, llvm.ReturnStatusAction); err != nil {
panic(err)
}
log.Printf("creating execution enging")
engine, err := llvm.NewExecutionEngine(bctx.M)
if err != nil {
panic(err)
}
log.Printf("running main function")
funcResult := engine.RunFunction(bctx.M.NamedFunction("main"), []llvm.GenericValue{})
fmt.Printf("\nOUTPUT:\n%d\n", funcResult.Int(false))
}
*/

129
vm/cmds.go Normal file
View File

@ -0,0 +1,129 @@
package vm
import (
"strconv"
"github.com/mediocregopher/ginger/lang"
"llvm.org/llvm/bindings/go/llvm"
)
type buildCmd struct {
pattern lang.Tuple
inTypeFn func(lang.Term) (llvm.Type, error)
outTypeFn func(lang.Term) (llvm.Type, error)
buildFn func(lang.Term) (llvm.Value, error)
}
func (cmd buildCmd) matches(t lang.Term) bool {
return lang.Match(cmd.pattern, t)
}
func (cmd buildCmd) inType(t lang.Term) (llvm.Type, error) {
if cmd.inTypeFn == nil {
return llvm.VoidType(), nil
}
return cmd.inTypeFn(t)
}
func (cmd buildCmd) outType(t lang.Term) (llvm.Type, error) {
if cmd.outTypeFn == nil {
return llvm.VoidType(), nil
}
return cmd.outTypeFn(t)
}
func (cmd buildCmd) build(t lang.Term) (llvm.Value, error) {
return cmd.buildFn(t)
}
func buildCmds(mod *Module) []buildCmd {
aPat := func(a lang.Atom) lang.Tuple {
return lang.Tuple{lang.AAtom, a}
}
cPat := func(t lang.Term) lang.Tuple {
return lang.Tuple{lang.AConst, t}
}
tPat := func(el ...lang.Term) lang.Tuple {
return lang.Tuple{lang.ATuple, lang.Tuple(el)}
}
buildPat := func(a lang.Atom, b lang.Tuple) lang.Tuple {
return tPat(aPat(a), b)
}
return []buildCmd{
{ // (int 42)
pattern: buildPat(lang.AInt, cPat(lang.AUnder)),
outTypeFn: func(t lang.Term) (llvm.Type, error) {
return llvm.Int64Type(), nil
},
buildFn: func(t lang.Term) (llvm.Value, error) {
con := t.(lang.Const)
coni, err := strconv.ParseInt(string(con), 10, 64)
if err != nil {
return llvm.Value{}, err
}
// TODO why does this have to be cast?
return llvm.ConstInt(llvm.Int64Type(), uint64(coni), false), nil
},
},
{ // (tup ((atom foo) (const 10)))
pattern: buildPat(lang.ATuple, lang.Tuple{lang.ATuple, lang.AUnder}),
outTypeFn: func(t lang.Term) (llvm.Type, error) {
tup := t.(lang.Tuple)
if len(tup) == 0 {
return llvm.VoidType(), nil
}
var err error
typs := make([]llvm.Type, len(tup))
for i := range tup {
if typs[i], err = mod.outType(tup[i]); err != nil {
return llvm.Type{}, err
}
}
return llvm.StructType(typs, false), nil
},
buildFn: func(t lang.Term) (llvm.Value, error) {
tup := t.(lang.Tuple)
// if the tuple is empty then it is a void
if len(tup) == 0 {
return llvm.Undef(llvm.VoidType()), nil
}
var err error
vals := make([]llvm.Value, len(tup))
typs := make([]llvm.Type, len(tup))
for i := range tup {
if vals[i], err = mod.build(tup[i]); err != nil {
return llvm.Value{}, err
}
typs[i] = vals[i].Type()
}
str := llvm.Undef(llvm.StructType(typs, false))
for i := range vals {
str = mod.b.CreateInsertValue(str, vals[i], i, "")
}
return str, nil
},
},
{ // (add ((const 5) (var foo)))
pattern: buildPat(lang.AAdd, tPat(lang.TDblUnder, lang.TDblUnder)),
outTypeFn: func(t lang.Term) (llvm.Type, error) {
return llvm.Int64Type(), nil
},
buildFn: func(t lang.Term) (llvm.Value, error) {
tup := t.(lang.Tuple)
v1, err := mod.build(tup[0])
if err != nil {
return llvm.Value{}, err
}
v2, err := mod.build(tup[1])
if err != nil {
return llvm.Value{}, err
}
return mod.b.CreateAdd(v1, v2, ""), nil
},
},
}
}

142
vm/vm.go
View File

@ -1,8 +1,8 @@
package vm package vm
import ( import (
"errors"
"fmt" "fmt"
"strconv"
"sync" "sync"
"github.com/mediocregopher/ginger/lang" "github.com/mediocregopher/ginger/lang"
@ -19,9 +19,10 @@ type Val struct {
// or compiled. A Module should be Dispose()'d of once it's no longer being // or compiled. A Module should be Dispose()'d of once it's no longer being
// used. // used.
type Module struct { type Module struct {
b llvm.Builder b llvm.Builder
m llvm.Module m llvm.Module
mainFn llvm.Value mainFn llvm.Value
buildCmds []buildCmd
} }
var initOnce sync.Once var initOnce sync.Once
@ -37,18 +38,13 @@ func Build(t lang.Term) (*Module, error) {
b: llvm.NewBuilder(), b: llvm.NewBuilder(),
m: llvm.NewModule(""), m: llvm.NewModule(""),
} }
mod.buildCmds = buildCmds(mod)
// TODO figure out types var err error
mod.mainFn = llvm.AddFunction(mod.m, "", llvm.FunctionType(llvm.Int64Type(), []llvm.Type{}, false)) if mod.mainFn, err = mod.buildFn(t); err != nil {
block := llvm.AddBasicBlock(mod.mainFn, "")
mod.b.SetInsertPoint(block, block.FirstInstruction())
out, err := mod.build(t)
if err != nil {
mod.Dispose() mod.Dispose()
return nil, err return nil, err
} }
mod.b.CreateRet(out.v)
if err := llvm.VerifyModule(mod.m, llvm.ReturnStatusAction); err != nil { if err := llvm.VerifyModule(mod.m, llvm.ReturnStatusAction); err != nil {
mod.Dispose() mod.Dispose()
@ -65,73 +61,73 @@ func (mod *Module) Dispose() {
//mod.b.Dispose() //mod.b.Dispose()
} }
func (mod *Module) build(t lang.Term) (Val, error) { func (mod *Module) matchingBuildCmd(t lang.Term) (buildCmd, error) {
aPat := func(a lang.Atom) lang.Tuple { for _, cmd := range mod.buildCmds {
return lang.Tuple{lang.AAtom, a} if !cmd.matches(t) {
continue
}
return cmd, nil
} }
cPat := func(t lang.Term) lang.Tuple { return buildCmd{}, fmt.Errorf("unknown compiler command %v", t)
return lang.Tuple{lang.AConst, t} }
func (mod *Module) inType(t lang.Term) (llvm.Type, error) {
cmd, err := mod.matchingBuildCmd(t)
if err != nil {
return llvm.Type{}, err
} }
tPat := func(el ...lang.Term) lang.Tuple { return cmd.inType(t.(lang.Tuple)[1])
return lang.Tuple{lang.ATuple, lang.Tuple(el)} }
func (mod *Module) outType(t lang.Term) (llvm.Type, error) {
cmd, err := mod.matchingBuildCmd(t)
if err != nil {
return llvm.Type{}, err
} }
match := func(a lang.Atom, b lang.Tuple) bool { return cmd.outType(t.(lang.Tuple)[1])
return lang.Match(tPat(aPat(a), b), t) }
func (mod *Module) build(t lang.Term) (llvm.Value, error) {
cmd, err := mod.matchingBuildCmd(t)
if err != nil {
return llvm.Value{}, err
}
return cmd.build(t.(lang.Tuple)[1])
}
func (mod *Module) buildFn(tt ...lang.Term) (llvm.Value, error) {
if len(tt) == 0 {
return llvm.Value{}, errors.New("function cannot be empty")
} }
switch { inType, err := mod.inType(tt[0])
// (int 42) if err != nil {
case match(lang.AInt, cPat(lang.AUnder)): return llvm.Value{}, err
con := t.(lang.Tuple)[1].(lang.Const)
coni, err := strconv.ParseInt(string(con), 10, 64)
if err != nil {
return Val{}, err
}
return Val{
// TODO why does this have to be cast?
v: llvm.ConstInt(llvm.Int64Type(), uint64(coni), false),
}, nil
// (tup ((atom foo) (const 10)))
case match(lang.ATuple, lang.Tuple{lang.ATuple, lang.AUnder}):
tup := t.(lang.Tuple)[1].(lang.Tuple)
// if the tuple is empty then it is a void
if len(tup) == 0 {
return Val{v: llvm.Undef(llvm.VoidType())}, nil
}
var err error
vals := make([]Val, len(tup))
typs := make([]llvm.Type, len(tup))
for i := range tup {
if vals[i], err = mod.build(tup[i]); err != nil {
return Val{}, err
}
typs[i] = vals[i].v.Type()
}
str := llvm.Undef(llvm.StructType(typs, false))
for i := range vals {
str = mod.b.CreateInsertValue(str, vals[i].v, i, "")
}
return Val{v: str}, nil
// (add ((const 5) (var foo)))
case match(lang.AAdd, tPat(lang.TDblUnder, lang.TDblUnder)):
tup := t.(lang.Tuple)[1].(lang.Tuple)
v1, err := mod.build(tup[0])
if err != nil {
return Val{}, err
}
v2, err := mod.build(tup[1])
if err != nil {
return Val{}, err
}
return Val{v: mod.b.CreateAdd(v1.v, v2.v, "")}, nil
default:
return Val{}, fmt.Errorf("unknown compiler command %v", t)
} }
var inTypes []llvm.Type
if inType.TypeKind() == llvm.VoidTypeKind {
inTypes = []llvm.Type{}
} else {
inTypes = []llvm.Type{inType}
}
outType, err := mod.outType(tt[len(tt)-1])
if err != nil {
return llvm.Value{}, err
}
fn := llvm.AddFunction(mod.m, "", llvm.FunctionType(outType, inTypes, false))
block := llvm.AddBasicBlock(fn, "")
mod.b.SetInsertPoint(block, block.FirstInstruction())
var out llvm.Value
for _, t := range tt {
if out, err = mod.build(t); err != nil {
return llvm.Value{}, err
}
}
mod.b.CreateRet(out)
return fn, nil
} }
// Dump dumps the Module's IR to stdout // Dump dumps the Module's IR to stdout