aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/walk/walk.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/walk/walk.go')
-rw-r--r--src/cmd/compile/internal/walk/walk.go300
1 files changed, 76 insertions, 224 deletions
diff --git a/src/cmd/compile/internal/walk/walk.go b/src/cmd/compile/internal/walk/walk.go
index 4271772fb7..b47d96dc4c 100644
--- a/src/cmd/compile/internal/walk/walk.go
+++ b/src/cmd/compile/internal/walk/walk.go
@@ -7,7 +7,6 @@ package walk
import (
"errors"
"fmt"
- "strings"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
@@ -47,13 +46,6 @@ func Walk(fn *ir.Func) {
ir.DumpList(s, ir.CurFunc.Body)
}
- zeroResults()
- heapmoves()
- if base.Flag.W != 0 && len(ir.CurFunc.Enter) > 0 {
- s := fmt.Sprintf("enter %v", ir.CurFunc.Sym())
- ir.DumpList(s, ir.CurFunc.Enter)
- }
-
if base.Flag.Cfg.Instrumenting {
instrument(fn)
}
@@ -64,23 +56,6 @@ func Walk(fn *ir.Func) {
}
}
-func paramoutheap(fn *ir.Func) bool {
- for _, ln := range fn.Dcl {
- switch ln.Class {
- case ir.PPARAMOUT:
- if ir.IsParamStackCopy(ln) || ln.Addrtaken() {
- return true
- }
-
- case ir.PAUTO:
- // stop early - parameters are over
- return false
- }
- }
-
- return false
-}
-
// walkRecv walks an ORECV node.
func walkRecv(n *ir.UnaryExpr) ir.Node {
if n.Typecheck() == 0 {
@@ -97,8 +72,6 @@ func convas(n *ir.AssignStmt, init *ir.Nodes) *ir.AssignStmt {
if n.Op() != ir.OAS {
base.Fatalf("convas: not OAS %v", n.Op())
}
- defer updateHasCall(n)
-
n.SetTypecheck(1)
if n.X == nil || n.Y == nil {
@@ -127,93 +100,10 @@ func convas(n *ir.AssignStmt, init *ir.Nodes) *ir.AssignStmt {
var stop = errors.New("stop")
-// paramstoheap returns code to allocate memory for heap-escaped parameters
-// and to copy non-result parameters' values from the stack.
-func paramstoheap(params *types.Type) []ir.Node {
- var nn []ir.Node
- for _, t := range params.Fields().Slice() {
- v := ir.AsNode(t.Nname)
- if v != nil && v.Sym() != nil && strings.HasPrefix(v.Sym().Name, "~r") { // unnamed result
- v = nil
- }
- if v == nil {
- continue
- }
-
- if stackcopy := v.Name().Stackcopy; stackcopy != nil {
- nn = append(nn, walkStmt(ir.NewDecl(base.Pos, ir.ODCL, v.(*ir.Name))))
- if stackcopy.Class == ir.PPARAM {
- nn = append(nn, walkStmt(typecheck.Stmt(ir.NewAssignStmt(base.Pos, v, stackcopy))))
- }
- }
- }
-
- return nn
-}
-
-// zeroResults zeros the return values at the start of the function.
-// We need to do this very early in the function. Defer might stop a
-// panic and show the return values as they exist at the time of
-// panic. For precise stacks, the garbage collector assumes results
-// are always live, so we need to zero them before any allocations,
-// even allocations to move params/results to the heap.
-// The generated code is added to Curfn's Enter list.
-func zeroResults() {
- for _, f := range ir.CurFunc.Type().Results().Fields().Slice() {
- v := ir.AsNode(f.Nname)
- if v != nil && v.Name().Heapaddr != nil {
- // The local which points to the return value is the
- // thing that needs zeroing. This is already handled
- // by a Needzero annotation in plive.go:livenessepilogue.
- continue
- }
- if ir.IsParamHeapCopy(v) {
- // TODO(josharian/khr): Investigate whether we can switch to "continue" here,
- // and document more in either case.
- // In the review of CL 114797, Keith wrote (roughly):
- // I don't think the zeroing below matters.
- // The stack return value will never be marked as live anywhere in the function.
- // It is not written to until deferreturn returns.
- v = v.Name().Stackcopy
- }
- // Zero the stack location containing f.
- ir.CurFunc.Enter.Append(ir.NewAssignStmt(ir.CurFunc.Pos(), v, nil))
- }
-}
-
-// returnsfromheap returns code to copy values for heap-escaped parameters
-// back to the stack.
-func returnsfromheap(params *types.Type) []ir.Node {
- var nn []ir.Node
- for _, t := range params.Fields().Slice() {
- v := ir.AsNode(t.Nname)
- if v == nil {
- continue
- }
- if stackcopy := v.Name().Stackcopy; stackcopy != nil && stackcopy.Class == ir.PPARAMOUT {
- nn = append(nn, walkStmt(typecheck.Stmt(ir.NewAssignStmt(base.Pos, stackcopy, v))))
- }
- }
-
- return nn
-}
-
-// heapmoves generates code to handle migrating heap-escaped parameters
-// between the stack and the heap. The generated code is added to Curfn's
-// Enter and Exit lists.
-func heapmoves() {
- lno := base.Pos
- base.Pos = ir.CurFunc.Pos()
- nn := paramstoheap(ir.CurFunc.Type().Recvs())
- nn = append(nn, paramstoheap(ir.CurFunc.Type().Params())...)
- nn = append(nn, paramstoheap(ir.CurFunc.Type().Results())...)
- ir.CurFunc.Enter.Append(nn...)
- base.Pos = ir.CurFunc.Endlineno
- ir.CurFunc.Exit.Append(returnsfromheap(ir.CurFunc.Type().Results())...)
- base.Pos = lno
-}
-
func vmkcall(fn ir.Node, t *types.Type, init *ir.Nodes, va []ir.Node) *ir.CallExpr {
+ if init == nil {
+ base.Fatalf("mkcall with nil init: %v", fn)
+ }
if fn.Type() == nil || fn.Type().Kind() != types.TFUNC {
base.Fatalf("mkcall %v %v", fn, fn.Type())
}
@@ -233,10 +123,24 @@ func mkcall(name string, t *types.Type, init *ir.Nodes, args ...ir.Node) *ir.Cal
return vmkcall(typecheck.LookupRuntime(name), t, init, args)
}
+func mkcallstmt(name string, args ...ir.Node) ir.Node {
+ return mkcallstmt1(typecheck.LookupRuntime(name), args...)
+}
+
func mkcall1(fn ir.Node, t *types.Type, init *ir.Nodes, args ...ir.Node) *ir.CallExpr {
return vmkcall(fn, t, init, args)
}
+func mkcallstmt1(fn ir.Node, args ...ir.Node) ir.Node {
+ var init ir.Nodes
+ n := vmkcall(fn, nil, &init, args)
+ if len(init) == 0 {
+ return n
+ }
+ init.Append(n)
+ return ir.NewBlockStmt(n.Pos(), init)
+}
+
func chanfn(name string, n int, t *types.Type) ir.Node {
if !t.IsChan() {
base.Fatalf("chanfn %v", t)
@@ -324,7 +228,7 @@ func mapfast(t *types.Type) int {
func walkAppendArgs(n *ir.CallExpr, init *ir.Nodes) {
walkExprListSafe(n.Args, init)
- // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
+ // walkExprListSafe will leave OINDEX (s[n]) alone if both s
// and n are name or literal, but those may index the slice we're
// modifying here. Fix explicitly.
ls := n.Args
@@ -356,8 +260,8 @@ func appendWalkStmt(init *ir.Nodes, stmt ir.Node) {
op := stmt.Op()
n := typecheck.Stmt(stmt)
if op == ir.OAS || op == ir.OAS2 {
- // If the assignment has side effects, walkexpr will append them
- // directly to init for us, while walkstmt will wrap it in an OBLOCK.
+ // If the assignment has side effects, walkExpr will append them
+ // directly to init for us, while walkStmt will wrap it in an OBLOCK.
// We need to append them directly.
// TODO(rsc): Clean this up.
n = walkExpr(n, init)
@@ -372,7 +276,7 @@ func appendWalkStmt(init *ir.Nodes, stmt ir.Node) {
const maxOpenDefers = 8
// backingArrayPtrLen extracts the pointer and length from a slice or string.
-// This constructs two nodes referring to n, so n must be a cheapexpr.
+// This constructs two nodes referring to n, so n must be a cheapExpr.
func backingArrayPtrLen(n ir.Node) (ptr, length ir.Node) {
var init ir.Nodes
c := cheapExpr(n, &init)
@@ -390,123 +294,71 @@ func backingArrayPtrLen(n ir.Node) (ptr, length ir.Node) {
return ptr, length
}
-// updateHasCall checks whether expression n contains any function
-// calls and sets the n.HasCall flag if so.
-func updateHasCall(n ir.Node) {
- if n == nil {
- return
- }
- n.SetHasCall(calcHasCall(n))
-}
-
-func calcHasCall(n ir.Node) bool {
- if len(n.Init()) != 0 {
- // TODO(mdempsky): This seems overly conservative.
+// mayCall reports whether evaluating expression n may require
+// function calls, which could clobber function call arguments/results
+// currently on the stack.
+func mayCall(n ir.Node) bool {
+ // When instrumenting, any expression might require function calls.
+ if base.Flag.Cfg.Instrumenting {
return true
}
- switch n.Op() {
- default:
- base.Fatalf("calcHasCall %+v", n)
- panic("unreachable")
+ isSoftFloat := func(typ *types.Type) bool {
+ return types.IsFloat[typ.Kind()] || types.IsComplex[typ.Kind()]
+ }
- case ir.OLITERAL, ir.ONIL, ir.ONAME, ir.OTYPE, ir.ONAMEOFFSET:
- if n.HasCall() {
- base.Fatalf("OLITERAL/ONAME/OTYPE should never have calls: %+v", n)
- }
- return false
- case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
- return true
- case ir.OANDAND, ir.OOROR:
- // hard with instrumented code
- n := n.(*ir.LogicalExpr)
- if base.Flag.Cfg.Instrumenting {
- return true
+ return ir.Any(n, func(n ir.Node) bool {
+ // walk should have already moved any Init blocks off of
+ // expressions.
+ if len(n.Init()) != 0 {
+ base.FatalfAt(n.Pos(), "mayCall %+v", n)
}
- return n.X.HasCall() || n.Y.HasCall()
- case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR,
- ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD:
- // These ops might panic, make sure they are done
- // before we start marshaling args for a call. See issue 16760.
- return true
- // When using soft-float, these ops might be rewritten to function calls
- // so we ensure they are evaluated first.
- case ir.OADD, ir.OSUB, ir.OMUL:
- n := n.(*ir.BinaryExpr)
- if ssagen.Arch.SoftFloat && (types.IsFloat[n.Type().Kind()] || types.IsComplex[n.Type().Kind()]) {
- return true
- }
- return n.X.HasCall() || n.Y.HasCall()
- case ir.ONEG:
- n := n.(*ir.UnaryExpr)
- if ssagen.Arch.SoftFloat && (types.IsFloat[n.Type().Kind()] || types.IsComplex[n.Type().Kind()]) {
- return true
- }
- return n.X.HasCall()
- case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT:
- n := n.(*ir.BinaryExpr)
- if ssagen.Arch.SoftFloat && (types.IsFloat[n.X.Type().Kind()] || types.IsComplex[n.X.Type().Kind()]) {
+ switch n.Op() {
+ default:
+ base.FatalfAt(n.Pos(), "mayCall %+v", n)
+
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
return true
- }
- return n.X.HasCall() || n.Y.HasCall()
- case ir.OCONV:
- n := n.(*ir.ConvExpr)
- if ssagen.Arch.SoftFloat && ((types.IsFloat[n.Type().Kind()] || types.IsComplex[n.Type().Kind()]) || (types.IsFloat[n.X.Type().Kind()] || types.IsComplex[n.X.Type().Kind()])) {
+
+ case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR,
+ ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD:
+ // These ops might panic, make sure they are done
+ // before we start marshaling args for a call. See issue 16760.
return true
+
+ case ir.OANDAND, ir.OOROR:
+ n := n.(*ir.LogicalExpr)
+ // The RHS expression may have init statements that
+ // should only execute conditionally, and so cannot be
+ // pulled out to the top-level init list. We could try
+ // to be more precise here.
+ return len(n.Y.Init()) != 0
+
+ // When using soft-float, these ops might be rewritten to function calls
+ // so we ensure they are evaluated first.
+ case ir.OADD, ir.OSUB, ir.OMUL, ir.ONEG:
+ return ssagen.Arch.SoftFloat && isSoftFloat(n.Type())
+ case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT:
+ n := n.(*ir.BinaryExpr)
+ return ssagen.Arch.SoftFloat && isSoftFloat(n.X.Type())
+ case ir.OCONV:
+ n := n.(*ir.ConvExpr)
+ return ssagen.Arch.SoftFloat && (isSoftFloat(n.Type()) || isSoftFloat(n.X.Type()))
+
+ case ir.OLITERAL, ir.ONIL, ir.ONAME, ir.OLINKSYMOFFSET, ir.OMETHEXPR,
+ ir.OAND, ir.OANDNOT, ir.OLSH, ir.OOR, ir.ORSH, ir.OXOR, ir.OCOMPLEX, ir.OEFACE,
+ ir.OADDR, ir.OBITNOT, ir.ONOT, ir.OPLUS,
+ ir.OCAP, ir.OIMAG, ir.OLEN, ir.OREAL,
+ ir.OCONVNOP, ir.ODOT,
+ ir.OCFUNC, ir.OIDATA, ir.OITAB, ir.OSPTR,
+ ir.OBYTES2STRTMP, ir.OGETG, ir.OSLICEHEADER:
+ // ok: operations that don't require function calls.
+ // Expand as needed.
}
- return n.X.HasCall()
-
- case ir.OAND, ir.OANDNOT, ir.OLSH, ir.OOR, ir.ORSH, ir.OXOR, ir.OCOPY, ir.OCOMPLEX, ir.OEFACE:
- n := n.(*ir.BinaryExpr)
- return n.X.HasCall() || n.Y.HasCall()
-
- case ir.OAS:
- n := n.(*ir.AssignStmt)
- return n.X.HasCall() || n.Y != nil && n.Y.HasCall()
-
- case ir.OADDR:
- n := n.(*ir.AddrExpr)
- return n.X.HasCall()
- case ir.OPAREN:
- n := n.(*ir.ParenExpr)
- return n.X.HasCall()
- case ir.OBITNOT, ir.ONOT, ir.OPLUS, ir.ORECV,
- ir.OALIGNOF, ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.ONEW,
- ir.OOFFSETOF, ir.OPANIC, ir.OREAL, ir.OSIZEOF,
- ir.OCHECKNIL, ir.OCFUNC, ir.OIDATA, ir.OITAB, ir.ONEWOBJ, ir.OSPTR, ir.OVARDEF, ir.OVARKILL, ir.OVARLIVE:
- n := n.(*ir.UnaryExpr)
- return n.X.HasCall()
- case ir.ODOT, ir.ODOTMETH, ir.ODOTINTER:
- n := n.(*ir.SelectorExpr)
- return n.X.HasCall()
-
- case ir.OGETG, ir.OMETHEXPR:
- return false
- // TODO(rsc): These look wrong in various ways but are what calcHasCall has always done.
- case ir.OADDSTR:
- // TODO(rsc): This used to check left and right, which are not part of OADDSTR.
- return false
- case ir.OBLOCK:
- // TODO(rsc): Surely the block's statements matter.
return false
- case ir.OCONVIFACE, ir.OCONVNOP, ir.OBYTES2STR, ir.OBYTES2STRTMP, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2BYTESTMP, ir.OSTR2RUNES, ir.ORUNESTR:
- // TODO(rsc): Some conversions are themselves calls, no?
- n := n.(*ir.ConvExpr)
- return n.X.HasCall()
- case ir.ODOTTYPE2:
- // TODO(rsc): Shouldn't this be up with ODOTTYPE above?
- n := n.(*ir.TypeAssertExpr)
- return n.X.HasCall()
- case ir.OSLICEHEADER:
- // TODO(rsc): What about len and cap?
- n := n.(*ir.SliceHeaderExpr)
- return n.Ptr.HasCall()
- case ir.OAS2DOTTYPE, ir.OAS2FUNC:
- // TODO(rsc): Surely we need to check List and Rlist.
- return false
- }
+ })
}
// itabType loads the _type field from a runtime.itab struct.
@@ -539,7 +391,7 @@ func runtimeField(name string, offset int64, typ *types.Type) *types.Field {
// ifaceData loads the data field from an interface.
// The concrete type must be known to have type t.
-// It follows the pointer if !isdirectiface(t).
+// It follows the pointer if !IsDirectIface(t).
func ifaceData(pos src.XPos, n ir.Node, t *types.Type) ir.Node {
if t.IsInterface() {
base.Fatalf("ifaceData interface: %v", t)