case ir.OSELRECV:
e.assign(n.Left(), n.Right(), "selrecv", n)
case ir.OSELRECV2:
- e.assign(n.Left(), n.Right(), "selrecv", n)
- e.assign(n.List().First(), nil, "selrecv", n)
+ e.assign(n.List().First(), n.Rlist().First(), "selrecv", n)
+ e.assign(n.List().Second(), nil, "selrecv", n)
case ir.ORECV:
// TODO(mdempsky): Consider e.discard(n.Left).
e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit
}
case ir.OAS2DOTTYPE: // v, ok = x.(type)
- e.assign(n.List().First(), n.Right(), "assign-pair-dot-type", n)
+ e.assign(n.List().First(), n.Rlist().First(), "assign-pair-dot-type", n)
e.assign(n.List().Second(), nil, "assign-pair-dot-type", n)
case ir.OAS2MAPR: // v, ok = m[k]
- e.assign(n.List().First(), n.Right(), "assign-pair-mapr", n)
+ e.assign(n.List().First(), n.Rlist().First(), "assign-pair-mapr", n)
e.assign(n.List().Second(), nil, "assign-pair-mapr", n)
case ir.OAS2RECV: // v, ok = <-ch
- e.assign(n.List().First(), n.Right(), "assign-pair-receive", n)
+ e.assign(n.List().First(), n.Rlist().First(), "assign-pair-receive", n)
e.assign(n.List().Second(), nil, "assign-pair-receive", n)
case ir.OAS2FUNC:
- e.stmts(n.Right().Init())
- e.call(e.addrs(n.List()), n.Right(), nil)
+ e.stmts(n.Rlist().First().Init())
+ e.call(e.addrs(n.List()), n.Rlist().First(), nil)
case ir.ORETURN:
results := e.curfn.Type().Results().FieldSlice()
for i, v := range n.List().Slice() {
// that represents storing into the represented location.
func (e *Escape) addr(n ir.Node) EscHole {
if n == nil || ir.IsBlank(n) {
- // Can happen at least in OSELRECV.
- // TODO(mdempsky): Anywhere else?
+ // Can happen in select case, range, maybe others.
return e.discardHole()
}
w.expr(n.Right())
}
- case ir.OAS2:
+ case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
w.op(ir.OAS2)
w.pos(n.Pos())
w.exprList(n.List())
w.exprList(n.Rlist())
- case ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
- w.op(ir.OAS2)
- w.pos(n.Pos())
- w.exprList(n.List())
- w.exprList(ir.AsNodes([]ir.Node{n.Right()}))
-
case ir.ORETURN:
w.op(ir.ORETURN)
w.pos(n.Pos())
case ir.OAS:
d.inspect(n.Right())
case ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
- d.inspect(n.Right())
+ d.inspect(n.Rlist().First())
case ir.ODCLFUNC:
d.inspectList(n.Body())
default:
}
// Turn an OINLCALL into a statement.
-func inlconv2stmt(n ir.Node) {
- n.SetOp(ir.OBLOCK)
-
- // n->ninit stays
- n.PtrList().Set(n.Body().Slice())
-
- n.PtrBody().Set(nil)
- n.PtrRlist().Set(nil)
+func inlconv2stmt(inlcall ir.Node) ir.Node {
+ n := ir.NodAt(inlcall.Pos(), ir.OBLOCK, nil, nil)
+ n.SetList(inlcall.Body())
+ n.SetInit(inlcall.Init())
+ return n
}
// Turn an OINLCALL into a single valued expression.
lno := setlineno(n)
inlnodelist(n.Init(), maxCost, inlMap)
- for _, n1 := range n.Init().Slice() {
+ init := n.Init().Slice()
+ for i, n1 := range init {
if n1.Op() == ir.OINLCALL {
- inlconv2stmt(n1)
+ init[i] = inlconv2stmt(n1)
}
}
n.SetRight(inlnode(n.Right(), maxCost, inlMap))
if n.Right() != nil && n.Right().Op() == ir.OINLCALL {
if n.Op() == ir.OFOR || n.Op() == ir.OFORUNTIL {
- inlconv2stmt(n.Right())
- } else if n.Op() == ir.OAS2FUNC {
- n.PtrRlist().Set(inlconv2list(n.Right()))
- n.SetRight(nil)
- n.SetOp(ir.OAS2)
- n.SetTypecheck(0)
- n = typecheck(n, ctxStmt)
+ n.SetRight(inlconv2stmt(n.Right()))
} else {
n.SetRight(inlconv2expr(n.Right()))
}
}
inlnodelist(n.List(), maxCost, inlMap)
+ s := n.List().Slice()
+ convert := inlconv2expr
if n.Op() == ir.OBLOCK {
- for _, n2 := range n.List().Slice() {
- if n2.Op() == ir.OINLCALL {
- inlconv2stmt(n2)
- }
- }
- } else {
- s := n.List().Slice()
- for i1, n1 := range s {
- if n1 != nil && n1.Op() == ir.OINLCALL {
- s[i1] = inlconv2expr(s[i1])
- }
+ convert = inlconv2stmt
+ }
+ for i, n1 := range s {
+ if n1 != nil && n1.Op() == ir.OINLCALL {
+ s[i] = convert(n1)
}
}
inlnodelist(n.Rlist(), maxCost, inlMap)
- s := n.Rlist().Slice()
- for i1, n1 := range s {
+
+ if n.Op() == ir.OAS2FUNC && n.Rlist().First().Op() == ir.OINLCALL {
+ n.PtrRlist().Set(inlconv2list(n.Rlist().First()))
+ n.SetOp(ir.OAS2)
+ n.SetTypecheck(0)
+ n = typecheck(n, ctxStmt)
+ }
+
+ s = n.Rlist().Slice()
+ for i, n1 := range s {
if n1.Op() == ir.OINLCALL {
if n.Op() == ir.OIF {
- inlconv2stmt(n1)
+ s[i] = inlconv2stmt(n1)
} else {
- s[i1] = inlconv2expr(s[i1])
+ s[i] = inlconv2expr(n1)
}
}
}
inlnodelist(n.Body(), maxCost, inlMap)
- for _, n := range n.Body().Slice() {
- if n.Op() == ir.OINLCALL {
- inlconv2stmt(n)
+ s = n.Body().Slice()
+ for i, n1 := range s {
+ if n1.Op() == ir.OINLCALL {
+ s[i] = inlconv2stmt(n1)
}
}
// and each use must redo the inlining.
// luckily these are small.
inlnodelist(call.Body(), maxCost, inlMap)
- for _, n := range call.Body().Slice() {
- if n.Op() == ir.OINLCALL {
- inlconv2stmt(n)
+ s := call.Body().Slice()
+ for i, n1 := range s {
+ if n1.Op() == ir.OINLCALL {
+ s[i] = inlconv2stmt(n1)
}
}
return n
}
- n := p.nod(stmt, ir.OAS, nil, nil) // assume common case
-
rhs := p.exprList(stmt.Rhs)
- lhs := p.assignList(stmt.Lhs, n, stmt.Op == syntax.Def)
-
- if len(lhs) == 1 && len(rhs) == 1 {
- // common case
- n.SetLeft(lhs[0])
- n.SetRight(rhs[0])
- } else {
- n.SetOp(ir.OAS2)
- n.PtrList().Set(lhs)
+ if list, ok := stmt.Lhs.(*syntax.ListExpr); ok && len(list.ElemList) != 1 || len(rhs) != 1 {
+ n := p.nod(stmt, ir.OAS2, nil, nil)
+ n.PtrList().Set(p.assignList(stmt.Lhs, n, stmt.Op == syntax.Def))
n.PtrRlist().Set(rhs)
+ return n
}
+
+ n := p.nod(stmt, ir.OAS, nil, nil)
+ n.SetLeft(p.assignList(stmt.Lhs, n, stmt.Op == syntax.Def)[0])
+ n.SetRight(rhs[0])
return n
case *syntax.BranchStmt:
// (The other candidate would be map access, but map access
// returns a pointer to the result data instead of taking a pointer
// to be filled in.)
+// TODO(rsc): t == n.Type() always; remove parameter.
func (o *Order) copyExpr(n ir.Node, t *types.Type, clear bool) ir.Node {
+ if t != n.Type() {
+ panic("copyExpr")
+ }
v := o.newTemp(t, clear)
a := ir.Nod(ir.OAS, v, n)
a = typecheck(a, ctxStmt)
// that we can ensure that if op panics
// because r is zero, the panic happens before
// the map assignment.
-
- n.SetLeft(o.safeExpr(n.Left()))
-
- // TODO(rsc): Why is this DeepCopy?
- // We should know enough about the form here
- // to do something more provably shallower.
- l := ir.DeepCopy(src.NoXPos, n.Left())
- if l.Op() == ir.OINDEXMAP {
- l.SetIndexMapLValue(false)
+ // DeepCopy is a big hammer here, but safeExpr
+ // makes sure there is nothing too deep being copied.
+ l1 := o.safeExpr(n.Left())
+ l2 := ir.DeepCopy(src.NoXPos, l1)
+ if l1.Op() == ir.OINDEXMAP {
+ l2.SetIndexMapLValue(false)
}
- l = o.copyExpr(l, n.Left().Type(), false)
- n.SetRight(ir.Nod(n.SubOp(), l, n.Right()))
- n.SetRight(typecheck(n.Right(), ctxExpr))
- n.SetRight(o.expr(n.Right(), nil))
-
- n.SetOp(ir.OAS)
- n.ResetAux()
+ l2 = o.copyExpr(l2, l2.Type(), false)
+ r := ir.NodAt(n.Pos(), n.SubOp(), l2, n.Right())
+ r = typecheck(r, ctxExpr)
+ r = o.expr(r, nil)
+ n = ir.NodAt(n.Pos(), ir.OAS, l1, r)
+ n = typecheck(n, ctxStmt)
}
o.mapAssign(n)
case ir.OAS2FUNC:
t := o.markTemp()
o.exprList(n.List())
- o.init(n.Right())
- o.call(n.Right())
+ o.init(n.Rlist().First())
+ o.call(n.Rlist().First())
o.as2(n)
o.cleanTemp(t)
t := o.markTemp()
o.exprList(n.List())
- switch r := n.Right(); r.Op() {
+ switch r := n.Rlist().First(); r.Op() {
case ir.ODOTTYPE2, ir.ORECV:
r.SetLeft(o.expr(r.Left(), nil))
case ir.OINDEXMAP:
ir.Dump("select case", r)
base.Fatalf("unknown op in select %v", r.Op())
- // 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.
case ir.OSELRECV, ir.OSELRECV2:
+ var dst, ok, recv ir.Node
+ if r.Op() == ir.OSELRECV {
+ // case x = <-c
+ // case <-c (dst is ir.BlankNode)
+ dst, ok, recv = r.Left(), ir.BlankNode, r.Right()
+ } else {
+ // case x, ok = <-c
+ dst, ok, recv = r.List().First(), r.List().Second(), r.Rlist().First()
+ }
+
+ // 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 r.Colas() {
- i := 0
- if r.Init().Len() != 0 && r.Init().First().Op() == ir.ODCL && r.Init().First().Left() == r.Left() {
- i++
- }
- if i < r.Init().Len() && r.Init().Index(i).Op() == ir.ODCL && r.List().Len() != 0 && r.Init().Index(i).Left() == r.List().First() {
- i++
+ init := r.Init().Slice()
+ if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].Left() == dst {
+ init = init[1:]
}
- if i >= r.Init().Len() {
- r.PtrInit().Set(nil)
+ if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].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 x = <-c
- // case x, ok = <-c
- // r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c.
- // r->left == N means 'case <-c'.
- // c is always evaluated; x and ok are only evaluated when assigned.
- r.Right().SetLeft(o.expr(r.Right().Left(), nil))
-
- if r.Right().Left().Op() != ir.ONAME {
- r.Right().SetLeft(o.copyExpr(r.Right().Left(), r.Right().Left().Type(), false))
+ recv.SetLeft(o.expr(recv.Left(), nil))
+ if recv.Left().Op() != ir.ONAME {
+ recv.SetLeft(o.copyExpr(recv.Left(), recv.Left().Type(), false))
}
// Introduce temporary for receive and move actual copy into case body.
// 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 r.Left() != nil && ir.IsBlank(r.Left()) {
- r.SetLeft(nil)
- }
- if r.Left() != nil {
+ 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.
- tmp1 := r.Left()
-
if r.Colas() {
- tmp2 := ir.Nod(ir.ODCL, tmp1, nil)
- tmp2 = typecheck(tmp2, ctxStmt)
- n2.PtrInit().Append(tmp2)
+ dcl := ir.Nod(ir.ODCL, dst, nil)
+ dcl = typecheck(dcl, ctxStmt)
+ n2.PtrInit().Append(dcl)
}
- r.SetLeft(o.newTemp(r.Right().Left().Type().Elem(), r.Right().Left().Type().Elem().HasPointers()))
- tmp2 := ir.Nod(ir.OAS, tmp1, r.Left())
- tmp2 = typecheck(tmp2, ctxStmt)
- n2.PtrInit().Append(tmp2)
+ tmp := o.newTemp(recv.Left().Type().Elem(), recv.Left().Type().Elem().HasPointers())
+ as := ir.Nod(ir.OAS, dst, tmp)
+ as = typecheck(as, ctxStmt)
+ n2.PtrInit().Append(as)
+ dst = tmp
}
-
- if r.List().Len() != 0 && ir.IsBlank(r.List().First()) {
- r.PtrList().Set(nil)
- }
- if r.List().Len() != 0 {
- tmp1 := r.List().First()
+ if !ir.IsBlank(ok) {
if r.Colas() {
- tmp2 := ir.Nod(ir.ODCL, tmp1, nil)
- tmp2 = typecheck(tmp2, ctxStmt)
- n2.PtrInit().Append(tmp2)
+ dcl := ir.Nod(ir.ODCL, ok, nil)
+ dcl = typecheck(dcl, ctxStmt)
+ n2.PtrInit().Append(dcl)
}
- r.PtrList().Set1(o.newTemp(types.Types[types.TBOOL], false))
- tmp2 := okas(tmp1, r.List().First())
- tmp2 = typecheck(tmp2, ctxStmt)
- n2.PtrInit().Append(tmp2)
+ tmp := o.newTemp(types.Types[types.TBOOL], false)
+ as := okas(ok, tmp)
+ as = typecheck(as, ctxStmt)
+ n2.PtrInit().Append(as)
+ ok = tmp
+ }
+
+ if r.Op() == ir.OSELRECV {
+ r.SetLeft(dst)
+ } else {
+ r.List().SetIndex(0, dst)
+ r.List().SetIndex(1, ok)
}
orderBlock(n2.PtrInit(), o.free)
func (o *Order) okAs2(n ir.Node) {
var tmp1, tmp2 ir.Node
if !ir.IsBlank(n.List().First()) {
- typ := n.Right().Type()
+ typ := n.Rlist().First().Type()
tmp1 = o.newTemp(typ, typ.HasPointers())
}
// simpler forms. The result must be assigned back to n.
// Node n may also be modified in place, and may also be
// the returned node.
-func walkrange(n ir.Node) ir.Node {
- if isMapClear(n) {
- m := n.Right()
+func walkrange(nrange ir.Node) ir.Node {
+ if isMapClear(nrange) {
+ m := nrange.Right()
lno := setlineno(m)
- n = mapClear(m)
+ n := mapClear(m)
base.Pos = lno
return n
}
+ nfor := ir.NodAt(nrange.Pos(), ir.OFOR, nil, nil)
+ nfor.SetInit(nrange.Init())
+ nfor.SetSym(nrange.Sym())
+
// variable name conventions:
// ohv1, hv1, hv2: hidden (old) val 1, 2
// ha, hit: hidden aggregate, iterator
// hb: hidden bool
// a, v1, v2: not hidden aggregate, val 1, 2
- t := n.Type()
+ t := nrange.Type()
- a := n.Right()
+ a := nrange.Right()
lno := setlineno(a)
- n.SetRight(nil)
var v1, v2 ir.Node
- l := n.List().Len()
+ l := nrange.List().Len()
if l > 0 {
- v1 = n.List().First()
+ v1 = nrange.List().First()
}
if l > 1 {
- v2 = n.List().Second()
+ v2 = nrange.List().Second()
}
if ir.IsBlank(v2) {
base.Fatalf("walkrange: v2 != nil while v1 == nil")
}
- // n.List has no meaning anymore, clear it
- // to avoid erroneous processing by racewalk.
- n.PtrList().Set(nil)
-
var ifGuard ir.Node
- translatedLoopOp := ir.OFOR
-
var body []ir.Node
var init []ir.Node
switch t.Etype {
base.Fatalf("walkrange")
case types.TARRAY, types.TSLICE:
- if arrayClear(n, v1, v2, a) {
+ if nn := arrayClear(nrange, v1, v2, a); nn != nil {
base.Pos = lno
- return n
+ return nn
}
// order.stmt arranged for a copy of the array/slice variable if needed.
init = append(init, ir.Nod(ir.OAS, hv1, nil))
init = append(init, ir.Nod(ir.OAS, hn, ir.Nod(ir.OLEN, ha, nil)))
- n.SetLeft(ir.Nod(ir.OLT, hv1, hn))
- n.SetRight(ir.Nod(ir.OAS, hv1, ir.Nod(ir.OADD, hv1, nodintconst(1))))
+ nfor.SetLeft(ir.Nod(ir.OLT, hv1, hn))
+ nfor.SetRight(ir.Nod(ir.OAS, hv1, ir.Nod(ir.OADD, hv1, nodintconst(1))))
// for range ha { body }
if v1 == nil {
}
// for v1, v2 := range ha { body }
- if cheapComputableIndex(n.Type().Elem().Width) {
+ if cheapComputableIndex(nrange.Type().Elem().Width) {
// v1, v2 = hv1, ha[hv1]
tmp := ir.Nod(ir.OINDEX, ha, hv1)
tmp.SetBounded(true)
// Enhance the prove pass to understand this.
ifGuard = ir.Nod(ir.OIF, nil, nil)
ifGuard.SetLeft(ir.Nod(ir.OLT, hv1, hn))
- translatedLoopOp = ir.OFORUNTIL
+ nfor.SetOp(ir.OFORUNTIL)
- hp := temp(types.NewPtr(n.Type().Elem()))
+ hp := temp(types.NewPtr(nrange.Type().Elem()))
tmp := ir.Nod(ir.OINDEX, ha, nodintconst(0))
tmp.SetBounded(true)
init = append(init, ir.Nod(ir.OAS, hp, ir.Nod(ir.OADDR, tmp, nil)))
// end of the allocation.
a = ir.Nod(ir.OAS, hp, addptr(hp, t.Elem().Width))
a = typecheck(a, ctxStmt)
- n.PtrList().Set1(a)
+ nfor.PtrList().Set1(a)
case types.TMAP:
// order.stmt allocated the iterator for us.
// we only use a once, so no copy needed.
ha := a
- hit := prealloc[n]
+ hit := prealloc[nrange]
th := hit.Type()
- n.SetLeft(nil)
keysym := th.Field(0).Sym // depends on layout of iterator struct. See reflect.go:hiter
elemsym := th.Field(1).Sym // ditto
fn = substArgTypes(fn, t.Key(), t.Elem(), th)
init = append(init, mkcall1(fn, nil, nil, typename(t), ha, ir.Nod(ir.OADDR, hit, nil)))
- n.SetLeft(ir.Nod(ir.ONE, nodSym(ir.ODOT, hit, keysym), nodnil()))
+ nfor.SetLeft(ir.Nod(ir.ONE, nodSym(ir.ODOT, hit, keysym), nodnil()))
fn = syslook("mapiternext")
fn = substArgTypes(fn, th)
- n.SetRight(mkcall1(fn, nil, nil, ir.Nod(ir.OADDR, hit, nil)))
+ nfor.SetRight(mkcall1(fn, nil, nil, ir.Nod(ir.OADDR, hit, nil)))
key := nodSym(ir.ODOT, hit, keysym)
key = ir.Nod(ir.ODEREF, key, nil)
// order.stmt arranged for a copy of the channel variable.
ha := a
- n.SetLeft(nil)
-
hv1 := temp(t.Elem())
hv1.SetTypecheck(1)
if t.Elem().HasPointers() {
}
hb := temp(types.Types[types.TBOOL])
- n.SetLeft(ir.Nod(ir.ONE, hb, nodbool(false)))
+ nfor.SetLeft(ir.Nod(ir.ONE, hb, nodbool(false)))
a := ir.Nod(ir.OAS2RECV, nil, nil)
a.SetTypecheck(1)
a.PtrList().Set2(hv1, hb)
- a.SetRight(ir.Nod(ir.ORECV, ha, nil))
- n.Left().PtrInit().Set1(a)
+ a.PtrRlist().Set1(ir.Nod(ir.ORECV, ha, nil))
+ nfor.Left().PtrInit().Set1(a)
if v1 == nil {
body = nil
} else {
init = append(init, ir.Nod(ir.OAS, hv1, nil))
// hv1 < len(ha)
- n.SetLeft(ir.Nod(ir.OLT, hv1, ir.Nod(ir.OLEN, ha, nil)))
+ nfor.SetLeft(ir.Nod(ir.OLT, hv1, ir.Nod(ir.OLEN, ha, nil)))
if v1 != nil {
// hv1t = hv1
}
}
- n.SetOp(translatedLoopOp)
typecheckslice(init, ctxStmt)
if ifGuard != nil {
ifGuard.PtrInit().Append(init...)
ifGuard = typecheck(ifGuard, ctxStmt)
} else {
- n.PtrInit().Append(init...)
+ nfor.PtrInit().Append(init...)
}
- typecheckslice(n.Left().Init().Slice(), ctxStmt)
+ typecheckslice(nfor.Left().Init().Slice(), ctxStmt)
- n.SetLeft(typecheck(n.Left(), ctxExpr))
- n.SetLeft(defaultlit(n.Left(), nil))
- n.SetRight(typecheck(n.Right(), ctxStmt))
+ nfor.SetLeft(typecheck(nfor.Left(), ctxExpr))
+ nfor.SetLeft(defaultlit(nfor.Left(), nil))
+ nfor.SetRight(typecheck(nfor.Right(), ctxStmt))
typecheckslice(body, ctxStmt)
- n.PtrBody().Prepend(body...)
+ nfor.PtrBody().Append(body...)
+ nfor.PtrBody().Append(nrange.Body().Slice()...)
+ var n ir.Node = nfor
if ifGuard != nil {
ifGuard.PtrBody().Set1(n)
n = ifGuard
// in which the evaluation of a is side-effect-free.
//
// Parameters are as in walkrange: "for v1, v2 = range a".
-func arrayClear(n, v1, v2, a ir.Node) bool {
+func arrayClear(loop, v1, v2, a ir.Node) ir.Node {
if base.Flag.N != 0 || instrumenting {
- return false
+ return nil
}
if v1 == nil || v2 != nil {
- return false
+ return nil
}
- if n.Body().Len() != 1 || n.Body().First() == nil {
- return false
+ if loop.Body().Len() != 1 || loop.Body().First() == nil {
+ return nil
}
- stmt := n.Body().First() // only stmt in body
+ stmt := loop.Body().First() // only stmt in body
if stmt.Op() != ir.OAS || stmt.Left().Op() != ir.OINDEX {
- return false
+ return nil
}
if !samesafeexpr(stmt.Left().Left(), a) || !samesafeexpr(stmt.Left().Right(), v1) {
- return false
+ return nil
}
- elemsize := n.Type().Elem().Width
+ elemsize := loop.Type().Elem().Width
if elemsize <= 0 || !isZero(stmt.Right()) {
- return false
+ return nil
}
// Convert to
// memclr{NoHeap,Has}Pointers(hp, hn)
// i = len(a) - 1
// }
- n.SetOp(ir.OIF)
-
+ n := ir.Nod(ir.OIF, nil, nil)
n.PtrBody().Set(nil)
n.SetLeft(ir.Nod(ir.ONE, ir.Nod(ir.OLEN, a, nil), nodintconst(0)))
n.SetLeft(defaultlit(n.Left(), nil))
typecheckslice(n.Body().Slice(), ctxStmt)
n = walkstmt(n)
- return true
+ return n
}
// addptr returns (*T)(uintptr(p) + n).
}
base.ErrorfAt(pos, "select case must be receive, send or assign recv")
- // convert x = <-c into OSELRECV(x, <-c).
- // remove implicit conversions; the eventual assignment
- // will reintroduce them.
case ir.OAS:
+ // convert x = <-c into OSELRECV(x, <-c).
+ // remove implicit conversions; the eventual assignment
+ // will reintroduce them.
if (n.Right().Op() == ir.OCONVNOP || n.Right().Op() == ir.OCONVIFACE) && n.Right().Implicit() {
n.SetRight(n.Right().Left())
}
-
if n.Right().Op() != ir.ORECV {
base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side")
break
}
-
n.SetOp(ir.OSELRECV)
- // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
case ir.OAS2RECV:
- if n.Right().Op() != ir.ORECV {
+ // 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
}
-
n.SetOp(ir.OSELRECV2)
- n.SetLeft(n.List().First())
- n.PtrList().Set1(n.List().Second())
- // convert <-c into OSELRECV(N, <-c)
case ir.ORECV:
- n = ir.NodAt(n.Pos(), ir.OSELRECV, nil, n)
-
+ // convert <-c into OSELRECV(_, <-c)
+ n = ir.NodAt(n.Pos(), ir.OSELRECV, ir.BlankNode, n)
n.SetTypecheck(1)
ncase.SetLeft(n)
case ir.OSEND:
// already ok
- case ir.OSELRECV, ir.OSELRECV2:
- if n.Op() == ir.OSELRECV || n.List().Len() == 0 {
- if n.Left() == nil {
- n = n.Right()
- } else {
- n.SetOp(ir.OAS)
- }
+ case ir.OSELRECV:
+ if ir.IsBlank(n.Left()) {
+ n = n.Right()
break
}
+ n.SetOp(ir.OAS)
- if n.Left() == nil {
- ir.BlankNode = typecheck(ir.BlankNode, ctxExpr|ctxAssign)
- n.SetLeft(ir.BlankNode)
+ case ir.OSELRECV2:
+ if ir.IsBlank(n.List().First()) && ir.IsBlank(n.List().Second()) {
+ n = n.Rlist().First()
+ break
}
-
- n.SetOp(ir.OAS2)
- n.PtrList().Prepend(n.Left())
- n.PtrRlist().Set1(n.Right())
- n.SetRight(nil)
- n.SetLeft(nil)
- n.SetTypecheck(0)
- n = typecheck(n, ctxStmt)
+ n.SetOp(ir.OAS2RECV)
}
l = append(l, n)
dflt = cas
continue
}
+
+ // Lower x, _ = <-c to x = <-c.
+ if n.Op() == ir.OSELRECV2 && ir.IsBlank(n.List().Second()) {
+ n = ir.NodAt(n.Pos(), ir.OSELRECV, n.List().First(), n.Rlist().First())
+ n.SetTypecheck(1)
+ cas.SetLeft(n)
+ }
+
switch n.Op() {
case ir.OSEND:
n.SetRight(ir.Nod(ir.OADDR, n.Right(), nil))
n.SetRight(typecheck(n.Right(), ctxExpr))
- case ir.OSELRECV, ir.OSELRECV2:
- if n.Op() == ir.OSELRECV2 && n.List().Len() == 0 {
- n.SetOp(ir.OSELRECV)
- }
-
- if n.Left() != nil {
+ case ir.OSELRECV:
+ if !ir.IsBlank(n.Left()) {
n.SetLeft(ir.Nod(ir.OADDR, n.Left(), nil))
n.SetLeft(typecheck(n.Left(), ctxExpr))
}
+
+ case ir.OSELRECV2:
+ if !ir.IsBlank(n.List().First()) {
+ n.List().SetIndex(0, ir.Nod(ir.OADDR, n.List().First(), nil))
+ n.List().SetIndex(0, typecheck(n.List().First(), ctxExpr))
+ }
}
}
setlineno(n)
r := ir.Nod(ir.OIF, nil, nil)
r.PtrInit().Set(cas.Init().Slice())
+ var call ir.Node
switch n.Op() {
default:
base.Fatalf("select %v", n.Op())
case ir.OSEND:
// if selectnbsend(c, v) { body } else { default body }
ch := n.Left()
- r.SetLeft(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 }
ch := n.Right().Left()
elem := n.Left()
- if elem == nil {
+ if ir.IsBlank(elem) {
elem = nodnil()
}
- r.SetLeft(mkcall1(chanfn("selectnbrecv", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, ch))
+ 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 }
- ch := n.Right().Left()
- elem := n.Left()
- if elem == nil {
+ ch := n.Rlist().First().Left()
+ elem := n.List().First()
+ if ir.IsBlank(elem) {
elem = nodnil()
}
- receivedp := ir.Nod(ir.OADDR, n.List().First(), nil)
+ receivedp := ir.Nod(ir.OADDR, n.List().Second(), nil)
receivedp = typecheck(receivedp, ctxExpr)
- r.SetLeft(mkcall1(chanfn("selectnbrecv2", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, receivedp, ch))
+ call = mkcall1(chanfn("selectnbrecv2", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, receivedp, ch)
}
- r.SetLeft(typecheck(r.Left(), ctxExpr))
+ r.SetLeft(typecheck(call, ctxExpr))
r.PtrBody().Set(cas.Body().Slice())
r.PtrRlist().Set(append(dflt.Init().Slice(), dflt.Body().Slice()...))
return []ir.Node{r, ir.Nod(ir.OBREAK, nil, nil)}
nsends++
c = n.Left()
elem = n.Right()
- case ir.OSELRECV, ir.OSELRECV2:
+ case ir.OSELRECV:
nrecvs++
i = ncas - nrecvs
c = n.Right().Left()
elem = n.Left()
+ case ir.OSELRECV2:
+ nrecvs++
+ i = ncas - nrecvs
+ c = n.Rlist().First().Left()
+ elem = n.List().First()
}
casorder[i] = cas
c = convnop(c, types.Types[types.TUNSAFEPTR])
setField("c", c)
- if elem != nil {
+ if !ir.IsBlank(elem) {
elem = convnop(elem, types.Types[types.TUNSAFEPTR])
setField("elem", elem)
}
r := ir.Nod(ir.OIF, cond, nil)
if n := cas.Left(); n != nil && n.Op() == ir.OSELRECV2 {
- x := ir.Nod(ir.OAS, n.List().First(), recvOK)
+ x := ir.Nod(ir.OAS, n.List().Second(), recvOK)
x = typecheck(x, ctxStmt)
r.PtrBody().Append(x)
}
s.callResult(n.Left(), callGo)
case ir.OAS2DOTTYPE:
- res, resok := s.dottype(n.Right(), true)
+ res, resok := s.dottype(n.Rlist().First(), true)
deref := false
- if !canSSAType(n.Right().Type()) {
+ if !canSSAType(n.Rlist().First().Type()) {
if res.Op != ssa.OpLoad {
s.Fatalf("dottype of non-load")
}
case ir.OAS2FUNC:
// We come here only when it is an intrinsic call returning two values.
- if !isIntrinsicCall(n.Right()) {
- s.Fatalf("non-intrinsic AS2FUNC not expanded %v", n.Right())
+ if !isIntrinsicCall(n.Rlist().First()) {
+ s.Fatalf("non-intrinsic AS2FUNC not expanded %v", n.Rlist().First())
}
- v := s.intrinsicCall(n.Right())
+ v := s.intrinsicCall(n.Rlist().First())
v1 := s.newValue1(ssa.OpSelect0, n.List().First().Type(), v)
v2 := s.newValue1(ssa.OpSelect1, n.List().Second().Type(), v)
s.assign(n.List().First(), v1, false, 0)
goto mismatch
}
n.SetOp(ir.OAS2FUNC)
- n.SetRight(r)
- n.PtrRlist().Set(nil)
for i, l := range n.List().Slice() {
f := r.Type().Field(i)
if f.Type != nil && l.Type() != nil {
n.SetOp(ir.OAS2DOTTYPE)
r.SetOp(ir.ODOTTYPE2)
}
- n.SetRight(r)
- n.PtrRlist().Set(nil)
if l.Type() != nil {
checkassignto(r.Type(), l)
}
types.Types[types.TBLANK] = types.New(types.TBLANK)
ir.AsNode(s.Def).SetType(types.Types[types.TBLANK])
ir.BlankNode = ir.AsNode(s.Def)
+ ir.BlankNode.SetTypecheck(1)
s = ir.BuiltinPkg.Lookup("_")
s.Block = -100
if n.Op() == ir.OASOP {
// Rewrite x op= y into x = x op y.
- n.SetRight(ir.Nod(n.SubOp(), n.Left(), n.Right()))
- n.SetRight(typecheck(n.Right(), ctxExpr))
-
- n.SetOp(ir.OAS)
- n.ResetAux()
+ n = ir.Nod(ir.OAS, n.Left(),
+ typecheck(ir.Nod(n.SubOp(), n.Left(), n.Right()), ctxExpr))
}
if oaslit(n, init) {
case ir.OAS2FUNC:
init.AppendNodes(n.PtrInit())
- r := n.Right()
+ r := n.Rlist().First()
walkexprlistsafe(n.List().Slice(), init)
r = walkexpr(r, init)
if isIntrinsicCall(r) {
- n.SetRight(r)
+ n.PtrRlist().Set1(r)
break
}
init.Append(r)
case ir.OAS2RECV:
init.AppendNodes(n.PtrInit())
- r := n.Right()
+ r := n.Rlist().First()
walkexprlistsafe(n.List().Slice(), init)
r.SetLeft(walkexpr(r.Left(), init))
var n1 ir.Node
case ir.OAS2MAPR:
init.AppendNodes(n.PtrInit())
- r := n.Right()
+ r := n.Rlist().First()
walkexprlistsafe(n.List().Slice(), init)
r.SetLeft(walkexpr(r.Left(), init))
r.SetRight(walkexpr(r.Right(), init))
if ok := n.List().Second(); !ir.IsBlank(ok) && ok.Type().IsBoolean() {
r.Type().Field(1).Type = ok.Type()
}
- n.SetRight(r)
+ n.PtrRlist().Set1(r)
n.SetOp(ir.OAS2FUNC)
// don't generate a = *var if a is _
case ir.OAS2DOTTYPE:
walkexprlistsafe(n.List().Slice(), init)
- n.SetRight(walkexpr(n.Right(), init))
+ n.PtrRlist().SetIndex(0, walkexpr(n.Rlist().First(), init))
case ir.OCONVIFACE:
n.SetLeft(walkexpr(n.Left(), init))
mode.Fprintf(s, "%v %#v= %v", n.Left(), n.SubOp(), n.Right())
- case OAS2:
+ case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
if n.Colas() && !complexinit {
mode.Fprintf(s, "%.v := %.v", n.List(), n.Rlist())
- break
+ } else {
+ mode.Fprintf(s, "%.v = %.v", n.List(), n.Rlist())
}
- fallthrough
-
- case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- mode.Fprintf(s, "%.v = %v", n.List(), n.Right())
case ORETURN:
mode.Fprintf(s, "return %.v", n.List())
ORECOVER // recover()
ORECV // <-Left
ORUNESTR // Type(Left) (Type is string, Left is rune)
- OSELRECV // Left = <-Right.Left: (appears as .Left of OCASE; Right.Op == ORECV)
- OSELRECV2 // List = <-Right.Left: (appears as .Left of OCASE; count(List) == 2, Right.Op == ORECV)
+ OSELRECV // like OAS: Left = Right where Right.Op = ORECV (appears as .Left of OCASE)
+ OSELRECV2 // like OAS2: List = Rlist where len(List)=2, len(Rlist)=1, Rlist[0].Op = ORECV (appears as .Left of OCASE)
OIOTA // iota
OREAL // real(Left)
OIMAG // imag(Left)
// Check at both 1 and 8-byte alignments.
t.Run("Copy", func(t *testing.T) {
const copyCode = `package x
-func s128a1(x *[128]int8) [128]int8 {
+func s128a1(x *[128]int8) [128]int8 {
return *x
}
func s127a1(x *[127]int8) [127]int8 {
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
- `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~R0 = \u003cN\u003e (assign-pair)"},`+
+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~R0 = \u0026y.b (assign-pair)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r2 = ~R0:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return (*int)(~R0) (return)"}]}`)
})