[dev.regabi] cmd/compile: use OSELRECV2 for all <-c variants

OSELRECV2 can represent all possible receive clauses that can appear
in a select statement, and it simplifies later code, so use it instead.

Follow up CL will remove OSELRECV.

Passes buildall w/ toolstash -cmp.

Change-Id: Ibbdae45287ffd888acd8dc89ca8d99e454277cd1
Reviewed-on: https://go-review.googlesource.com/c/go/+/275458
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Cuong Manh Le 2020-12-07 03:24:04 +07:00
parent 88e1415d08
commit 0328c3b660
3 changed files with 61 additions and 133 deletions

View file

@ -399,9 +399,6 @@ func (e *Escape) stmt(n ir.Node) {
e.stmt(cas.Left()) e.stmt(cas.Left())
e.block(cas.Body()) e.block(cas.Body())
} }
case ir.OSELRECV:
n := n.(*ir.AssignStmt)
e.assign(n.Left(), n.Right(), "selrecv", n)
case ir.OSELRECV2: case ir.OSELRECV2:
n := n.(*ir.AssignListStmt) n := n.(*ir.AssignListStmt)
e.assign(n.List().First(), n.Rlist().First(), "selrecv", n) e.assign(n.List().First(), n.Rlist().First(), "selrecv", n)

View file

