switch n.Op() {
// Call is okay if inlinable and we have the budget for the body.
case ir.OCALLFUNC:
+ n := n.(*ir.CallExpr)
// Functions that call runtime.getcaller{pc,sp} can not be inlined
// because getcaller{pc,sp} expect a pointer to the caller's first argument.
//
// runtime.throw is a "cheap call" like panic in normal code.
- if n.Left().Op() == ir.ONAME && n.Left().Class() == ir.PFUNC && isRuntimePkg(n.Left().Sym().Pkg) {
- fn := n.Left().Sym().Name
- if fn == "getcallerpc" || fn == "getcallersp" {
- return errors.New("call to " + fn)
- }
- if fn == "throw" {
- v.budget -= inlineExtraThrowCost
- break
+ if n.Left().Op() == ir.ONAME {
+ name := n.Left().(*ir.Name)
+ if name.Class() == ir.PFUNC && isRuntimePkg(name.Sym().Pkg) {
+ fn := name.Sym().Name
+ if fn == "getcallerpc" || fn == "getcallersp" {
+ return errors.New("call to " + fn)
+ }
+ if fn == "throw" {
+ v.budget -= inlineExtraThrowCost
+ break
+ }
}
}
- if isIntrinsicCall(n.(*ir.CallExpr)) {
+ if isIntrinsicCall(n) {
// Treat like any other node.
break
}
// These nodes don't produce code; omit from inlining budget.
return nil
- case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH:
- // ORANGE, OSELECT in "unhandled" above
+ case ir.OFOR, ir.OFORUNTIL:
if n.Sym() != nil {
return errors.New("labeled control")
}
+ case ir.OSWITCH:
+ if n.Sym() != nil {
+ return errors.New("labeled control")
+ }
+ // case ir.ORANGE, ir.OSELECT in "unhandled" above
case ir.OBREAK, ir.OCONTINUE:
if n.Sym() != nil {
}
// Turn an OINLCALL into a statement.
-func inlconv2stmt(inlcall ir.Node) ir.Node {
+func inlconv2stmt(inlcall *ir.InlinedCallExpr) ir.Node {
n := ir.NodAt(inlcall.Pos(), ir.OBLOCK, nil, nil)
n.SetList(inlcall.Init())
n.PtrList().AppendNodes(inlcall.PtrBody())
// 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.Node) ir.Node {
+func inlconv2expr(n *ir.InlinedCallExpr) ir.Node {
r := n.Rlist().First()
return initExpr(append(n.Init().Slice(), n.Body().Slice()...), r)
}
// 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.Node) []ir.Node {
+func inlconv2list(n *ir.InlinedCallExpr) []ir.Node {
if n.Op() != ir.OINLCALL || n.Rlist().Len() == 0 {
base.Fatalf("inlconv2list %+v\n", n)
}
switch n.Op() {
case ir.ODEFER, ir.OGO:
- switch n.Left().Op() {
+ switch call := n.Left(); call.Op() {
case ir.OCALLFUNC, ir.OCALLMETH:
- n.Left().SetNoInline(true)
+ call.SetNoInline(true)
}
// TODO do them here (or earlier),
ir.EditChildren(n, edit)
- if n.Op() == ir.OAS2FUNC && n.Rlist().First().Op() == ir.OINLCALL {
- n.PtrRlist().Set(inlconv2list(n.Rlist().First()))
- n.SetOp(ir.OAS2)
- n.SetTypecheck(0)
- n = typecheck(n, ctxStmt)
+ if as := n; as.Op() == ir.OAS2FUNC {
+ if as.Rlist().First().Op() == ir.OINLCALL {
+ as.PtrRlist().Set(inlconv2list(as.Rlist().First().(*ir.InlinedCallExpr)))
+ as.SetOp(ir.OAS2)
+ as.SetTypecheck(0)
+ n = typecheck(as, ctxStmt)
+ }
}
// with all the branches out of the way, it is now time to
}
}
- var call ir.Node
+ var call *ir.CallExpr
switch n.Op() {
case ir.OCALLFUNC:
- call = n
+ call = n.(*ir.CallExpr)
if base.Flag.LowerM > 3 {
- fmt.Printf("%v:call to func %+v\n", ir.Line(n), n.Left())
+ fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.Left())
}
- if isIntrinsicCall(n.(*ir.CallExpr)) {
+ if isIntrinsicCall(call) {
break
}
- if fn := inlCallee(n.Left()); fn != nil && fn.Inl != nil {
- n = mkinlcall(n, fn, maxCost, inlMap, edit)
+ if fn := inlCallee(call.Left()); fn != nil && fn.Inl != nil {
+ n = mkinlcall(call, fn, maxCost, inlMap, edit)
}
case ir.OCALLMETH:
- call = n
+ call = n.(*ir.CallExpr)
if base.Flag.LowerM > 3 {
- fmt.Printf("%v:call to meth %L\n", ir.Line(n), n.Left().Right())
+ fmt.Printf("%v:call to meth %v\n", ir.Line(n), call.Left().(*ir.SelectorExpr).Sel)
}
// typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
- if n.Left().Type() == nil {
- base.Fatalf("no function type for [%p] %+v\n", n.Left(), n.Left())
+ if call.Left().Type() == nil {
+ base.Fatalf("no function type for [%p] %+v\n", call.Left(), call.Left())
}
- n = mkinlcall(n, methodExprName(n.Left()).Func(), maxCost, inlMap, edit)
+ n = mkinlcall(call, methodExprName(call.Left()).Func(), maxCost, inlMap, edit)
}
base.Pos = lno
if n.Op() == ir.OINLCALL {
- switch call.(*ir.CallExpr).Use {
+ ic := n.(*ir.InlinedCallExpr)
+ switch call.Use {
default:
ir.Dump("call", call)
base.Fatalf("call missing use")
case ir.CallUseExpr:
- n = inlconv2expr(n)
+ n = inlconv2expr(ic)
case ir.CallUseStmt:
- n = inlconv2stmt(n)
+ n = inlconv2stmt(ic)
case ir.CallUseList:
// leave for caller to convert
}
// that it refers to if statically known. Otherwise, it returns nil.
func inlCallee(fn ir.Node) *ir.Func {
fn = staticValue(fn)
- switch {
- case fn.Op() == ir.OMETHEXPR:
+ switch fn.Op() {
+ case ir.OMETHEXPR:
n := methodExprName(fn)
// Check that receiver type matches fn.Left.
// TODO(mdempsky): Handle implicit dereference
return nil
}
return n.Func()
- case fn.Op() == ir.ONAME && fn.Class() == ir.PFUNC:
- return fn.Func()
- case fn.Op() == ir.OCLOSURE:
+ case ir.ONAME:
+ if fn.Class() == ir.PFUNC {
+ return fn.Func()
+ }
+ case ir.OCLOSURE:
c := fn.Func()
caninl(c)
return c
func staticValue(n ir.Node) ir.Node {
for {
if n.Op() == ir.OCONVNOP {
- n = n.Left()
+ n = n.(*ir.ConvExpr).Left()
continue
}
// staticValue1 implements a simple SSA-like optimization. If n is a local variable
// that is initialized and never reassigned, staticValue1 returns the initializer
// expression. Otherwise, it returns nil.
-func staticValue1(n ir.Node) ir.Node {
- if n.Op() != ir.ONAME || n.Class() != ir.PAUTO || n.Name().Addrtaken() {
+func staticValue1(nn ir.Node) ir.Node {
+ if nn.Op() != ir.ONAME {
+ return nil
+ }
+ n := nn.(*ir.Name)
+ if n.Class() != ir.PAUTO || n.Name().Addrtaken() {
return nil
}
base.Fatalf("RHS is nil: %v", defn)
}
- if reassigned(n.(*ir.Name)) {
+ if reassigned(n) {
return nil
}
// parameters.
// The result of mkinlcall MUST be assigned back to n, e.g.
// n.Left = mkinlcall(n.Left, fn, isddd)
-func mkinlcall(n ir.Node, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
+func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
if fn.Inl == nil {
if logopt.Enabled() {
logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(Curfn),
if n.Op() == ir.OCALLFUNC {
callee := n.Left()
for callee.Op() == ir.OCONVNOP {
- ninit.AppendNodes(callee.PtrInit())
- callee = callee.Left()
+ conv := callee.(*ir.ConvExpr)
+ ninit.AppendNodes(conv.PtrInit())
+ callee = conv.Left()
}
if callee.Op() != ir.ONAME && callee.Op() != ir.OCLOSURE && callee.Op() != ir.OMETHEXPR {
base.Fatalf("unexpected callee expression: %v", callee)
as := ir.Nod(ir.OAS2, nil, nil)
as.SetColas(true)
if n.Op() == ir.OCALLMETH {
- if n.Left().Left() == nil {
+ sel := n.Left().(*ir.SelectorExpr)
+ if sel.Left() == nil {
base.Fatalf("method call without receiver: %+v", n)
}
- as.PtrRlist().Append(n.Left().Left())
+ as.PtrRlist().Append(sel.Left())
}
as.PtrRlist().Append(n.List().Slice()...)
// For non-dotted calls to variadic functions, we assign the
// variadic parameter's temp name separately.
- var vas ir.Node
+ var vas *ir.AssignStmt
if recv := fn.Type().Recv(); recv != nil {
as.PtrList().Append(inlParam(recv, as, inlvars))
}
varargs := as.List().Slice()[x:]
- vas = ir.Nod(ir.OAS, nil, nil)
+ vas = ir.NewAssignStmt(base.Pos, nil, nil)
vas.SetLeft(inlParam(param, vas, inlvars))
if len(varargs) == 0 {
vas.SetRight(nodnil())
vas.Right().SetType(param.Type)
} else {
- vas.SetRight(ir.Nod(ir.OCOMPLIT, nil, ir.TypeNode(param.Type)))
- vas.Right().PtrList().Set(varargs)
+ lit := ir.Nod(ir.OCOMPLIT, nil, ir.TypeNode(param.Type))
+ lit.PtrList().Set(varargs)
+ vas.SetRight(lit)
}
}
typecheckslice(init, ctxStmt)
return ir.NewBlockStmt(base.Pos, init)
- case ir.OGOTO, ir.OLABEL:
- m := ir.Copy(n)
+ case ir.OGOTO:
+ m := ir.Copy(n).(*ir.BranchStmt)
m.SetPos(subst.updatedPos(m.Pos()))
m.PtrInit().Set(nil)
p := fmt.Sprintf("%s·%d", n.Sym().Name, inlgen)
m.SetSym(lookup(p))
+ return m
+ case ir.OLABEL:
+ m := ir.Copy(n).(*ir.LabelStmt)
+ m.SetPos(subst.updatedPos(m.Pos()))
+ m.PtrInit().Set(nil)
+ p := fmt.Sprintf("%s·%d", n.Sym().Name, inlgen)
+ m.SetSym(lookup(p))
return m
}
Curfn = fn
ir.VisitList(fn.Body(), func(n ir.Node) {
if n.Op() == ir.OCALLINTER {
- devirtualizeCall(n)
+ devirtualizeCall(n.(*ir.CallExpr))
}
})
}
-func devirtualizeCall(call ir.Node) {
- recv := staticValue(call.Left().Left())
- if recv.Op() != ir.OCONVIFACE {
+func devirtualizeCall(call *ir.CallExpr) {
+ sel := call.Left().(*ir.SelectorExpr)
+ r := staticValue(sel.Left())
+ if r.Op() != ir.OCONVIFACE {
return
}
+ recv := r.(*ir.ConvExpr)
typ := recv.Left().Type()
if typ.IsInterface() {
return
}
- dt := ir.NodAt(call.Left().Pos(), ir.ODOTTYPE, call.Left().Left(), nil)
+ dt := ir.NodAt(sel.Pos(), ir.ODOTTYPE, sel.Left(), nil)
dt.SetType(typ)
- x := typecheck(nodlSym(call.Left().Pos(), ir.OXDOT, dt, call.Left().Sym()), ctxExpr|ctxCallee)
+ x := typecheck(nodlSym(sel.Pos(), ir.OXDOT, dt, sel.Sym()), ctxExpr|ctxCallee)
switch x.Op() {
case ir.ODOTMETH:
if base.Flag.LowerM != 0 {
- base.WarnfAt(call.Pos(), "devirtualizing %v to %v", call.Left(), typ)
+ base.WarnfAt(call.Pos(), "devirtualizing %v to %v", sel, typ)
}
call.SetOp(ir.OCALLMETH)
call.SetLeft(x)
case ir.ODOTINTER:
// Promoted method from embedded interface-typed field (#42279).
if base.Flag.LowerM != 0 {
- base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", call.Left(), typ)
+ base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", sel, typ)
}
call.SetOp(ir.OCALLINTER)
call.SetLeft(x)