1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
8 "cmd/compile/internal/base"
9 "cmd/compile/internal/ir"
10 "cmd/compile/internal/typecheck"
11 "cmd/compile/internal/types"
14 func walkSelect(sel *ir.SelectStmt) {
17 base.Fatalf("double walkSelect")
21 init := ir.TakeInit(sel)
23 init = append(init, walkSelectCases(sel.Cases)...)
27 walkStmtList(sel.Compiled)
32 func walkSelectCases(cases []*ir.CommClause) []ir.Node {
36 // optimization: zero-case select
38 return []ir.Node{mkcallstmt("block")}
41 // optimization: one-case select: single op.
46 if cas.Comm != nil { // not default:
48 l = append(l, ir.TakeInit(n)...)
51 base.Fatalf("select %v", n.Op())
57 r := n.(*ir.AssignListStmt)
58 if ir.IsBlank(r.Lhs[0]) && ir.IsBlank(r.Lhs[1]) {
68 l = append(l, cas.Body...)
69 l = append(l, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))
73 // convert case value arguments to addresses.
74 // this rewrite is used by both the general code and the next optimization.
75 var dflt *ir.CommClause
76 for _, cas := range cases {
86 n.Value = typecheck.NodAddr(n.Value)
87 n.Value = typecheck.Expr(n.Value)
90 n := n.(*ir.AssignListStmt)
91 if !ir.IsBlank(n.Lhs[0]) {
92 n.Lhs[0] = typecheck.NodAddr(n.Lhs[0])
93 n.Lhs[0] = typecheck.Expr(n.Lhs[0])
98 // optimization: two-case select but one is default: single non-blocking op.
99 if ncas == 2 && dflt != nil {
107 r := ir.NewIfStmt(base.Pos, nil, nil, nil)
108 r.SetInit(cas.Init())
112 base.Fatalf("select %v", n.Op())
115 // if selectnbsend(c, v) { body } else { default body }
116 n := n.(*ir.SendStmt)
118 cond = mkcall1(chanfn("selectnbsend", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), ch, n.Value)
121 n := n.(*ir.AssignListStmt)
122 recv := n.Rhs[0].(*ir.UnaryExpr)
125 if ir.IsBlank(elem) {
126 elem = typecheck.NodNil()
128 cond = typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TBOOL])
129 fn := chanfn("selectnbrecv", 2, ch.Type())
130 call := mkcall1(fn, fn.Type().Results(), r.PtrInit(), elem, ch)
131 as := ir.NewAssignListStmt(r.Pos(), ir.OAS2, []ir.Node{cond, n.Lhs[1]}, []ir.Node{call})
132 r.PtrInit().Append(typecheck.Stmt(as))
135 r.Cond = typecheck.Expr(cond)
137 r.Else = append(dflt.Init(), dflt.Body...)
138 return []ir.Node{r, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)}
144 casorder := make([]*ir.CommClause, ncas)
145 nsends, nrecvs := 0, 0
149 // generate sel-struct
151 selv := typecheck.TempAt(base.Pos, ir.CurFunc, types.NewArray(scasetype(), int64(ncas)))
152 init = append(init, typecheck.Stmt(ir.NewAssignStmt(base.Pos, selv, nil)))
154 // No initialization for order; runtime.selectgo is responsible for that.
155 order := typecheck.TempAt(base.Pos, ir.CurFunc, types.NewArray(types.Types[types.TUINT16], 2*int64(ncas)))
159 pcs = typecheck.TempAt(base.Pos, ir.CurFunc, types.NewArray(types.Types[types.TUINTPTR], int64(ncas)))
160 pc0 = typecheck.Expr(typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(base.Pos, 0))))
162 pc0 = typecheck.NodNil()
166 for _, cas := range cases {
169 init = append(init, ir.TakeInit(cas)...)
172 if n == nil { // default:
180 base.Fatalf("select %v", n.Op())
182 n := n.(*ir.SendStmt)
188 n := n.(*ir.AssignListStmt)
191 recv := n.Rhs[0].(*ir.UnaryExpr)
198 setField := func(f string, val ir.Node) {
199 r := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, ir.NewIndexExpr(base.Pos, selv, ir.NewInt(base.Pos, int64(i))), typecheck.Lookup(f)), val)
200 init = append(init, typecheck.Stmt(r))
203 c = typecheck.ConvNop(c, types.Types[types.TUNSAFEPTR])
205 if !ir.IsBlank(elem) {
206 elem = typecheck.ConvNop(elem, types.Types[types.TUNSAFEPTR])
207 setField("elem", elem)
210 // TODO(mdempsky): There should be a cleaner way to
213 r := mkcallstmt("selectsetpc", typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(base.Pos, int64(i)))))
214 init = append(init, r)
217 if nsends+nrecvs != ncas {
218 base.Fatalf("walkSelectCases: miscount: %v + %v != %v", nsends, nrecvs, ncas)
223 chosen := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
224 recvOK := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TBOOL])
225 r := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
226 r.Lhs = []ir.Node{chosen, recvOK}
227 fn := typecheck.LookupRuntime("selectgo")
229 r.Rhs = []ir.Node{mkcall1(fn, fn.Type().Results(), &fnInit, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, ir.NewInt(base.Pos, int64(nsends)), ir.NewInt(base.Pos, int64(nrecvs)), ir.NewBool(base.Pos, dflt == nil))}
230 init = append(init, fnInit...)
231 init = append(init, typecheck.Stmt(r))
233 // selv, order, and pcs (if race) are no longer alive after selectgo.
236 dispatch := func(cond ir.Node, cas *ir.CommClause) {
239 if n := cas.Comm; n != nil && n.Op() == ir.OSELRECV2 {
240 n := n.(*ir.AssignListStmt)
241 if !ir.IsBlank(n.Lhs[1]) {
242 x := ir.NewAssignStmt(base.Pos, n.Lhs[1], recvOK)
243 list.Append(typecheck.Stmt(x))
247 list.Append(cas.Body.Take()...)
248 list.Append(ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))
252 cond = typecheck.Expr(cond)
253 cond = typecheck.DefaultLit(cond, nil)
254 r = ir.NewIfStmt(base.Pos, cond, list, nil)
256 r = ir.NewBlockStmt(base.Pos, list)
259 init = append(init, r)
264 dispatch(ir.NewBinaryExpr(base.Pos, ir.OLT, chosen, ir.NewInt(base.Pos, 0)), dflt)
266 for i, cas := range casorder {
268 if i == len(casorder)-1 {
272 dispatch(ir.NewBinaryExpr(base.Pos, ir.OEQ, chosen, ir.NewInt(base.Pos, int64(i))), cas)
278 // bytePtrToIndex returns a Node representing "(*byte)(&n[i])".
279 func bytePtrToIndex(n ir.Node, i int64) ir.Node {
280 s := typecheck.NodAddr(ir.NewIndexExpr(base.Pos, n, ir.NewInt(base.Pos, i)))
281 t := types.NewPtr(types.Types[types.TUINT8])
282 return typecheck.ConvNop(s, t)
285 var scase *types.Type
287 // Keep in sync with src/runtime/select.go.
288 func scasetype() *types.Type {
290 scase = types.NewStruct([]*types.Field{
291 types.NewField(base.Pos, typecheck.Lookup("c"), types.Types[types.TUNSAFEPTR]),
292 types.NewField(base.Pos, typecheck.Lookup("elem"), types.Types[types.TUNSAFEPTR]),