argumentFunc(fn, e.tagHole(ks, fn, param), &args[i])
}
+ case ir.OINLCALL:
+ call := call.(*ir.InlinedCallExpr)
+ e.stmts(call.Body)
+ for i, result := range call.ReturnVars {
+ k := e.discardHole()
+ if ks != nil {
+ k = ks[i]
+ }
+ e.expr(k, result)
+ }
+
case ir.OAPPEND:
call := call.(*ir.CallExpr)
args := call.Args
n := n.(*ir.UnaryExpr)
e.discard(n.X)
- case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVER, ir.OUNSAFEADD, ir.OUNSAFESLICE:
+ case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OINLCALL, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVER, ir.OUNSAFEADD, ir.OUNSAFESLICE:
e.call([]hole{k}, n)
case ir.ONEW:
dsts[i] = res.Nname.(*ir.Name)
}
e.assignList(dsts, n.Results, "return", n)
- case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OINLCALL, 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)
ir.CurFunc = savefn
}
-// Turn an OINLCALL into a statement.
-func inlconv2stmt(inlcall *ir.InlinedCallExpr) ir.Node {
- n := ir.NewBlockStmt(inlcall.Pos(), nil)
- n.List = inlcall.Init()
- n.List.Append(inlcall.Body.Take()...)
- return n
-}
-
-// Turn an OINLCALL into a single valued expression.
-// The result of inlconv2expr MUST be assigned back to n, e.g.
-// n.Left = inlconv2expr(n.Left)
-func inlconv2expr(n *ir.InlinedCallExpr) ir.Node {
- r := n.ReturnVars[0]
- return ir.InitExpr(append(n.Init(), n.Body...), r)
-}
-
-// Turn the rlist (with the return values) of the OINLCALL in
-// n into an expression list lumping the ninit and body
-// containing the inlined statements on the first list element so
-// order will be preserved. Used in return, oas2func and call
-// statements.
-func inlconv2list(n *ir.InlinedCallExpr) []ir.Node {
- if n.Op() != ir.OINLCALL || len(n.ReturnVars) == 0 {
- base.Fatalf("inlconv2list %+v\n", n)
- }
-
- s := n.ReturnVars
- s[0] = ir.InitExpr(append(n.Init(), n.Body...), s[0])
- return s
-}
-
// inlnode recurses over the tree to find inlineable calls, which will
// be turned into OINLCALLs by mkinlcall. When the recursion comes
// back up will examine left, right, list, rlist, ninit, ntest, nincr,
ir.EditChildren(n, edit)
- if as := n; as.Op() == ir.OAS2FUNC {
- as := as.(*ir.AssignListStmt)
- if as.Rhs[0].Op() == ir.OINLCALL {
- as.Rhs = inlconv2list(as.Rhs[0].(*ir.InlinedCallExpr))
- as.SetOp(ir.OAS2)
- as.SetTypecheck(0)
- n = typecheck.Stmt(as)
- }
- }
-
// with all the branches out of the way, it is now time to
// transmogrify this node itself unless inhibited by the
// switch at the top of this function.
switch n.Op() {
case ir.OCALLMETH:
base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
- case ir.OCALLFUNC:
- n := n.(*ir.CallExpr)
- if n.NoInline {
- return n
- }
- }
- var call *ir.CallExpr
- switch n.Op() {
case ir.OCALLFUNC:
- call = n.(*ir.CallExpr)
+ call := n.(*ir.CallExpr)
+ if call.NoInline {
+ break
+ }
if base.Flag.LowerM > 3 {
fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.X)
}
if fn := inlCallee(call.X); fn != nil && fn.Inl != nil {
n = mkinlcall(call, fn, maxCost, inlMap, edit)
}
- case ir.OCALLMETH:
- base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
}
base.Pos = lno
- if n.Op() == ir.OINLCALL {
- ic := n.(*ir.InlinedCallExpr)
- switch call.Use {
- default:
- ir.Dump("call", call)
- base.Fatalf("call missing use")
- case ir.CallUseExpr:
- n = inlconv2expr(ic)
- case ir.CallUseStmt:
- n = inlconv2stmt(ic)
- case ir.CallUseList:
- // leave for caller to convert
- }
- }
-
return n
}
return res
}
+// CalleeEffects appends any side effects from evaluating callee to init.
+func CalleeEffects(init *ir.Nodes, callee ir.Node) {
+ for {
+ switch callee.Op() {
+ case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR:
+ return // done
+
+ case ir.OCONVNOP:
+ conv := callee.(*ir.ConvExpr)
+ init.Append(ir.TakeInit(conv)...)
+ callee = conv.X
+
+ case ir.OINLCALL:
+ ic := callee.(*ir.InlinedCallExpr)
+ init.Append(ir.TakeInit(ic)...)
+ init.Append(ic.Body.Take()...)
+ callee = ic.SingleResult()
+
+ default:
+ base.FatalfAt(callee.Pos(), "unexpected callee expression: %v", callee)
+ }
+ }
+}
+
// oldInline creates an InlinedCallExpr to replace the given call
// expression. fn is the callee function to be inlined. inlIndex is
// the inlining tree position index, for use with src.NewInliningBase
ninit := call.Init()
// For normal function calls, the function callee expression
- // may contain side effects (e.g., added by addinit during
- // inlconv2expr or inlconv2list). Make sure to preserve these,
+ // may contain side effects. Make sure to preserve these,
// if necessary (#42703).
if call.Op() == ir.OCALLFUNC {
- callee := call.X
- for callee.Op() == ir.OCONVNOP {
- conv := callee.(*ir.ConvExpr)
- ninit.Append(ir.TakeInit(conv)...)
- callee = conv.X
- }
- if callee.Op() != ir.ONAME && callee.Op() != ir.OCLOSURE && callee.Op() != ir.OMETHEXPR {
- base.Fatalf("unexpected callee expression: %v", callee)
- }
+ CalleeEffects(&ninit, call.X)
}
// Make temp names to use instead of the originals.
n := typecheck.NewName(var_.Sym())
n.SetType(var_.Type())
+ n.SetTypecheck(1)
n.Class = ir.PAUTO
n.SetUsed(true)
n.SetAutoTemp(var_.AutoTemp())
func retvar(t *types.Field, i int) *ir.Name {
n := typecheck.NewName(typecheck.LookupNum("~R", i))
n.SetType(t.Type)
+ n.SetTypecheck(1)
n.Class = ir.PAUTO
n.SetUsed(true)
n.Curfn = ir.CurFunc // the calling function, not the called one
type InlinedCallExpr struct {
miniExpr
Body Nodes
- ReturnVars Nodes
+ ReturnVars Nodes // must be side-effect free
}
func NewInlinedCallExpr(pos src.XPos, body, retvars []Node) *InlinedCallExpr {
return n
}
+func (n *InlinedCallExpr) SingleResult() Node {
+ if have := len(n.ReturnVars); have != 1 {
+ base.FatalfAt(n.Pos(), "inlined call has %v results, expected 1", have)
+ }
+ return n.ReturnVars[0]
+}
+
// A LogicalExpr is a expression X Op Y where Op is && or ||.
// It is separate from BinaryExpr to make room for statements
// that must be executed before Y but after X.
continue
}
+ if n.Op() == OINLCALL {
+ n = n.(*InlinedCallExpr).SingleResult()
+ continue
+ }
+
n1 := staticValue1(n)
if n1 == nil {
return n
}
fmt.Fprintf(s, "(%.v)", n.Args)
+ case OINLCALL:
+ n := n.(*InlinedCallExpr)
+ // TODO(mdempsky): Print Init and/or Body?
+ if len(n.ReturnVars) == 1 {
+ fmt.Fprintf(s, "%v", n.ReturnVars[0])
+ return
+ }
+ fmt.Fprintf(s, "(.%v)", n.ReturnVars)
+
case OMAKEMAP, OMAKECHAN, OMAKESLICE:
n := n.(*MakeExpr)
if n.Cap != nil {
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~R0 = \u0026y.b (assign-pair)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 = ~R0:"},`+
- `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return (*int)(~R0) (return)"}]}`)
+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return ~R0 (return)"}]}`)
})
}
"cmd/compile/internal/base"
"cmd/compile/internal/deadcode"
"cmd/compile/internal/dwarfgen"
+ "cmd/compile/internal/inline"
"cmd/compile/internal/ir"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/typecheck"
init := ir.TakeInit(call)
// For normal function calls, the function callee expression
- // may contain side effects (e.g., added by addinit during
- // inlconv2expr or inlconv2list). Make sure to preserve these,
+ // may contain side effects. Make sure to preserve these,
// if necessary (#42703).
if call.Op() == ir.OCALLFUNC {
- callee := call.X
- for callee.Op() == ir.OCONVNOP {
- conv := callee.(*ir.ConvExpr)
- init.Append(ir.TakeInit(conv)...)
- callee = conv.X
- }
-
- switch callee.Op() {
- case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR:
- // ok
- default:
- base.Fatalf("unexpected callee expression: %v", callee)
- }
+ inline.CalleeEffects(&init, call.X)
}
var args ir.Nodes
n := ir.NewNameAt(pos, s)
s.Def = n
n.SetType(t)
+ n.SetTypecheck(1)
n.Class = ir.PAUTO
n.SetEsc(ir.EscNever)
n.Curfn = curfn
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,
o.out = append(o.out, n)
o.cleanTemp(t)
+ 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()
}
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)