[dev.cc] cmd/internal/gc, cmd/new6g etc: convert from cmd/gc, cmd/6g etc

First draft of converted Go compiler, using rsc.io/c2go rev 83d795a.

Change-Id: I29f4c7010de07d2ff1947bbca9865879d83c32c3
Reviewed-on: https://go-review.googlesource.com/4851
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/src/cmd/internal/gc/select.go b/src/cmd/internal/gc/select.go
new file mode 100644
index 0000000..9e659d1
--- /dev/null
+++ b/src/cmd/internal/gc/select.go
@@ -0,0 +1,389 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gc
+
+/*
+ * select
+ */
+func typecheckselect(sel *Node) {
+	var ncase *Node
+	var n *Node
+	var def *Node
+	var l *NodeList
+	var lno int
+	var count int
+
+	def = nil
+	lno = int(setlineno(sel))
+	count = 0
+	typechecklist(sel.Ninit, Etop)
+	for l = sel.List; l != nil; l = l.Next {
+		count++
+		ncase = l.N
+		setlineno(ncase)
+		if ncase.Op != OXCASE {
+			Fatal("typecheckselect %v", Oconv(int(ncase.Op), 0))
+		}
+
+		if ncase.List == nil {
+			// default
+			if def != nil {
+				Yyerror("multiple defaults in select (first at %v)", def.Line())
+			} else {
+				def = ncase
+			}
+		} else if ncase.List.Next != nil {
+			Yyerror("select cases cannot be lists")
+		} else {
+			n = typecheck(&ncase.List.N, Etop)
+			ncase.Left = n
+			ncase.List = nil
+			setlineno(n)
+			switch n.Op {
+			default:
+				Yyerror("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 OAS:
+				if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit != 0 {
+					n.Right = n.Right.Left
+				}
+
+				if n.Right.Op != ORECV {
+					Yyerror("select assignment must have receive on right hand side")
+					break
+				}
+
+				n.Op = OSELRECV
+
+				// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
+			case OAS2RECV:
+				if n.Rlist.N.Op != ORECV {
+					Yyerror("select assignment must have receive on right hand side")
+					break
+				}
+
+				n.Op = OSELRECV2
+				n.Left = n.List.N
+				n.Ntest = n.List.Next.N
+				n.List = nil
+				n.Right = n.Rlist.N
+				n.Rlist = nil
+
+				// convert <-c into OSELRECV(N, <-c)
+			case ORECV:
+				n = Nod(OSELRECV, nil, n)
+
+				n.Typecheck = 1
+				ncase.Left = n
+
+			case OSEND:
+				break
+			}
+		}
+
+		typechecklist(ncase.Nbody, Etop)
+	}
+
+	sel.Xoffset = int64(count)
+	lineno = int32(lno)
+}
+
+func walkselect(sel *Node) {
+	var lno int
+	var i int
+	var n *Node
+	var r *Node
+	var a *Node
+	var var_ *Node
+	var selv *Node
+	var cas *Node
+	var dflt *Node
+	var ch *Node
+	var l *NodeList
+	var init *NodeList
+
+	if sel.List == nil && sel.Xoffset != 0 {
+		Fatal("double walkselect") // already rewrote
+	}
+
+	lno = int(setlineno(sel))
+	i = count(sel.List)
+
+	// optimization: zero-case select
+	if i == 0 {
+		sel.Nbody = list1(mkcall("block", nil, nil))
+		goto out
+	}
+
+	// optimization: one-case select: single op.
+	// TODO(rsc): Reenable optimization once order.c can handle it.
+	// golang.org/issue/7672.
+	if i == 1 {
+		cas = sel.List.N
+		setlineno(cas)
+		l = cas.Ninit
+		if cas.Left != nil { // not default:
+			n = cas.Left
+			l = concat(l, n.Ninit)
+			n.Ninit = nil
+			switch n.Op {
+			default:
+				Fatal("select %v", Oconv(int(n.Op), 0))
+				fallthrough
+
+				// ok already
+			case OSEND:
+				ch = n.Left
+
+			case OSELRECV,
+				OSELRECV2:
+				ch = n.Right.Left
+				if n.Op == OSELRECV || n.Ntest == nil {
+					if n.Left == nil {
+						n = n.Right
+					} else {
+						n.Op = OAS
+					}
+					break
+				}
+
+				if n.Left == nil {
+					typecheck(&nblank, Erv|Easgn)
+					n.Left = nblank
+				}
+
+				n.Op = OAS2
+				n.List = list(list1(n.Left), n.Ntest)
+				n.Rlist = list1(n.Right)
+				n.Right = nil
+				n.Left = nil
+				n.Ntest = nil
+				n.Typecheck = 0
+				typecheck(&n, Etop)
+			}
+
+			// if ch == nil { block() }; n;
+			a = Nod(OIF, nil, nil)
+
+			a.Ntest = Nod(OEQ, ch, nodnil())
+			a.Nbody = list1(mkcall("block", nil, &l))
+			typecheck(&a, Etop)
+			l = list(l, a)
+			l = list(l, n)
+		}
+
+		l = concat(l, cas.Nbody)
+		sel.Nbody = l
+		goto out
+	}
+
+	// convert case value arguments to addresses.
+	// this rewrite is used by both the general code and the next optimization.
+	for l = sel.List; l != nil; l = l.Next {
+		cas = l.N
+		setlineno(cas)
+		n = cas.Left
+		if n == nil {
+			continue
+		}
+		switch n.Op {
+		case OSEND:
+			n.Right = Nod(OADDR, n.Right, nil)
+			typecheck(&n.Right, Erv)
+
+		case OSELRECV,
+			OSELRECV2:
+			if n.Op == OSELRECV2 && n.Ntest == nil {
+				n.Op = OSELRECV
+			}
+			if n.Op == OSELRECV2 {
+				n.Ntest = Nod(OADDR, n.Ntest, nil)
+				typecheck(&n.Ntest, Erv)
+			}
+
+			if n.Left == nil {
+				n.Left = nodnil()
+			} else {
+				n.Left = Nod(OADDR, n.Left, nil)
+				typecheck(&n.Left, Erv)
+			}
+		}
+	}
+
+	// optimization: two-case select but one is default: single non-blocking op.
+	if i == 2 && (sel.List.N.Left == nil || sel.List.Next.N.Left == nil) {
+		if sel.List.N.Left == nil {
+			cas = sel.List.Next.N
+			dflt = sel.List.N
+		} else {
+			dflt = sel.List.Next.N
+			cas = sel.List.N
+		}
+
+		n = cas.Left
+		setlineno(n)
+		r = Nod(OIF, nil, nil)
+		r.Ninit = cas.Ninit
+		switch n.Op {
+		default:
+			Fatal("select %v", Oconv(int(n.Op), 0))
+			fallthrough
+
+			// if selectnbsend(c, v) { body } else { default body }
+		case OSEND:
+			ch = n.Left
+
+			r.Ntest = mkcall1(chanfn("selectnbsend", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), ch, n.Right)
+
+			// if c != nil && selectnbrecv(&v, c) { body } else { default body }
+		case OSELRECV:
+			r = Nod(OIF, nil, nil)
+
+			r.Ninit = cas.Ninit
+			ch = n.Right.Left
+			r.Ntest = mkcall1(chanfn("selectnbrecv", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, ch)
+
+			// if c != nil && selectnbrecv2(&v, c) { body } else { default body }
+		case OSELRECV2:
+			r = Nod(OIF, nil, nil)
+
+			r.Ninit = cas.Ninit
+			ch = n.Right.Left
+			r.Ntest = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, n.Ntest, ch)
+		}
+
+		typecheck(&r.Ntest, Erv)
+		r.Nbody = cas.Nbody
+		r.Nelse = concat(dflt.Ninit, dflt.Nbody)
+		sel.Nbody = list1(r)
+		goto out
+	}
+
+	init = sel.Ninit
+	sel.Ninit = nil
+
+	// generate sel-struct
+	setlineno(sel)
+
+	selv = temp(selecttype(int32(sel.Xoffset)))
+	r = Nod(OAS, selv, nil)
+	typecheck(&r, Etop)
+	init = list(init, r)
+	var_ = conv(conv(Nod(OADDR, selv, nil), Types[TUNSAFEPTR]), Ptrto(Types[TUINT8]))
+	r = mkcall("newselect", nil, nil, var_, Nodintconst(selv.Type.Width), Nodintconst(sel.Xoffset))
+	typecheck(&r, Etop)
+	init = list(init, r)
+
+	// register cases
+	for l = sel.List; l != nil; l = l.Next {
+		cas = l.N
+		setlineno(cas)
+		n = cas.Left
+		r = Nod(OIF, nil, nil)
+		r.Ninit = cas.Ninit
+		cas.Ninit = nil
+		if n != nil {
+			r.Ninit = concat(r.Ninit, n.Ninit)
+			n.Ninit = nil
+		}
+
+		if n == nil {
+			// selectdefault(sel *byte);
+			r.Ntest = mkcall("selectdefault", Types[TBOOL], &r.Ninit, var_)
+		} else {
+			switch n.Op {
+			default:
+				Fatal("select %v", Oconv(int(n.Op), 0))
+				fallthrough
+
+				// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
+			case OSEND:
+				r.Ntest = mkcall1(chanfn("selectsend", 2, n.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Left, n.Right)
+
+				// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
+			case OSELRECV:
+				r.Ntest = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left)
+
+				// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
+			case OSELRECV2:
+				r.Ntest = mkcall1(chanfn("selectrecv2", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left, n.Ntest)
+			}
+		}
+
+		// selv is no longer alive after use.
+		r.Nbody = list(r.Nbody, Nod(OVARKILL, selv, nil))
+
+		r.Nbody = concat(r.Nbody, cas.Nbody)
+		r.Nbody = list(r.Nbody, Nod(OBREAK, nil, nil))
+		init = list(init, r)
+	}
+
+	// run the select
+	setlineno(sel)
+
+	init = list(init, mkcall("selectgo", nil, nil, var_))
+	sel.Nbody = init
+
+out:
+	sel.List = nil
+	walkstmtlist(sel.Nbody)
+	lineno = int32(lno)
+}
+
+// Keep in sync with src/runtime/chan.h.
+func selecttype(size int32) *Type {
+	var sel *Node
+	var sudog *Node
+	var scase *Node
+	var arr *Node
+
+	// TODO(dvyukov): it's possible to generate SudoG and Scase only once
+	// and then cache; and also cache Select per size.
+	sudog = Nod(OTSTRUCT, nil, nil)
+
+	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("g")), typenod(Ptrto(Types[TUINT8]))))
+	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("selectdone")), typenod(Ptrto(Types[TUINT8]))))
+	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("next")), typenod(Ptrto(Types[TUINT8]))))
+	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("prev")), typenod(Ptrto(Types[TUINT8]))))
+	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8]))))
+	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64])))
+	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("nrelease")), typenod(Types[TINT32])))
+	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("waitlink")), typenod(Ptrto(Types[TUINT8]))))
+	typecheck(&sudog, Etype)
+	sudog.Type.Noalg = 1
+	sudog.Type.Local = 1
+
+	scase = Nod(OTSTRUCT, nil, nil)
+	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8]))))
+	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("chan")), typenod(Ptrto(Types[TUINT8]))))
+	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("pc")), typenod(Types[TUINTPTR])))
+	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("kind")), typenod(Types[TUINT16])))
+	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("so")), typenod(Types[TUINT16])))
+	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("receivedp")), typenod(Ptrto(Types[TUINT8]))))
+	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64])))
+	typecheck(&scase, Etype)
+	scase.Type.Noalg = 1
+	scase.Type.Local = 1
+
+	sel = Nod(OTSTRUCT, nil, nil)
+	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("tcase")), typenod(Types[TUINT16])))
+	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("ncase")), typenod(Types[TUINT16])))
+	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorder")), typenod(Ptrto(Types[TUINT8]))))
+	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorder")), typenod(Ptrto(Types[TUINT8]))))
+	arr = Nod(OTARRAY, Nodintconst(int64(size)), scase)
+	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("scase")), arr))
+	arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Ptrto(Types[TUINT8])))
+	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorderarr")), arr))
+	arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Types[TUINT16]))
+	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorderarr")), arr))
+	typecheck(&sel, Etype)
+	sel.Type.Noalg = 1
+	sel.Type.Local = 1
+
+	return sel.Type
+}