diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:25:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:25:22 +0000 |
commit | f6ad4dcef54c5ce997a4bad5a6d86de229015700 (patch) | |
tree | 7cfa4e31ace5c2bd95c72b154d15af494b2bcbef /src/cmd/compile/internal/devirtualize/devirtualize.go | |
parent | Initial commit. (diff) | |
download | golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.tar.xz golang-1.22-f6ad4dcef54c5ce997a4bad5a6d86de229015700.zip |
Adding upstream version 1.22.1.upstream/1.22.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/compile/internal/devirtualize/devirtualize.go')
-rw-r--r-- | src/cmd/compile/internal/devirtualize/devirtualize.go | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/devirtualize/devirtualize.go b/src/cmd/compile/internal/devirtualize/devirtualize.go new file mode 100644 index 0000000..5d1b952 --- /dev/null +++ b/src/cmd/compile/internal/devirtualize/devirtualize.go @@ -0,0 +1,140 @@ +// 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 two "devirtualization" optimization passes: +// +// - "Static" devirtualization which replaces interface method calls with +// direct concrete-type method calls where possible. +// - "Profile-guided" devirtualization which replaces indirect calls with a +// conditional direct call to the hottest concrete callee from a profile, as +// well as a fallback using the original indirect call. +package devirtualize + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" +) + +// StaticCall devirtualizes the given call if possible when the concrete callee +// is available statically. +func StaticCall(call *ir.CallExpr) { + // For promoted methods (including value-receiver methods promoted + // to pointer-receivers), the interface method wrapper may contain + // expressions that can panic (e.g., ODEREF, ODOTPTR, + // ODOTINTER). Devirtualization involves inlining these expressions + // (and possible panics) to the call site. This normally isn't a + // problem, but for go/defer statements it can move the panic from + // when/where the call executes to the go/defer statement itself, + // which is a visible change in semantics (e.g., #52072). To prevent + // this, we skip devirtualizing calls within go/defer statements + // altogether. + if call.GoDefer { + return + } + + if call.Op() != ir.OCALLINTER { + return + } + + sel := call.Fun.(*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 + } + + // If typ is a shape type, then it was a type argument originally + // and we'd need an indirect call through the dictionary anyway. + // We're unable to devirtualize this call. + if typ.IsShape() { + return + } + + // If typ *has* a shape type, then it's a shaped, instantiated + // type like T[go.shape.int], and its methods (may) have an extra + // dictionary parameter. We could devirtualize this call if we + // could derive an appropriate dictionary argument. + // + // TODO(mdempsky): If typ has has a promoted non-generic method, + // then that method won't require a dictionary argument. We could + // still devirtualize those calls. + // + // TODO(mdempsky): We have the *runtime.itab in recv.TypeWord. It + // should be possible to compute the represented type's runtime + // dictionary from this (e.g., by adding a pointer from T[int]'s + // *runtime._type to .dict.T[int]; or by recognizing static + // references to go:itab.T[int],iface and constructing a direct + // reference to .dict.T[int]). + if typ.HasShape() { + if base.Flag.LowerM != 0 { + base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped receiver %v", call, typ) + } + return + } + + // Further, if sel.X's type has a shape type, then it's a shaped + // interface type. In this case, the (non-dynamic) TypeAssertExpr + // we construct below would attempt to create an itab + // corresponding to this shaped interface type; but the actual + // itab pointer in the interface value will correspond to the + // original (non-shaped) interface type instead. These are + // functionally equivalent, but they have distinct pointer + // identities, which leads to the type assertion failing. + // + // TODO(mdempsky): We know the type assertion here is safe, so we + // could instead set a flag so that walk skips the itab check. For + // now, punting is easy and safe. + if sel.X.Type().HasShape() { + if base.Flag.LowerM != 0 { + base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped interface %v", call, sel.X.Type()) + } + return + } + + dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil) + dt.SetType(typ) + x := typecheck.XDotMethod(sel.Pos(), dt, sel.Sel, true) + switch x.Op() { + case ir.ODOTMETH: + if base.Flag.LowerM != 0 { + base.WarnfAt(call.Pos(), "devirtualizing %v to %v", sel, typ) + } + call.SetOp(ir.OCALLMETH) + call.Fun = x + case ir.ODOTINTER: + // Promoted method from embedded interface-typed field (#42279). + if base.Flag.LowerM != 0 { + base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", sel, typ) + } + call.SetOp(ir.OCALLINTER) + call.Fun = x + default: + base.FatalfAt(call.Pos(), "failed to devirtualize %v (%v)", x, x.Op()) + } + + // 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.Result(0).Type) + default: + call.SetType(ft.ResultsTuple()) + } + + // Desugar OCALLMETH, if we created one (#57309). + typecheck.FixMethodCall(call) +} |