aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/walk/order.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/walk/order.go')
-rw-r--r--src/cmd/compile/internal/walk/order.go460
1 files changed, 66 insertions, 394 deletions
diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go
index b733d3a29f..6e336f565c 100644
--- a/src/cmd/compile/internal/walk/order.go
+++ b/src/cmd/compile/internal/walk/order.go
@@ -7,10 +7,8 @@ package walk
import (
"fmt"
"go/constant"
- "internal/buildcfg"
"cmd/compile/internal/base"
- "cmd/compile/internal/escape"
"cmd/compile/internal/ir"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/staticinit"
@@ -53,7 +51,7 @@ import (
type orderState struct {
out []ir.Node // list of generated statements
temp []*ir.Name // stack of temporary variables
- free map[string][]*ir.Name // free list of unused temporaries, by type.LongString().
+ free map[string][]*ir.Name // free list of unused temporaries, by type.LinkString().
edit func(ir.Node) ir.Node // cached closure of o.exprNoLHS
}
@@ -78,20 +76,14 @@ func (o *orderState) append(stmt ir.Node) {
// If clear is true, newTemp emits code to zero the temporary.
func (o *orderState) newTemp(t *types.Type, clear bool) *ir.Name {
var v *ir.Name
- // Note: LongString is close to the type equality we want,
- // but not exactly. We still need to double-check with types.Identical.
- key := t.LongString()
- a := o.free[key]
- for i, n := range a {
- if types.Identical(t, n.Type()) {
- v = a[i]
- a[i] = a[len(a)-1]
- a = a[:len(a)-1]
- o.free[key] = a
- break
+ key := t.LinkString()
+ if a := o.free[key]; len(a) > 0 {
+ v = a[len(a)-1]
+ if !types.Identical(t, v.Type()) {
+ base.Fatalf("expected %L to have type %v", v, t)
}
- }
- if v == nil {
+ o.free[key] = a[:len(a)-1]
+ } else {
v = typecheck.Temp(t)
}
if clear {
@@ -372,7 +364,7 @@ func (o *orderState) markTemp() ordermarker {
// which must have been returned by markTemp.
func (o *orderState) popTemp(mark ordermarker) {
for _, n := range o.temp[mark:] {
- key := n.Type().LongString()
+ key := n.Type().LinkString()
o.free[key] = append(o.free[key], n)
}
o.temp = o.temp[:mark]
@@ -514,15 +506,18 @@ func (o *orderState) init(n ir.Node) {
}
// call orders the call expression n.
-// n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
+// n.Op is OCALLFUNC/OCALLINTER or a builtin like OCOPY.
func (o *orderState) call(nn ir.Node) {
if len(nn.Init()) > 0 {
// Caller should have already called o.init(nn).
base.Fatalf("%v with unexpected ninit", nn.Op())
}
+ if nn.Op() == ir.OCALLMETH {
+ base.FatalfAt(nn.Pos(), "OCALLMETH missed by typecheck")
+ }
// Builtin functions.
- if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLMETH && nn.Op() != ir.OCALLINTER {
+ if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLINTER {
switch n := nn.(type) {
default:
base.Fatalf("unexpected call: %+v", n)
@@ -554,39 +549,6 @@ func (o *orderState) call(nn ir.Node) {
n.X = o.expr(n.X, nil)
o.exprList(n.Args)
-
- if n.Op() == ir.OCALLINTER {
- return
- }
- keepAlive := func(arg ir.Node) {
- // If the argument is really a pointer being converted to uintptr,
- // arrange for the pointer to be kept alive until the call returns,
- // by copying it into a temp and marking that temp
- // still alive when we pop the temp stack.
- if arg.Op() == ir.OCONVNOP {
- arg := arg.(*ir.ConvExpr)
- if arg.X.Type().IsUnsafePtr() {
- x := o.copyExpr(arg.X)
- arg.X = x
- x.SetAddrtaken(true) // ensure SSA keeps the x variable
- n.KeepAlive = append(n.KeepAlive, x)
- }
- }
- }
-
- // Check for "unsafe-uintptr" tag provided by escape analysis.
- for i, param := range n.X.Type().Params().FieldSlice() {
- if param.Note == escape.UnsafeUintptrNote || param.Note == escape.UintptrEscapesNote {
- if arg := n.Args[i]; arg.Op() == ir.OSLICELIT {
- arg := arg.(*ir.CompLitExpr)
- for _, elt := range arg.List {
- keepAlive(elt)
- }
- } else {
- keepAlive(arg)
- }
- }
- }
}
// mapAssign appends n to o.out.
@@ -693,9 +655,20 @@ func (o *orderState) stmt(n ir.Node) {
n := n.(*ir.AssignListStmt)
t := o.markTemp()
o.exprList(n.Lhs)
- o.init(n.Rhs[0])
- o.call(n.Rhs[0])
- o.as2func(n)
+ call := n.Rhs[0]
+ o.init(call)
+ if ic, ok := call.(*ir.InlinedCallExpr); ok {
+ o.stmtList(ic.Body)
+
+ n.SetOp(ir.OAS2)
+ n.Rhs = ic.ReturnVars
+
+ o.exprList(n.Rhs)
+ o.out = append(o.out, n)
+ } else {
+ o.call(call)
+ o.as2func(n)
+ }
o.cleanTemp(t)
// Special: use temporary variables to hold result,
@@ -713,6 +686,10 @@ func (o *orderState) stmt(n ir.Node) {
case ir.ODOTTYPE2:
r := r.(*ir.TypeAssertExpr)
r.X = o.expr(r.X, nil)
+ case ir.ODYNAMICDOTTYPE2:
+ r := r.(*ir.DynamicTypeAssertExpr)
+ r.X = o.expr(r.X, nil)
+ r.T = o.expr(r.T, nil)
case ir.ORECV:
r := r.(*ir.UnaryExpr)
r.X = o.expr(r.X, nil)
@@ -748,14 +725,25 @@ func (o *orderState) stmt(n ir.Node) {
o.out = append(o.out, n)
// Special: handle call arguments.
- case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH:
+ case ir.OCALLFUNC, ir.OCALLINTER:
n := n.(*ir.CallExpr)
t := o.markTemp()
o.call(n)
o.out = append(o.out, n)
o.cleanTemp(t)
- case ir.OCLOSE, ir.ORECV:
+ case ir.OINLCALL:
+ n := n.(*ir.InlinedCallExpr)
+ o.stmtList(n.Body)
+
+ // discard results; double-check for no side effects
+ for _, result := range n.ReturnVars {
+ if staticinit.AnySideEffects(result) {
+ base.FatalfAt(result.Pos(), "inlined call result has side effects: %v", result)
+ }
+ }
+
+ case ir.OCHECKNIL, ir.OCLOSE, ir.OPANIC, ir.ORECV:
n := n.(*ir.UnaryExpr)
t := o.markTemp()
n.X = o.expr(n.X, nil)
@@ -770,10 +758,10 @@ func (o *orderState) stmt(n ir.Node) {
o.out = append(o.out, n)
o.cleanTemp(t)
- case ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
+ case ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP:
n := n.(*ir.CallExpr)
t := o.markTemp()
- o.exprList(n.Args)
+ o.call(n)
o.out = append(o.out, n)
o.cleanTemp(t)
@@ -783,16 +771,6 @@ func (o *orderState) stmt(n ir.Node) {
t := o.markTemp()
o.init(n.Call)
o.call(n.Call)
- if n.Call.Op() == ir.ORECOVER {
- // Special handling of "defer recover()". We need to evaluate the FP
- // argument before wrapping.
- var init ir.Nodes
- n.Call = walkRecover(n.Call.(*ir.CallExpr), &init)
- o.stmtList(init)
- }
- if buildcfg.Experiment.RegabiDefer {
- o.wrapGoDefer(n)
- }
o.out = append(o.out, n)
o.cleanTemp(t)
@@ -830,16 +808,6 @@ func (o *orderState) stmt(n ir.Node) {
orderBlock(&n.Else, o.free)
o.out = append(o.out, n)
- case ir.OPANIC:
- n := n.(*ir.UnaryExpr)
- t := o.markTemp()
- n.X = o.expr(n.X, nil)
- if !n.X.Type().IsEmptyInterface() {
- base.FatalfAt(n.Pos(), "bad argument to panic: %L", n.X)
- }
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
case ir.ORANGE:
// n.Right is the expression being ranged over.
// order it, and then make a copy if we need one.
@@ -1192,23 +1160,26 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
// concrete type (not interface) argument might need an addressable
// temporary to pass to the runtime conversion routine.
- case ir.OCONVIFACE:
+ case ir.OCONVIFACE, ir.OCONVIDATA:
n := n.(*ir.ConvExpr)
n.X = o.expr(n.X, nil)
if n.X.Type().IsInterface() {
return n
}
- if _, _, needsaddr := convFuncName(n.X.Type(), n.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
+ if _, _, needsaddr := dataWordFuncName(n.X.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
// Need a temp if we need to pass the address to the conversion function.
// We also process static composite literal node here, making a named static global
- // whose address we can put directly in an interface (see OCONVIFACE case in walk).
+ // whose address we can put directly in an interface (see OCONVIFACE/OCONVIDATA case in walk).
n.X = o.addrTemp(n.X)
}
return n
case ir.OCONVNOP:
n := n.(*ir.ConvExpr)
- if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER || n.X.Op() == ir.OCALLMETH) {
+ if n.X.Op() == ir.OCALLMETH {
+ base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
+ }
+ if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER) {
call := n.X.(*ir.CallExpr)
// When reordering unsafe.Pointer(f()) into a separate
// statement, the conversion and function call must stay
@@ -1261,9 +1232,12 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
o.out = append(o.out, nif)
return r
+ case ir.OCALLMETH:
+ base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+ panic("unreachable")
+
case ir.OCALLFUNC,
ir.OCALLINTER,
- ir.OCALLMETH,
ir.OCAP,
ir.OCOMPLEX,
ir.OCOPY,
@@ -1275,7 +1249,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
ir.OMAKESLICECOPY,
ir.ONEW,
ir.OREAL,
- ir.ORECOVER,
+ ir.ORECOVERFP,
ir.OSTR2BYTES,
ir.OSTR2BYTESTMP,
ir.OSTR2RUNES:
@@ -1293,6 +1267,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
}
return n
+ case ir.OINLCALL:
+ n := n.(*ir.InlinedCallExpr)
+ o.stmtList(n.Body)
+ return n.SingleResult()
+
case ir.OAPPEND:
// Check for append(x, make([]T, y)...) .
n := n.(*ir.CallExpr)
@@ -1327,11 +1306,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
}
return n
- case ir.OCALLPART:
+ case ir.OMETHVALUE:
n := n.(*ir.SelectorExpr)
n.X = o.expr(n.X, nil)
if n.Transient() {
- t := typecheck.PartialCallType(n)
+ t := typecheck.MethodValueType(n)
n.Prealloc = o.newTemp(t, false)
}
return n
@@ -1498,313 +1477,6 @@ func (o *orderState) as2ok(n *ir.AssignListStmt) {
o.stmt(typecheck.Stmt(as))
}
-var wrapGoDefer_prgen int
-
-// wrapGoDefer wraps the target of a "go" or "defer" statement with a
-// new "function with no arguments" closure. Specifically, it converts
-//
-// defer f(x, y)
-//
-// to
-//
-// x1, y1 := x, y
-// defer func() { f(x1, y1) }()
-//
-// This is primarily to enable a quicker bringup of defers under the
-// new register ABI; by doing this conversion, we can simplify the
-// code in the runtime that invokes defers on the panic path.
-func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
- call := n.Call
-
- var callX ir.Node // thing being called
- var callArgs []ir.Node // call arguments
- var keepAlive []*ir.Name // KeepAlive list from call, if present
-
- // A helper to recreate the call within the closure.
- var mkNewCall func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node
-
- // Defer calls come in many shapes and sizes; not all of them
- // are ir.CallExpr's. Examine the type to see what we're dealing with.
- switch x := call.(type) {
- case *ir.CallExpr:
- callX = x.X
- callArgs = x.Args
- keepAlive = x.KeepAlive
- mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
- newcall := ir.NewCallExpr(pos, op, fun, args)
- newcall.IsDDD = x.IsDDD
- return ir.Node(newcall)
- }
- case *ir.UnaryExpr: // ex: OCLOSE
- callArgs = []ir.Node{x.X}
- mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
- if len(args) != 1 {
- panic("internal error, expecting single arg")
- }
- return ir.Node(ir.NewUnaryExpr(pos, op, args[0]))
- }
- case *ir.BinaryExpr: // ex: OCOPY
- callArgs = []ir.Node{x.X, x.Y}
- mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
- if len(args) != 2 {
- panic("internal error, expecting two args")
- }
- return ir.Node(ir.NewBinaryExpr(pos, op, args[0], args[1]))
- }
- default:
- panic("unhandled op")
- }
-
- // No need to wrap if called func has no args, no receiver, and no results.
- // However in the case of "defer func() { ... }()" we need to
- // protect against the possibility of directClosureCall rewriting
- // things so that the call does have arguments.
- //
- // Do wrap method calls (OCALLMETH, OCALLINTER), because it has
- // a receiver.
- //
- // Also do wrap builtin functions, because they may be expanded to
- // calls with arguments (e.g. ORECOVER).
- //
- // TODO: maybe not wrap if the called function has no arguments and
- // only in-register results?
- if len(callArgs) == 0 && call.Op() == ir.OCALLFUNC && callX.Type().NumResults() == 0 {
- if c, ok := call.(*ir.CallExpr); ok && callX != nil && callX.Op() == ir.OCLOSURE {
- cloFunc := callX.(*ir.ClosureExpr).Func
- cloFunc.SetClosureCalled(false)
- c.PreserveClosure = true
- }
- return
- }
-
- if c, ok := call.(*ir.CallExpr); ok {
- // To simplify things, turn f(a, b, []T{c, d, e}...) back
- // into f(a, b, c, d, e) -- when the final call is run through the
- // type checker below, it will rebuild the proper slice literal.
- undoVariadic(c)
- callX = c.X
- callArgs = c.Args
- }
-
- // This is set to true if the closure we're generating escapes
- // (needs heap allocation).
- cloEscapes := func() bool {
- if n.Op() == ir.OGO {
- // For "go", assume that all closures escape.
- return true
- }
- // For defer, just use whatever result escape analysis
- // has determined for the defer.
- return n.Esc() != ir.EscNever
- }()
-
- // A helper for making a copy of an argument. Note that it is
- // not safe to use o.copyExpr(arg) if we're putting a
- // reference to the temp into the closure (as opposed to
- // copying it in by value), since in the by-reference case we
- // need a temporary whose lifetime extends to the end of the
- // function (as opposed to being local to the current block or
- // statement being ordered).
- mkArgCopy := func(arg ir.Node) *ir.Name {
- t := arg.Type()
- byval := t.Size() <= 128 || cloEscapes
- var argCopy *ir.Name
- if byval {
- argCopy = o.copyExpr(arg)
- } else {
- argCopy = typecheck.Temp(t)
- o.append(ir.NewAssignStmt(base.Pos, argCopy, arg))
- }
- // The value of 128 below is meant to be consistent with code
- // in escape analysis that picks byval/byaddr based on size.
- argCopy.SetByval(byval)
- return argCopy
- }
-
- // getUnsafeArg looks for an unsafe.Pointer arg that has been
- // previously captured into the call's keepalive list, returning
- // the name node for it if found.
- getUnsafeArg := func(arg ir.Node) *ir.Name {
- // Look for uintptr(unsafe.Pointer(name))
- if arg.Op() != ir.OCONVNOP {
- return nil
- }
- if !arg.Type().IsUintptr() {
- return nil
- }
- if !arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
- return nil
- }
- arg = arg.(*ir.ConvExpr).X
- argname, ok := arg.(*ir.Name)
- if !ok {
- return nil
- }
- for i := range keepAlive {
- if argname == keepAlive[i] {
- return argname
- }
- }
- return nil
- }
-
- // Copy the arguments to the function into temps.
- //
- // For calls with uintptr(unsafe.Pointer(...)) args that are being
- // kept alive (see code in (*orderState).call that does this), use
- // the existing arg copy instead of creating a new copy.
- unsafeArgs := make([]*ir.Name, len(callArgs))
- origArgs := callArgs
- var newNames []*ir.Name
- for i := range callArgs {
- arg := callArgs[i]
- var argname *ir.Name
- unsafeArgName := getUnsafeArg(arg)
- if unsafeArgName != nil {
- // arg has been copied already, use keepalive copy
- argname = unsafeArgName
- unsafeArgs[i] = unsafeArgName
- } else {
- argname = mkArgCopy(arg)
- }
- newNames = append(newNames, argname)
- }
-
- // Deal with cases where the function expression (what we're
- // calling) is not a simple function symbol.
- var fnExpr *ir.Name
- var methSelectorExpr *ir.SelectorExpr
- if callX != nil {
- switch {
- case callX.Op() == ir.ODOTMETH || callX.Op() == ir.ODOTINTER:
- // Handle defer of a method call, e.g. "defer v.MyMethod(x, y)"
- n := callX.(*ir.SelectorExpr)
- n.X = mkArgCopy(n.X)
- methSelectorExpr = n
- if callX.Op() == ir.ODOTINTER {
- // Currently for "defer i.M()" if i is nil it panics at the
- // point of defer statement, not when deferred function is called.
- // (I think there is an issue discussing what is the intended
- // behavior but I cannot find it.)
- // We need to do the nil check outside of the wrapper.
- tab := typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X))
- c := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, tab)
- c.SetTypecheck(1)
- o.append(c)
- }
- case !(callX.Op() == ir.ONAME && callX.(*ir.Name).Class == ir.PFUNC):
- // Deal with "defer returnsafunc()(x, y)" (for
- // example) by copying the callee expression.
- fnExpr = mkArgCopy(callX)
- if callX.Op() == ir.OCLOSURE {
- // For "defer func(...)", in addition to copying the
- // closure into a temp, mark it as no longer directly
- // called.
- callX.(*ir.ClosureExpr).Func.SetClosureCalled(false)
- }
- }
- }
-
- // Create a new no-argument function that we'll hand off to defer.
- var noFuncArgs []*ir.Field
- noargst := ir.NewFuncType(base.Pos, nil, noFuncArgs, nil)
- wrapGoDefer_prgen++
- outerfn := ir.CurFunc
- wrapname := fmt.Sprintf("%v·dwrap·%d", outerfn, wrapGoDefer_prgen)
- sym := types.LocalPkg.Lookup(wrapname)
- fn := typecheck.DeclFunc(sym, noargst)
- fn.SetIsHiddenClosure(true)
- fn.SetWrapper(true)
-
- // helper for capturing reference to a var declared in an outer scope.
- capName := func(pos src.XPos, fn *ir.Func, n *ir.Name) *ir.Name {
- t := n.Type()
- cv := ir.CaptureName(pos, fn, n)
- cv.SetType(t)
- return typecheck.Expr(cv).(*ir.Name)
- }
-
- // Call args (x1, y1) need to be captured as part of the newly
- // created closure.
- newCallArgs := []ir.Node{}
- for i := range newNames {
- var arg ir.Node
- arg = capName(callArgs[i].Pos(), fn, newNames[i])
- if unsafeArgs[i] != nil {
- arg = ir.NewConvExpr(arg.Pos(), origArgs[i].Op(), origArgs[i].Type(), arg)
- }
- newCallArgs = append(newCallArgs, arg)
- }
- // Also capture the function or method expression (if needed) into
- // the closure.
- if fnExpr != nil {
- callX = capName(callX.Pos(), fn, fnExpr)
- }
- if methSelectorExpr != nil {
- methSelectorExpr.X = capName(callX.Pos(), fn, methSelectorExpr.X.(*ir.Name))
- }
- ir.FinishCaptureNames(n.Pos(), outerfn, fn)
-
- // This flags a builtin as opposed to a regular call.
- irregular := (call.Op() != ir.OCALLFUNC &&
- call.Op() != ir.OCALLMETH &&
- call.Op() != ir.OCALLINTER)
-
- // Construct new function body: f(x1, y1)
- op := ir.OCALL
- if irregular {
- op = call.Op()
- }
- newcall := mkNewCall(call.Pos(), op, callX, newCallArgs)
-
- // Type-check the result.
- if !irregular {
- typecheck.Call(newcall.(*ir.CallExpr))
- } else {
- typecheck.Stmt(newcall)
- }
-
- // Finalize body, register function on the main decls list.
- fn.Body = []ir.Node{newcall}
- typecheck.FinishFuncBody()
- typecheck.Func(fn)
- typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
-
- // Create closure expr
- clo := ir.NewClosureExpr(n.Pos(), fn)
- fn.OClosure = clo
- clo.SetType(fn.Type())
-
- // Set escape properties for closure.
- if n.Op() == ir.OGO {
- // For "go", assume that the closure is going to escape
- // (with an exception for the runtime, which doesn't
- // permit heap-allocated closures).
- if base.Ctxt.Pkgpath != "runtime" {
- clo.SetEsc(ir.EscHeap)
- }
- } else {
- // For defer, just use whatever result escape analysis
- // has determined for the defer.
- if n.Esc() == ir.EscNever {
- clo.SetTransient(true)
- clo.SetEsc(ir.EscNone)
- }
- }
-
- // Create new top level call to closure over argless function.
- topcall := ir.NewCallExpr(n.Pos(), ir.OCALL, clo, []ir.Node{})
- typecheck.Call(topcall)
-
- // Tag the call to insure that directClosureCall doesn't undo our work.
- topcall.PreserveClosure = true
-
- fn.SetClosureCalled(false)
-
- // Finally, point the defer statement at the newly generated call.
- n.Call = topcall
-}
-
// isFuncPCIntrinsic returns whether n is a direct call of internal/abi.FuncPCABIxxx functions.
func isFuncPCIntrinsic(n *ir.CallExpr) bool {
if n.Op() != ir.OCALLFUNC || n.X.Op() != ir.ONAME {