diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go index d009a55a96b..5124af945ef 100644 --- a/src/cmd/compile/internal/gc/escape.go +++ b/src/cmd/compile/internal/gc/escape.go @@ -399,9 +399,6 @@ func (e *Escape) stmt(n ir.Node) { e.stmt(cas.Left()) e.block(cas.Body()) } - case ir.OSELRECV: - n := n.(*ir.AssignStmt) - e.assign(n.Left(), n.Right(), "selrecv", n) case ir.OSELRECV2: n := n.(*ir.AssignListStmt) e.assign(n.List().First(), n.Rlist().First(), "selrecv", n) diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index b0a9c9be3e1..0034556995b 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -872,15 +872,14 @@ func (o *Order) stmt(n ir.Node) { // give this away). case ir.OSELECT: t := o.markTemp() - - for _, cas := range n.List().Slice() { - cas := cas.(*ir.CaseStmt) - r := cas.Left() - setlineno(cas) + for _, ncas := range n.List().Slice() { + ncas := ncas.(*ir.CaseStmt) + r := ncas.Left() + setlineno(ncas) // Append any new body prologue to ninit. // The next loop will insert ninit into nbody. - if cas.Init().Len() != 0 { + if ncas.Init().Len() != 0 { base.Fatalf("order select ninit") } if r == nil { @@ -891,84 +890,48 @@ func (o *Order) stmt(n ir.Node) { ir.Dump("select case", r) base.Fatalf("unknown op in select %v", r.Op()) - case ir.OSELRECV, ir.OSELRECV2: - var dst, ok ir.Node - var recv *ir.UnaryExpr - var def bool - if r.Op() == ir.OSELRECV { - // case x = <-c - // case <-c (dst is ir.BlankNode) - def, dst, ok, recv = r.Colas(), r.Left(), ir.BlankNode, r.Right().(*ir.UnaryExpr) - } else { - r := r.(*ir.AssignListStmt) - // case x, ok = <-c - def, dst, ok, recv = r.Colas(), r.List().First(), r.List().Second(), r.Rlist().First().(*ir.UnaryExpr) - } - - // If this is case x := <-ch or case x, y := <-ch, the case has - // the ODCL nodes to declare x and y. We want to delay that - // declaration (and possible allocation) until inside the case body. - // Delete the ODCL nodes here and recreate them inside the body below. - if def { - init := r.Init().Slice() - if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).Left() == dst { - init = init[1:] - } - if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).Left() == ok { - init = init[1:] - } - r.PtrInit().Set(init) - } - if r.Init().Len() != 0 { - ir.DumpList("ninit", r.Init()) - base.Fatalf("ninit on select recv") - } - + case ir.OSELRECV2: + // case x, ok = <-c + recv := r.Rlist().First().(*ir.UnaryExpr) recv.SetLeft(o.expr(recv.Left(), nil)) if recv.Left().Op() != ir.ONAME { recv.SetLeft(o.copyExpr(recv.Left())) } + r := r.(*ir.AssignListStmt) + init := r.PtrInit().Slice() + r.PtrInit().Set(nil) - // Introduce temporary for receive and move actual copy into case body. - // avoids problems with target being addressed, as usual. - // NOTE: If we wanted to be clever, we could arrange for just one - // temporary per distinct type, sharing the temp among all receives - // with that temp. Similarly one ok bool could be shared among all - // the x,ok receives. Not worth doing until there's a clear need. - if !ir.IsBlank(dst) { - // use channel element type for temporary to avoid conversions, - // such as in case interfacevalue = <-intchan. - // the conversion happens in the OAS instead. - if def { - dcl := ir.Nod(ir.ODCL, dst, nil) - cas.PtrInit().Append(typecheck(dcl, ctxStmt)) + colas := r.Colas() + do := func(i int, t *types.Type) { + n := r.List().Index(i) + if ir.IsBlank(n) { + return } - - tmp := o.newTemp(recv.Left().Type().Elem(), recv.Left().Type().Elem().HasPointers()) - as := ir.Nod(ir.OAS, dst, tmp) - cas.PtrInit().Append(typecheck(as, ctxStmt)) - dst = tmp - } - if !ir.IsBlank(ok) { - if def { - dcl := ir.Nod(ir.ODCL, ok, nil) - cas.PtrInit().Append(typecheck(dcl, ctxStmt)) + // If this is case x := <-ch or case x, y := <-ch, the case has + // the ODCL nodes to declare x and y. We want to delay that + // declaration (and possible allocation) until inside the case body. + // Delete the ODCL nodes here and recreate them inside the body below. + if colas { + if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).Left() == n { + init = init[1:] + } + dcl := ir.Nod(ir.ODCL, n, nil) + dcl = typecheck(dcl, ctxStmt) + ncas.PtrInit().Append(dcl) } - - tmp := o.newTemp(types.Types[types.TBOOL], false) - as := ir.Nod(ir.OAS, ok, conv(tmp, ok.Type())) - cas.PtrInit().Append(typecheck(as, ctxStmt)) - ok = tmp + tmp := o.newTemp(t, t.HasPointers()) + as := ir.Nod(ir.OAS, n, conv(tmp, n.Type())) + as = typecheck(as, ctxStmt) + ncas.PtrInit().Append(as) + r.PtrList().SetIndex(i, tmp) } - - if r.Op() == ir.OSELRECV { - r.SetLeft(dst) - } else { - r := r.(*ir.AssignListStmt) - r.List().SetIndex(0, dst) - r.List().SetIndex(1, ok) + do(0, recv.Left().Type().Elem()) + do(1, types.Types[types.TBOOL]) + if len(init) != 0 { + ir.DumpList("ninit", r.Init()) + base.Fatalf("ninit on select recv") } - orderBlock(cas.PtrInit(), o.free) + orderBlock(ncas.PtrInit(), o.free) case ir.OSEND: if r.Init().Len() != 0 { diff --git a/src/cmd/compile/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go index a3ce14128cb..c017b8e29aa 100644 --- a/src/cmd/compile/internal/gc/select.go +++ b/src/cmd/compile/internal/gc/select.go @@ -32,6 +32,14 @@ func typecheckselect(sel *ir.SelectStmt) { n := ncase.List().First() ncase.SetLeft(n) ncase.PtrList().Set(nil) + oselrecv2 := func(dst, recv ir.Node, colas bool) { + n := ir.NodAt(n.Pos(), ir.OSELRECV2, nil, nil) + n.PtrList().Set2(dst, ir.BlankNode) + n.PtrRlist().Set1(recv) + n.SetColas(colas) + n.SetTypecheck(1) + ncase.SetLeft(n) + } switch n.Op() { default: pos := n.Pos() @@ -45,7 +53,7 @@ func typecheckselect(sel *ir.SelectStmt) { base.ErrorfAt(pos, "select case must be receive, send or assign recv") case ir.OAS: - // convert x = <-c into OSELRECV(x, <-c). + // convert x = <-c into x, _ = <-c // remove implicit conversions; the eventual assignment // will reintroduce them. if r := n.Right(); r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE { @@ -57,10 +65,9 @@ func typecheckselect(sel *ir.SelectStmt) { base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side") break } - n.SetOp(ir.OSELRECV) + oselrecv2(n.Left(), n.Right(), n.Colas()) case ir.OAS2RECV: - // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok if n.Rlist().First().Op() != ir.ORECV { base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side") break @@ -68,12 +75,8 @@ func typecheckselect(sel *ir.SelectStmt) { n.SetOp(ir.OSELRECV2) case ir.ORECV: - // convert <-c into OSELRECV(_, <-c) - as := ir.NewAssignStmt(n.Pos(), ir.BlankNode, n) - as.SetOp(ir.OSELRECV) - as.SetTypecheck(1) - n = as - ncase.SetLeft(n) + // convert <-c into _, _ = <-c + oselrecv2(ir.BlankNode, n, false) case ir.OSEND: break @@ -129,14 +132,6 @@ func walkselectcases(cases ir.Nodes) []ir.Node { case ir.OSEND: // already ok - case ir.OSELRECV: - r := n.(*ir.AssignStmt) - if ir.IsBlank(r.Left()) { - n = r.Right() - break - } - r.SetOp(ir.OAS) - case ir.OSELRECV2: r := n.(*ir.AssignListStmt) if ir.IsBlank(r.List().First()) && ir.IsBlank(r.List().Second()) { @@ -165,29 +160,11 @@ func walkselectcases(cases ir.Nodes) []ir.Node { dflt = cas continue } - - // Lower x, _ = <-c to x = <-c. - if sel := n; sel.Op() == ir.OSELRECV2 { - if ir.IsBlank(sel.List().Second()) { - as := ir.NewAssignStmt(sel.Pos(), sel.List().First(), sel.Rlist().First()) - as.SetOp(ir.OSELRECV) - as.SetTypecheck(1) - n = as - cas.SetLeft(n) - } - } - switch n.Op() { case ir.OSEND: n.SetRight(nodAddr(n.Right())) n.SetRight(typecheck(n.Right(), ctxExpr)) - case ir.OSELRECV: - if !ir.IsBlank(n.Left()) { - n.SetLeft(nodAddr(n.Left())) - n.SetLeft(typecheck(n.Left(), ctxExpr)) - } - case ir.OSELRECV2: if !ir.IsBlank(n.List().First()) { n.List().SetIndex(0, nodAddr(n.List().First())) @@ -217,26 +194,23 @@ func walkselectcases(cases ir.Nodes) []ir.Node { ch := n.Left() call = mkcall1(chanfn("selectnbsend", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), ch, n.Right()) - case ir.OSELRECV: - // if selectnbrecv(&v, c) { body } else { default body } - recv := n.Right().(*ir.UnaryExpr) - ch := recv.Left() - elem := n.Left() - if ir.IsBlank(elem) { - elem = nodnil() - } - call = mkcall1(chanfn("selectnbrecv", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, ch) - case ir.OSELRECV2: - // if selectnbrecv2(&v, &received, c) { body } else { default body } recv := n.Rlist().First().(*ir.UnaryExpr) ch := recv.Left() elem := n.List().First() if ir.IsBlank(elem) { elem = nodnil() } - receivedp := typecheck(nodAddr(n.List().Second()), ctxExpr) - call = mkcall1(chanfn("selectnbrecv2", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, receivedp, ch) + if ir.IsBlank(n.List().Second()) { + // if selectnbrecv(&v, c) { body } else { default body } + call = mkcall1(chanfn("selectnbrecv", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, ch) + } else { + // TODO(cuonglm): make this use selectnbrecv() + // if selectnbrecv2(&v, &received, c) { body } else { default body } + receivedp := ir.Nod(ir.OADDR, n.List().Second(), nil) + receivedp = typecheck(receivedp, ctxExpr) + call = mkcall1(chanfn("selectnbrecv2", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, receivedp, ch) + } } r.SetLeft(typecheck(call, ctxExpr)) @@ -292,12 +266,6 @@ func walkselectcases(cases ir.Nodes) []ir.Node { nsends++ c = n.Left() elem = n.Right() - case ir.OSELRECV: - nrecvs++ - i = ncas - nrecvs - recv := n.Right().(*ir.UnaryExpr) - c = recv.Left() - elem = n.Left() case ir.OSELRECV2: nrecvs++ i = ncas - nrecvs @@ -355,7 +323,7 @@ func walkselectcases(cases ir.Nodes) []ir.Node { r := ir.Nod(ir.OIF, cond, nil) - if n := cas.Left(); n != nil && n.Op() == ir.OSELRECV2 { + if n := cas.Left(); n != nil && n.Op() == ir.OSELRECV2 && !ir.IsBlank(n.List().Second()) { x := ir.Nod(ir.OAS, n.List().Second(), recvOK) r.PtrBody().Append(typecheck(x, ctxStmt)) }