Skip to content

cmd/compile,runtime: compile multiway select statements as switch statements #19331

@mdempsky

Description

@mdempsky

Currently, code like:

select {
case c1 <- v1:      f1()
case v2 = <-c2:     f2()
case v3, ok = <-c3: f3()
default:            f4()
}

gets rewritten by cmd/compile into:

var sel struct {
	tcase, ncase               uint16
	pollorder, lockorder       *uint8
	scase                      [4]runtime.scase
	lockorderarr, pollorderarr [4]uint8
}
runtime.newselect(&sel, unsafe.Sizeof(sel), 4)
if runtime.selectsend(&sel, c1, &v1)       { f1(); goto after }
if runtime.selectrecv(&sel, c2, &v2)       { f2(); goto after }
if runtime.selectrecv2(&sel, c3, &v3, &ok) { f3(); goto after }
if runtime.selectdefault(&sel)             { f4(); goto after }
runtime.selectgo(&sel)
after:

The select{send,recv,recv2,default} functions always return false the first time they're called, but internally they save the caller's PC into &sel. runtime.selectgo never returns; instead it waits for a channel operation that can succeed, and then returns to the appropriate PC, behaving as though the function call returned twice.

(To make a C analogy, select{send,recv,recv2,default} are like setjmp, and selectgo is like longjmp.)

This proposal is to instead compile it as (something like):

var sel = struct{...}{tcase: 4, scase: [4]runtime.scase{
    {elem: &v1, chan: c1, kind: runtime.caseSend},
    {elem: &v2, chan: c2, kind: runtime.caseRecv},
    {elem: &v3, chan: c3, kind: runtime.caseRecv, receivedp: &ok},
    {kind: runtime.caseDefault},
}}
switch runtime.select(&sel) {
case 0: f1()
case 1: f2()
case 2: f3()
case 3: f4()
default: undef
}

Pros:

  1. The returns-twice and returns-never logic complicates the CFG. For example, the liveness analysis pass needs to traverse these implicit edges by recognizing runtime.selectfoo function calls. It has also caused bugs in SSA optimizations (for example https://go-review.googlesource.com/#/c/37376/).

  2. gccgo already does this according to @ianlancetaylor

  3. I wouldn't be surprised if the compiler is able to more efficiently initialize the select data structure as a straight forward composite literal, than as a bunch of runtime calls.

  4. We can eliminate a few fields. For example, hselect.ncase and scase.{pc,so}. Potentially more simplifications.

Cons:

  1. Currently switch statements are compiled into binary searches, whereas the current select implementation is able to directly jump to the appropriate destination PC. We could optimize switch statements to use jump tables though, at least for the special case of lowered select statements.

@ianlancetaylor @randall77 @rsc @aclements

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions