mirror of
https://github.com/golang/go.git
synced 2025-11-10 21:51:05 +00:00
102 lines
2.9 KiB
Go
102 lines
2.9 KiB
Go
|
|
// 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())
|
||
|
|
}
|
||
|
|
}
|