From e62adb1c0b8e7ca49eefc70389fbb9f739d6e32c Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 29 Oct 2020 13:30:54 -0700 Subject: [PATCH] cmd/compile: fix devirtualization of promoted interface methods A method selector expression can pick out a method or promoted method (represented by ODOTMETH), but it can also pick out an interface method from an embedded interface-typed field (represented by ODOTINTER). In the case that we're picking out an interface method, we're not able to fully devirtualize the method call. However, we're still able to improve escape analysis somewhat. E.g., the included test case demonstrates that we can optimize "i.M()" to "i.(T).I.M()", which means the T literal can be stack allocated instead of heap allocated. Fixes #42279. Change-Id: Ifa21d19011e2f008d84f9624b7055b4676b6d188 Reviewed-on: https://go-review.googlesource.com/c/go/+/266300 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: David Chase --- src/cmd/compile/internal/gc/inl.go | 26 +++++++++++++++++--------- test/escape_iface.go | 8 ++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 6c69867789..5b58908299 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -1446,19 +1446,27 @@ func devirtualizeCall(call *Node) { x.Type = typ x = nodlSym(call.Left.Pos, OXDOT, x, call.Left.Sym) x = typecheck(x, ctxExpr|ctxCallee) - if x.Op != ODOTMETH { - // TODO(mdempsky): Figure out how to avoid this and - // turn back into a Fatalf. + switch x.Op { + case ODOTMETH: if Debug.m != 0 { - Warnl(call.Pos, "failed to devirtualize %v", x) + Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ) + } + call.Op = OCALLMETH + call.Left = x + case ODOTINTER: + // Promoted method from embedded interface-typed field (#42279). + if Debug.m != 0 { + Warnl(call.Pos, "partially devirtualizing %v to %v", call.Left, typ) + } + call.Op = OCALLINTER + call.Left = x + default: + // TODO(mdempsky): Turn back into Fatalf after more testing. + if Debug.m != 0 { + Warnl(call.Pos, "failed to devirtualize %v (%v)", x, x.Op) } return } - if Debug.m != 0 { - Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ) - } - call.Op = OCALLMETH - call.Left = x // Duplicated logic from typecheck for function call return // value types. diff --git a/test/escape_iface.go b/test/escape_iface.go index 5a232fdbd4..dba08e3cb3 100644 --- a/test/escape_iface.go +++ b/test/escape_iface.go @@ -255,3 +255,11 @@ func dotTypeEscape2() { // #13805, #15796 sink, *(&ok) = y.(*int) } } + +func issue42279() { + type I interface{ M() } + type T struct{ I } + + var i I = T{} // ERROR "T\{\} does not escape" + i.M() // ERROR "partially devirtualizing i.M to T" +}