Commit b76e6f88 authored by Bryan C. Mills's avatar Bryan C. Mills

Revert "cmd/compile, cmd/link, runtime: make defers low-cost through inline...

Revert "cmd/compile, cmd/link, runtime: make defers low-cost through inline code and extra funcdata"

This reverts CL 190098.

Reason for revert: broke several builders.

Change-Id: I69161352f9ded02537d8815f259c4d391edd9220
Reviewed-on: https://go-review.googlesource.com/c/go/+/201519
Run-TryBot: Bryan C. Mills <bcmills@google.com>
Reviewed-by: default avatarAustin Clements <austin@google.com>
Reviewed-by: default avatarDan Scales <danscales@google.com>
parent 2718789b
...@@ -371,7 +371,6 @@ func (e *Escape) stmt(n *Node) { ...@@ -371,7 +371,6 @@ func (e *Escape) stmt(n *Node) {
e.stmts(n.Right.Ninit) e.stmts(n.Right.Ninit)
e.call(e.addrs(n.List), n.Right, nil) e.call(e.addrs(n.List), n.Right, nil)
case ORETURN: case ORETURN:
e.curfn.Func.numReturns++
results := e.curfn.Type.Results().FieldSlice() results := e.curfn.Type.Results().FieldSlice()
for i, v := range n.List.Slice() { for i, v := range n.List.Slice() {
e.assign(asNode(results[i].Nname), v, "return", n) e.assign(asNode(results[i].Nname), v, "return", n)
...@@ -379,16 +378,6 @@ func (e *Escape) stmt(n *Node) { ...@@ -379,16 +378,6 @@ func (e *Escape) stmt(n *Node) {
case OCALLFUNC, OCALLMETH, OCALLINTER, OCLOSE, OCOPY, ODELETE, OPANIC, OPRINT, OPRINTN, ORECOVER: case OCALLFUNC, OCALLMETH, OCALLINTER, OCLOSE, OCOPY, ODELETE, OPANIC, OPRINT, OPRINTN, ORECOVER:
e.call(nil, n, nil) e.call(nil, n, nil)
case OGO, ODEFER: case OGO, ODEFER:
if n.Op == ODEFER {
e.curfn.Func.SetHasDefer(true)
e.curfn.Func.numDefers++
if e.curfn.Func.numDefers > maxOpenDefers {
// Don't allow open defers if there are more than
// 8 defers in the function, since we use a single
// byte to record active defers.
e.curfn.Func.SetOpenCodedDeferDisallowed(true)
}
}
e.stmts(n.Left.Ninit) e.stmts(n.Left.Ninit)
e.call(nil, n.Left, n) e.call(nil, n.Left, n)
...@@ -883,13 +872,8 @@ func (e *Escape) augmentParamHole(k EscHole, where *Node) EscHole { ...@@ -883,13 +872,8 @@ func (e *Escape) augmentParamHole(k EscHole, where *Node) EscHole {
// non-transient location to avoid arguments from being // non-transient location to avoid arguments from being
// transiently allocated. // transiently allocated.
if where.Op == ODEFER && e.loopDepth == 1 { if where.Op == ODEFER && e.loopDepth == 1 {
// force stack allocation of defer record, unless open-coded where.Esc = EscNever // force stack allocation of defer record (see ssa.go)
// defers are used (see ssa.go)
where.Esc = EscNever
return e.later(k) return e.later(k)
} else if where.Op == ODEFER {
// If any defer occurs in a loop, open-coded defers cannot be used
e.curfn.Func.SetOpenCodedDeferDisallowed(true)
} }
return e.heapHole() return e.heapHole()
......
...@@ -52,7 +52,6 @@ var ( ...@@ -52,7 +52,6 @@ var (
Debug_typecheckinl int Debug_typecheckinl int
Debug_gendwarfinl int Debug_gendwarfinl int
Debug_softfloat int Debug_softfloat int
Debug_defer int
) )
// Debug arguments. // Debug arguments.
...@@ -82,7 +81,6 @@ var debugtab = []struct { ...@@ -82,7 +81,6 @@ var debugtab = []struct {
{"typecheckinl", "eager typechecking of inline function bodies", &Debug_typecheckinl}, {"typecheckinl", "eager typechecking of inline function bodies", &Debug_typecheckinl},
{"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl}, {"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl},
{"softfloat", "force compiler to emit soft-float code", &Debug_softfloat}, {"softfloat", "force compiler to emit soft-float code", &Debug_softfloat},
{"defer", "print information about defer compilation", &Debug_defer},
} }
const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
......
...@@ -294,9 +294,6 @@ func addGCLocals() { ...@@ -294,9 +294,6 @@ func addGCLocals() {
} }
ggloblsym(x, int32(len(x.P)), attr) ggloblsym(x, int32(len(x.P)), attr)
} }
if x := s.Func.OpenCodedDeferInfo; x != nil {
ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
}
} }
} }
......
...@@ -338,7 +338,6 @@ func deferstruct(stksize int64) *types.Type { ...@@ -338,7 +338,6 @@ func deferstruct(stksize int64) *types.Type {
makefield("siz", types.Types[TUINT32]), makefield("siz", types.Types[TUINT32]),
makefield("started", types.Types[TBOOL]), makefield("started", types.Types[TBOOL]),
makefield("heap", types.Types[TBOOL]), makefield("heap", types.Types[TBOOL]),
makefield("openDefer", types.Types[TBOOL]),
makefield("sp", types.Types[TUINTPTR]), makefield("sp", types.Types[TUINTPTR]),
makefield("pc", types.Types[TUINTPTR]), makefield("pc", types.Types[TUINTPTR]),
// Note: the types here don't really matter. Defer structures // Note: the types here don't really matter. Defer structures
...@@ -347,9 +346,6 @@ func deferstruct(stksize int64) *types.Type { ...@@ -347,9 +346,6 @@ func deferstruct(stksize int64) *types.Type {
makefield("fn", types.Types[TUINTPTR]), makefield("fn", types.Types[TUINTPTR]),
makefield("_panic", types.Types[TUINTPTR]), makefield("_panic", types.Types[TUINTPTR]),
makefield("link", types.Types[TUINTPTR]), makefield("link", types.Types[TUINTPTR]),
makefield("framepc", types.Types[TUINTPTR]),
makefield("varp", types.Types[TUINTPTR]),
makefield("fd", types.Types[TUINTPTR]),
makefield("args", argtype), makefield("args", argtype),
} }
......
...@@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) { ...@@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms _32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms _64bit uintptr // size on 64bit platforms
}{ }{
{Func{}, 124, 224}, {Func{}, 116, 208},
{Name{}, 32, 56}, {Name{}, 32, 56},
{Param{}, 24, 48}, {Param{}, 24, 48},
{Node{}, 76, 128}, {Node{}, 76, 128},
......
This diff is collapsed.
...@@ -492,8 +492,6 @@ type Func struct { ...@@ -492,8 +492,6 @@ type Func struct {
Pragma syntax.Pragma // go:xxx function annotations Pragma syntax.Pragma // go:xxx function annotations
flags bitset16 flags bitset16
numDefers int // number of defer calls in the function
numReturns int // number of explicit returns in the function
// nwbrCalls records the LSyms of functions called by this // nwbrCalls records the LSyms of functions called by this
// function for go:nowritebarrierrec analysis. Only filled in // function for go:nowritebarrierrec analysis. Only filled in
...@@ -534,7 +532,6 @@ const ( ...@@ -534,7 +532,6 @@ const (
funcInlinabilityChecked // inliner has already determined whether the function is inlinable funcInlinabilityChecked // inliner has already determined whether the function is inlinable
funcExportInline // include inline body in export data funcExportInline // include inline body in export data
funcInstrumentBody // add race/msan instrumentation during SSA construction funcInstrumentBody // add race/msan instrumentation during SSA construction
funcOpenCodedDeferDisallowed // can't do open-coded defers
) )
func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 } func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 }
...@@ -547,7 +544,6 @@ func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisa ...@@ -547,7 +544,6 @@ func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisa
func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 } func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 }
func (f *Func) ExportInline() bool { return f.flags&funcExportInline != 0 } func (f *Func) ExportInline() bool { return f.flags&funcExportInline != 0 }
func (f *Func) InstrumentBody() bool { return f.flags&funcInstrumentBody != 0 } func (f *Func) InstrumentBody() bool { return f.flags&funcInstrumentBody != 0 }
func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) } func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) }
func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) } func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) }
...@@ -559,7 +555,6 @@ func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDis ...@@ -559,7 +555,6 @@ func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDis
func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) } func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) }
func (f *Func) SetExportInline(b bool) { f.flags.set(funcExportInline, b) } func (f *Func) SetExportInline(b bool) { f.flags.set(funcExportInline, b) }
func (f *Func) SetInstrumentBody(b bool) { f.flags.set(funcInstrumentBody, b) } func (f *Func) SetInstrumentBody(b bool) { f.flags.set(funcInstrumentBody, b) }
func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
func (f *Func) setWBPos(pos src.XPos) { func (f *Func) setWBPos(pos src.XPos) {
if Debug_wb != 0 { if Debug_wb != 0 {
......
...@@ -213,6 +213,7 @@ func walkstmt(n *Node) *Node { ...@@ -213,6 +213,7 @@ func walkstmt(n *Node) *Node {
yyerror("case statement out of place") yyerror("case statement out of place")
case ODEFER: case ODEFER:
Curfn.Func.SetHasDefer(true)
fallthrough fallthrough
case OGO: case OGO:
switch n.Left.Op { switch n.Left.Op {
......
...@@ -170,11 +170,6 @@ func elimDeadAutosGeneric(f *Func) { ...@@ -170,11 +170,6 @@ func elimDeadAutosGeneric(f *Func) {
return return
case OpVarLive: case OpVarLive:
// Don't delete the auto if it needs to be kept alive. // Don't delete the auto if it needs to be kept alive.
// We depend on this check to keep the autotmp stack slots
// for open-coded defers from being removed (since they
// may not be used by the inline code, but will be used by
// panic processing).
n, ok := v.Aux.(GCNode) n, ok := v.Aux.(GCNode)
if !ok || n.StorageClass() != ClassAuto { if !ok || n.StorageClass() != ClassAuto {
return return
......
...@@ -32,14 +32,6 @@ type Func struct { ...@@ -32,14 +32,6 @@ type Func struct {
Type *types.Type // type signature of the function. Type *types.Type // type signature of the function.
Blocks []*Block // unordered set of all basic blocks (note: not indexable by ID) Blocks []*Block // unordered set of all basic blocks (note: not indexable by ID)
Entry *Block // the entry basic block Entry *Block // the entry basic block
// If we are using open-coded defers, this is the first call to a deferred
// function in the final defer exit sequence that we generated. This call
// should be after all defer statements, and will have all args, etc. of
// all defer calls as live. The liveness info of this call will be used
// for the deferreturn/ret segment generated for functions with open-coded
// defers.
LastDeferExit *Value
bid idAlloc // block ID allocator bid idAlloc // block ID allocator
vid idAlloc // value ID allocator vid idAlloc // value ID allocator
......
...@@ -409,7 +409,6 @@ type FuncInfo struct { ...@@ -409,7 +409,6 @@ type FuncInfo struct {
GCLocals *LSym GCLocals *LSym
GCRegs *LSym GCRegs *LSym
StackObjects *LSym StackObjects *LSym
OpenCodedDeferInfo *LSym
} }
type InlMark struct { type InlMark struct {
......
...@@ -20,7 +20,6 @@ const ( ...@@ -20,7 +20,6 @@ const (
FUNCDATA_RegPointerMaps = 2 FUNCDATA_RegPointerMaps = 2
FUNCDATA_StackObjects = 3 FUNCDATA_StackObjects = 3
FUNCDATA_InlTree = 4 FUNCDATA_InlTree = 4
FUNCDATA_OpenCodedDeferInfo = 5
// ArgsSizeUnknown is set in Func.argsize to mark all functions // ArgsSizeUnknown is set in Func.argsize to mark all functions
// whose argument size is unknown (C vararg functions, and // whose argument size is unknown (C vararg functions, and
......
...@@ -85,12 +85,6 @@ func GetFuncID(name, file string) FuncID { ...@@ -85,12 +85,6 @@ func GetFuncID(name, file string) FuncID {
return FuncID_panicwrap return FuncID_panicwrap
case "runtime.handleAsyncEvent": case "runtime.handleAsyncEvent":
return FuncID_handleAsyncEvent return FuncID_handleAsyncEvent
case "runtime.deferreturn":
// Don't show in the call stack (used when invoking defer functions)
return FuncID_wrapper
case "runtime.runOpenDeferFrame":
// Don't show in the call stack (used when invoking defer functions)
return FuncID_wrapper
} }
if file == "<autogenerated>" { if file == "<autogenerated>" {
return FuncID_wrapper return FuncID_wrapper
......
...@@ -18,7 +18,7 @@ const ( ...@@ -18,7 +18,7 @@ const (
) )
// Initialize StackGuard and StackLimit according to target system. // Initialize StackGuard and StackLimit according to target system.
var StackGuard = 896*stackGuardMultiplier() + StackSystem var StackGuard = 880*stackGuardMultiplier() + StackSystem
var StackLimit = StackGuard - StackSystem - StackSmall var StackLimit = StackGuard - StackSystem - StackSmall
// stackGuardMultiplier returns a multiplier to apply to the default // stackGuardMultiplier returns a multiplier to apply to the default
......
...@@ -11,7 +11,6 @@ import ( ...@@ -11,7 +11,6 @@ import (
"cmd/internal/sys" "cmd/internal/sys"
"cmd/link/internal/sym" "cmd/link/internal/sym"
"encoding/binary" "encoding/binary"
"fmt"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
...@@ -256,23 +255,13 @@ func (ctxt *Link) pclntab() { ...@@ -256,23 +255,13 @@ func (ctxt *Link) pclntab() {
} }
if r.Type.IsDirectJump() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" { if r.Type.IsDirectJump() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" {
if ctxt.Arch.Family == sys.Wasm { if ctxt.Arch.Family == sys.Wasm {
deferreturn = lastWasmAddr - 1 deferreturn = lastWasmAddr
} else { } else {
// Note: the relocation target is in the call instruction, but // Note: the relocation target is in the call instruction, but
// is not necessarily the whole instruction (for instance, on // is not necessarily the whole instruction (for instance, on
// x86 the relocation applies to bytes [1:5] of the 5 byte call // x86 the relocation applies to bytes [1:5] of the 5 byte call
// instruction). // instruction).
deferreturn = uint32(r.Off) deferreturn = uint32(r.Off)
switch ctxt.Arch.Family {
case sys.AMD64, sys.I386, sys.MIPS, sys.MIPS64, sys.RISCV64:
deferreturn--
case sys.PPC64, sys.ARM, sys.ARM64:
// no change
case sys.S390X:
deferreturn -= 2
default:
panic(fmt.Sprint("Unhandled architecture:", ctxt.Arch.Family))
}
} }
break // only need one break // only need one
} }
......
...@@ -498,8 +498,7 @@ func (ctxt *Link) symtab() { ...@@ -498,8 +498,7 @@ func (ctxt *Link) symtab() {
case strings.HasPrefix(s.Name, "gcargs."), case strings.HasPrefix(s.Name, "gcargs."),
strings.HasPrefix(s.Name, "gclocals."), strings.HasPrefix(s.Name, "gclocals."),
strings.HasPrefix(s.Name, "gclocals·"), strings.HasPrefix(s.Name, "gclocals·"),
strings.HasPrefix(s.Name, "inltree."), strings.HasPrefix(s.Name, "inltree."):
strings.HasSuffix(s.Name, ".opendefer"):
s.Type = sym.SGOFUNC s.Type = sym.SGOFUNC
s.Attr |= sym.AttrNotInSymbolTable s.Attr |= sym.AttrNotInSymbolTable
s.Outer = symgofunc s.Outer = symgofunc
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
package runtime_test package runtime_test
import ( import (
"internal/race"
"reflect" "reflect"
"runtime" "runtime"
"strings" "strings"
...@@ -13,19 +12,19 @@ import ( ...@@ -13,19 +12,19 @@ import (
) )
func f1(pan bool) []uintptr { func f1(pan bool) []uintptr {
return f2(pan) // line 16 return f2(pan) // line 15
} }
func f2(pan bool) []uintptr { func f2(pan bool) []uintptr {
return f3(pan) // line 20 return f3(pan) // line 19
} }
func f3(pan bool) []uintptr { func f3(pan bool) []uintptr {
if pan { if pan {
panic("f3") // line 25 panic("f3") // line 24
} }
ret := make([]uintptr, 20) ret := make([]uintptr, 20)
return ret[:runtime.Callers(0, ret)] // line 28 return ret[:runtime.Callers(0, ret)] // line 27
} }
func testCallers(t *testing.T, pcs []uintptr, pan bool) { func testCallers(t *testing.T, pcs []uintptr, pan bool) {
...@@ -49,16 +48,16 @@ func testCallers(t *testing.T, pcs []uintptr, pan bool) { ...@@ -49,16 +48,16 @@ func testCallers(t *testing.T, pcs []uintptr, pan bool) {
var f3Line int var f3Line int
if pan { if pan {
f3Line = 25 f3Line = 24
} else { } else {
f3Line = 28 f3Line = 27
} }
want := []struct { want := []struct {
name string name string
line int line int
}{ }{
{"f1", 16}, {"f1", 15},
{"f2", 20}, {"f2", 19},
{"f3", f3Line}, {"f3", f3Line},
} }
for _, w := range want { for _, w := range want {
...@@ -189,36 +188,3 @@ func TestCallersDivZeroPanic(t *testing.T) { ...@@ -189,36 +188,3 @@ func TestCallersDivZeroPanic(t *testing.T) {
t.Fatal("did not see divide-by-sizer panic") t.Fatal("did not see divide-by-sizer panic")
} }
} }
// This test will have a slightly different callstack if non-open-coded defers are
// called (e.g. if race checks enabled), because of a difference in the way the
// defer function is invoked.
func TestCallersDeferNilFuncPanic(t *testing.T) {
if race.Enabled {
t.Skip("skipping TestCallersDeferNilFuncPanic under race detector")
}
// Make sure we don't have any extra frames on the stack (due to
// open-coded defer processing)
state := 1
want := []string{"runtime.Callers", "runtime_test.TestCallersDeferNilFuncPanic.func1",
"runtime.gopanic", "runtime.panicmem", "runtime.sigpanic",
"runtime_test.TestCallersDeferNilFuncPanic"}
defer func() {
if r := recover(); r == nil {
t.Fatal("did not panic")
}
pcs := make([]uintptr, 20)
pcs = pcs[:runtime.Callers(0, pcs)]
testCallersEqual(t, pcs, want)
if state == 1 {
t.Fatal("nil defer func panicked at defer time rather than function exit time")
}
}()
var f func()
defer f()
// Use the value of 'state' to make sure nil defer func f causes panic at
// function exit, rather than at the defer statement.
state = 2
}
...@@ -15,11 +15,11 @@ import ( ...@@ -15,11 +15,11 @@ import (
// unconditional panic (hence no return from the function) // unconditional panic (hence no return from the function)
func TestUnconditionalPanic(t *testing.T) { func TestUnconditionalPanic(t *testing.T) {
defer func() { defer func() {
if recover() != "testUnconditional" { if recover() == nil {
t.Fatal("expected unconditional panic") t.Fatal("expected unconditional panic")
} }
}() }()
panic("testUnconditional") panic("panic should be recovered")
} }
var glob int = 3 var glob int = 3
...@@ -30,7 +30,7 @@ func TestOpenAndNonOpenDefers(t *testing.T) { ...@@ -30,7 +30,7 @@ func TestOpenAndNonOpenDefers(t *testing.T) {
for { for {
// Non-open defer because in a loop // Non-open defer because in a loop
defer func(n int) { defer func(n int) {
if recover() != "testNonOpenDefer" { if recover() == nil {
t.Fatal("expected testNonOpen panic") t.Fatal("expected testNonOpen panic")
} }
}(3) }(3)
...@@ -45,7 +45,7 @@ func TestOpenAndNonOpenDefers(t *testing.T) { ...@@ -45,7 +45,7 @@ func TestOpenAndNonOpenDefers(t *testing.T) {
//go:noinline //go:noinline
func testOpen(t *testing.T, arg int) { func testOpen(t *testing.T, arg int) {
defer func(n int) { defer func(n int) {
if recover() != "testOpenDefer" { if recover() == nil {
t.Fatal("expected testOpen panic") t.Fatal("expected testOpen panic")
} }
}(4) }(4)
...@@ -61,7 +61,7 @@ func TestNonOpenAndOpenDefers(t *testing.T) { ...@@ -61,7 +61,7 @@ func TestNonOpenAndOpenDefers(t *testing.T) {
for { for {
// Non-open defer because in a loop // Non-open defer because in a loop
defer func(n int) { defer func(n int) {
if recover() != "testNonOpenDefer" { if recover() == nil {
t.Fatal("expected testNonOpen panic") t.Fatal("expected testNonOpen panic")
} }
}(3) }(3)
...@@ -80,7 +80,7 @@ func TestConditionalDefers(t *testing.T) { ...@@ -80,7 +80,7 @@ func TestConditionalDefers(t *testing.T) {
list = make([]int, 0, 10) list = make([]int, 0, 10)
defer func() { defer func() {
if recover() != "testConditional" { if recover() == nil {
t.Fatal("expected panic") t.Fatal("expected panic")
} }
want := []int{4, 2, 1} want := []int{4, 2, 1}
...@@ -106,7 +106,7 @@ func testConditionalDefers(n int) { ...@@ -106,7 +106,7 @@ func testConditionalDefers(n int) {
defer doappend(4) defer doappend(4)
} }
} }
panic("testConditional") panic("test")
} }
// Test that there is no compile-time or run-time error if an open-coded defer // Test that there is no compile-time or run-time error if an open-coded defer
...@@ -174,52 +174,3 @@ func TestRecoverMatching(t *testing.T) { ...@@ -174,52 +174,3 @@ func TestRecoverMatching(t *testing.T) {
}() }()
panic("panic1") panic("panic1")
} }
type nonSSAable [128]byte
type bigStruct struct {
x, y, z, w, p, q int64
}
func mknonSSAable() nonSSAable {
globint1++
return nonSSAable{0, 0, 0, 0, 5}
}
var globint1, globint2 int
//go:noinline
func sideeffect(n int64) int64 {
globint2++
return n
}
// Test that nonSSAable arguments to defer are handled correctly and only evaluated once.
func TestNonSSAableArgs(t *testing.T) {
globint1 = 0
globint2 = 0
var save1 byte
var save2 int64
defer func() {
if globint1 != 1 {
t.Fatal(fmt.Sprintf("globint1: wanted: 1, got %v", globint1))
}
if save1 != 5 {
t.Fatal(fmt.Sprintf("save1: wanted: 5, got %v", save1))
}
if globint2 != 1 {
t.Fatal(fmt.Sprintf("globint2: wanted: 1, got %v", globint2))
}
if save2 != 2 {
t.Fatal(fmt.Sprintf("save2: wanted: 2, got %v", save2))
}
}()
defer func(n nonSSAable) {
save1 = n[4]
}(mknonSSAable())
defer func(b bigStruct) {
save2 = b.y
}(bigStruct{1, 2, 3, 4, 5, sideeffect(6)})
}
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#define FUNCDATA_RegPointerMaps 2 #define FUNCDATA_RegPointerMaps 2
#define FUNCDATA_StackObjects 3 #define FUNCDATA_StackObjects 3
#define FUNCDATA_InlTree 4 #define FUNCDATA_InlTree 4
#define FUNCDATA_OpenCodedDeferInfo 5 /* info for func with open-coded defers */
// Pseudo-assembly statements. // Pseudo-assembly statements.
......
This diff is collapsed.
...@@ -701,7 +701,7 @@ type _func struct { ...@@ -701,7 +701,7 @@ type _func struct {
nameoff int32 // function name nameoff int32 // function name
args int32 // in/out args size args int32 // in/out args size
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any. deferreturn uint32 // offset of a deferreturn block from entry, if any.
pcsp int32 pcsp int32
pcfile int32 pcfile int32
...@@ -774,7 +774,7 @@ func extendRandom(r []byte, n int) { ...@@ -774,7 +774,7 @@ func extendRandom(r []byte, n int) {
} }
// A _defer holds an entry on the list of deferred calls. // A _defer holds an entry on the list of deferred calls.
// If you add a field here, add code to clear it in freedefer and deferProcStack // If you add a field here, add code to clear it in freedefer.
// This struct must match the code in cmd/compile/internal/gc/reflect.go:deferstruct // This struct must match the code in cmd/compile/internal/gc/reflect.go:deferstruct
// and cmd/compile/internal/gc/ssa.go:(*state).call. // and cmd/compile/internal/gc/ssa.go:(*state).call.
// Some defers will be allocated on the stack and some on the heap. // Some defers will be allocated on the stack and some on the heap.
...@@ -785,27 +785,11 @@ type _defer struct { ...@@ -785,27 +785,11 @@ type _defer struct {
siz int32 // includes both arguments and results siz int32 // includes both arguments and results
started bool started bool
heap bool heap bool
// openDefer indicates that this _defer is for a frame with open-coded
// defers. We have only one defer record for the entire frame (which may
// currently have 0, 1, or more defers active).
openDefer bool
sp uintptr // sp at time of defer sp uintptr // sp at time of defer
pc uintptr // pc at time of defer pc uintptr
fn *funcval fn *funcval
_panic *_panic // panic that is running defer _panic *_panic // panic that is running defer
link *_defer link *_defer
// If openDefer is true, the fields below record values about the stack
// frame and associated function that has the open-coded defer(s). sp
// above will be the sp for the frame, and pc will be address of the
// deferreturn call in the function.
fd unsafe.Pointer // funcdata for the function associated with the frame
varp uintptr // value of varp for the stack frame
// framepc is the current pc associated with the stack frame. Together,
// with sp above (which is the sp associated with the stack frame),
// framepc/sp can be used as pc/sp pair to continue a stack trace via
// gentraceback().
framepc uintptr
} }
// A _panic holds information about an active panic. // A _panic holds information about an active panic.
......
...@@ -89,7 +89,7 @@ func BenchmarkDefer(b *testing.B) { ...@@ -89,7 +89,7 @@ func BenchmarkDefer(b *testing.B) {
} }
func defer1() { func defer1() {
func(x, y, z int) { defer func(x, y, z int) {
if recover() != nil || x != 1 || y != 2 || z != 3 { if recover() != nil || x != 1 || y != 2 || z != 3 {
panic("bad recover") panic("bad recover")
} }
......
...@@ -91,7 +91,7 @@ const ( ...@@ -91,7 +91,7 @@ const (
// The stack guard is a pointer this many bytes above the // The stack guard is a pointer this many bytes above the
// bottom of the stack. // bottom of the stack.
_StackGuard = 896*sys.StackGuardMultiplier + _StackSystem _StackGuard = 880*sys.StackGuardMultiplier + _StackSystem
// After a stack split check the SP is allowed to be this // After a stack split check the SP is allowed to be this
// many bytes below the stack guard. This saves an instruction // many bytes below the stack guard. This saves an instruction
...@@ -736,8 +736,6 @@ func adjustdefers(gp *g, adjinfo *adjustinfo) { ...@@ -736,8 +736,6 @@ func adjustdefers(gp *g, adjinfo *adjustinfo) {
adjustpointer(adjinfo, unsafe.Pointer(&d.sp)) adjustpointer(adjinfo, unsafe.Pointer(&d.sp))
adjustpointer(adjinfo, unsafe.Pointer(&d._panic)) adjustpointer(adjinfo, unsafe.Pointer(&d._panic))
adjustpointer(adjinfo, unsafe.Pointer(&d.link)) adjustpointer(adjinfo, unsafe.Pointer(&d.link))
adjustpointer(adjinfo, unsafe.Pointer(&d.varp))
adjustpointer(adjinfo, unsafe.Pointer(&d.fd))
} }
// Adjust defer argument blocks the same way we adjust active stack frames. // Adjust defer argument blocks the same way we adjust active stack frames.
......
...@@ -221,7 +221,6 @@ const ( ...@@ -221,7 +221,6 @@ const (
_FUNCDATA_RegPointerMaps = 2 _FUNCDATA_RegPointerMaps = 2
_FUNCDATA_StackObjects = 3 _FUNCDATA_StackObjects = 3
_FUNCDATA_InlTree = 4 _FUNCDATA_InlTree = 4
_FUNCDATA_OpenCodedDeferInfo = 5
_ArgsSizeUnknown = -0x80000000 _ArgsSizeUnknown = -0x80000000
) )
......
// errorcheck -0 -l -d=defer
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// check that open-coded defers are used in expected situations
package main
import "fmt"
var glob = 3
func f1() {
for i := 0; i < 10; i++ {
fmt.Println("loop")
}
defer func() { // ERROR "open-coded defer in function f1"
fmt.Println("defer")
}()
}
func f2() {
for {
defer func() { // ERROR "heap-allocated defer in function f2"
fmt.Println("defer1")
}()
if glob > 2 {
break
}
}
defer func() { // ERROR "stack-allocated defer in function f2"
fmt.Println("defer2")
}()
}
func f3() {
defer func() { // ERROR "stack-allocated defer in function f3"
fmt.Println("defer2")
}()
for {
defer func() { // ERROR "heap-allocated defer in function f3"
fmt.Println("defer1")
}()
if glob > 2 {
break
}
}
}
func f4() {
defer func() { // ERROR "open-coded defer in function f4"
fmt.Println("defer")
}()
label:
fmt.Println("goto loop")
if glob > 2 {
goto label
}
}
func f5() {
label:
fmt.Println("goto loop")
defer func() { // ERROR "heap-allocated defer in function f5"
fmt.Println("defer")
}()
if glob > 2 {
goto label
}
}
func f6() {
label:
fmt.Println("goto loop")
if glob > 2 {
goto label
}
// The current analysis doesn't end a backward goto loop, so this defer is
// considered to be inside a loop
defer func() { // ERROR "heap-allocated defer in function f6"
fmt.Println("defer")
}()
}
...@@ -367,19 +367,16 @@ func f24() { ...@@ -367,19 +367,16 @@ func f24() {
m2[[2]string{"x", "y"}] = nil m2[[2]string{"x", "y"}] = nil
} }
// Non-open-coded defers should not cause autotmps. (Open-coded defers do create extra autotmps). // defer should not cause spurious ambiguously live variables
func f25(b bool) { func f25(b bool) {
for i := 0; i < 2; i++ {
// Put in loop to make sure defer is not open-coded
defer g25() defer g25()
}
if b { if b {
return return
} }
var x string var x string
x = g14() x = g14()
printstring(x) printstring(x)
return
} }
func g25() func g25()
...@@ -420,8 +417,7 @@ func f27defer(b bool) { ...@@ -420,8 +417,7 @@ func f27defer(b bool) {
defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{" defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
} }
defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{" defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ .autotmp_[0-9]+" printnl()
return // ERROR "live at call to call27: .autotmp_[0-9]+"
} }
// and newproc (go) escapes to the heap // and newproc (go) escapes to the heap
...@@ -691,12 +687,12 @@ type R struct{ *T } // ERRORAUTO "live at entry to \(\*R\)\.Foo: \.this ptr" "li ...@@ -691,12 +687,12 @@ type R struct{ *T } // ERRORAUTO "live at entry to \(\*R\)\.Foo: \.this ptr" "li
// In particular, at printint r must be live. // In particular, at printint r must be live.
func f41(p, q *int) (r *int) { // ERROR "live at entry to f41: p q$" func f41(p, q *int) (r *int) { // ERROR "live at entry to f41: p q$"
r = p r = p
defer func() { defer func() { // ERROR "live at call to deferprocStack: q r$" "live at call to deferreturn: r$"
recover() recover()
}() }()
printint(0) // ERROR "live at call to printint: q r .autotmp_[0-9]+$" printint(0) // ERROR "live at call to printint: q r$"
r = q r = q
return // ERROR "live at call to f41.func1: r .autotmp_[0-9]+$" return // ERROR "live at call to deferreturn: r$"
} }
func f42() { func f42() {
......
...@@ -309,17 +309,17 @@ TestCases: ...@@ -309,17 +309,17 @@ TestCases:
name := m[1] name := m[1]
size, _ := strconv.Atoi(m[2]) size, _ := strconv.Atoi(m[2])
// The limit was originally 128 but is now 768 (896-128). // The limit was originally 128 but is now 752 (880-128).
// Instead of rewriting the test cases above, adjust // Instead of rewriting the test cases above, adjust
// the first stack frame to use up the extra bytes. // the first stack frame to use up the extra bytes.
if i == 0 { if i == 0 {
size += (896 - 128) - 128 size += (880 - 128) - 128
// Noopt builds have a larger stackguard. // Noopt builds have a larger stackguard.
// See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier // See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier
// This increase is included in objabi.StackGuard // This increase is included in objabi.StackGuard
for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") { for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") {
if s == "-N" { if s == "-N" {
size += 896 size += 880
} }
} }
} }
......
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