123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- package vm
- import (
- "fmt"
- . "kumachan/runtime/common"
- "kumachan/runtime/common/rx"
- "kumachan/runtime/lib"
- )
- func assert(ok bool, msg string) {
- if !ok { panic(msg) }
- }
- func execute(p Program, m *Machine) {
- var L = len(p.Functions) + len(p.Constants) + len(p.Constants)
- assert(L <= GlobalSlotMaxSize, "maximum registry size exceeded")
- var N = lib.NativeFunctions // TODO: change to GetNativeFunction(i)
- m.globalSlot = make([]Value, 0, L)
- for _, v := range p.DataValues {
- m.globalSlot = append(m.globalSlot, v.ToValue())
- }
- for i, _ := range p.Functions {
- var f = &(p.Functions[i])
- m.globalSlot = append(m.globalSlot, f.ToValue(N))
- }
- for i, _ := range p.Closures {
- var f = &(p.Closures[i])
- m.globalSlot = append(m.globalSlot, f.ToValue(nil))
- }
- for i, _ := range p.Constants {
- var f = &(p.Constants[i])
- var v = f.ToValue(N)
- m.globalSlot = append(m.globalSlot, m.Call(v, nil))
- }
- var ctx = rx.Background()
- for i, _ := range p.Effects {
- var f = &(p.Effects[i])
- var v = f.ToValue(nil)
- var e = (m.Call(v, nil)).(rx.Effect)
- m.scheduler.RunTopLevel(e, rx.Receiver {
- Context: ctx,
- Values: nil,
- Error: nil,
- })
- }
- }
- func call(f FunctionValue, arg Value, m *Machine) Value {
- var ec = m.contextPool.Get().(*ExecutionContext)
- defer (func() {
- var err = recover()
- if err != nil {
- PrintRuntimeErrorMessage(err, ec)
- panic(err)
- }
- }) ()
- ec.pushCall(f, arg)
- outer: for len(ec.callStack) > 0 {
- var code = ec.workingFrame.function.Code
- var base_addr = ec.workingFrame.baseAddr
- var inst_ptr_ref = &(ec.workingFrame.instPtr)
- for *inst_ptr_ref < uint(len(code)) {
- var inst = code[*inst_ptr_ref]
- *inst_ptr_ref += 1
- switch inst.OpCode {
- case NOP:
- // do nothing
- case NIL:
- ec.pushValue(nil)
- case GLOBAL:
- var id = inst.GetGlobalIndex()
- var gv = m.globalSlot[id]
- ec.pushValue(gv)
- case LOAD:
- var offset = inst.GetOffset()
- var value = ec.dataStack[base_addr + offset]
- ec.pushValue(value)
- case STORE:
- var offset = inst.GetOffset()
- var value = ec.popValue()
- ec.dataStack[base_addr + offset] = value
- case SUM:
- var index = inst.GetRawShortIndexOrSize()
- var value = ec.popValue()
- ec.pushValue(SumValue {
- Index: index,
- Value: value,
- })
- case JIF:
- switch sum := ec.getCurrentValue().(type) {
- case SumValue:
- if sum.Index == inst.GetRawShortIndexOrSize() {
- var new_inst_ptr = inst.GetDestAddr()
- assert(new_inst_ptr < uint(len(code)),
- "JIF: invalid address")
- *inst_ptr_ref = new_inst_ptr
- } else {
- // do nothing
- }
- default:
- panic("JIF: cannot execute on non-sum value")
- }
- case JMP:
- var new_inst_ptr = inst.GetDestAddr()
- assert(new_inst_ptr < uint(len(code)),
- "JMP: invalid address")
- *inst_ptr_ref = new_inst_ptr
- case PROD:
- var size = inst.GetShortIndexOrSize()
- var elements = make([]Value, size)
- for i := uint(0); i < size; i += 1 {
- elements[size-1-i] = ec.popValue()
- }
- ec.pushValue(ProductValue {
- Elements: elements,
- })
- case GET:
- var index = inst.GetShortIndexOrSize()
- switch prod := ec.getCurrentValue().(type) {
- case ProductValue:
- assert(index < uint(len(prod.Elements)),
- "GET: invalid index")
- ec.pushValue(prod.Elements[index])
- default:
- panic("GET: cannot execute on non-product value")
- }
- case SET:
- var index = inst.GetShortIndexOrSize()
- var value = ec.popValue()
- switch prod := ec.popValue().(type) {
- case ProductValue:
- var L = uint(len(prod.Elements))
- assert(index < L, "SET: invalid index")
- var draft = make([]Value, L)
- copy(draft, prod.Elements)
- draft[index] = value
- ec.pushValue(ProductValue {
- Elements: draft,
- })
- default:
- panic("GET: cannot execute on non-product value")
- }
- case CTX:
- var is_recursive = (inst.Arg1 != 0)
- switch prod := ec.popValue().(type) {
- case ProductValue:
- var ctx = prod.Elements
- switch f := ec.popValue().(type) {
- case FunctionValue:
- var required = int(f.Underlying.BaseSize.Context)
- var given = len(ctx)
- if is_recursive { given += 1 }
- assert(given == required, "CTX: invalid context size")
- assert((len(f.ContextValues) == 0), "CTX: context already injected")
- if is_recursive { ctx = append(ctx, nil) }
- var fv = FunctionValue {
- Underlying: f.Underlying,
- ContextValues: ctx,
- }
- if is_recursive { ctx[len(ctx)-1] = fv }
- ec.pushValue(fv)
- default:
- panic("CTX: cannot inject context for non-function value")
- }
- default:
- panic("CTX: cannot use non-product value as context")
- }
- case CALL:
- switch f := ec.popValue().(type) {
- case FunctionValue:
- // check if the function is valid
- var required = int(f.Underlying.BaseSize.Context)
- var current = len(f.ContextValues)
- assert(current == required,
- "CALL: missing correct context")
- var arg = ec.popValue()
- // tail call optimization
- var L = uint(len(code))
- var next_inst_ptr = *inst_ptr_ref
- if next_inst_ptr < L {
- var next = code[next_inst_ptr]
- if next.OpCode == JMP && next.GetDestAddr() == L-1 {
- ec.popTailCall()
- }
- } else {
- ec.popTailCall()
- }
- // push the function to call stack
- ec.pushCall(f, arg)
- // check if call stack size exceeded
- var stack_size = uint(len(ec.dataStack))
- assert(stack_size < m.maxStackSize,
- "CALL: stack overflow")
- // work on the pushed new frame
- continue outer
- case NativeFunctionValue:
- var arg = ec.popValue()
- var ret = f(arg, m)
- ec.pushValue(ret)
- default:
- panic("CALL: cannot execute on non-callable value")
- }
- case ARRAY:
- var size = inst.GetGlobalIndex()
- ec.pushValue(make([]Value, 0, size))
- case APPEND:
- var val = ec.popValue()
- var arr, ok = ec.popValue().([]Value)
- if !ok {
- panic("APPEND: cannot append to non-array value")
- }
- ec.pushValue(append(arr, val))
- default:
- panic(fmt.Sprintf("invalid instruction %+v", inst))
- }
- }
- ec.popCall()
- }
- var ret = ec.popValue()
- ec.workingFrame = CallStackFrame {}
- for i, _ := range ec.callStack {
- ec.callStack[i] = CallStackFrame {}
- }
- ec.callStack = ec.callStack[:0]
- for i, _ := range ec.dataStack {
- ec.dataStack[i] = nil
- }
- ec.dataStack = ec.dataStack[:0]
- m.contextPool.Put(ec)
- return ret
- }
- func (ec *ExecutionContext) getCurrentValue() Value {
- return ec.dataStack[len(ec.dataStack) - 1]
- }
- func (ec *ExecutionContext) pushValue(v Value) {
- ec.dataStack = append(ec.dataStack, v)
- }
- func (ec *ExecutionContext) popValue() Value {
- var L = len(ec.dataStack)
- assert(L > 0, "cannot pop empty data stack")
- var cur = (L - 1)
- var popped = ec.dataStack[cur]
- ec.dataStack[cur] = nil
- ec.dataStack = ec.dataStack[:cur]
- return popped
- }
- func (ec *ExecutionContext) popValuesTo(addr uint) {
- var L = uint(len(ec.dataStack))
- assert(L > 0, "cannot pop empty data stack")
- assert(addr < L, "invalid data stack address")
- for i := addr; i < L; i += 1 {
- ec.dataStack[i] = nil
- }
- ec.dataStack = ec.dataStack[:addr]
- }
- func (ec *ExecutionContext) pushCall(f FunctionValue, arg Value) {
- var context_size = int(f.Underlying.BaseSize.Context)
- var reserved_size = int(f.Underlying.BaseSize.Reserved)
- assert(context_size == len(f.ContextValues),
- "invalid number of context values")
- var new_base_addr = uint(len(ec.dataStack))
- for i := 0; i < context_size; i += 1 {
- ec.pushValue(f.ContextValues[i])
- }
- for i := 0; i < reserved_size; i += 1 {
- ec.pushValue(nil)
- }
- ec.pushValue(arg)
- ec.callStack = append(ec.callStack, ec.workingFrame)
- ec.workingFrame = CallStackFrame {
- function: f.Underlying,
- baseAddr: new_base_addr,
- instPtr: 0,
- }
- }
- func (ec *ExecutionContext) popCall() {
- var L = len(ec.callStack)
- assert(L > 0, "cannot pop empty call stack")
- var cur = (L - 1)
- var popped = ec.callStack[cur]
- ec.callStack[cur] = CallStackFrame {}
- ec.callStack = ec.callStack[:cur]
- var ret = ec.popValue()
- ec.popValuesTo(ec.workingFrame.baseAddr)
- ec.pushValue(ret)
- ec.workingFrame = popped
- }
- func (ec *ExecutionContext) popTailCall() {
- var L = len(ec.callStack)
- assert(L > 0, "cannot pop empty call stack")
- var cur = (L - 1)
- var popped = ec.callStack[cur]
- ec.callStack[cur] = CallStackFrame {}
- ec.callStack = ec.callStack[:cur]
- ec.popValuesTo(ec.workingFrame.baseAddr)
- ec.workingFrame = popped
- }
|