From 8937201f5c716b484f221a4478cc480283b95411 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Sat, 11 Feb 2017 13:35:02 -0700 Subject: [PATCH] improve how buildCmds are defined --- main.go | 67 ------------------------- vm/cmds.go | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ vm/vm.go | 142 ++++++++++++++++++++++++++--------------------------- 3 files changed, 198 insertions(+), 140 deletions(-) create mode 100644 vm/cmds.go diff --git a/main.go b/main.go index 83ec625..1a7b5db 100644 --- a/main.go +++ b/main.go @@ -24,70 +24,3 @@ func main() { out, err := mod.Run() 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)) -} -*/ diff --git a/vm/cmds.go b/vm/cmds.go new file mode 100644 index 0000000..4464154 --- /dev/null +++ b/vm/cmds.go @@ -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 + }, + }, + } +} diff --git a/vm/vm.go b/vm/vm.go index 649d8f3..2e83a19 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -1,8 +1,8 @@ package vm import ( + "errors" "fmt" - "strconv" "sync" "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 // used. type Module struct { - b llvm.Builder - m llvm.Module - mainFn llvm.Value + b llvm.Builder + m llvm.Module + mainFn llvm.Value + buildCmds []buildCmd } var initOnce sync.Once @@ -37,18 +38,13 @@ func Build(t lang.Term) (*Module, error) { b: llvm.NewBuilder(), m: llvm.NewModule(""), } + mod.buildCmds = buildCmds(mod) - // TODO figure out types - mod.mainFn = llvm.AddFunction(mod.m, "", llvm.FunctionType(llvm.Int64Type(), []llvm.Type{}, false)) - block := llvm.AddBasicBlock(mod.mainFn, "") - mod.b.SetInsertPoint(block, block.FirstInstruction()) - - out, err := mod.build(t) - if err != nil { + var err error + if mod.mainFn, err = mod.buildFn(t); err != nil { mod.Dispose() return nil, err } - mod.b.CreateRet(out.v) if err := llvm.VerifyModule(mod.m, llvm.ReturnStatusAction); err != nil { mod.Dispose() @@ -65,73 +61,73 @@ func (mod *Module) Dispose() { //mod.b.Dispose() } -func (mod *Module) build(t lang.Term) (Val, error) { - aPat := func(a lang.Atom) lang.Tuple { - return lang.Tuple{lang.AAtom, a} +func (mod *Module) matchingBuildCmd(t lang.Term) (buildCmd, error) { + for _, cmd := range mod.buildCmds { + if !cmd.matches(t) { + continue + } + return cmd, nil } - cPat := func(t lang.Term) lang.Tuple { - return lang.Tuple{lang.AConst, t} + return buildCmd{}, fmt.Errorf("unknown compiler command %v", 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 lang.Tuple{lang.ATuple, lang.Tuple(el)} + return cmd.inType(t.(lang.Tuple)[1]) +} + +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 lang.Match(tPat(aPat(a), b), t) + return cmd.outType(t.(lang.Tuple)[1]) +} + +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 { - // (int 42) - case match(lang.AInt, cPat(lang.AUnder)): - 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) + inType, err := mod.inType(tt[0]) + if err != nil { + return llvm.Value{}, err } + 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