Commit 5a09f1b3 authored by Alan Donovan's avatar Alan Donovan

exp/ssa: make invokation of deferred procedure calls explicit.

The correct semantics of named result parameters and deferred
procedures cannot be implemented with the existing Ret
instruction alone, since the required sequence is:
(1) evaluate return operands and parallel-assign them to
    named result parameters
(2) invoke deferred procedures
(3) load named result parameters to form result tuple.

We introduce a new 'rundefers' instruction that explicitly
invokes the deferred procedure calls, and we generate code
that follows the sequence above.

Most functions do not use deferred procedures but this cannot
be known in a single pass.  So, we add an optimisation to
eliminate redundant 'rundefers'; it is piggybacked on the
existing pass done for "lifting".

Added tests.

R=gri
CC=golang-dev
https://golang.org/cl/7411043
parent 3fc8cd05
...@@ -2276,39 +2276,44 @@ start: ...@@ -2276,39 +2276,44 @@ start:
block = t._break block = t._break
} }
} }
// Run function calls deferred in this init
// block when explicitly returning from it.
fn.emit(new(RunDefers))
emitJump(fn, block) emitJump(fn, block)
fn.currentBlock = fn.newBasicBlock("unreachable") fn.currentBlock = fn.newBasicBlock("unreachable")
return return
} }
var results []Value
// Per the spec, there are three distinct cases of return.
switch {
case len(s.Results) == 0:
// Return with no arguments.
// Prior assigns to named result params are
// reloaded into results tuple.
// A void function is a degenerate case of this.
for _, r := range fn.results {
results = append(results, emitLoad(fn, r))
}
case len(s.Results) == 1 && len(fn.Signature.Results) > 1: var results []Value
if len(s.Results) == 1 && len(fn.Signature.Results) > 1 {
// Return of one expression in a multi-valued function. // Return of one expression in a multi-valued function.
tuple := b.exprN(fn, s.Results[0]) tuple := b.exprN(fn, s.Results[0])
for i, v := range tuple.Type().(*types.Result).Values { for i, v := range tuple.Type().(*types.Result).Values {
results = append(results, emitExtract(fn, tuple, i, v.Type)) results = append(results, emitExtract(fn, tuple, i, v.Type))
} }
} else {
default: // 1:1 return, or no-arg return in non-void function.
// Return one or more single-valued expressions. for i, r := range s.Results {
// These become the scalar or tuple result. v := emitConv(fn, b.expr(fn, r), fn.Signature.Results[i].Type)
for _, r := range s.Results { results = append(results, v)
results = append(results, b.expr(fn, r)) }
} }
if fn.namedResults != nil {
// Function has named result parameters (NRPs).
// Perform parallel assignment of return operands to NRPs.
for i, r := range results {
emitStore(fn, fn.namedResults[i], r)
}
}
// Run function calls deferred in this
// function when explicitly returning from it.
fn.emit(new(RunDefers))
if fn.namedResults != nil {
// Reload NRPs to form the result tuple.
results = results[:0]
for _, r := range fn.namedResults {
results = append(results, emitLoad(fn, r))
} }
// Perform implicit conversions.
for i := range results {
results[i] = emitConv(fn, results[i], fn.Signature.Results[i].Type)
} }
fn.emit(&Ret{Results: results}) fn.emit(&Ret{Results: results})
fn.currentBlock = fn.newBasicBlock("unreachable") fn.currentBlock = fn.newBasicBlock("unreachable")
...@@ -2410,7 +2415,9 @@ func (b *Builder) buildFunction(fn *Function) { ...@@ -2410,7 +2415,9 @@ func (b *Builder) buildFunction(fn *Function) {
fn.start(b.idents) fn.start(b.idents)
b.stmt(fn, fn.syntax.body) b.stmt(fn, fn.syntax.body)
if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) { if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) {
// We fell off the end: an implicit no-arg return statement. // Run function calls deferred in this function when
// falling off the end of the body block.
fn.emit(new(RunDefers))
fn.emit(new(Ret)) fn.emit(new(Ret))
} }
fn.finish() fn.finish()
...@@ -2686,6 +2693,9 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) { ...@@ -2686,6 +2693,9 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) {
_break: next, _break: next,
} }
b.stmt(init, decl.Body) b.stmt(init, decl.Body)
// Run function calls deferred in this init
// block when falling off the end of the block.
init.emit(new(RunDefers))
emitJump(init, next) emitJump(init, next)
init.targets = init.targets.tail init.targets = init.targets.tail
init.currentBlock = next init.currentBlock = next
...@@ -2792,6 +2802,7 @@ func (b *Builder) BuildPackage(p *Package) { ...@@ -2792,6 +2802,7 @@ func (b *Builder) BuildPackage(p *Package) {
// Finish up. // Finish up.
emitJump(init, done) emitJump(init, done)
init.currentBlock = done init.currentBlock = done
init.emit(new(RunDefers))
init.emit(new(Ret)) init.emit(new(Ret))
init.finish() init.finish()
} }
...@@ -222,6 +222,7 @@ func emitExtract(f *Function, tuple Value, index int, typ types.Type) Value { ...@@ -222,6 +222,7 @@ func emitExtract(f *Function, tuple Value, index int, typ types.Type) Value {
} }
// emitTailCall emits to f a function call in tail position. // emitTailCall emits to f a function call in tail position.
// Precondition: f does/will not use deferred procedure calls.
// Postcondition: f.currentBlock is nil. // Postcondition: f.currentBlock is nil.
// //
func emitTailCall(f *Function, call *Call) { func emitTailCall(f *Function, call *Call) {
......
...@@ -225,12 +225,12 @@ func (f *Function) start(idents map[*ast.Ident]types.Object) { ...@@ -225,12 +225,12 @@ func (f *Function) start(idents map[*ast.Ident]types.Object) {
} }
} }
// Results. // Named results.
if f.syntax.resultFields != nil { if f.syntax.resultFields != nil {
for _, field := range f.syntax.resultFields.List { for _, field := range f.syntax.resultFields.List {
// Implicit "var" decl of locals for named results. // Implicit "var" decl of locals for named results.
for _, n := range field.Names { for _, n := range field.Names {
f.results = append(f.results, f.addNamedLocal(idents[n])) f.namedResults = append(f.namedResults, f.addNamedLocal(idents[n]))
} }
} }
} }
...@@ -286,7 +286,7 @@ func buildReferrers(f *Function) { ...@@ -286,7 +286,7 @@ func buildReferrers(f *Function) {
// finish() finalizes the function after SSA code generation of its body. // finish() finalizes the function after SSA code generation of its body.
func (f *Function) finish() { func (f *Function) finish() {
f.objects = nil f.objects = nil
f.results = nil f.namedResults = nil
f.currentBlock = nil f.currentBlock = nil
f.lblocks = nil f.lblocks = nil
f.syntax = nil f.syntax = nil
......
...@@ -119,6 +119,16 @@ func (fr *frame) get(key ssa.Value) value { ...@@ -119,6 +119,16 @@ func (fr *frame) get(key ssa.Value) value {
panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name())) panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name()))
} }
func (fr *frame) rundefers() {
for i := range fr.defers {
if fr.i.mode&EnableTracing != 0 {
fmt.Fprintln(os.Stderr, "Invoking deferred function", i)
}
fr.defers[len(fr.defers)-1-i]()
}
fr.defers = fr.defers[:0]
}
// findMethodSet returns the method set for type typ, which may be one // findMethodSet returns the method set for type typ, which may be one
// of the interpreter's fake types. // of the interpreter's fake types.
func findMethodSet(i *interpreter, typ types.Type) ssa.MethodSet { func findMethodSet(i *interpreter, typ types.Type) ssa.MethodSet {
...@@ -170,12 +180,15 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { ...@@ -170,12 +180,15 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
default: default:
var res []value var res []value
for _, r := range instr.Results { for _, r := range instr.Results {
res = append(res, copyVal(fr.get(r))) res = append(res, fr.get(r))
} }
fr.result = tuple(res) fr.result = tuple(res)
} }
return kReturn return kReturn
case *ssa.RunDefers:
fr.rundefers()
case *ssa.Panic: case *ssa.Panic:
panic(targetPanic{fr.get(instr.X)}) panic(targetPanic{fr.get(instr.X)})
...@@ -466,12 +479,7 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, ...@@ -466,12 +479,7 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function,
} }
fr.status, fr.panic = stPanic, recover() fr.status, fr.panic = stPanic, recover()
} }
for i := range fr.defers { fr.rundefers()
if fr.i.mode&EnableTracing != 0 {
fmt.Fprintln(os.Stderr, "Invoking deferred function", i)
}
fr.defers[len(fr.defers)-1-i]()
}
// Destroy the locals to avoid accidental use after return. // Destroy the locals to avoid accidental use after return.
for i := range fn.Locals { for i := range fn.Locals {
fr.locals[i] = bad{} fr.locals[i] = bad{}
......
...@@ -390,3 +390,51 @@ two: ...@@ -390,3 +390,51 @@ two:
func init() { func init() {
multipleLabels() multipleLabels()
} }
////////////////////////////////////////////////////////////////////////
// Defer
func deferMutatesResults(noArgReturn bool) (a, b int) {
defer func() {
if a != 1 || b != 2 {
panic(fmt.Sprint(a, b))
}
a, b = 3, 4
}()
if noArgReturn {
a, b = 1, 2
return
}
return 1, 2
}
func init() {
a, b := deferMutatesResults(true)
if a != 3 || b != 4 {
panic(fmt.Sprint(a, b))
}
a, b = deferMutatesResults(false)
if a != 3 || b != 4 {
panic(fmt.Sprint(a, b))
}
}
// We concatenate init blocks to make a single function, but we must
// run defers at the end of each block, not the combined function.
var deferCount = 0
func init() {
deferCount = 1
defer func() {
deferCount++
}()
// defer runs HERE
}
func init() {
// Strictly speaking the spec says deferCount may be 0 or 2
// since the relative order of init blocks is unspecified.
if deferCount != 2 {
panic(deferCount) // defer call has not run!
}
}
...@@ -154,22 +154,33 @@ func lift(fn *Function) { ...@@ -154,22 +154,33 @@ func lift(fn *Function) {
// concatenation of all non-dead newPhis and non-nil Instrs // concatenation of all non-dead newPhis and non-nil Instrs
// for the block, reusing the original array if space permits. // for the block, reusing the original array if space permits.
// While we're here, we also eliminate 'rundefers'
// instructions in functions that contain no 'defer'
// instructions.
usesDefer := false
// Determine which allocs we can lift and number them densely. // Determine which allocs we can lift and number them densely.
// The renaming phase uses this numbering for compact maps. // The renaming phase uses this numbering for compact maps.
numAllocs := 0 numAllocs := 0
for _, b := range fn.Blocks { for _, b := range fn.Blocks {
b.gaps = 0 b.gaps = 0
b.rundefers = 0
for i, instr := range b.Instrs { for i, instr := range b.Instrs {
if alloc, ok := instr.(*Alloc); ok { switch instr := instr.(type) {
if liftAlloc(df, alloc, newPhis) { case *Alloc:
alloc.index = numAllocs if liftAlloc(df, instr, newPhis) {
instr.index = numAllocs
numAllocs++ numAllocs++
// Delete the alloc. // Delete the alloc.
b.Instrs[i] = nil b.Instrs[i] = nil
b.gaps++ b.gaps++
} else { } else {
alloc.index = -1 instr.index = -1
} }
case *Defer:
usesDefer = true
case *RunDefers:
b.rundefers++
} }
} }
} }
...@@ -202,23 +213,34 @@ func lift(fn *Function) { ...@@ -202,23 +213,34 @@ func lift(fn *Function) {
} }
nps = nps[:j] nps = nps[:j]
if j+b.gaps == 0 { rundefersToKill := b.rundefers
continue // fast path: no new phis and no gaps if usesDefer {
rundefersToKill = 0
}
if j+b.gaps+rundefersToKill == 0 {
continue // fast path: no new phis or gaps
} }
// Compact nps + non-nil Instrs into a new slice. // Compact nps + non-nil Instrs into a new slice.
// TODO(adonovan): opt: compact in situ if there is // TODO(adonovan): opt: compact in situ if there is
// sufficient space or slack in the slice. // sufficient space or slack in the slice.
dst := make([]Instruction, j+len(b.Instrs)-b.gaps) dst := make([]Instruction, len(b.Instrs)+j-b.gaps-rundefersToKill)
for i, np := range nps { for i, np := range nps {
dst[i] = np.phi dst[i] = np.phi
} }
for _, instr := range b.Instrs { for _, instr := range b.Instrs {
if instr != nil { if instr == nil {
continue
}
if !usesDefer {
if _, ok := instr.(*RunDefers); ok {
continue
}
}
dst[j] = instr dst[j] = instr
j++ j++
} }
}
for i, np := range nps { for i, np := range nps {
dst[i] = np.phi dst[i] = np.phi
} }
......
...@@ -300,6 +300,10 @@ func (s *Ret) String() string { ...@@ -300,6 +300,10 @@ func (s *Ret) String() string {
return b.String() return b.String()
} }
func (*RunDefers) String() string {
return "rundefers"
}
func (s *Send) String() string { func (s *Send) String() string {
return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s)) return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
} }
......
...@@ -120,32 +120,33 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { ...@@ -120,32 +120,33 @@ func (s *sanity) checkInstr(idx int, instr Instruction) {
} }
} }
case *Call:
case *BinOp: case *BinOp:
case *UnOp: case *Call:
case *MakeClosure: case *ChangeInterface:
case *MakeChan: case *Conv:
case *MakeMap: case *Defer:
case *MakeSlice: case *Extract:
case *Slice:
case *Field: case *Field:
case *FieldAddr: case *FieldAddr:
case *IndexAddr: case *Go:
case *Index: case *Index:
case *Select: case *IndexAddr:
case *Lookup:
case *MakeChan:
case *MakeClosure:
case *MakeInterface:
case *MakeMap:
case *MakeSlice:
case *MapUpdate:
case *Next:
case *Range: case *Range:
case *TypeAssert: case *RunDefers:
case *Extract: case *Select:
case *Go:
case *Defer:
case *Send: case *Send:
case *Slice:
case *Store: case *Store:
case *MapUpdate: case *TypeAssert:
case *Next: case *UnOp:
case *Lookup:
case *Conv:
case *ChangeInterface:
case *MakeInterface:
// TODO(adonovan): implement checks. // TODO(adonovan): implement checks.
default: default:
panic(fmt.Sprintf("Unknown instruction type: %T", instr)) panic(fmt.Sprintf("Unknown instruction type: %T", instr))
......
...@@ -240,7 +240,7 @@ type Function struct { ...@@ -240,7 +240,7 @@ type Function struct {
// then cleared. // then cleared.
currentBlock *BasicBlock // where to emit code currentBlock *BasicBlock // where to emit code
objects map[types.Object]Value // addresses of local variables objects map[types.Object]Value // addresses of local variables
results []*Alloc // tuple of named results namedResults []*Alloc // tuple of named results
syntax *funcSyntax // abstract syntax trees for Go source functions syntax *funcSyntax // abstract syntax trees for Go source functions
targets *targets // linked stack of branch targets targets *targets // linked stack of branch targets
lblocks map[*ast.Object]*lblock // labelled blocks lblocks map[*ast.Object]*lblock // labelled blocks
...@@ -270,6 +270,7 @@ type BasicBlock struct { ...@@ -270,6 +270,7 @@ type BasicBlock struct {
succs2 [2]*BasicBlock // initial space for Succs. succs2 [2]*BasicBlock // initial space for Succs.
dom *domNode // node in dominator tree; optional. dom *domNode // node in dominator tree; optional.
gaps int // number of nil Instrs (transient). gaps int // number of nil Instrs (transient).
rundefers int // number of rundefers (transient)
} }
// Pure values ---------------------------------------- // Pure values ----------------------------------------
...@@ -817,9 +818,7 @@ type If struct { ...@@ -817,9 +818,7 @@ type If struct {
// Ret returns values and control back to the calling function. // Ret returns values and control back to the calling function.
// //
// len(Results) is always equal to the number of results in the // len(Results) is always equal to the number of results in the
// function's signature. A source-level 'return' statement with no // function's signature.
// operands in a multiple-return value function is desugared to make
// the results explicit.
// //
// If len(Results) > 1, Ret returns a tuple value with the specified // If len(Results) > 1, Ret returns a tuple value with the specified
// components which the caller must access using Extract instructions. // components which the caller must access using Extract instructions.
...@@ -827,9 +826,6 @@ type If struct { ...@@ -827,9 +826,6 @@ type If struct {
// There is no instruction to return a ready-made tuple like those // There is no instruction to return a ready-made tuple like those
// returned by a "value,ok"-mode TypeAssert, Lookup or UnOp(ARROW) or // returned by a "value,ok"-mode TypeAssert, Lookup or UnOp(ARROW) or
// a tail-call to a function with multiple result parameters. // a tail-call to a function with multiple result parameters.
// TODO(adonovan): consider defining one; but: dis- and re-assembling
// the tuple is unavoidable if assignability conversions are required
// on the components.
// //
// Ret must be the last instruction of its containing BasicBlock. // Ret must be the last instruction of its containing BasicBlock.
// Such a block has no successors. // Such a block has no successors.
...@@ -843,6 +839,20 @@ type Ret struct { ...@@ -843,6 +839,20 @@ type Ret struct {
Results []Value Results []Value
} }
// RunDefers pops and invokes the entire stack of procedure calls
// pushed by Defer instructions in this function.
//
// It is legal to encounter multiple 'rundefers' instructions in a
// single control-flow path through a function; this is useful in
// the combined init() function, for example.
//
// Example printed form:
// rundefers
//
type RunDefers struct {
anInstruction
}
// Panic initiates a panic with value X. // Panic initiates a panic with value X.
// //
// A Panic instruction must be the last instruction of its containing // A Panic instruction must be the last instruction of its containing
...@@ -875,8 +885,7 @@ type Go struct { ...@@ -875,8 +885,7 @@ type Go struct {
} }
// Defer pushes the specified call onto a stack of functions // Defer pushes the specified call onto a stack of functions
// to be called immediately prior to returning from the // to be called by a RunDefers instruction or by a panic.
// current function.
// //
// See CallCommon for generic function call documentation. // See CallCommon for generic function call documentation.
// //
...@@ -1146,6 +1155,7 @@ func (*Panic) ImplementsInstruction() {} ...@@ -1146,6 +1155,7 @@ func (*Panic) ImplementsInstruction() {}
func (*Phi) ImplementsInstruction() {} func (*Phi) ImplementsInstruction() {}
func (*Range) ImplementsInstruction() {} func (*Range) ImplementsInstruction() {}
func (*Ret) ImplementsInstruction() {} func (*Ret) ImplementsInstruction() {}
func (*RunDefers) ImplementsInstruction() {}
func (*Select) ImplementsInstruction() {} func (*Select) ImplementsInstruction() {}
func (*Send) ImplementsInstruction() {} func (*Send) ImplementsInstruction() {}
func (*Slice) ImplementsInstruction() {} func (*Slice) ImplementsInstruction() {}
...@@ -1267,6 +1277,10 @@ func (s *Ret) Operands(rands []*Value) []*Value { ...@@ -1267,6 +1277,10 @@ func (s *Ret) Operands(rands []*Value) []*Value {
return rands return rands
} }
func (*RunDefers) Operands(rands []*Value) []*Value {
return rands
}
func (v *Select) Operands(rands []*Value) []*Value { func (v *Select) Operands(rands []*Value) []*Value {
for i := range v.States { for i := range v.States {
rands = append(rands, &v.States[i].Chan, &v.States[i].Send) rands = append(rands, &v.States[i].Chan, &v.States[i].Send)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment