improve how buildCmds are defined
This commit is contained in:
parent
54448fda80
commit
8937201f5c
67
main.go
67
main.go
@ -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
129
vm/cmds.go
Normal 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
142
vm/vm.go
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user