2017-02-11 17:24:02 +00:00
|
|
|
package vm
|
|
|
|
|
|
|
|
import (
|
2017-02-11 20:35:02 +00:00
|
|
|
"errors"
|
2017-02-11 17:24:02 +00:00
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/mediocregopher/ginger/lang"
|
|
|
|
|
|
|
|
"llvm.org/llvm/bindings/go/llvm"
|
|
|
|
)
|
|
|
|
|
2017-02-12 18:32:44 +00:00
|
|
|
// Types supported by the vm in addition to those which are part of lang
|
|
|
|
var (
|
|
|
|
Atom = lang.AAtom
|
|
|
|
Tuple = lang.ATuple
|
|
|
|
Int = lang.Atom("int")
|
|
|
|
)
|
|
|
|
|
|
|
|
// Commands supported by the vm. Each of the types are also commands
|
|
|
|
var (
|
|
|
|
Add = lang.Atom("add")
|
|
|
|
)
|
2017-02-11 17:24:02 +00:00
|
|
|
|
|
|
|
// Module contains a compiled set of code which can be run, dumped in IR form,
|
|
|
|
// or compiled. A Module should be Dispose()'d of once it's no longer being
|
|
|
|
// used.
|
|
|
|
type Module struct {
|
2017-02-12 18:32:44 +00:00
|
|
|
b llvm.Builder
|
|
|
|
m llvm.Module
|
|
|
|
mainFn llvm.Value
|
2017-02-11 17:24:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var initOnce sync.Once
|
|
|
|
|
|
|
|
// Build creates a new Module by compiling the given Term as code
|
|
|
|
func Build(t lang.Term) (*Module, error) {
|
|
|
|
initOnce.Do(func() {
|
|
|
|
llvm.LinkInMCJIT()
|
|
|
|
llvm.InitializeNativeTarget()
|
|
|
|
llvm.InitializeNativeAsmPrinter()
|
|
|
|
})
|
|
|
|
mod := &Module{
|
|
|
|
b: llvm.NewBuilder(),
|
|
|
|
m: llvm.NewModule(""),
|
|
|
|
}
|
|
|
|
|
2017-02-11 20:35:02 +00:00
|
|
|
var err error
|
|
|
|
if mod.mainFn, err = mod.buildFn(t); err != nil {
|
2017-02-11 17:24:02 +00:00
|
|
|
mod.Dispose()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := llvm.VerifyModule(mod.m, llvm.ReturnStatusAction); err != nil {
|
|
|
|
mod.Dispose()
|
|
|
|
return nil, fmt.Errorf("could not verify module: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return mod, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dispose cleans up all resources held by the Module
|
|
|
|
func (mod *Module) Dispose() {
|
|
|
|
// TODO this panics for some reason...
|
|
|
|
//mod.m.Dispose()
|
|
|
|
//mod.b.Dispose()
|
|
|
|
}
|
|
|
|
|
2017-02-12 15:55:27 +00:00
|
|
|
// TODO make this return a val once we get function types
|
2017-02-11 20:35:02 +00:00
|
|
|
func (mod *Module) buildFn(tt ...lang.Term) (llvm.Value, error) {
|
|
|
|
if len(tt) == 0 {
|
|
|
|
return llvm.Value{}, errors.New("function cannot be empty")
|
|
|
|
}
|
2017-02-11 17:24:02 +00:00
|
|
|
|
2017-02-12 18:32:44 +00:00
|
|
|
cmds := make([]cmd, len(tt))
|
|
|
|
var err error
|
|
|
|
for i := range tt {
|
|
|
|
if cmds[i], err = matchCmd(tt[i]); err != nil {
|
|
|
|
return llvm.Value{}, err
|
|
|
|
}
|
2017-02-11 20:35:02 +00:00
|
|
|
}
|
2017-02-11 17:24:02 +00:00
|
|
|
|
2017-02-12 18:32:44 +00:00
|
|
|
var llvmIns []llvm.Type
|
|
|
|
if in := cmds[0].inType(); in.llvm.TypeKind() == llvm.VoidTypeKind {
|
|
|
|
llvmIns = []llvm.Type{}
|
|
|
|
} else {
|
|
|
|
llvmIns = []llvm.Type{in.llvm}
|
2017-02-11 20:35:02 +00:00
|
|
|
}
|
2017-02-12 18:32:44 +00:00
|
|
|
llvmOut := cmds[len(cmds)-1].outType().llvm
|
2017-02-11 17:24:02 +00:00
|
|
|
|
2017-02-12 18:32:44 +00:00
|
|
|
fn := llvm.AddFunction(mod.m, "", llvm.FunctionType(llvmOut, llvmIns, false))
|
2017-02-11 20:35:02 +00:00
|
|
|
block := llvm.AddBasicBlock(fn, "")
|
|
|
|
mod.b.SetInsertPoint(block, block.FirstInstruction())
|
|
|
|
|
2017-02-12 18:32:44 +00:00
|
|
|
var out llvm.Value
|
|
|
|
for i := range cmds {
|
|
|
|
if out, err = cmds[i].build(mod); err != nil {
|
|
|
|
return llvm.Value{}, nil
|
2017-02-11 20:35:02 +00:00
|
|
|
}
|
2017-02-11 17:24:02 +00:00
|
|
|
}
|
2017-02-12 18:32:44 +00:00
|
|
|
mod.b.CreateRet(out)
|
2017-02-11 20:35:02 +00:00
|
|
|
return fn, nil
|
2017-02-11 17:24:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Dump dumps the Module's IR to stdout
|
|
|
|
func (mod *Module) Dump() {
|
|
|
|
mod.m.Dump()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run executes the Module
|
|
|
|
// TODO input and output?
|
|
|
|
func (mod *Module) Run() (interface{}, error) {
|
|
|
|
engine, err := llvm.NewExecutionEngine(mod.m)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer engine.Dispose()
|
|
|
|
|
|
|
|
funcResult := engine.RunFunction(mod.mainFn, []llvm.GenericValue{})
|
|
|
|
defer funcResult.Dispose()
|
|
|
|
return funcResult.Int(false), nil
|
|
|
|
}
|