Skip to content

Commit 0a8e720

Browse files
committed
compiler: make stack depth calcuations work
1 parent 853ec56 commit 0a8e720

File tree

2 files changed

+269
-13
lines changed

2 files changed

+269
-13
lines changed

compile/compile.go

+268-13
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ func Compile(str, filename, mode string, flags int, dont_inherit bool) py.Object
9999
fmt.Println(ast.Dump(Ast))
100100
code := &py.Code{
101101
Filename: filename,
102-
Stacksize: 1, // FIXME
103102
Firstlineno: 1, // FIXME
104103
Name: "<module>", // FIXME
105104
Flags: 64, // FIXME
@@ -121,6 +120,7 @@ func Compile(str, filename, mode string, flags int, dont_inherit bool) py.Object
121120
}
122121
c.Op(vm.RETURN_VALUE)
123122
code.Code = c.OpCodes.Assemble()
123+
code.Stacksize = int32(c.OpCodes.StackDepth())
124124
return code
125125
}
126126

@@ -496,16 +496,16 @@ func (is *Instructions) Add(i Instruction) {
496496
func (is Instructions) Pass(pass int) bool {
497497
addr := uint32(0)
498498
changed := false
499-
for _, i := range is {
500-
posChanged := i.SetPos(addr)
499+
for i, instr := range is {
500+
posChanged := instr.SetPos(i, addr)
501501
changed = changed || posChanged
502502
if pass > 0 {
503503
// Only resolve addresses on 2nd pass
504-
if resolver, ok := i.(Resolver); ok {
504+
if resolver, ok := instr.(Resolver); ok {
505505
resolver.Resolve()
506506
}
507507
}
508-
addr += i.Size()
508+
addr += instr.Size()
509509
}
510510
return changed
511511
}
@@ -527,31 +527,271 @@ done:
527527
return string(out)
528528
}
529529

530+
// Calculate number of arguments for CALL_FUNCTION etc
531+
func nArgs(o uint32) int {
532+
return (int(o) & 0xFF) + 2*((int(o)>>8)&0xFF)
533+
}
534+
535+
// Effect the opcode has on the stack
536+
func opcodeStackEffect(opcode byte, oparg uint32) int {
537+
switch opcode {
538+
case vm.POP_TOP:
539+
return -1
540+
case vm.ROT_TWO, vm.ROT_THREE:
541+
return 0
542+
case vm.DUP_TOP:
543+
return 1
544+
case vm.DUP_TOP_TWO:
545+
return 2
546+
case vm.UNARY_POSITIVE, vm.UNARY_NEGATIVE, vm.UNARY_NOT, vm.UNARY_INVERT:
547+
return 0
548+
case vm.SET_ADD, vm.LIST_APPEND:
549+
return -1
550+
case vm.MAP_ADD:
551+
return -2
552+
case vm.BINARY_POWER, vm.BINARY_MULTIPLY, vm.BINARY_MODULO, vm.BINARY_ADD, vm.BINARY_SUBTRACT, vm.BINARY_SUBSCR, vm.BINARY_FLOOR_DIVIDE, vm.BINARY_TRUE_DIVIDE:
553+
return -1
554+
case vm.INPLACE_FLOOR_DIVIDE, vm.INPLACE_TRUE_DIVIDE:
555+
return -1
556+
case vm.INPLACE_ADD, vm.INPLACE_SUBTRACT, vm.INPLACE_MULTIPLY, vm.INPLACE_MODULO:
557+
return -1
558+
case vm.STORE_SUBSCR:
559+
return -3
560+
case vm.STORE_MAP:
561+
return -2
562+
case vm.DELETE_SUBSCR:
563+
return -2
564+
case vm.BINARY_LSHIFT, vm.BINARY_RSHIFT, vm.BINARY_AND, vm.BINARY_XOR, vm.BINARY_OR:
565+
return -1
566+
case vm.INPLACE_POWER:
567+
return -1
568+
case vm.GET_ITER:
569+
return 0
570+
case vm.PRINT_EXPR:
571+
return -1
572+
case vm.LOAD_BUILD_CLASS:
573+
return 1
574+
case vm.INPLACE_LSHIFT, vm.INPLACE_RSHIFT, vm.INPLACE_AND, vm.INPLACE_XOR, vm.INPLACE_OR:
575+
return -1
576+
case vm.BREAK_LOOP:
577+
return 0
578+
case vm.SETUP_WITH:
579+
return 7
580+
case vm.WITH_CLEANUP:
581+
return -1 /* XXX Sometimes more */
582+
case vm.RETURN_VALUE:
583+
return -1
584+
case vm.IMPORT_STAR:
585+
return -1
586+
case vm.YIELD_VALUE:
587+
return 0
588+
case vm.YIELD_FROM:
589+
return -1
590+
case vm.POP_BLOCK:
591+
return 0
592+
case vm.POP_EXCEPT:
593+
return 0 /* -3 except if bad bytecode */
594+
case vm.END_FINALLY:
595+
return -1 /* or -2 or -3 if exception occurred */
596+
case vm.STORE_NAME:
597+
return -1
598+
case vm.DELETE_NAME:
599+
return 0
600+
case vm.UNPACK_SEQUENCE:
601+
return int(oparg) - 1
602+
case vm.UNPACK_EX:
603+
return (int(oparg) & 0xFF) + (int(oparg) >> 8)
604+
case vm.FOR_ITER:
605+
return 1 /* or -1, at end of iterator */
606+
case vm.STORE_ATTR:
607+
return -2
608+
case vm.DELETE_ATTR:
609+
return -1
610+
case vm.STORE_GLOBAL:
611+
return -1
612+
case vm.DELETE_GLOBAL:
613+
return 0
614+
case vm.LOAD_CONST:
615+
return 1
616+
case vm.LOAD_NAME:
617+
return 1
618+
case vm.BUILD_TUPLE, vm.BUILD_LIST, vm.BUILD_SET:
619+
return 1 - int(oparg)
620+
case vm.BUILD_MAP:
621+
return 1
622+
case vm.LOAD_ATTR:
623+
return 0
624+
case vm.COMPARE_OP:
625+
return -1
626+
case vm.IMPORT_NAME:
627+
return -1
628+
case vm.IMPORT_FROM:
629+
return 1
630+
case vm.JUMP_FORWARD, vm.JUMP_ABSOLUTE:
631+
return 0
632+
case vm.JUMP_IF_TRUE_OR_POP: /* -1 if jump not taken */
633+
return 0
634+
case vm.JUMP_IF_FALSE_OR_POP: /* "" */
635+
return 0
636+
case vm.POP_JUMP_IF_FALSE, vm.POP_JUMP_IF_TRUE:
637+
return -1
638+
case vm.LOAD_GLOBAL:
639+
return 1
640+
case vm.CONTINUE_LOOP:
641+
return 0
642+
case vm.SETUP_LOOP:
643+
return 0
644+
case vm.SETUP_EXCEPT, vm.SETUP_FINALLY:
645+
// can push 3 values for the new exception
646+
// + 3 others for the previous exception state
647+
return 6
648+
case vm.LOAD_FAST:
649+
return 1
650+
case vm.STORE_FAST:
651+
return -1
652+
case vm.DELETE_FAST:
653+
return 0
654+
655+
case vm.RAISE_VARARGS:
656+
return -int(oparg)
657+
case vm.CALL_FUNCTION:
658+
return -nArgs(oparg)
659+
case vm.CALL_FUNCTION_VAR, vm.CALL_FUNCTION_KW:
660+
return -nArgs(oparg) - 1
661+
case vm.CALL_FUNCTION_VAR_KW:
662+
return -nArgs(oparg) - 2
663+
case vm.MAKE_FUNCTION:
664+
return -1 - nArgs(oparg) - ((int(oparg) >> 16) & 0xffff)
665+
case vm.MAKE_CLOSURE:
666+
return -2 - nArgs(oparg) - ((int(oparg) >> 16) & 0xffff)
667+
case vm.BUILD_SLICE:
668+
if oparg == 3 {
669+
return -2
670+
} else {
671+
return -1
672+
}
673+
case vm.LOAD_CLOSURE:
674+
return 1
675+
case vm.LOAD_DEREF, vm.LOAD_CLASSDEREF:
676+
return 1
677+
case vm.STORE_DEREF:
678+
return -1
679+
case vm.DELETE_DEREF:
680+
return 0
681+
default:
682+
panic("Unknown opcode in StackEffect")
683+
}
684+
}
685+
686+
// Recursive instruction walker to find max stack depth
687+
func (is Instructions) stackDepthWalk(baseIs Instructions, seen map[int]bool, startDepth map[int]int, depth int, maxdepth int) int {
688+
// var i, target_depth, effect int
689+
// var instr *struct instr
690+
// if b.b_seen || b.b_startdepth >= depth {
691+
// return maxdepth
692+
// }
693+
// b.b_seen = 1
694+
// b.b_startdepth = depth
695+
if len(is) == 0 {
696+
return maxdepth
697+
}
698+
start := is[0].Number()
699+
if seen[start] {
700+
// We are processing this block already
701+
return maxdepth
702+
}
703+
if d, ok := startDepth[start]; ok && d >= depth {
704+
// We've processed this block with a larger depth already
705+
return maxdepth
706+
}
707+
seen[start] = true
708+
startDepth[start] = depth
709+
for _, instr := range is {
710+
depth += instr.StackEffect()
711+
if depth > maxdepth {
712+
maxdepth = depth
713+
}
714+
if depth < 0 {
715+
panic("Stack depth negative")
716+
}
717+
jrel, isJrel := instr.(*JumpRel)
718+
jabs, isJabs := instr.(*JumpAbs)
719+
if isJrel || isJabs {
720+
var oparg *OpArg
721+
var dest *Label
722+
if isJrel {
723+
oparg = &jrel.OpArg
724+
dest = jrel.Dest
725+
} else {
726+
oparg = &jabs.OpArg
727+
dest = jabs.Dest
728+
}
729+
opcode := oparg.Op
730+
target_depth := depth
731+
if opcode == vm.FOR_ITER {
732+
target_depth = depth - 2
733+
} else if opcode == vm.SETUP_FINALLY || opcode == vm.SETUP_EXCEPT {
734+
target_depth = depth + 3
735+
if target_depth > maxdepth {
736+
maxdepth = target_depth
737+
}
738+
} else if opcode == vm.JUMP_IF_TRUE_OR_POP || opcode == vm.JUMP_IF_FALSE_OR_POP {
739+
depth = depth - 1
740+
}
741+
isTarget := baseIs[dest.Number():]
742+
maxdepth = isTarget.stackDepthWalk(baseIs, seen, startDepth, target_depth, maxdepth)
743+
if opcode == vm.JUMP_ABSOLUTE ||
744+
opcode == vm.JUMP_FORWARD {
745+
goto out // remaining code is dead
746+
}
747+
}
748+
}
749+
out:
750+
seen[start] = false
751+
return maxdepth
752+
}
753+
754+
// Find the flow path that needs the largest stack. We assume that
755+
// cycles in the flow graph have no net effect on the stack depth.
756+
func (is Instructions) StackDepth() int {
757+
return is.stackDepthWalk(is, make(map[int]bool), make(map[int]int), 0, 0)
758+
}
759+
530760
type Instruction interface {
531761
Pos() uint32
532-
SetPos(uint32) bool
762+
Number() int
763+
SetPos(int, uint32) bool
533764
Size() uint32
534765
Output() []byte
766+
StackEffect() int
535767
}
536768

537769
type Resolver interface {
538770
Resolve()
539771
}
540772

541773
// Position
542-
type pos uint32
774+
type pos struct {
775+
n uint32
776+
p uint32
777+
}
778+
779+
// Read instruction number
780+
func (p *pos) Number() int {
781+
return int(p.n)
782+
}
543783

544784
// Read position
545785
func (p *pos) Pos() uint32 {
546-
return uint32(*p)
786+
return p.p
547787
}
548788

549789
// Set Position - returns changed
550-
func (p *pos) SetPos(newPos uint32) bool {
551-
oldP := *p
552-
newP := pos(newPos)
553-
*p = newP
554-
return oldP != newP
790+
func (p *pos) SetPos(number int, newPos uint32) bool {
791+
p.n = uint32(number)
792+
oldPos := p.p
793+
p.p = newPos
794+
return oldPos != newPos
555795
}
556796

557797
// A plain opcode
@@ -570,6 +810,11 @@ func (o *Op) Output() []byte {
570810
return []byte{byte(o.Op)}
571811
}
572812

813+
// StackEffect
814+
func (o *Op) StackEffect() int {
815+
return opcodeStackEffect(o.Op, 0)
816+
}
817+
573818
// An opcode with argument
574819
type OpArg struct {
575820
pos
@@ -595,6 +840,11 @@ func (o *OpArg) Output() []byte {
595840
return out
596841
}
597842

843+
// StackEffect
844+
func (o *OpArg) StackEffect() int {
845+
return opcodeStackEffect(o.Op, o.Arg)
846+
}
847+
598848
// A label
599849
type Label struct {
600850
pos
@@ -610,6 +860,11 @@ func (o Label) Output() []byte {
610860
return []byte{}
611861
}
612862

863+
// StackEffect
864+
func (o *Label) StackEffect() int {
865+
return 0
866+
}
867+
613868
// An absolute JUMP with destination label
614869
type JumpAbs struct {
615870
pos

compile/compile_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func TestCompile(t *testing.T) {
8787
if !ok {
8888
t.Fatalf("Expecting *py.Code but got %T", codeObj)
8989
}
90+
t.Logf("Testing %q", test.in)
9091
EqCode(t, &test.out, code)
9192
}
9293
}

0 commit comments

Comments
 (0)