| // Copyright 2020 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. |
| |
| // Package devirtualize implements a simple "devirtualization" |
| // optimization pass, which replaces interface method calls with |
| // direct concrete-type method calls where possible. |
| package devirtualize |
| |
| import ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| ) |
| |
| // Func devirtualizes calls within fn where possible. |
| func Func(fn *ir.Func) { |
| ir.CurFunc = fn |
| ir.VisitList(fn.Body, func(n ir.Node) { |
| if call, ok := n.(*ir.CallExpr); ok { |
| Call(call) |
| } |
| }) |
| } |
| |
| // Call devirtualizes the given call if possible. |
| func Call(call *ir.CallExpr) { |
| if call.Op() != ir.OCALLINTER { |
| return |
| } |
| 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()) |
| } |
| } |