golang/go

cmd/compile: calling a method causes interface with non-pointer receiver to escape

Open

#78336 opened on Mar 25, 2026

View on GitHub
 (6 comments) (1 reaction) (0 assignees)Go (133,883 stars) (19,008 forks)batch import
FeatureRequestNeedsInvestigationPerformancecompiler/runtimehelp wanted

Description

Consider the following program:

package p

type k int

func (k) F() {}

type y interface {
    F()
}

func f(k k)  {
    sink(k)
}

//go:noinline
func sink(v y) {
    v.F()
}

Here is f's assembly:

TEXT    .f(SB), ABIInternal, $24-8
        PUSHQ   BP
        MOVQ    SP, BP
        SUBQ    $16, SP
        CALL    runtime.convT64(SB)
        MOVQ    AX, BX
        LEAQ    go:.k,.y(SB), AX
        NOP
        CALL    .sink(SB)
        ADDQ    $16, SP
        POPQ    BP
        RET

Notice that the argument is heap-allocated by calling runtime.convT64. If I remove the interface call in the method, the interface value does not escape and lives on the heap instead.

I think that what's happening here is that because for a general interface, calling an interface method can cause the receiver to escape, the escape bits are populated as such in the middle-end. However, the caller knows the interface's value, and if it knew that escape depends on the interface's methods escaping their first argument, it would know that the interface cannot escape.

This is a limitation in how escape information is recorded across function calls; there is no way to record "arg n escapes only if function pointer arg m's kth argument escapes".

It is a little bit annoying on my end... I don't have a (painless) workaround for this in one of my APIs, but it actually does impact performance. I don't expect this to get fixed any time soon, because it requires a bit of a data model enhancement in the escape bits.

Contributor guide