block = t._break
}
}
+ // Run function calls deferred in this init
+ // block when explicitly returning from it.
+ fn.emit(new(RunDefers))
emitJump(fn, block)
fn.currentBlock = fn.newBasicBlock("unreachable")
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.
tuple := b.exprN(fn, s.Results[0])
for i, v := range tuple.Type().(*types.Result).Values {
results = append(results, emitExtract(fn, tuple, i, v.Type))
}
-
- default:
- // Return one or more single-valued expressions.
- // These become the scalar or tuple result.
- for _, r := range s.Results {
- results = append(results, b.expr(fn, r))
+ } else {
+ // 1:1 return, or no-arg return in non-void function.
+ for i, r := range s.Results {
+ v := emitConv(fn, b.expr(fn, r), fn.Signature.Results[i].Type)
+ results = append(results, v)
+ }
+ }
+ 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.currentBlock = fn.newBasicBlock("unreachable")
fn.start(b.idents)
b.stmt(fn, fn.syntax.body)
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.finish()
_break: next,
}
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)
init.targets = init.targets.tail
init.currentBlock = next
// Finish up.
emitJump(init, done)
init.currentBlock = done
+ init.emit(new(RunDefers))
init.emit(new(Ret))
init.finish()
}
}
// emitTailCall emits to f a function call in tail position.
+// Precondition: f does/will not use deferred procedure calls.
// Postcondition: f.currentBlock is nil.
//
func emitTailCall(f *Function, call *Call) {
}
}
- // Results.
+ // Named results.
if f.syntax.resultFields != nil {
for _, field := range f.syntax.resultFields.List {
// Implicit "var" decl of locals for named results.
for _, n := range field.Names {
- f.results = append(f.results, f.addNamedLocal(idents[n]))
+ f.namedResults = append(f.namedResults, f.addNamedLocal(idents[n]))
}
}
}
// finish() finalizes the function after SSA code generation of its body.
func (f *Function) finish() {
f.objects = nil
- f.results = nil
+ f.namedResults = nil
f.currentBlock = nil
f.lblocks = nil
f.syntax = nil
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
// of the interpreter's fake types.
func findMethodSet(i *interpreter, typ types.Type) ssa.MethodSet {
default:
var res []value
for _, r := range instr.Results {
- res = append(res, copyVal(fr.get(r)))
+ res = append(res, fr.get(r))
}
fr.result = tuple(res)
}
return kReturn
+ case *ssa.RunDefers:
+ fr.rundefers()
+
case *ssa.Panic:
panic(targetPanic{fr.get(instr.X)})
}
fr.status, fr.panic = stPanic, recover()
}
- 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.rundefers()
// Destroy the locals to avoid accidental use after return.
for i := range fn.Locals {
fr.locals[i] = bad{}
func init() {
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!
+ }
+}
// concatenation of all non-dead newPhis and non-nil Instrs
// 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.
// The renaming phase uses this numbering for compact maps.
numAllocs := 0
for _, b := range fn.Blocks {
b.gaps = 0
+ b.rundefers = 0
for i, instr := range b.Instrs {
- if alloc, ok := instr.(*Alloc); ok {
- if liftAlloc(df, alloc, newPhis) {
- alloc.index = numAllocs
+ switch instr := instr.(type) {
+ case *Alloc:
+ if liftAlloc(df, instr, newPhis) {
+ instr.index = numAllocs
numAllocs++
// Delete the alloc.
b.Instrs[i] = nil
b.gaps++
} else {
- alloc.index = -1
+ instr.index = -1
}
+ case *Defer:
+ usesDefer = true
+ case *RunDefers:
+ b.rundefers++
}
}
}
}
nps = nps[:j]
- if j+b.gaps == 0 {
- continue // fast path: no new phis and no gaps
+ rundefersToKill := b.rundefers
+ 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.
// TODO(adonovan): opt: compact in situ if there is
// 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 {
dst[i] = np.phi
}
for _, instr := range b.Instrs {
- if instr != nil {
- dst[j] = instr
- j++
+ if instr == nil {
+ continue
}
+ if !usesDefer {
+ if _, ok := instr.(*RunDefers); ok {
+ continue
+ }
+ }
+ dst[j] = instr
+ j++
}
for i, np := range nps {
dst[i] = np.phi
return b.String()
}
+func (*RunDefers) String() string {
+ return "rundefers"
+}
+
func (s *Send) String() string {
return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
}
}
}
- case *Call:
case *BinOp:
- case *UnOp:
- case *MakeClosure:
- case *MakeChan:
- case *MakeMap:
- case *MakeSlice:
- case *Slice:
+ case *Call:
+ case *ChangeInterface:
+ case *Conv:
+ case *Defer:
+ case *Extract:
case *Field:
case *FieldAddr:
- case *IndexAddr:
+ case *Go:
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 *TypeAssert:
- case *Extract:
- case *Go:
- case *Defer:
+ case *RunDefers:
+ case *Select:
case *Send:
+ case *Slice:
case *Store:
- case *MapUpdate:
- case *Next:
- case *Lookup:
- case *Conv:
- case *ChangeInterface:
- case *MakeInterface:
+ case *TypeAssert:
+ case *UnOp:
// TODO(adonovan): implement checks.
default:
panic(fmt.Sprintf("Unknown instruction type: %T", instr))
// then cleared.
currentBlock *BasicBlock // where to emit code
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
targets *targets // linked stack of branch targets
lblocks map[*ast.Object]*lblock // labelled blocks
succs2 [2]*BasicBlock // initial space for Succs.
dom *domNode // node in dominator tree; optional.
gaps int // number of nil Instrs (transient).
+ rundefers int // number of rundefers (transient)
}
// Pure values ----------------------------------------
// Ret returns values and control back to the calling function.
//
// len(Results) is always equal to the number of results in the
-// function's signature. A source-level 'return' statement with no
-// operands in a multiple-return value function is desugared to make
-// the results explicit.
+// function's signature.
//
// If len(Results) > 1, Ret returns a tuple value with the specified
// components which the caller must access using Extract instructions.
// There is no instruction to return a ready-made tuple like those
// returned by a "value,ok"-mode TypeAssert, Lookup or UnOp(ARROW) or
// 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.
// Such a block has no successors.
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.
//
// A Panic instruction must be the last instruction of its containing
}
// Defer pushes the specified call onto a stack of functions
-// to be called immediately prior to returning from the
-// current function.
+// to be called by a RunDefers instruction or by a panic.
//
// See CallCommon for generic function call documentation.
//
func (*Phi) ImplementsInstruction() {}
func (*Range) ImplementsInstruction() {}
func (*Ret) ImplementsInstruction() {}
+func (*RunDefers) ImplementsInstruction() {}
func (*Select) ImplementsInstruction() {}
func (*Send) ImplementsInstruction() {}
func (*Slice) ImplementsInstruction() {}
return rands
}
+func (*RunDefers) Operands(rands []*Value) []*Value {
+ return rands
+}
+
func (v *Select) Operands(rands []*Value) []*Value {
for i := range v.States {
rands = append(rands, &v.States[i].Chan, &v.States[i].Send)