type Escape struct {
allLocs []*EscLocation
+ labels map[*types.Sym]labelState // known labels
curfn ir.Node
ir.InspectList(fn.Body(), func(n ir.Node) bool {
switch n.Op() {
case ir.OLABEL:
- n.Sym().Label = nonlooping
+ if e.labels == nil {
+ e.labels = make(map[*types.Sym]labelState)
+ }
+ e.labels[n.Sym()] = nonlooping
case ir.OGOTO:
// If we visited the label before the goto,
// then this is a looping label.
- if n.Sym().Label == nonlooping {
- n.Sym().Label = looping
+ if e.labels[n.Sym()] == nonlooping {
+ e.labels[n.Sym()] = looping
}
}
e.curfn = fn
e.loopDepth = 1
e.block(fn.Body())
+
+ if len(e.labels) != 0 {
+ base.FatalfAt(fn.Pos(), "leftover labels after walkFunc")
+ }
}
// Below we implement the methods for walking the AST and recording
}
case ir.OLABEL:
- switch ir.AsNode(n.Sym().Label) {
+ switch e.labels[n.Sym()] {
case nonlooping:
if base.Flag.LowerM > 2 {
fmt.Printf("%v:%v non-looping label\n", base.FmtPos(base.Pos), n)
default:
base.Fatalf("label missing tag")
}
- n.Sym().Label = nil
+ delete(e.labels, n.Sym())
case ir.OIF:
e.discard(n.Left())
}
// Mark labels that have no backjumps to them as not increasing e.loopdepth.
-// Walk hasn't generated (goto|label).Left.Sym.Label yet, so we'll cheat
-// and set it to one of the following two. Then in esc we'll clear it again.
-var (
- looping = ir.Nod(ir.OXXX, nil, nil)
- nonlooping = ir.Nod(ir.OXXX, nil, nil)
+type labelState int
+
+const (
+ looping labelState = 1 + iota
+ nonlooping
)
func isSliceSelfAssign(dst, src ir.Node) bool {
// These nodes don't produce code; omit from inlining budget.
return false
- case ir.OLABEL:
- // TODO(mdempsky): Add support for inlining labeled control statements.
- if labeledControl(n) != nil {
+ case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH:
+ // ORANGE, OSELECT in "unhandled" above
+ if n.Sym() != nil {
v.reason = "labeled control"
return true
}
case ir.OBREAK, ir.OCONTINUE:
if n.Sym() != nil {
- // Should have short-circuited due to labeledControl above.
+ // Should have short-circuited due to labeled control error above.
base.Fatalf("unexpected labeled break/continue: %v", n)
}
}
func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) ir.Node {
- lhs := p.nodSym(label, ir.OLABEL, nil, p.name(label.Label))
+ sym := p.name(label.Label)
+ lhs := p.nodSym(label, ir.OLABEL, nil, sym)
var ls ir.Node
if label.Stmt != nil { // TODO(mdempsky): Should always be present.
ls = p.stmtFall(label.Stmt, fallOK)
+ switch label.Stmt.(type) {
+ case *syntax.ForStmt, *syntax.SwitchStmt, *syntax.SelectStmt:
+ // Attach label directly to control statement too.
+ ls.SetSym(sym)
+ }
}
- lhs.Name().Defn = ls
l := []ir.Node{lhs}
if ls != nil {
if ls.Op() == ir.OBLOCK && ls.Init().Len() == 0 {
// Allocate starting values
s.labels = map[string]*ssaLabel{}
- s.labeledNodes = map[ir.Node]*ssaLabel{}
s.fwdVars = map[ir.Node]*ssa.Value{}
s.startmem = s.entryNewValue0(ssa.OpInitMem, types.TypeMem)
// Node for function
curfn ir.Node
- // labels and labeled control flow nodes (OFOR, OFORUNTIL, OSWITCH, OSELECT) in f
- labels map[string]*ssaLabel
- labeledNodes map[ir.Node]*ssaLabel
+ // labels in f
+ labels map[string]*ssaLabel
// unlabeled break and continue statement tracking
breakTo *ssa.Block // current target for plain break statement
sym := n.Sym()
lab := s.label(sym)
- // Associate label with its control flow node, if any
- if ctl := labeledControl(n); ctl != nil {
- s.labeledNodes[ctl] = lab
- }
-
// The label might already have a target block via a goto.
if lab.target == nil {
lab.target = s.f.NewBlock(ssa.BlockPlain)
prevBreak := s.breakTo
s.continueTo = bIncr
s.breakTo = bEnd
- lab := s.labeledNodes[n]
- if lab != nil {
+ var lab *ssaLabel
+ if sym := n.Sym(); sym != nil {
// labeled for loop
+ lab = s.label(sym)
lab.continueTarget = bIncr
lab.breakTarget = bEnd
}
prevBreak := s.breakTo
s.breakTo = bEnd
- lab := s.labeledNodes[n]
- if lab != nil {
+ var lab *ssaLabel
+ if sym := n.Sym(); sym != nil {
// labeled
+ lab = s.label(sym)
lab.breakTarget = bEnd
}
return ptr, len
}
-// labeledControl returns the control flow Node (for, switch, select)
-// associated with the label n, if any.
-func labeledControl(n ir.Node) ir.Node {
- if n.Op() != ir.OLABEL {
- base.Fatalf("labeledControl %v", n.Op())
- }
- ctl := n.Name().Defn
- if ctl == nil {
- return nil
- }
- switch ctl.Op() {
- case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH, ir.OSELECT:
- return ctl
- }
- return nil
-}
-
func syslook(name string) ir.Node {
s := Runtimepkg.Lookup(name)
if s == nil || s.Def == nil {
return true
}
-func markbreak(n ir.Node, implicit ir.Node) {
+func markbreak(labels *map[*types.Sym]ir.Node, n ir.Node, implicit ir.Node) {
if n == nil {
return
}
implicit.SetHasBreak(true)
}
} else {
- lab := ir.AsNode(n.Sym().Label)
- if lab != nil {
+ if lab := (*labels)[n.Sym()]; lab != nil {
lab.SetHasBreak(true)
}
}
case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH, ir.OTYPESW, ir.OSELECT, ir.ORANGE:
implicit = n
+ if sym := n.Sym(); sym != nil {
+ if *labels == nil {
+ // Map creation delayed until we need it - most functions don't.
+ *labels = make(map[*types.Sym]ir.Node)
+ }
+ (*labels)[sym] = n
+ defer delete(*labels, sym)
+ }
fallthrough
default:
- markbreak(n.Left(), implicit)
- markbreak(n.Right(), implicit)
- markbreaklist(n.Init(), implicit)
- markbreaklist(n.Body(), implicit)
- markbreaklist(n.List(), implicit)
- markbreaklist(n.Rlist(), implicit)
+ markbreak(labels, n.Left(), implicit)
+ markbreak(labels, n.Right(), implicit)
+ markbreaklist(labels, n.Init(), implicit)
+ markbreaklist(labels, n.Body(), implicit)
+ markbreaklist(labels, n.List(), implicit)
+ markbreaklist(labels, n.Rlist(), implicit)
}
}
-func markbreaklist(l ir.Nodes, implicit ir.Node) {
+func markbreaklist(labels *map[*types.Sym]ir.Node, l ir.Nodes, implicit ir.Node) {
s := l.Slice()
for i := 0; i < len(s); i++ {
- n := s[i]
- if n == nil {
- continue
- }
- if n.Op() == ir.OLABEL && i+1 < len(s) && n.Name().Defn == s[i+1] {
- switch n.Name().Defn.Op() {
- case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH, ir.OTYPESW, ir.OSELECT, ir.ORANGE:
- n.Sym().Label = n.Name().Defn
- markbreak(n.Name().Defn, n.Name().Defn)
- n.Sym().Label = nil
- i++
- continue
- }
- }
-
- markbreak(n, implicit)
+ markbreak(labels, s[i], implicit)
}
}
// checkreturn makes sure that fn terminates appropriately.
func checkreturn(fn ir.Node) {
if fn.Type().NumResults() != 0 && fn.Body().Len() != 0 {
- markbreaklist(fn.Body(), nil)
+ var labels map[*types.Sym]ir.Node
+ markbreaklist(&labels, fn.Body(), nil)
if !isTermNodes(fn.Body()) {
base.ErrorfAt(fn.Func().Endlineno, "missing return at end of function")
}
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
- {Sym{}, 60, 104},
+ {Sym{}, 52, 88},
{Type{}, 56, 96},
{Map{}, 20, 40},
{Forward{}, 20, 32},
Name string // object name
// saved and restored by dcopy
- Def IRNode // definition: ONAME OTYPE OPACK or OLITERAL
+ Def IRNode // definition: ONAME OTYPE OPACK or OLITERAL
Block int32 // blocknumber to catch redeclaration
Lastlineno src.XPos // last declaration for diagnostic
flags bitset8
- Label IRNode // corresponding label (ephemeral)
- Origpkg *Pkg // original package for . import
+ Origpkg *Pkg // original package for . import
}
const (