[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
+}