| // Copyright 2018 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. |
| |
| package escape |
| |
| import ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "fmt" |
| ) |
| |
| // stmt evaluates a single Go statement. |
| func (e *escape) stmt(n ir.Node) { |
| if n == nil { |
| return |
| } |
| |
| lno := ir.SetPos(n) |
| defer func() { |
| base.Pos = lno |
| }() |
| |
| if base.Flag.LowerM > 2 { |
| fmt.Printf("%v:[%d] %v stmt: %v\n", base.FmtPos(base.Pos), e.loopDepth, e.curfn, n) |
| } |
| |
| e.stmts(n.Init()) |
| |
| switch n.Op() { |
| default: |
| base.Fatalf("unexpected stmt: %v", n) |
| |
| case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL, ir.OINLMARK: |
| // nop |
| |
| case ir.OBREAK, ir.OCONTINUE, ir.OGOTO: |
| // TODO(mdempsky): Handle dead code? |
| |
| case ir.OBLOCK: |
| n := n.(*ir.BlockStmt) |
| e.stmts(n.List) |
| |
| case ir.ODCL: |
| // Record loop depth at declaration. |
| n := n.(*ir.Decl) |
| if !ir.IsBlank(n.X) { |
| e.dcl(n.X) |
| } |
| |
| case ir.OLABEL: |
| n := n.(*ir.LabelStmt) |
| if n.Label.IsBlank() { |
| break |
| } |
| switch e.labels[n.Label] { |
| case nonlooping: |
| if base.Flag.LowerM > 2 { |
| fmt.Printf("%v:%v non-looping label\n", base.FmtPos(base.Pos), n) |
| } |
| case looping: |
| if base.Flag.LowerM > 2 { |
| fmt.Printf("%v: %v looping label\n", base.FmtPos(base.Pos), n) |
| } |
| e.loopDepth++ |
| default: |
| base.Fatalf("label %v missing tag", n.Label) |
| } |
| delete(e.labels, n.Label) |
| |
| case ir.OIF: |
| n := n.(*ir.IfStmt) |
| e.discard(n.Cond) |
| e.block(n.Body) |
| e.block(n.Else) |
| |
| case ir.OCHECKNIL: |
| n := n.(*ir.UnaryExpr) |
| e.discard(n.X) |
| |
| case ir.OFOR: |
| n := n.(*ir.ForStmt) |
| base.Assert(!n.DistinctVars) // Should all be rewritten before escape analysis |
| e.loopDepth++ |
| e.discard(n.Cond) |
| e.stmt(n.Post) |
| e.block(n.Body) |
| e.loopDepth-- |
| |
| case ir.ORANGE: |
| // for Key, Value = range X { Body } |
| n := n.(*ir.RangeStmt) |
| base.Assert(!n.DistinctVars) // Should all be rewritten before escape analysis |
| |
| // X is evaluated outside the loop. |
| tmp := e.newLoc(nil, false) |
| e.expr(tmp.asHole(), n.X) |
| |
| e.loopDepth++ |
| ks := e.addrs([]ir.Node{n.Key, n.Value}) |
| if n.X.Type().IsArray() { |
| e.flow(ks[1].note(n, "range"), tmp) |
| } else { |
| e.flow(ks[1].deref(n, "range-deref"), tmp) |
| } |
| e.reassigned(ks, n) |
| |
| e.block(n.Body) |
| e.loopDepth-- |
| |
| case ir.OSWITCH: |
| n := n.(*ir.SwitchStmt) |
| |
| if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok { |
| var ks []hole |
| if guard.Tag != nil { |
| for _, cas := range n.Cases { |
| cv := cas.Var |
| k := e.dcl(cv) // type switch variables have no ODCL. |
| if cv.Type().HasPointers() { |
| ks = append(ks, k.dotType(cv.Type(), cas, "switch case")) |
| } |
| } |
| } |
| e.expr(e.teeHole(ks...), n.Tag.(*ir.TypeSwitchGuard).X) |
| } else { |
| e.discard(n.Tag) |
| } |
| |
| for _, cas := range n.Cases { |
| e.discards(cas.List) |
| e.block(cas.Body) |
| } |
| |
| case ir.OSELECT: |
| n := n.(*ir.SelectStmt) |
| for _, cas := range n.Cases { |
| e.stmt(cas.Comm) |
| e.block(cas.Body) |
| } |
| case ir.ORECV: |
| // TODO(mdempsky): Consider e.discard(n.Left). |
| n := n.(*ir.UnaryExpr) |
| e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit |
| case ir.OSEND: |
| n := n.(*ir.SendStmt) |
| e.discard(n.Chan) |
| e.assignHeap(n.Value, "send", n) |
| |
| case ir.OAS: |
| n := n.(*ir.AssignStmt) |
| e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n) |
| case ir.OASOP: |
| n := n.(*ir.AssignOpStmt) |
| // TODO(mdempsky): Worry about OLSH/ORSH? |
| e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n) |
| case ir.OAS2: |
| n := n.(*ir.AssignListStmt) |
| e.assignList(n.Lhs, n.Rhs, "assign-pair", n) |
| |
| case ir.OAS2DOTTYPE: // v, ok = x.(type) |
| n := n.(*ir.AssignListStmt) |
| e.assignList(n.Lhs, n.Rhs, "assign-pair-dot-type", n) |
| case ir.OAS2MAPR: // v, ok = m[k] |
| n := n.(*ir.AssignListStmt) |
| e.assignList(n.Lhs, n.Rhs, "assign-pair-mapr", n) |
| case ir.OAS2RECV, ir.OSELRECV2: // v, ok = <-ch |
| n := n.(*ir.AssignListStmt) |
| e.assignList(n.Lhs, n.Rhs, "assign-pair-receive", n) |
| |
| case ir.OAS2FUNC: |
| n := n.(*ir.AssignListStmt) |
| e.stmts(n.Rhs[0].Init()) |
| ks := e.addrs(n.Lhs) |
| e.call(ks, n.Rhs[0]) |
| e.reassigned(ks, n) |
| case ir.ORETURN: |
| n := n.(*ir.ReturnStmt) |
| results := e.curfn.Type().Results().FieldSlice() |
| dsts := make([]ir.Node, len(results)) |
| for i, res := range results { |
| dsts[i] = res.Nname.(*ir.Name) |
| } |
| e.assignList(dsts, n.Results, "return", n) |
| case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OINLCALL, ir.OCLEAR, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: |
| e.call(nil, n) |
| case ir.OGO, ir.ODEFER: |
| n := n.(*ir.GoDeferStmt) |
| e.goDeferStmt(n) |
| |
| case ir.OTAILCALL: |
| n := n.(*ir.TailCallStmt) |
| e.call(nil, n.Call) |
| } |
| } |
| |
| func (e *escape) stmts(l ir.Nodes) { |
| for _, n := range l { |
| e.stmt(n) |
| } |
| } |
| |
| // block is like stmts, but preserves loopDepth. |
| func (e *escape) block(l ir.Nodes) { |
| old := e.loopDepth |
| e.stmts(l) |
| e.loopDepth = old |
| } |
| |
| func (e *escape) dcl(n *ir.Name) hole { |
| if n.Curfn != e.curfn || n.IsClosureVar() { |
| base.Fatalf("bad declaration of %v", n) |
| } |
| loc := e.oldLoc(n) |
| loc.loopDepth = e.loopDepth |
| return loc.asHole() |
| } |