// Copyright 2011 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. // // The inlining facility makes 2 passes: first caninl determines which // functions are suitable for inlining, and for those that are it // saves a copy of the body. Then inlcalls walks each function body to // expand calls to inlinable functions. // // The Debug.l flag controls the aggressiveness. Note that main() swaps level 0 and 1, // making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and // are not supported. // 0: disabled // 1: 80-nodes leaf functions, oneliners, panic, lazy typechecking (default) // 2: (unassigned) // 3: (unassigned) // 4: allow non-leaf functions // // At some point this may get another default and become switch-offable with -N. // // The -d typcheckinl flag enables early typechecking of all imported bodies, // which is useful to flush out bugs. // // The Debug.m flag enables diagnostic output. a single -m is useful for verifying // which calls get inlined or not, more is for debugging, and may go away at any point. package devirtualize import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" ) // Devirtualize replaces interface method calls within fn with direct // concrete-type method calls where applicable. func Func(fn *ir.Func) { ir.CurFunc = fn ir.VisitList(fn.Body, func(n ir.Node) { if n.Op() == ir.OCALLINTER { Call(n.(*ir.CallExpr)) } }) } func Call(call *ir.CallExpr) { sel := call.X.(*ir.SelectorExpr) r := ir.StaticValue(sel.X) if r.Op() != ir.OCONVIFACE { return } recv := r.(*ir.ConvExpr) typ := recv.X.Type() if typ.IsInterface() { return } dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil) dt.SetType(typ) x := typecheck.Callee(ir.NewSelectorExpr(sel.Pos(), ir.OXDOT, dt, sel.Sel)) switch x.Op() { case ir.ODOTMETH: x := x.(*ir.SelectorExpr) if base.Flag.LowerM != 0 { base.WarnfAt(call.Pos(), "devirtualizing %v to %v", sel, typ) } call.SetOp(ir.OCALLMETH) call.X = x case ir.ODOTINTER: // Promoted method from embedded interface-typed field (#42279). x := x.(*ir.SelectorExpr) if base.Flag.LowerM != 0 { base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", sel, typ) } call.SetOp(ir.OCALLINTER) call.X = x default: // TODO(mdempsky): Turn back into Fatalf after more testing. if base.Flag.LowerM != 0 { base.WarnfAt(call.Pos(), "failed to devirtualize %v (%v)", x, x.Op()) } return } // Duplicated logic from typecheck for function call return // value types. // // Receiver parameter size may have changed; need to update // call.Type to get correct stack offsets for result // parameters. types.CheckSize(x.Type()) switch ft := x.Type(); ft.NumResults() { case 0: case 1: call.SetType(ft.Results().Field(0).Type) default: call.SetType(ft.Results()) } }