github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/gc/select.go (about)

     1  // Do not edit. Bootstrap copy of /Users/rsc/g/go/src/cmd/internal/gc/select.go
     2  
     3  // Copyright 2009 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package gc
     8  
     9  /*
    10   * select
    11   */
    12  func typecheckselect(sel *Node) {
    13  	var ncase *Node
    14  	var n *Node
    15  
    16  	var def *Node
    17  	lno := int(setlineno(sel))
    18  	count := 0
    19  	typechecklist(sel.Ninit, Etop)
    20  	for l := sel.List; l != nil; l = l.Next {
    21  		count++
    22  		ncase = l.N
    23  		setlineno(ncase)
    24  		if ncase.Op != OXCASE {
    25  			Fatal("typecheckselect %v", Oconv(int(ncase.Op), 0))
    26  		}
    27  
    28  		if ncase.List == nil {
    29  			// default
    30  			if def != nil {
    31  				Yyerror("multiple defaults in select (first at %v)", def.Line())
    32  			} else {
    33  				def = ncase
    34  			}
    35  		} else if ncase.List.Next != nil {
    36  			Yyerror("select cases cannot be lists")
    37  		} else {
    38  			n = typecheck(&ncase.List.N, Etop)
    39  			ncase.Left = n
    40  			ncase.List = nil
    41  			setlineno(n)
    42  			switch n.Op {
    43  			default:
    44  				Yyerror("select case must be receive, send or assign recv")
    45  
    46  				// convert x = <-c into OSELRECV(x, <-c).
    47  			// remove implicit conversions; the eventual assignment
    48  			// will reintroduce them.
    49  			case OAS:
    50  				if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit {
    51  					n.Right = n.Right.Left
    52  				}
    53  
    54  				if n.Right.Op != ORECV {
    55  					Yyerror("select assignment must have receive on right hand side")
    56  					break
    57  				}
    58  
    59  				n.Op = OSELRECV
    60  
    61  				// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
    62  			case OAS2RECV:
    63  				if n.Rlist.N.Op != ORECV {
    64  					Yyerror("select assignment must have receive on right hand side")
    65  					break
    66  				}
    67  
    68  				n.Op = OSELRECV2
    69  				n.Left = n.List.N
    70  				n.Ntest = n.List.Next.N
    71  				n.List = nil
    72  				n.Right = n.Rlist.N
    73  				n.Rlist = nil
    74  
    75  				// convert <-c into OSELRECV(N, <-c)
    76  			case ORECV:
    77  				n = Nod(OSELRECV, nil, n)
    78  
    79  				n.Typecheck = 1
    80  				ncase.Left = n
    81  
    82  			case OSEND:
    83  				break
    84  			}
    85  		}
    86  
    87  		typechecklist(ncase.Nbody, Etop)
    88  	}
    89  
    90  	sel.Xoffset = int64(count)
    91  	lineno = int32(lno)
    92  }
    93  
    94  func walkselect(sel *Node) {
    95  	if sel.List == nil && sel.Xoffset != 0 {
    96  		Fatal("double walkselect") // already rewrote
    97  	}
    98  
    99  	lno := int(setlineno(sel))
   100  	i := count(sel.List)
   101  
   102  	// optimization: zero-case select
   103  	var init *NodeList
   104  	var r *Node
   105  	var n *Node
   106  	var var_ *Node
   107  	var selv *Node
   108  	var cas *Node
   109  	if i == 0 {
   110  		sel.Nbody = list1(mkcall("block", nil, nil))
   111  		goto out
   112  	}
   113  
   114  	// optimization: one-case select: single op.
   115  	// TODO(rsc): Reenable optimization once order.c can handle it.
   116  	// golang.org/issue/7672.
   117  	if i == 1 {
   118  		cas := sel.List.N
   119  		setlineno(cas)
   120  		l := cas.Ninit
   121  		if cas.Left != nil { // not default:
   122  			n := cas.Left
   123  			l = concat(l, n.Ninit)
   124  			n.Ninit = nil
   125  			var ch *Node
   126  			switch n.Op {
   127  			default:
   128  				Fatal("select %v", Oconv(int(n.Op), 0))
   129  
   130  				// ok already
   131  			case OSEND:
   132  				ch = n.Left
   133  
   134  			case OSELRECV, OSELRECV2:
   135  				ch = n.Right.Left
   136  				if n.Op == OSELRECV || n.Ntest == nil {
   137  					if n.Left == nil {
   138  						n = n.Right
   139  					} else {
   140  						n.Op = OAS
   141  					}
   142  					break
   143  				}
   144  
   145  				if n.Left == nil {
   146  					typecheck(&nblank, Erv|Easgn)
   147  					n.Left = nblank
   148  				}
   149  
   150  				n.Op = OAS2
   151  				n.List = list(list1(n.Left), n.Ntest)
   152  				n.Rlist = list1(n.Right)
   153  				n.Right = nil
   154  				n.Left = nil
   155  				n.Ntest = nil
   156  				n.Typecheck = 0
   157  				typecheck(&n, Etop)
   158  			}
   159  
   160  			// if ch == nil { block() }; n;
   161  			a := Nod(OIF, nil, nil)
   162  
   163  			a.Ntest = Nod(OEQ, ch, nodnil())
   164  			a.Nbody = list1(mkcall("block", nil, &l))
   165  			typecheck(&a, Etop)
   166  			l = list(l, a)
   167  			l = list(l, n)
   168  		}
   169  
   170  		l = concat(l, cas.Nbody)
   171  		sel.Nbody = l
   172  		goto out
   173  	}
   174  
   175  	// convert case value arguments to addresses.
   176  	// this rewrite is used by both the general code and the next optimization.
   177  	for l := sel.List; l != nil; l = l.Next {
   178  		cas = l.N
   179  		setlineno(cas)
   180  		n = cas.Left
   181  		if n == nil {
   182  			continue
   183  		}
   184  		switch n.Op {
   185  		case OSEND:
   186  			n.Right = Nod(OADDR, n.Right, nil)
   187  			typecheck(&n.Right, Erv)
   188  
   189  		case OSELRECV, OSELRECV2:
   190  			if n.Op == OSELRECV2 && n.Ntest == nil {
   191  				n.Op = OSELRECV
   192  			}
   193  			if n.Op == OSELRECV2 {
   194  				n.Ntest = Nod(OADDR, n.Ntest, nil)
   195  				typecheck(&n.Ntest, Erv)
   196  			}
   197  
   198  			if n.Left == nil {
   199  				n.Left = nodnil()
   200  			} else {
   201  				n.Left = Nod(OADDR, n.Left, nil)
   202  				typecheck(&n.Left, Erv)
   203  			}
   204  		}
   205  	}
   206  
   207  	// optimization: two-case select but one is default: single non-blocking op.
   208  	if i == 2 && (sel.List.N.Left == nil || sel.List.Next.N.Left == nil) {
   209  		var cas *Node
   210  		var dflt *Node
   211  		if sel.List.N.Left == nil {
   212  			cas = sel.List.Next.N
   213  			dflt = sel.List.N
   214  		} else {
   215  			dflt = sel.List.Next.N
   216  			cas = sel.List.N
   217  		}
   218  
   219  		n := cas.Left
   220  		setlineno(n)
   221  		r := Nod(OIF, nil, nil)
   222  		r.Ninit = cas.Ninit
   223  		switch n.Op {
   224  		default:
   225  			Fatal("select %v", Oconv(int(n.Op), 0))
   226  
   227  			// if selectnbsend(c, v) { body } else { default body }
   228  		case OSEND:
   229  			ch := n.Left
   230  
   231  			r.Ntest = mkcall1(chanfn("selectnbsend", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), ch, n.Right)
   232  
   233  			// if c != nil && selectnbrecv(&v, c) { body } else { default body }
   234  		case OSELRECV:
   235  			r = Nod(OIF, nil, nil)
   236  
   237  			r.Ninit = cas.Ninit
   238  			ch := n.Right.Left
   239  			r.Ntest = mkcall1(chanfn("selectnbrecv", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, ch)
   240  
   241  			// if c != nil && selectnbrecv2(&v, c) { body } else { default body }
   242  		case OSELRECV2:
   243  			r = Nod(OIF, nil, nil)
   244  
   245  			r.Ninit = cas.Ninit
   246  			ch := n.Right.Left
   247  			r.Ntest = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, n.Ntest, ch)
   248  		}
   249  
   250  		typecheck(&r.Ntest, Erv)
   251  		r.Nbody = cas.Nbody
   252  		r.Nelse = concat(dflt.Ninit, dflt.Nbody)
   253  		sel.Nbody = list1(r)
   254  		goto out
   255  	}
   256  
   257  	init = sel.Ninit
   258  	sel.Ninit = nil
   259  
   260  	// generate sel-struct
   261  	setlineno(sel)
   262  
   263  	selv = temp(selecttype(int32(sel.Xoffset)))
   264  	r = Nod(OAS, selv, nil)
   265  	typecheck(&r, Etop)
   266  	init = list(init, r)
   267  	var_ = conv(conv(Nod(OADDR, selv, nil), Types[TUNSAFEPTR]), Ptrto(Types[TUINT8]))
   268  	r = mkcall("newselect", nil, nil, var_, Nodintconst(selv.Type.Width), Nodintconst(sel.Xoffset))
   269  	typecheck(&r, Etop)
   270  	init = list(init, r)
   271  
   272  	// register cases
   273  	for l := sel.List; l != nil; l = l.Next {
   274  		cas = l.N
   275  		setlineno(cas)
   276  		n = cas.Left
   277  		r = Nod(OIF, nil, nil)
   278  		r.Ninit = cas.Ninit
   279  		cas.Ninit = nil
   280  		if n != nil {
   281  			r.Ninit = concat(r.Ninit, n.Ninit)
   282  			n.Ninit = nil
   283  		}
   284  
   285  		if n == nil {
   286  			// selectdefault(sel *byte);
   287  			r.Ntest = mkcall("selectdefault", Types[TBOOL], &r.Ninit, var_)
   288  		} else {
   289  			switch n.Op {
   290  			default:
   291  				Fatal("select %v", Oconv(int(n.Op), 0))
   292  
   293  				// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
   294  			case OSEND:
   295  				r.Ntest = mkcall1(chanfn("selectsend", 2, n.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Left, n.Right)
   296  
   297  				// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
   298  			case OSELRECV:
   299  				r.Ntest = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left)
   300  
   301  				// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
   302  			case OSELRECV2:
   303  				r.Ntest = mkcall1(chanfn("selectrecv2", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left, n.Ntest)
   304  			}
   305  		}
   306  
   307  		// selv is no longer alive after use.
   308  		r.Nbody = list(r.Nbody, Nod(OVARKILL, selv, nil))
   309  
   310  		r.Nbody = concat(r.Nbody, cas.Nbody)
   311  		r.Nbody = list(r.Nbody, Nod(OBREAK, nil, nil))
   312  		init = list(init, r)
   313  	}
   314  
   315  	// run the select
   316  	setlineno(sel)
   317  
   318  	init = list(init, mkcall("selectgo", nil, nil, var_))
   319  	sel.Nbody = init
   320  
   321  out:
   322  	sel.List = nil
   323  	walkstmtlist(sel.Nbody)
   324  	lineno = int32(lno)
   325  }
   326  
   327  // Keep in sync with src/runtime/runtime2.go and src/runtime/select.go.
   328  func selecttype(size int32) *Type {
   329  	// TODO(dvyukov): it's possible to generate SudoG and Scase only once
   330  	// and then cache; and also cache Select per size.
   331  	sudog := Nod(OTSTRUCT, nil, nil)
   332  
   333  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("g")), typenod(Ptrto(Types[TUINT8]))))
   334  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("selectdone")), typenod(Ptrto(Types[TUINT8]))))
   335  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("next")), typenod(Ptrto(Types[TUINT8]))))
   336  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("prev")), typenod(Ptrto(Types[TUINT8]))))
   337  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8]))))
   338  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64])))
   339  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("nrelease")), typenod(Types[TINT32])))
   340  	sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("waitlink")), typenod(Ptrto(Types[TUINT8]))))
   341  	typecheck(&sudog, Etype)
   342  	sudog.Type.Noalg = 1
   343  	sudog.Type.Local = true
   344  
   345  	scase := Nod(OTSTRUCT, nil, nil)
   346  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8]))))
   347  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("chan")), typenod(Ptrto(Types[TUINT8]))))
   348  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("pc")), typenod(Types[TUINTPTR])))
   349  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("kind")), typenod(Types[TUINT16])))
   350  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("so")), typenod(Types[TUINT16])))
   351  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("receivedp")), typenod(Ptrto(Types[TUINT8]))))
   352  	scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64])))
   353  	typecheck(&scase, Etype)
   354  	scase.Type.Noalg = 1
   355  	scase.Type.Local = true
   356  
   357  	sel := Nod(OTSTRUCT, nil, nil)
   358  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("tcase")), typenod(Types[TUINT16])))
   359  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("ncase")), typenod(Types[TUINT16])))
   360  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorder")), typenod(Ptrto(Types[TUINT8]))))
   361  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorder")), typenod(Ptrto(Types[TUINT8]))))
   362  	arr := Nod(OTARRAY, Nodintconst(int64(size)), scase)
   363  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("scase")), arr))
   364  	arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Ptrto(Types[TUINT8])))
   365  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorderarr")), arr))
   366  	arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Types[TUINT16]))
   367  	sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorderarr")), arr))
   368  	typecheck(&sel, Etype)
   369  	sel.Type.Noalg = 1
   370  	sel.Type.Local = true
   371  
   372  	return sel.Type
   373  }