@ -872,15 +872,14 @@ func (o *Order) stmt(n ir.Node) {
// give this away). // give this away).
case ir.OSELECT: case ir.OSELECT:
t := o.markTemp() t := o.markTemp()
for _, ncas := range n.List().Slice() {
for _, cas := range n.List().Slice() { ncas := ncas.(*ir.CaseStmt)
cas := cas.(*ir.CaseStmt) r := ncas.Left()
r := cas.Left() setlineno(ncas)
setlineno(cas)
// Append any new body prologue to ninit. // Append any new body prologue to ninit.
// The next loop will insert ninit into nbody. // The next loop will insert ninit into nbody.
if cas.Init().Len() != 0 { if ncas.Init().Len() != 0 {
base.Fatalf("order select ninit") base.Fatalf("order select ninit")
} }
if r == nil { if r == nil {
@ -891,84 +890,48 @@ func (o *Order) stmt(n ir.Node) {
ir.Dump("select case", r) ir.Dump("select case", r)
base.Fatalf("unknown op in select %v", r.Op()) base.Fatalf("unknown op in select %v", r.Op())
case ir.OSELRECV, ir.OSELRECV2: case ir.OSELRECV2:
var dst, ok ir.Node // case x, ok = <-c
var recv *ir.UnaryExpr recv := r.Rlist().First().(*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")
}
recv.SetLeft(o.expr(recv.Left(), nil)) recv.SetLeft(o.expr(recv.Left(), nil))
if recv.Left().Op() != ir.ONAME { if recv.Left().Op() != ir.ONAME {
recv.SetLeft(o.copyExpr(recv.Left())) 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. colas := r.Colas()
// avoids problems with target being addressed, as usual. do := func(i int, t *types.Type) {
// NOTE: If we wanted to be clever, we could arrange for just one n := r.List().Index(i)
// temporary per distinct type, sharing the temp among all receives if ir.IsBlank(n) {
// with that temp. Similarly one ok bool could be shared among all return
// 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))
} }
// If this is case x := <-ch or case x, y := <-ch, the case has
tmp := o.newTemp(recv.Left().Type().Elem(), recv.Left().Type().Elem().HasPointers()) // the ODCL nodes to declare x and y. We want to delay that
as := ir.Nod(ir.OAS, dst, tmp) // declaration (and possible allocation) until inside the case body.
cas.PtrInit().Append(typecheck(as, ctxStmt)) // Delete the ODCL nodes here and recreate them inside the body below.
dst = tmp if colas {
} if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).Left() == n {
if !ir.IsBlank(ok) { init = init[1:]
if def { }
dcl := ir.Nod(ir.ODCL, ok, nil) dcl := ir.Nod(ir.ODCL, n, nil)
cas.PtrInit().Append(typecheck(dcl, ctxStmt)) dcl = typecheck(dcl, ctxStmt)
ncas.PtrInit().Append(dcl)
} }
tmp := o.newTemp(t, t.HasPointers())
tmp := o.newTemp(types.Types[types.TBOOL], false) as := ir.Nod(ir.OAS, n, conv(tmp, n.Type()))
as := ir.Nod(ir.OAS, ok, conv(tmp, ok.Type())) as = typecheck(as, ctxStmt)
cas.PtrInit().Append(typecheck(as, ctxStmt)) ncas.PtrInit().Append(as)
ok = tmp r.PtrList().SetIndex(i, tmp)
} }
do(0, recv.Left().Type().Elem())
if r.Op() == ir.OSELRECV { do(1, types.Types[types.TBOOL])
r.SetLeft(dst) if len(init) != 0 {
} else { ir.DumpList("ninit", r.Init())
r := r.(*ir.AssignListStmt) base.Fatalf("ninit on select recv")
r.List().SetIndex(0, dst)
r.List().SetIndex(1, ok)
} }
orderBlock(cas.PtrInit(), o.free) orderBlock(ncas.PtrInit(), o.free)
case ir.OSEND: case ir.OSEND:
if r.Init().Len() != 0 { if r.Init().Len() != 0 {

View file

@ -32,6 +32,14 @@ func typecheckselect(sel *ir.SelectStmt) {
n := ncase.List().First() n := ncase.List().First()
ncase.SetLeft(n) ncase.SetLeft(n)
ncase.PtrList().Set(nil) 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() { switch n.Op() {
default: default:
pos := n.Pos() pos := n.Pos()
@ -45,7 +53,7 @@ func typecheckselect(sel *ir.SelectStmt) {
base.ErrorfAt(pos, "select case must be receive, send or assign recv") base.ErrorfAt(pos, "select case must be receive, send or assign recv")
case ir.OAS: case ir.OAS:
// convert x = <-c into OSELRECV(x, <-c). // convert x = <-c into x, _ = <-c
// remove implicit conversions; the eventual assignment // remove implicit conversions; the eventual assignment
// will reintroduce them. // will reintroduce them.
if r := n.Right(); r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE { 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") base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side")
break break
} }
n.SetOp(ir.OSELRECV) oselrecv2(n.Left(), n.Right(), n.Colas())
case ir.OAS2RECV: case ir.OAS2RECV:
// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
if n.Rlist().First().Op() != ir.ORECV { if n.Rlist().First().Op() != ir.ORECV {
base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side") base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side")
break break
@ -68,12 +75,8 @@ func typecheckselect(sel *ir.SelectStmt) {
n.SetOp(ir.OSELRECV2) n.SetOp(ir.OSELRECV2)
case ir.ORECV: case ir.ORECV:
// convert <-c into OSELRECV(_, <-c) // convert <-c into _, _ = <-c
as := ir.NewAssignStmt(n.Pos(), ir.BlankNode, n) oselrecv2(ir.BlankNode, n, false)
as.SetOp(ir.OSELRECV)
as.SetTypecheck(1)
n = as
ncase.SetLeft(n)
case ir.OSEND: case ir.OSEND:
break break
@ -129,14 +132,6 @@ func walkselectcases(cases ir.Nodes) []ir.Node {
case ir.OSEND: case ir.OSEND:
// already ok // 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: case ir.OSELRECV2:
r := n.(*ir.AssignListStmt) r := n.(*ir.AssignListStmt)
if ir.IsBlank(r.List().First()) && ir.IsBlank(r.List().Second()) { if ir.IsBlank(r.List().First()) && ir.IsBlank(r.List().Second()) {
@ -165,29 +160,11 @@ func walkselectcases(cases ir.Nodes) []ir.Node {
dflt = cas dflt = cas
continue 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() { switch n.Op() {
case ir.OSEND: case ir.OSEND:
n.SetRight(nodAddr(n.Right())) n.SetRight(nodAddr(n.Right()))
n.SetRight(typecheck(n.Right(), ctxExpr)) 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: case ir.OSELRECV2:
if !ir.IsBlank(n.List().First()) { if !ir.IsBlank(n.List().First()) {
n.List().SetIndex(0, nodAddr(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() ch := n.Left()
call = mkcall1(chanfn("selectnbsend", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), ch, n.Right()) 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: case ir.OSELRECV2:
// if selectnbrecv2(&v, &received, c) { body } else { default body }
recv := n.Rlist().First().(*ir.UnaryExpr) recv := n.Rlist().First().(*ir.UnaryExpr)
ch := recv.Left() ch := recv.Left()
elem := n.List().First() elem := n.List().First()
if ir.IsBlank(elem) { if ir.IsBlank(elem) {
elem = nodnil() elem = nodnil()
} }
receivedp := typecheck(nodAddr(n.List().Second()), ctxExpr) if ir.IsBlank(n.List().Second()) {
call = mkcall1(chanfn("selectnbrecv2", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, receivedp, ch) // 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)) r.SetLeft(typecheck(call, ctxExpr))
@ -292,12 +266,6 @@ func walkselectcases(cases ir.Nodes) []ir.Node {
nsends++ nsends++
c = n.Left() c = n.Left()
elem = n.Right() elem = n.Right()
case ir.OSELRECV:
nrecvs++
i = ncas - nrecvs
recv := n.Right().(*ir.UnaryExpr)
c = recv.Left()
elem = n.Left()
case ir.OSELRECV2: case ir.OSELRECV2:
nrecvs++ nrecvs++
i = ncas - nrecvs i = ncas - nrecvs
@ -355,7 +323,7 @@ func walkselectcases(cases ir.Nodes) []ir.Node {
r := ir.Nod(ir.OIF, cond, nil) 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) x := ir.Nod(ir.OAS, n.List().Second(), recvOK)
r.PtrBody().Append(typecheck(x, ctxStmt)) r.PtrBody().Append(typecheck(x, ctxStmt))
} }