aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/compile/internal/devirtualize/devirtualize.go101
-rw-r--r--src/cmd/compile/internal/gc/main.go3
-rw-r--r--src/cmd/compile/internal/inline/inl.go67
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