Skip to content

Commit dec1bae

Browse files
committed
cmd/compile: additional paranoia and checking in plive.go
The main check here is that liveness now crashes if it finds an instruction using a variable that should be tracked but is not. Comments and adjustments in nodarg to explain what's going on and to remove the "-1" argument added a few months ago, plus a sketch of a future simplification. The need for n.Orig in the earlier CL seems to have been an intermediate problem rather than fundamental: the new explanations in nodarg make clear that nodarg is not causing the problem I thought, and in fact now using n instead of n.Orig works fine in plive.go. Change-Id: I3f5cf9f6e4438a6d27abac7d490e7521545cd552 Reviewed-on: https://go-review.googlesource.com/23450 Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent e9228dd commit dec1bae

File tree

8 files changed

+139
-111
lines changed

8 files changed

+139
-111
lines changed

src/cmd/compile/internal/gc/closure.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ func transformclosure(xfunc *Node) {
303303
continue
304304
}
305305
fld := newField()
306-
fld.Funarg = true
306+
fld.Funarg = FunargParams
307307
if v.Name.Byval {
308308
// If v is captured by value, we merely downgrade it to PPARAM.
309309
v.Class = PPARAM

src/cmd/compile/internal/gc/dcl.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -828,14 +828,14 @@ func tostruct0(t *Type, l []*Node) {
828828
}
829829
}
830830

