diff options
-rw-r--r-- | src/cmd/compile/internal/devirtualize/devirtualize.go | 101 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/main.go | 3 | ||||
-rw-r--r-- | src/cmd/compile/internal/inline/inl.go | 67 |
3 files changed, 103 insertions, 68 deletions
diff --git a/src/cmd/compile/internal/devirtualize/devirtualize.go b/src/cmd/compile/internal/devirtualize/devirtualize.go new file mode 100644 index 0000000000..95b28eff61 --- /dev/null +++ b/src/cmd/compile/internal/devirtualize/devirtualize.go @@ -0,0 +1,101 @@ +// 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()) + } +} diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 8483c87a38..ba3620e676 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -10,6 +10,7 @@ import ( "bufio" "bytes" "cmd/compile/internal/base" + "cmd/compile/internal/devirtualize" "cmd/compile/internal/dwarfgen" "cmd/compile/internal/escape" "cmd/compile/internal/inline" @@ -237,7 +238,7 @@ func Main(archInit func(*ssagen.ArchInfo)) { // Devirtualize. for _, n := range typecheck.Target.Decls { if n.Op() == ir.ODCLFUNC { - inline.Devirtualize(n.(*ir.Func)) + devirtualize.Func(n.(*ir.Func)) } } ir.CurFunc = nil diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go index 222e62d0cc..9ffb08048a 100644 --- a/src/cmd/compile/internal/inline/inl.go +++ b/src/cmd/compile/internal/inline/inl.go @@ -1203,73 +1203,6 @@ func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name { return s } -// Devirtualize replaces interface method calls within fn with direct -// concrete-type method calls where applicable. -func Devirtualize(fn *ir.Func) { - ir.CurFunc = fn - ir.VisitList(fn.Body, func(n ir.Node) { - if n.Op() == ir.OCALLINTER { - devirtualizeCall(n.(*ir.CallExpr)) - } - }) -} - -func devirtualizeCall(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()) - } -} - // numNonClosures returns the number of functions in list which are not closures. func numNonClosures(list []*ir.Func) int { count := 0 |