Skip to content

Commit aa100dc

Browse files
committed
Put the stack in the Frame like cpython in preparation for generators
1 parent ac76658 commit aa100dc

File tree

3 files changed

+83
-65
lines changed

3 files changed

+83
-65
lines changed

py/frame.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ type TryBlock struct {
1616
// A python Frame object
1717
type Frame struct {
1818
// Back *Frame // previous frame, or nil
19-
Code *Code // code segment
20-
Builtins StringDict // builtin symbol table
21-
Globals StringDict // global symbol table
22-
Locals StringDict // local symbol table
23-
Valuestack *Object // points after the last local
19+
Code *Code // code segment
20+
Builtins StringDict // builtin symbol table
21+
Globals StringDict // global symbol table
22+
Locals StringDict // local symbol table
23+
Stack []Object // Valuestack
2424
// Next free slot in f_valuestack. Frame creation sets to f_valuestack.
2525
// Frame evaluation usually NULLs it, but a frame that yields sets it
2626
// to the current stack top.

vm/eval.go

+76-57
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
// Evaluate opcodes
22
package vm
33

4+
/* FIXME
5+
6+
cpython has one stack per frame, not one stack in total
7+
8+
We know how big each frame needs to be from
9+
10+
code->co_stacksize
11+
12+
The frame then becomes the important thing
13+
14+
cpython keeps a zombie frame on each code object to speed up execution
15+
of a code object so a frame doesn't have to be allocated and
16+
deallocated each time which seems like a good idea. If we want to
17+
work with go routines then it might have to be more sophisticated.
18+
19+
To implmenent generators need to check Code.Flags & CO_GENERATOR at
20+
the start of vmRum and if so wrap the created frame into a generator
21+
object.
22+
23+
FIXME could make the stack be permanently allocated and just keep a
24+
pointer into it rather than using append etc...
25+
26+
If we are caching the frames need to make sure we clear the stack
27+
objects so they can be GCed
28+
29+
*/
30+
431
import (
532
"errors"
633
"fmt"
@@ -9,32 +36,32 @@ import (
936
)
1037

1138
// Stack operations
12-
func (vm *Vm) STACK_LEVEL() int { return len(vm.stack) }
13-
func (vm *Vm) EMPTY() bool { return len(vm.stack) == 0 }
14-
func (vm *Vm) TOP() py.Object { return vm.stack[len(vm.stack)-1] }
15-
func (vm *Vm) SECOND() py.Object { return vm.stack[len(vm.stack)-2] }
16-
func (vm *Vm) THIRD() py.Object { return vm.stack[len(vm.stack)-3] }
17-
func (vm *Vm) FOURTH() py.Object { return vm.stack[len(vm.stack)-4] }
18-
func (vm *Vm) PEEK(n int) py.Object { return vm.stack[len(vm.stack)-n] }
19-
func (vm *Vm) SET_TOP(v py.Object) { vm.stack[len(vm.stack)-1] = v }
20-
func (vm *Vm) SET_SECOND(v py.Object) { vm.stack[len(vm.stack)-2] = v }
21-
func (vm *Vm) SET_THIRD(v py.Object) { vm.stack[len(vm.stack)-3] = v }
22-
func (vm *Vm) SET_FOURTH(v py.Object) { vm.stack[len(vm.stack)-4] = v }
23-
func (vm *Vm) SET_VALUE(n int, v py.Object) { vm.stack[len(vm.stack)-(n)] = (v) }
24-
func (vm *Vm) DROP() { vm.stack = vm.stack[:len(vm.stack)-1] }
25-
func (vm *Vm) DROPN(n int) { vm.stack = vm.stack[:len(vm.stack)-n] }
39+
func (vm *Vm) STACK_LEVEL() int { return len(vm.frame.Stack) }
40+
func (vm *Vm) EMPTY() bool { return len(vm.frame.Stack) == 0 }
41+
func (vm *Vm) TOP() py.Object { return vm.frame.Stack[len(vm.frame.Stack)-1] }
42+
func (vm *Vm) SECOND() py.Object { return vm.frame.Stack[len(vm.frame.Stack)-2] }
43+
func (vm *Vm) THIRD() py.Object { return vm.frame.Stack[len(vm.frame.Stack)-3] }
44+
func (vm *Vm) FOURTH() py.Object { return vm.frame.Stack[len(vm.frame.Stack)-4] }
45+
func (vm *Vm) PEEK(n int) py.Object { return vm.frame.Stack[len(vm.frame.Stack)-n] }
46+
func (vm *Vm) SET_TOP(v py.Object) { vm.frame.Stack[len(vm.frame.Stack)-1] = v }
47+
func (vm *Vm) SET_SECOND(v py.Object) { vm.frame.Stack[len(vm.frame.Stack)-2] = v }
48+
func (vm *Vm) SET_THIRD(v py.Object) { vm.frame.Stack[len(vm.frame.Stack)-3] = v }
49+
func (vm *Vm) SET_FOURTH(v py.Object) { vm.frame.Stack[len(vm.frame.Stack)-4] = v }
50+
func (vm *Vm) SET_VALUE(n int, v py.Object) { vm.frame.Stack[len(vm.frame.Stack)-(n)] = (v) }
51+
func (vm *Vm) DROP() { vm.frame.Stack = vm.frame.Stack[:len(vm.frame.Stack)-1] }
52+
func (vm *Vm) DROPN(n int) { vm.frame.Stack = vm.frame.Stack[:len(vm.frame.Stack)-n] }
2653

2754
// Pop from top of vm stack
2855
func (vm *Vm) POP() py.Object {
2956
// FIXME what if empty?
30-
out := vm.stack[len(vm.stack)-1]
31-
vm.stack = vm.stack[:len(vm.stack)-1]
57+
out := vm.frame.Stack[len(vm.frame.Stack)-1]
58+
vm.frame.Stack = vm.frame.Stack[:len(vm.frame.Stack)-1]
3259
return out
3360
}
3461

3562
// Push to top of vm stack
3663
func (vm *Vm) PUSH(obj py.Object) {
37-
vm.stack = append(vm.stack, obj)
64+
vm.frame.Stack = append(vm.frame.Stack, obj)
3865
}
3966

4067
// Illegal instruction
@@ -333,7 +360,7 @@ func do_BREAK_LOOP(vm *Vm, arg int32) {
333360
// Jump
334361
vm.frame.Lasti = vm.frame.Block.Handler
335362
// Reset the stack (FIXME?)
336-
vm.stack = vm.stack[:vm.frame.Block.Level]
363+
vm.frame.Stack = vm.frame.Stack[:vm.frame.Block.Level]
337364
vm.frame.PopBlock()
338365
}
339366

@@ -385,6 +412,11 @@ func do_MAP_ADD(vm *Vm, i int32) {
385412

386413
// Returns with TOS to the caller of the function.
387414
func do_RETURN_VALUE(vm *Vm, arg int32) {
415+
vm.result = vm.POP()
416+
if len(vm.frame.Stack) != 0 {
417+
fmt.Printf("vmstack = %#v\n", vm.frame.Stack)
418+
panic("vm stack should be empty at this point")
419+
}
388420
vm.PopFrame()
389421
}
390422

@@ -529,9 +561,9 @@ func do_LOAD_NAME(vm *Vm, namei int32) {
529561
// the resulting tuple onto the stack.
530562
func do_BUILD_TUPLE(vm *Vm, count int32) {
531563
tuple := make(py.Tuple, count)
532-
p := len(vm.stack) - int(count)
564+
p := len(vm.frame.Stack) - int(count)
533565
for i := range tuple {
534-
tuple[i] = vm.stack[p+i]
566+
tuple[i] = vm.frame.Stack[p+i]
535567
}
536568
vm.DROPN(int(count))
537569
vm.PUSH(tuple)
@@ -545,9 +577,9 @@ func do_BUILD_SET(vm *Vm, count int32) {
545577
// Works as BUILD_TUPLE, but creates a list.
546578
func do_BUILD_LIST(vm *Vm, count int32) {
547579
list := make(py.List, count)
548-
p := len(vm.stack) - int(count)
580+
p := len(vm.frame.Stack) - int(count)
549581
for i := range list {
550-
list[i] = vm.stack[p+i]
582+
list[i] = vm.frame.Stack[p+i]
551583
}
552584
vm.DROPN(int(count))
553585
vm.PUSH(list)
@@ -681,19 +713,19 @@ func do_LOAD_GLOBAL(vm *Vm, namei int32) {
681713
// Pushes a block for a loop onto the block stack. The block spans
682714
// from the current instruction with a size of delta bytes.
683715
func do_SETUP_LOOP(vm *Vm, delta int32) {
684-
vm.frame.PushBlock(SETUP_LOOP, vm.frame.Lasti+delta, len(vm.stack))
716+
vm.frame.PushBlock(SETUP_LOOP, vm.frame.Lasti+delta, len(vm.frame.Stack))
685717
}
686718

687719
// Pushes a try block from a try-except clause onto the block
688720
// stack. delta points to the first except block.
689721
func do_SETUP_EXCEPT(vm *Vm, delta int32) {
690-
vm.frame.PushBlock(SETUP_EXCEPT, vm.frame.Lasti+delta, len(vm.stack))
722+
vm.frame.PushBlock(SETUP_EXCEPT, vm.frame.Lasti+delta, len(vm.frame.Stack))
691723
}
692724

693725
// Pushes a try block from a try-except clause onto the block
694726
// stack. delta points to the finally block.
695727
func do_SETUP_FINALLY(vm *Vm, delta int32) {
696-
vm.frame.PushBlock(SETUP_FINALLY, vm.frame.Lasti+delta, len(vm.stack))
728+
vm.frame.PushBlock(SETUP_FINALLY, vm.frame.Lasti+delta, len(vm.frame.Stack))
697729
}
698730

699731
// Store a key and value pair in a dictionary. Pops the key and value
@@ -769,19 +801,19 @@ func do_RAISE_VARARGS(vm *Vm, argc int32) {
769801
// function arguments, and the function itself off the stack, and
770802
// pushes the return value.
771803
func do_CALL_FUNCTION(vm *Vm, argc int32) {
772-
// fmt.Printf("Stack: %v\n", vm.stack)
804+
// fmt.Printf("Stack: %v\n", vm.frame.Stack)
773805
// fmt.Printf("Locals: %v\n", vm.frame.Locals)
774806
// fmt.Printf("Globals: %v\n", vm.frame.Globals)
775807
nargs := int(argc & 0xFF)
776808
nkwargs := int((argc >> 8) & 0xFF)
777-
p, q := len(vm.stack)-2*nkwargs, len(vm.stack)
778-
kwargs := vm.stack[p:q]
809+
p, q := len(vm.frame.Stack)-2*nkwargs, len(vm.frame.Stack)
810+
kwargs := vm.frame.Stack[p:q]
779811
p, q = p-nargs, p
780-
args := py.Tuple(vm.stack[p:q])
812+
args := py.Tuple(vm.frame.Stack[p:q])
781813
p, q = p-1, p
782-
fn := vm.stack[p]
814+
fn := vm.frame.Stack[p]
783815
// Drop everything off the stack
784-
vm.stack = vm.stack[:p]
816+
vm.frame.Stack = vm.frame.Stack[:p]
785817
vm.Call(fn, args, kwargs)
786818
}
787819

@@ -891,7 +923,7 @@ func do_CALL_FUNCTION_VAR_KW(vm *Vm, argc int32) {
891923
// NotImplemented
892924
func (vm *Vm) NotImplemented(name string, arg int32) {
893925
fmt.Printf("%s %d NOT IMPLEMENTED\n", name, arg)
894-
fmt.Printf("vmstack = %#v\n", vm.stack)
926+
fmt.Printf("vmstack = %#v\n", vm.frame.Stack)
895927
panic(fmt.Sprintf("Opcode %s %d NOT IMPLEMENTED", name, arg))
896928
}
897929

@@ -922,20 +954,8 @@ func (vm *Vm) Call(fnObj py.Object, args []py.Object, kwargsTuple []py.Object) {
922954
fnObj = vm.frame.Lookup(string(fn))
923955
}
924956

925-
// Call py.Functions in this vm
926-
// FIXME make this an interface? LocalsForCall ?
927-
if fn, ok := fnObj.(*py.Function); ok {
928-
var locals py.StringDict
929-
if kwargs != nil {
930-
locals = fn.LocalsForCallWithKeywords(args, kwargs)
931-
} else {
932-
locals = fn.LocalsForCall(args)
933-
}
934-
vm.PushFrame(fn.Globals, locals, fn.Code)
935-
} else {
936-
// Call everything else directly
937-
vm.PUSH(py.Call(fnObj, args, kwargs))
938-
}
957+
// Call the function pushing the return on the stack
958+
vm.PUSH(py.Call(fnObj, args, kwargs))
939959
}
940960

941961
// Make a new Frame with globals, locals and Code on the frames stack
@@ -945,6 +965,7 @@ func (vm *Vm) PushFrame(globals, locals py.StringDict, code *py.Code) {
945965
Locals: locals,
946966
Code: code,
947967
Builtins: py.Builtins.Globals,
968+
Stack: make([]py.Object, 0, code.Stacksize),
948969
}
949970
vm.frames = append(vm.frames, frame)
950971
vm.frame = &vm.frames[len(vm.frames)-1]
@@ -1013,16 +1034,14 @@ func Run(globals, locals py.StringDict, code *py.Code) (res py.Object, err error
10131034
}
10141035
vm.extended = false
10151036
jumpTable[opcode](vm, arg)
1016-
fmt.Printf("* Stack = %#v\n", vm.stack)
1017-
// if len(vm.stack) > 0 {
1018-
// if t, ok := vm.TOP().(*py.Type); ok {
1019-
// fmt.Printf(" * TOP = %#v\n", t)
1020-
// }
1021-
// }
1022-
}
1023-
if len(vm.stack) != 1 {
1024-
fmt.Printf("vmstack = %#v\n", vm.stack)
1025-
panic("vm stack should only have 1 entry on at this point")
1037+
if vm.frame != nil {
1038+
fmt.Printf("* Stack = %#v\n", vm.frame.Stack)
1039+
// if len(vm.frame.Stack) > 0 {
1040+
// if t, ok := vm.TOP().(*py.Type); ok {
1041+
// fmt.Printf(" * TOP = %#v\n", t)
1042+
// }
1043+
// }
1044+
}
10261045
}
1027-
return vm.POP(), nil
1046+
return vm.result, nil
10281047
}

vm/vm.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77

88
// Virtual machine state
99
type Vm struct {
10-
// Object stack
11-
stack []py.Object
1210
// Frame stack
1311
frames []py.Frame
1412
// Current frame
@@ -17,12 +15,13 @@ type Vm struct {
1715
extended bool
1816
// 16 bit extension for argument for next opcode
1917
ext int32
18+
// Return value
19+
result py.Object
2020
}
2121

2222
// Make a new VM
2323
func NewVm() *Vm {
2424
return &Vm{
25-
stack: make([]py.Object, 0, 16),
2625
frames: make([]py.Frame, 0, 16),
2726
}
2827
}

0 commit comments

Comments
 (0)