831-
func tofunargs(l []*Node) *Type {
831+
func tofunargs(l []*Node, funarg Funarg) *Type {
832832
t := typ(TSTRUCT)
833-
t.StructType().Funarg = true
833+
t.StructType().Funarg = funarg
834834

835835
fields := make([]*Field, len(l))
836836
for i, n := range l {
837837
f := structfield(n)
838-
f.Funarg = true
838+
f.Funarg = funarg
839839

840840
// esc.go needs to find f given a PPARAM to add the tag.
841841
if n.Left != nil && n.Left.Class == PPARAM {
@@ -1026,9 +1026,9 @@ func functype0(t *Type, this *Node, in, out []*Node) {
10261026
if this != nil {
10271027
rcvr = []*Node{this}
10281028
}
1029-
*t.RecvsP() = tofunargs(rcvr)
1030-
*t.ResultsP() = tofunargs(out)
1031-
*t.ParamsP() = tofunargs(in)
1029+
*t.RecvsP() = tofunargs(rcvr, FunargRcvr)
1030+
*t.ResultsP() = tofunargs(out, FunargResults)
1031+
*t.ParamsP() = tofunargs(in, FunargParams)
10321032

10331033
checkdupfields("argument", t.Recvs(), t.Results(), t.Params())
10341034

src/cmd/compile/internal/gc/fmt.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,7 +1659,7 @@ func Fldconv(f *Field, flag FmtFlag) string {
16591659
}
16601660

16611661
if s != nil && f.Embedded == 0 {
1662-
if f.Funarg {
1662+
if f.Funarg != FunargNone {
16631663
name = Nconv(f.Nname, 0)
16641664
} else if flag&FmtLong != 0 {
16651665
name = sconv(s, FmtShort|FmtByte) // qualify non-exported names (used on structs, not on funarg)
@@ -1692,7 +1692,7 @@ func Fldconv(f *Field, flag FmtFlag) string {
16921692
// (The escape analysis tags do not apply to func vars.)
16931693
// But it must not suppress struct field tags.
16941694
// See golang.org/issue/13777 and golang.org/issue/14331.
1695-
if flag&FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != "" {
1695+
if flag&FmtShort == 0 && (!fmtbody || f.Funarg == FunargNone) && f.Note != "" {
16961696
str += " " + strconv.Quote(f.Note)
16971697
}
16981698

src/cmd/compile/internal/gc/gsubr.go

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -515,25 +515,36 @@ func newplist() *obj.Plist {
515515
return pl
516516
}
517517

518-
// nodarg does something that depends on the value of
519-
// fp (this was previously completely undocumented).
518+
// nodarg returns a Node for the function argument denoted by t,
519+
// which is either the entire function argument or result struct (t is a struct *Type)
520+
// or a specific argument (t is a *Field within a struct *Type).
520521
//
521-
// fp=1 corresponds to input args
522-
// fp=0 corresponds to output args
523-
// fp=-1 is a special case of output args for a
524-
// specific call from walk that previously (and
525-
// incorrectly) passed a 1; the behavior is exactly
526-
// the same as it is for 1, except that PARAMOUT is
527-
// generated instead of PARAM.
522+
// If fp is 0, the node is for use by a caller invoking the given
523+
// function, preparing the arguments before the call
524+
// or retrieving the results after the call.
525+
// In this case, the node will correspond to an outgoing argument
526+
// slot like 8(SP).
527+
//
528+
// If fp is 1, the node is for use by the function itself
529+
// (the callee), to retrieve its arguments or write its results.
530+
// In this case the node will be an ONAME with an appropriate
531+
// type and offset.
528532
func nodarg(t interface{}, fp int) *Node {
529533
var n *Node
530534

535+
var funarg Funarg
531536
switch t := t.(type) {
537+
default:
538+
Fatalf("bad nodarg %T(%v)", t, t)
539+
532540
case *Type:
533-
// entire argument struct, not just one arg
541+
// Entire argument struct, not just one arg
534542
if !t.IsFuncArgStruct() {
535543
Fatalf("nodarg: bad type %v", t)
536544
}
545+
funarg = t.StructType().Funarg
546+
547+
// Build fake variable name for whole arg struct.
537548
n = Nod(ONAME, nil, nil)
538549
n.Sym = Lookup(".args")
539550
n.Type = t
@@ -546,15 +557,43 @@ func nodarg(t interface{}, fp int) *Node {
546557
}
547558
n.Xoffset = first.Offset
548559
n.Addable = true
560+
549561
case *Field:
550-
if fp == 1 || fp == -1 {
562+
funarg = t.Funarg
563+
if fp == 1 {
564+
// NOTE(rsc): This should be using t.Nname directly,
565+
// except in the case where t.Nname.Sym is the blank symbol and
566+
// so the assignment would be discarded during code generation.
567+
// In that case we need to make a new node, and there is no harm
568+
// in optimization passes to doing so. But otherwise we should
569+
// definitely be using the actual declaration and not a newly built node.
570+
// The extra Fatalf checks here are verifying that this is the case,
571+
// without changing the actual logic (at time of writing, it's getting
572+
// toward time for the Go 1.7 beta).
573+
// At some quieter time (assuming we've never seen these Fatalfs happen)
574+
// we could change this code to use "expect" directly.
575+
expect := t.Nname
576+
if expect.isParamHeapCopy() {
577+
expect = expect.Name.Param.Stackcopy
578+
}
579+
551580
for _, n := range Curfn.Func.Dcl {
552581
if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym {
582+
if n != expect {
583+
Fatalf("nodarg: unexpected node: %v (%p %v) vs %v (%p %v)", n, n, n.Op, t.Nname, t.Nname, t.Nname.Op)
584+
}
553585
return n
554586
}
555587
}
588+
589+
if !isblanksym(expect.Sym) {
590+
Fatalf("nodarg: did not find node in dcl list: %v", expect)
591+
}
556592
}
557593

594+
// Build fake name for individual variable.
595+
// This is safe because if there was a real declared name
596+
// we'd have used it above.
558597
n = Nod(ONAME, nil, nil)
559598
n.Type = t.Type
560599
n.Sym = t.Sym
@@ -564,8 +603,6 @@ func nodarg(t interface{}, fp int) *Node {
564603
n.Xoffset = t.Offset
565604
n.Addable = true
566605
n.Orig = t.Nname
567-
default:
568-
panic("unreachable")
569606
}
570607

571608
// Rewrite argument named _ to __,
@@ -576,23 +613,23 @@ func nodarg(t interface{}, fp int) *Node {
576613
}
577614

578615
switch fp {
579-
case 0: // output arg
580-
n.Op = OINDREG
616+
default:
617+
Fatalf("bad fp")
581618

619+
case 0: // preparing arguments for call
620+
n.Op = OINDREG
582621
n.Reg = int16(Thearch.REGSP)
583622
n.Xoffset += Ctxt.FixedFrameSize()
584623

585-
case 1: // input arg
624+
case 1: // reading arguments inside call
586625
n.Class = PPARAM
587-
588-
case -1: // output arg from paramstoheap
589-
n.Class = PPARAMOUT
590-
591-
case 2: // offset output arg
592-
Fatalf("shouldn't be used")
626+
if funarg == FunargResults {
627+
n.Class = PPARAMOUT
628+
}
593629
}
594630

595631
n.Typecheck = 1
632+
n.Addrtaken = true // keep optimizers at bay
596633
return n
597634
}
598635

src/cmd/compile/internal/gc/plive.go

Lines changed: 48 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -197,62 +197,41 @@ func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool {
197197
return false
198198
}
199199

200-
// Collects and returns a slice of *Nodes for functions arguments and local
201-
// variables.
202-
func getvariables(fn *Node) []*Node {
203-
var result []*Node
204-
for _, ln := range fn.Func.Dcl {
205-
if ln.Op == ONAME {
206-
switch ln.Class {
207-
case PAUTO, PPARAM, PPARAMOUT, PFUNC, PAUTOHEAP:
208-
// ok
209-
default:
210-
Dump("BAD NODE", ln)
211-
Fatalf("getvariables")
212-
}
200+
// livenessShouldTrack reports whether the liveness analysis
201+
// should track the variable n.
202+
// We don't care about variables that have no pointers,
203+
// nor do we care about non-local variables,
204+
// nor do we care about empty structs (handled by the pointer check),
205+
// nor do we care about the fake PAUTOHEAP variables.
206+
func livenessShouldTrack(n *Node) bool {
207+
return n.Op == ONAME && (n.Class == PAUTO || n.Class == PPARAM || n.Class == PPARAMOUT) && haspointers(n.Type)
208+
}
213209

214-
// In order for GODEBUG=gcdead=1 to work, each bitmap needs
215-
// to contain information about all variables covered by the bitmap.
216-
// For local variables, the bitmap only covers the stkptrsize
217-
// bytes in the frame where variables containing pointers live.
218-
// For arguments and results, the bitmap covers all variables,
219-
// so we must include all the variables, even the ones without
220-
// pointers.
221-
//
210+
// getvariables returns the list of on-stack variables that we need to track.
211+
func getvariables(fn *Node) []*Node {
212+
var vars []*Node
213+
for _, n := range fn.Func.Dcl {
214+
if n.Op == ONAME {
222215
// The Node.opt field is available for use by optimization passes.
223-
// We use it to hold the index of the node in the variables array, plus 1
224-
// (so that 0 means the Node is not in the variables array).
225-
// Each pass should clear opt when done, but you never know,
226-
// so clear them all ourselves too.
216+
// We use it to hold the index of the node in the variables array
217+
// (nil means the Node is not in the variables array).
227218
// The Node.curfn field is supposed to be set to the current function
228219
// already, but for some compiler-introduced names it seems not to be,
229220
// so fix that here.
230221
// Later, when we want to find the index of a node in the variables list,
231-
// we will check that n.curfn == curfn and n.opt > 0. Then n.opt - 1
222+
// we will check that n.Curfn == Curfn and n.Opt() != nil. Then n.Opt().(int32)
232223
// is the index in the variables list.
233-
ln.SetOpt(nil)
234-
235-
// The compiler doesn't emit initializations for zero-width parameters or results.
236-
if ln.Type.Width == 0 {
237-
continue
238-
}
239-
240-
ln.Name.Curfn = Curfn
241-
switch ln.Class {
242-
case PAUTO:
243-
if haspointers(ln.Type) {
244-
ln.SetOpt(int32(len(result)))
245-
result = append(result, ln)
246-
}
224+
n.SetOpt(nil)
225+
n.Name.Curfn = Curfn
226+
}
247227

248-
case PPARAM, PPARAMOUT:
249-
ln.SetOpt(int32(len(result)))
250-
result = append(result, ln)
251-
}
228+
if livenessShouldTrack(n) {
229+
n.SetOpt(int32(len(vars)))
230+
vars = append(vars, n)
252231
}
253232
}
254233

255-
return result
234+
return vars
256235
}
257236

258237
// A pretty printer for control flow graphs. Takes a slice of *BasicBlocks.
@@ -617,17 +596,9 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
617596

618597
if prog.Info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 {
619598
from := &prog.From
620-
if from.Node != nil && from.Sym != nil && ((from.Node).(*Node)).Name.Curfn == Curfn {
621-
switch ((from.Node).(*Node)).Class {
622-
case PAUTO, PPARAM, PPARAMOUT:
623-
n := from.Node.(*Node).Orig // orig needed for certain nodarg results
624-
pos, ok := n.Opt().(int32) // index in vars
625-
if !ok {
626-
break
627-
}
628-
if pos >= int32(len(vars)) || vars[pos] != n {
629-
Fatalf("bad bookkeeping in liveness %v %d", Nconv(n, 0), pos)
630-
}
599+
if from.Node != nil && from.Sym != nil {
600+
n := from.Node.(*Node)
601+
if pos := liveIndex(n, vars); pos >= 0 {
631602
if n.Addrtaken {
632603
bvset(avarinit, pos)
633604
} else {
@@ -646,17 +617,9 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
646617

647618
if prog.Info.Flags&(RightRead|RightWrite|RightAddr) != 0 {
648619
to := &prog.To
649-
if to.Node != nil && to.Sym != nil && ((to.Node).(*Node)).Name.Curfn == Curfn {
650-
switch ((to.Node).(*Node)).Class {
651-
case PAUTO, PPARAM, PPARAMOUT:
652-
n := to.Node.(*Node).Orig // orig needed for certain nodarg results
653-
pos, ok := n.Opt().(int32) // index in vars
654-
if !ok {
655-
return
656-
}
657-
if pos >= int32(len(vars)) || vars[pos] != n {
658-
Fatalf("bad bookkeeping in liveness %v %d", Nconv(n, 0), pos)
659-
}
620+
if to.Node != nil && to.Sym != nil {
621+
n := to.Node.(*Node)
622+
if pos := liveIndex(n, vars); pos >= 0 {
660623
if n.Addrtaken {
661624
if prog.As != obj.AVARKILL {
662625
bvset(avarinit, pos)
@@ -687,6 +650,24 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
687650
}
688651
}
689652

653+
// liveIndex returns the index of n in the set of tracked vars.
654+
// If n is not a tracked var, liveIndex returns -1.
655+
// If n is not a tracked var but should be tracked, liveIndex crashes.
656+
func liveIndex(n *Node, vars []*Node) int32 {
657+
if n.Name.Curfn != Curfn || !livenessShouldTrack(n) {
658+
return -1
659+
}
660+
661+
pos, ok := n.Opt().(int32) // index in vars
662+
if !ok {
663+
Fatalf("lost track of variable in liveness: %v (%p, %p)", n, n, n.Orig)
664+
}
665+
if pos >= int32(len(vars)) || vars[pos] != n {
666+
Fatalf("bad bookkeeping in liveness: %v (%p, %p)", n, n, n.Orig)
667+
}
668+
return pos
669+
}
670+
690671
// Constructs a new liveness structure used to hold the global state of the
691672
// liveness computation. The cfg argument is a slice of *BasicBlocks and the
692673
// vars argument is a slice of *Nodes.

src/cmd/compile/internal/gc/type.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,20 @@ type StructType struct {
223223
// Map links such structs back to their map type.
224224
Map *Type
225225

226-
Funarg bool // whether this struct represents function parameters
227-
Haspointers uint8 // 0 unknown, 1 no, 2 yes
226+
Funarg Funarg // type of function arguments for arg struct
227+
Haspointers uint8 // 0 unknown, 1 no, 2 yes
228228
}
229229

230+
// Fnstruct records the kind of function argument
231+
type Funarg uint8
232+
233+
const (
234+
FunargNone Funarg = iota
235+
FunargRcvr // receiver
236+
FunargParams // input parameters
237+
FunargResults // output results
238+
)
239+
230240
// StructType returns t's extra struct-specific fields.
231241
func (t *Type) StructType() *StructType {
232242
t.wantEtype(TSTRUCT)
@@ -287,7 +297,7 @@ type SliceType struct {
287297
type Field struct {
288298
Nointerface bool
289299
Embedded uint8 // embedded field
290-
Funarg bool
300+
Funarg Funarg
291301
Broke bool // broken field definition
292302
Isddd bool // field is ... argument
293303

@@ -786,7 +796,7 @@ func (t *Type) SetNname(n *Node) {
786796

787797
// IsFuncArgStruct reports whether t is a struct representing function parameters.
788798
func (t *Type) IsFuncArgStruct() bool {
789-
return t.Etype == TSTRUCT && t.Extra.(*StructType).Funarg
799+
return t.Etype == TSTRUCT && t.Extra.(*StructType).Funarg != FunargNone
790800
}
791801

792802
func (t *Type) Methods() *Fields {

0 commit comments

Comments
 (0)