in internal/compile/compile.go [510:681]
func (pcomp *pcomp) function(name string, pos syntax.Position, stmts []syntax.Stmt, locals, freevars []*resolve.Binding) *Funcode {
fcomp := &fcomp{
pcomp: pcomp,
pos: pos,
fn: &Funcode{
Prog: pcomp.prog,
Pos: pos,
Name: name,
Doc: docStringFromBody(stmts),
Locals: bindings(locals),
Freevars: bindings(freevars),
},
}
// Record indices of locals that require cells.
for i, local := range locals {
if local.Scope == resolve.Cell {
fcomp.fn.Cells = append(fcomp.fn.Cells, i)
}
}
if debug {
fmt.Fprintf(os.Stderr, "start function(%s @ %s)\n", name, pos)
}
// Convert AST to a CFG of instructions.
entry := fcomp.newBlock()
fcomp.block = entry
fcomp.stmts(stmts)
if fcomp.block != nil {
fcomp.emit(NONE)
fcomp.emit(RETURN)
}
var oops bool // something bad happened
setinitialstack := func(b *block, depth int) {
if b.initialstack == -1 {
b.initialstack = depth
} else if b.initialstack != depth {
fmt.Fprintf(os.Stderr, "%d: setinitialstack: depth mismatch: %d vs %d\n",
b.index, b.initialstack, depth)
oops = true
}
}
// Linearize the CFG:
// compute order, address, and initial
// stack depth of each reachable block.
var pc uint32
var blocks []*block
var maxstack int
var visit func(b *block)
visit = func(b *block) {
if b.index >= 0 {
return // already visited
}
b.index = len(blocks)
b.addr = pc
blocks = append(blocks, b)
stack := b.initialstack
if debug {
fmt.Fprintf(os.Stderr, "%s block %d: (stack = %d)\n", name, b.index, stack)
}
var cjmpAddr *uint32
var isiterjmp int
for i, insn := range b.insns {
pc++
// Compute size of argument.
if insn.op >= OpcodeArgMin {
switch insn.op {
case ITERJMP:
isiterjmp = 1
fallthrough
case CJMP:
cjmpAddr = &b.insns[i].arg
pc += 4
default:
pc += uint32(argLen(insn.arg))
}
}
// Compute effect on stack.
se := insn.stackeffect()
if debug {
fmt.Fprintln(os.Stderr, "\t", insn.op, stack, stack+se)
}
stack += se
if stack < 0 {
fmt.Fprintf(os.Stderr, "After pc=%d: stack underflow\n", pc)
oops = true
}
if stack+isiterjmp > maxstack {
maxstack = stack + isiterjmp
}
}
if debug {
fmt.Fprintf(os.Stderr, "successors of block %d (start=%d):\n",
b.addr, b.index)
if b.jmp != nil {
fmt.Fprintf(os.Stderr, "jmp to %d\n", b.jmp.index)
}
if b.cjmp != nil {
fmt.Fprintf(os.Stderr, "cjmp to %d\n", b.cjmp.index)
}
}
// Place the jmp block next.
if b.jmp != nil {
// jump threading (empty cycles are impossible)
for b.jmp.insns == nil {
b.jmp = b.jmp.jmp
}
setinitialstack(b.jmp, stack+isiterjmp)
if b.jmp.index < 0 {
// Successor is not yet visited:
// place it next and fall through.
visit(b.jmp)
} else {
// Successor already visited;
// explicit backward jump required.
pc += 5
}
}
// Then the cjmp block.
if b.cjmp != nil {
// jump threading (empty cycles are impossible)
for b.cjmp.insns == nil {
b.cjmp = b.cjmp.jmp
}
setinitialstack(b.cjmp, stack)
visit(b.cjmp)
// Patch the CJMP/ITERJMP, if present.
if cjmpAddr != nil {
*cjmpAddr = b.cjmp.addr
}
}
}
setinitialstack(entry, 0)
visit(entry)
fn := fcomp.fn
fn.MaxStack = maxstack
// Emit bytecode (and position table).
if Disassemble {
fmt.Fprintf(os.Stderr, "Function %s: (%d blocks, %d bytes)\n", name, len(blocks), pc)
}
fcomp.generate(blocks, pc)
if debug {
fmt.Fprintf(os.Stderr, "code=%d maxstack=%d\n", fn.Code, fn.MaxStack)
}
// Don't panic until we've completed printing of the function.
if oops {
panic("internal error")
}
if debug {
fmt.Fprintf(os.Stderr, "end function(%s @ %s)\n", name, pos)
}
return fn
}