Source file src/cmd/compile/internal/devirtualize/devirtualize.go

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package devirtualize implements a simple "devirtualization"
     6  // optimization pass, which replaces interface method calls with
     7  // direct concrete-type method calls where possible.
     8  package devirtualize
     9  
    10  import (
    11  	"cmd/compile/internal/base"
    12  	"cmd/compile/internal/ir"
    13  	"cmd/compile/internal/typecheck"
    14  	"cmd/compile/internal/types"
    15  )
    16  
    17  // Func devirtualizes calls within fn where possible.
    18  func Func(fn *ir.Func) {
    19  	ir.CurFunc = fn
    20  	ir.VisitList(fn.Body, func(n ir.Node) {
    21  		if call, ok := n.(*ir.CallExpr); ok {
    22  			Call(call)
    23  		}
    24  	})
    25  }
    26  
    27  // Call devirtualizes the given call if possible.
    28  func Call(call *ir.CallExpr) {
    29  	if call.Op() != ir.OCALLINTER {
    30  		return
    31  	}
    32  	sel := call.X.(*ir.SelectorExpr)
    33  	r := ir.StaticValue(sel.X)
    34  	if r.Op() != ir.OCONVIFACE {
    35  		return
    36  	}
    37  	recv := r.(*ir.ConvExpr)
    38  
    39  	typ := recv.X.Type()
    40  	if typ.IsInterface() {
    41  		return
    42  	}
    43  
    44  	dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil)
    45  	dt.SetType(typ)
    46  	x := typecheck.Callee(ir.NewSelectorExpr(sel.Pos(), ir.OXDOT, dt, sel.Sel))
    47  	switch x.Op() {
    48  	case ir.ODOTMETH:
    49  		x := x.(*ir.SelectorExpr)
    50  		if base.Flag.LowerM != 0 {
    51  			base.WarnfAt(call.Pos(), "devirtualizing %v to %v", sel, typ)
    52  		}
    53  		call.SetOp(ir.OCALLMETH)
    54  		call.X = x
    55  	case ir.ODOTINTER:
    56  		// Promoted method from embedded interface-typed field (#42279).
    57  		x := x.(*ir.SelectorExpr)
    58  		if base.Flag.LowerM != 0 {
    59  			base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", sel, typ)
    60  		}
    61  		call.SetOp(ir.OCALLINTER)
    62  		call.X = x
    63  	default:
    64  		// TODO(mdempsky): Turn back into Fatalf after more testing.
    65  		if base.Flag.LowerM != 0 {
    66  			base.WarnfAt(call.Pos(), "failed to devirtualize %v (%v)", x, x.Op())
    67  		}
    68  		return
    69  	}
    70  
    71  	// Duplicated logic from typecheck for function call return
    72  	// value types.
    73  	//
    74  	// Receiver parameter size may have changed; need to update
    75  	// call.Type to get correct stack offsets for result
    76  	// parameters.
    77  	types.CheckSize(x.Type())
    78  	switch ft := x.Type(); ft.NumResults() {
    79  	case 0:
    80  	case 1:
    81  		call.SetType(ft.Results().Field(0).Type)
    82  	default:
    83  		call.SetType(ft.Results())
    84  	}
    85  }
    86  

View as plain text