Skip to content

cmd/compile: optimize away select receive cases with known-zero chans #75608

@bradfitz

Description

@bradfitz

Go version

go version go1.26-devel_6dceff8bad Thu Sep 25 09:24:46 2025 -0700 darwin/arm64

What did you see happen?

I'd hope Go would notice this select is known to always be nil and optimize it away in the select:

package main

type T struct {
	SomeSymbol int
}

var v any

func main() {
	var c <-chan T
	select {
	case v = <-c:
	}
}

Instead, go tool compile -S x.go says:

main.main STEXT size=128 args=0x0 locals=0x28 funcid=0x0 align=0x0
        0x0000 00000 (/Users/bradfitz/hack/go/x.go:9)   TEXT    main.main(SB), ABIInternal, $48-0
        0x0000 00000 (/Users/bradfitz/hack/go/x.go:9)   MOVD    16(g), R16
        0x0004 00004 (/Users/bradfitz/hack/go/x.go:9)   PCDATA  $0, $-2
        0x0004 00004 (/Users/bradfitz/hack/go/x.go:9)   CMP     R16, RSP
        0x0008 00008 (/Users/bradfitz/hack/go/x.go:9)   BLS     108
        0x000c 00012 (/Users/bradfitz/hack/go/x.go:9)   PCDATA  $0, $-1
        0x000c 00012 (/Users/bradfitz/hack/go/x.go:9)   MOVD.W  R30, -48(RSP)
        0x0010 00016 (/Users/bradfitz/hack/go/x.go:9)   MOVD    R29, -8(RSP)
        0x0014 00020 (/Users/bradfitz/hack/go/x.go:9)   SUB     $8, RSP, R29
        0x0018 00024 (/Users/bradfitz/hack/go/x.go:9)   FUNCDATA        $0, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
        0x0018 00024 (/Users/bradfitz/hack/go/x.go:9)   FUNCDATA        $1, gclocals·g5+hNtRBP6YXNjfog7aZjQ==(SB)
        0x0018 00024 (/Users/bradfitz/hack/go/x.go:12)  MOVD    ZR, R0
        0x001c 00028 (/Users/bradfitz/hack/go/x.go:12)  MOVD    $main..autotmp_3-8(SP), R1
        0x0020 00032 (/Users/bradfitz/hack/go/x.go:12)  PCDATA  $1, $0
        0x0020 00032 (/Users/bradfitz/hack/go/x.go:12)  CALL    runtime.chanrecv2(SB)
        0x0024 00036 (/Users/bradfitz/hack/go/x.go:12)  MOVD    main..autotmp_3-8(SP), R0
        0x0028 00040 (/Users/bradfitz/hack/go/x.go:12)  CALL    runtime.convT64(SB)
        0x002c 00044 (/Users/bradfitz/hack/go/x.go:12)  MOVD    $type:<unlinkable>.T(SB), R2
        0x0034 00052 (/Users/bradfitz/hack/go/x.go:12)  PCDATA  $0, $-3
        0x0034 00052 (/Users/bradfitz/hack/go/x.go:12)  MOVD    R2, main.v(SB)
        0x003c 00060 (/Users/bradfitz/hack/go/x.go:12)  PCDATA  $0, $-4
        0x003c 00060 (/Users/bradfitz/hack/go/x.go:12)  MOVWU   runtime.writeBarrier(SB), R2
        0x0044 00068 (/Users/bradfitz/hack/go/x.go:12)  PCDATA  $0, $-1
        0x0044 00068 (/Users/bradfitz/hack/go/x.go:12)  PCDATA  $0, $-2
        0x0044 00068 (/Users/bradfitz/hack/go/x.go:12)  CBZW    R2, 88
        0x0048 00072 (/Users/bradfitz/hack/go/x.go:12)  MOVD    main.v+8(SB), R1
        0x0050 00080 (/Users/bradfitz/hack/go/x.go:12)  CALL    runtime.gcWriteBarrier2(SB)
        0x0054 00084 (/Users/bradfitz/hack/go/x.go:12)  STP     (R0, R1), (R25)
        0x0058 00088 (/Users/bradfitz/hack/go/x.go:12)  MOVD    R0, main.v+8(SB)
        0x0060 00096 (/Users/bradfitz/hack/go/x.go:14)  PCDATA  $0, $-1
        0x0060 00096 (/Users/bradfitz/hack/go/x.go:14)  MOVD    -8(RSP), R29
        0x0064 00100 (/Users/bradfitz/hack/go/x.go:14)  MOVD.P  48(RSP), R30
        0x0068 00104 (/Users/bradfitz/hack/go/x.go:14)  RET     (R30)
        0x006c 00108 (/Users/bradfitz/hack/go/x.go:14)  NOP
        0x006c 00108 (/Users/bradfitz/hack/go/x.go:9)   PCDATA  $1, $-1
        0x006c 00108 (/Users/bradfitz/hack/go/x.go:9)   PCDATA  $0, $-2
        0x006c 00108 (/Users/bradfitz/hack/go/x.go:9)   MOVD    R30, R3
        0x0070 00112 (/Users/bradfitz/hack/go/x.go:9)   CALL    runtime.morestack_noctxt(SB)
        0x0074 00116 (/Users/bradfitz/hack/go/x.go:9)   PCDATA  $0, $-1
        0x0074 00116 (/Users/bradfitz/hack/go/x.go:9)   JMP     0

And the T type needlessly shows up in my binary:

% ./bin/go build -ldflags=-dumpdep x.go 2>&1 | grep SomeSymbol
type:main.T <UsedInIface> -> type:.namedata.SomeSymbol.

While this may look contrived, this ends up happening in real programs that use build tags to set boolean constants that guard conditional initialization of chans to receive on in selects.

/cc @randall77 @dr2chase @adonovan @mdempsky

What did you expect to see?

0x0018 00024 (/Users/bradfitz/hack/go/x.go:10)  CALL    runtime.block(SB)

... instead of runtime.chanrecv2, as if you commented out at select receive case and it were just select{}.

And I shouldn't see type info for T or SomeSymbol in my binary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ImplementationIssues describing a semantics-preserving change to the Go implementation.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.compiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions