Source file src/cmd/compile/internal/walk/select.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package walk
     6  
     7  import (
     8  	"cmd/compile/internal/base"
     9  	"cmd/compile/internal/ir"
    10  	"cmd/compile/internal/typecheck"
    11  	"cmd/compile/internal/types"
    12  )
    13  
    14  func walkSelect(sel *ir.SelectStmt) {
    15  	lno := ir.SetPos(sel)
    16  	if sel.Walked() {
    17  		base.Fatalf("double walkSelect")
    18  	}
    19  	sel.SetWalked(true)
    20  
    21  	init := ir.TakeInit(sel)
    22  
    23  	init = append(init, walkSelectCases(sel.Cases)...)
    24  	sel.Cases = nil
    25  
    26  	sel.Compiled = init
    27  	walkStmtList(sel.Compiled)
    28  
    29  	base.Pos = lno
    30  }
    31  
    32  func walkSelectCases(cases []*ir.CommClause) []ir.Node {
    33  	ncas := len(cases)
    34  	sellineno := base.Pos
    35  
    36  	// optimization: zero-case select
    37  	if ncas == 0 {
    38  		return []ir.Node{mkcallstmt("block")}
    39  	}
    40  
    41  	// optimization: one-case select: single op.
    42  	if ncas == 1 {
    43  		cas := cases[0]
    44  		ir.SetPos(cas)
    45  		l := cas.Init()
    46  		if cas.Comm != nil { // not default:
    47  			n := cas.Comm
    48  			l = append(l, ir.TakeInit(n)...)
    49  			switch n.Op() {
    50  			default:
    51  				base.Fatalf("select %v", n.Op())
    52  
    53  			case ir.OSEND:
    54  				// already ok
    55  
    56  			case ir.OSELRECV2:
    57  				r := n.(*ir.AssignListStmt)
    58  				if ir.IsBlank(r.Lhs[0]) && ir.IsBlank(r.Lhs[1]) {
    59  					n = r.Rhs[0]
    60  					break
    61  				}
    62  				r.SetOp(ir.OAS2RECV)
    63  			}
    64  
    65  			l = append(l, n)
    66  		}
    67  
    68  		l = append(l, cas.Body...)
    69  		l = append(l, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))
    70  		return l
    71  	}
    72  
    73  	// convert case value arguments to addresses.
    74  	// this rewrite is used by both the general code and the next optimization.
    75  	var dflt *ir.CommClause
    76  	for _, cas := range cases {
    77  		ir.SetPos(cas)
    78  		n := cas.Comm
    79  		if n == nil {
    80  			dflt = cas
    81  			continue
    82  		}
    83  		switch n.Op() {
    84  		case ir.OSEND:
    85  			n := n.(*ir.SendStmt)
    86  			n.Value = typecheck.NodAddr(n.Value)
    87  			n.Value = typecheck.Expr(n.Value)
    88  
    89  		case ir.OSELRECV2:
    90  			n := n.(*ir.AssignListStmt)
    91  			if !ir.IsBlank(n.Lhs[0]) {
    92  				n.Lhs[0] = typecheck.NodAddr(n.Lhs[0])
    93  				n.Lhs[0] = typecheck.Expr(n.Lhs[0])
    94  			}
    95  		}
    96  	}
    97  
    98  	// optimization: two-case select but one is default: single non-blocking op.
    99  	if ncas == 2 && dflt != nil {
   100  		cas := cases[0]
   101  		if cas == dflt {
   102  			cas = cases[1]
   103  		}
   104  
   105  		n := cas.Comm
   106  		ir.SetPos(n)
   107  		r := ir.NewIfStmt(base.Pos, nil, nil, nil)
   108  		r.SetInit(cas.Init())
   109  		var cond ir.Node
   110  		switch n.Op() {
   111  		default:
   112  			base.Fatalf("select %v", n.Op())
   113  
   114  		case ir.OSEND:
   115  			// if selectnbsend(c, v) { body } else { default body }
   116  			n := n.(*ir.SendStmt)
   117  			ch := n.Chan
   118  			cond = mkcall1(chanfn("selectnbsend", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), ch, n.Value)
   119  
   120  		case ir.OSELRECV2:
   121  			n := n.(*ir.AssignListStmt)
   122  			recv := n.Rhs[0].(*ir.UnaryExpr)
   123  			ch := recv.X
   124  			elem := n.Lhs[0]
   125  			if ir.IsBlank(elem) {
   126  				elem = typecheck.NodNil()
   127  			}
   128  			cond = typecheck.Temp(types.Types[types.TBOOL])
   129  			fn := chanfn("selectnbrecv", 2, ch.Type())
   130  			call := mkcall1(fn, fn.Type().Results(), r.PtrInit(), elem, ch)
   131  			as := ir.NewAssignListStmt(r.Pos(), ir.OAS2, []ir.Node{cond, n.Lhs[1]}, []ir.Node{call})
   132  			r.PtrInit().Append(typecheck.Stmt(as))
   133  		}
   134  
   135  		r.Cond = typecheck.Expr(cond)
   136  		r.Body = cas.Body
   137  		r.Else = append(dflt.Init(), dflt.Body...)
   138  		return []ir.Node{r, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)}
   139  	}
   140  
   141  	if dflt != nil {
   142  		ncas--
   143  	}
   144  	casorder := make([]*ir.CommClause, ncas)
   145  	nsends, nrecvs := 0, 0
   146  
   147  	var init []ir.Node
   148  
   149  	// generate sel-struct
   150  	base.Pos = sellineno
   151  	selv := typecheck.Temp(types.NewArray(scasetype(), int64(ncas)))
   152  	init = append(init, typecheck.Stmt(ir.NewAssignStmt(base.Pos, selv, nil)))
   153  
   154  	// No initialization for order; runtime.selectgo is responsible for that.
   155  	order := typecheck.Temp(types.NewArray(types.Types[types.TUINT16], 2*int64(ncas)))
   156  
   157  	var pc0, pcs ir.Node
   158  	if base.Flag.Race {
   159  		pcs = typecheck.Temp(types.NewArray(types.Types[types.TUINTPTR], int64(ncas)))
   160  		pc0 = typecheck.Expr(typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(0))))
   161  	} else {
   162  		pc0 = typecheck.NodNil()
   163  	}
   164  
   165  	// register cases
   166  	for _, cas := range cases {
   167  		ir.SetPos(cas)
   168  
   169  		init = append(init, ir.TakeInit(cas)...)
   170  
   171  		n := cas.Comm
   172  		if n == nil { // default:
   173  			continue
   174  		}
   175  
   176  		var i int
   177  		var c, elem ir.Node
   178  		switch n.Op() {
   179  		default:
   180  			base.Fatalf("select %v", n.Op())
   181  		case ir.OSEND:
   182  			n := n.(*ir.SendStmt)
   183  			i = nsends
   184  			nsends++
   185  			c = n.Chan
   186  			elem = n.Value
   187  		case ir.OSELRECV2:
   188  			n := n.(*ir.AssignListStmt)
   189  			nrecvs++
   190  			i = ncas - nrecvs
   191  			recv := n.Rhs[0].(*ir.UnaryExpr)
   192  			c = recv.X
   193  			elem = n.Lhs[0]
   194  		}
   195  
   196  		casorder[i] = cas
   197  
   198  		setField := func(f string, val ir.Node) {
   199  			r := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, ir.NewIndexExpr(base.Pos, selv, ir.NewInt(int64(i))), typecheck.Lookup(f)), val)
   200  			init = append(init, typecheck.Stmt(r))
   201  		}
   202  
   203  		c = typecheck.ConvNop(c, types.Types[types.TUNSAFEPTR])
   204  		setField("c", c)
   205  		if !ir.IsBlank(elem) {
   206  			elem = typecheck.ConvNop(elem, types.Types[types.TUNSAFEPTR])
   207  			setField("elem", elem)
   208  		}
   209  
   210  		// TODO(mdempsky): There should be a cleaner way to
   211  		// handle this.
   212  		if base.Flag.Race {
   213  			r := mkcallstmt("selectsetpc", typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(int64(i)))))
   214  			init = append(init, r)
   215  		}
   216  	}
   217  	if nsends+nrecvs != ncas {
   218  		base.Fatalf("walkSelectCases: miscount: %v + %v != %v", nsends, nrecvs, ncas)
   219  	}
   220  
   221  	// run the select
   222  	base.Pos = sellineno
   223  	chosen := typecheck.Temp(types.Types[types.TINT])
   224  	recvOK := typecheck.Temp(types.Types[types.TBOOL])
   225  	r := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
   226  	r.Lhs = []ir.Node{chosen, recvOK}
   227  	fn := typecheck.LookupRuntime("selectgo")
   228  	var fnInit ir.Nodes
   229  	r.Rhs = []ir.Node{mkcall1(fn, fn.Type().Results(), &fnInit, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, ir.NewInt(int64(nsends)), ir.NewInt(int64(nrecvs)), ir.NewBool(dflt == nil))}
   230  	init = append(init, fnInit...)
   231  	init = append(init, typecheck.Stmt(r))
   232  
   233  	// selv and order are no longer alive after selectgo.
   234  	init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, selv))
   235  	init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, order))
   236  	if base.Flag.Race {
   237  		init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, pcs))
   238  	}
   239  
   240  	// dispatch cases
   241  	dispatch := func(cond ir.Node, cas *ir.CommClause) {
   242  		cond = typecheck.Expr(cond)
   243  		cond = typecheck.DefaultLit(cond, nil)
   244  
   245  		r := ir.NewIfStmt(base.Pos, cond, nil, nil)
   246  
   247  		if n := cas.Comm; n != nil && n.Op() == ir.OSELRECV2 {
   248  			n := n.(*ir.AssignListStmt)
   249  			if !ir.IsBlank(n.Lhs[1]) {
   250  				x := ir.NewAssignStmt(base.Pos, n.Lhs[1], recvOK)
   251  				r.Body.Append(typecheck.Stmt(x))
   252  			}
   253  		}
   254  
   255  		r.Body.Append(cas.Body.Take()...)
   256  		r.Body.Append(ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))
   257  		init = append(init, r)
   258  	}
   259  
   260  	if dflt != nil {
   261  		ir.SetPos(dflt)
   262  		dispatch(ir.NewBinaryExpr(base.Pos, ir.OLT, chosen, ir.NewInt(0)), dflt)
   263  	}
   264  	for i, cas := range casorder {
   265  		ir.SetPos(cas)
   266  		dispatch(ir.NewBinaryExpr(base.Pos, ir.OEQ, chosen, ir.NewInt(int64(i))), cas)
   267  	}
   268  
   269  	return init
   270  }
   271  
   272  // bytePtrToIndex returns a Node representing "(*byte)(&n[i])".
   273  func bytePtrToIndex(n ir.Node, i int64) ir.Node {
   274  	s := typecheck.NodAddr(ir.NewIndexExpr(base.Pos, n, ir.NewInt(i)))
   275  	t := types.NewPtr(types.Types[types.TUINT8])
   276  	return typecheck.ConvNop(s, t)
   277  }
   278  
   279  var scase *types.Type
   280  
   281  // Keep in sync with src/runtime/select.go.
   282  func scasetype() *types.Type {
   283  	if scase == nil {
   284  		scase = types.NewStruct(types.NoPkg, []*types.Field{
   285  			types.NewField(base.Pos, typecheck.Lookup("c"), types.Types[types.TUNSAFEPTR]),
   286  			types.NewField(base.Pos, typecheck.Lookup("elem"), types.Types[types.TUNSAFEPTR]),
   287  		})
   288  		scase.SetNoalg(true)
   289  	}
   290  	return scase
   291  }
   292  

View as plain text