github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/walk/switch.go (about)

     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  	"github.com/bir3/gocompiler/src/go/constant"
     9  	"github.com/bir3/gocompiler/src/go/token"
    10  	"sort"
    11  
    12  	"github.com/bir3/gocompiler/src/cmd/compile/internal/base"
    13  	"github.com/bir3/gocompiler/src/cmd/compile/internal/ir"
    14  	"github.com/bir3/gocompiler/src/cmd/compile/internal/ssagen"
    15  	"github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck"
    16  	"github.com/bir3/gocompiler/src/cmd/compile/internal/types"
    17  	"github.com/bir3/gocompiler/src/cmd/internal/src"
    18  )
    19  
    20  // walkSwitch walks a switch statement.
    21  func walkSwitch(sw *ir.SwitchStmt) {
    22  	// Guard against double walk, see #25776.
    23  	if sw.Walked() {
    24  		return // Was fatal, but eliminating every possible source of double-walking is hard
    25  	}
    26  	sw.SetWalked(true)
    27  
    28  	if sw.Tag != nil && sw.Tag.Op() == ir.OTYPESW {
    29  		walkSwitchType(sw)
    30  	} else {
    31  		walkSwitchExpr(sw)
    32  	}
    33  }
    34  
    35  // walkSwitchExpr generates an AST implementing sw.  sw is an
    36  // expression switch.
    37  func walkSwitchExpr(sw *ir.SwitchStmt) {
    38  	lno := ir.SetPos(sw)
    39  
    40  	cond := sw.Tag
    41  	sw.Tag = nil
    42  
    43  	// convert switch {...} to switch true {...}
    44  	if cond == nil {
    45  		cond = ir.NewBool(true)
    46  		cond = typecheck.Expr(cond)
    47  		cond = typecheck.DefaultLit(cond, nil)
    48  	}
    49  
    50  	// Given "switch string(byteslice)",
    51  	// with all cases being side-effect free,
    52  	// use a zero-cost alias of the byte slice.
    53  	// Do this before calling walkExpr on cond,
    54  	// because walkExpr will lower the string
    55  	// conversion into a runtime call.
    56  	// See issue 24937 for more discussion.
    57  	if cond.Op() == ir.OBYTES2STR && allCaseExprsAreSideEffectFree(sw) {
    58  		cond := cond.(*ir.ConvExpr)
    59  		cond.SetOp(ir.OBYTES2STRTMP)
    60  	}
    61  
    62  	cond = walkExpr(cond, sw.PtrInit())
    63  	if cond.Op() != ir.OLITERAL && cond.Op() != ir.ONIL {
    64  		cond = copyExpr(cond, cond.Type(), &sw.Compiled)
    65  	}
    66  
    67  	base.Pos = lno
    68  
    69  	s := exprSwitch{
    70  		pos:      lno,
    71  		exprname: cond,
    72  	}
    73  
    74  	var defaultGoto ir.Node
    75  	var body ir.Nodes
    76  	for _, ncase := range sw.Cases {
    77  		label := typecheck.AutoLabel(".s")
    78  		jmp := ir.NewBranchStmt(ncase.Pos(), ir.OGOTO, label)
    79  
    80  		// Process case dispatch.
    81  		if len(ncase.List) == 0 {
    82  			if defaultGoto != nil {
    83  				base.Fatalf("duplicate default case not detected during typechecking")
    84  			}
    85  			defaultGoto = jmp
    86  		}
    87  
    88  		for i, n1 := range ncase.List {
    89  			var rtype ir.Node
    90  			if i < len(ncase.RTypes) {
    91  				rtype = ncase.RTypes[i]
    92  			}
    93  			s.Add(ncase.Pos(), n1, rtype, jmp)
    94  		}
    95  
    96  		// Process body.
    97  		body.Append(ir.NewLabelStmt(ncase.Pos(), label))
    98  		body.Append(ncase.Body...)
    99  		if fall, pos := endsInFallthrough(ncase.Body); !fall {
   100  			br := ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)
   101  			br.SetPos(pos)
   102  			body.Append(br)
   103  		}
   104  	}
   105  	sw.Cases = nil
   106  
   107  	if defaultGoto == nil {
   108  		br := ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)
   109  		br.SetPos(br.Pos().WithNotStmt())
   110  		defaultGoto = br
   111  	}
   112  
   113  	s.Emit(&sw.Compiled)
   114  	sw.Compiled.Append(defaultGoto)
   115  	sw.Compiled.Append(body.Take()...)
   116  	walkStmtList(sw.Compiled)
   117  }
   118  
   119  // An exprSwitch walks an expression switch.
   120  type exprSwitch struct {
   121  	pos      src.XPos
   122  	exprname ir.Node // value being switched on
   123  
   124  	done    ir.Nodes
   125  	clauses []exprClause
   126  }
   127  
   128  type exprClause struct {
   129  	pos    src.XPos
   130  	lo, hi ir.Node
   131  	rtype  ir.Node // *runtime._type for OEQ node
   132  	jmp    ir.Node
   133  }
   134  
   135  func (s *exprSwitch) Add(pos src.XPos, expr, rtype, jmp ir.Node) {
   136  	c := exprClause{pos: pos, lo: expr, hi: expr, rtype: rtype, jmp: jmp}
   137  	if types.IsOrdered[s.exprname.Type().Kind()] && expr.Op() == ir.OLITERAL {
   138  		s.clauses = append(s.clauses, c)
   139  		return
   140  	}
   141  
   142  	s.flush()
   143  	s.clauses = append(s.clauses, c)
   144  	s.flush()
   145  }
   146  
   147  func (s *exprSwitch) Emit(out *ir.Nodes) {
   148  	s.flush()
   149  	out.Append(s.done.Take()...)
   150  }
   151  
   152  func (s *exprSwitch) flush() {
   153  	cc := s.clauses
   154  	s.clauses = nil
   155  	if len(cc) == 0 {
   156  		return
   157  	}
   158  
   159  	// Caution: If len(cc) == 1, then cc[0] might not an OLITERAL.
   160  	// The code below is structured to implicitly handle this case
   161  	// (e.g., sort.Slice doesn't need to invoke the less function
   162  	// when there's only a single slice element).
   163  
   164  	if s.exprname.Type().IsString() && len(cc) >= 2 {
   165  		// Sort strings by length and then by value. It is
   166  		// much cheaper to compare lengths than values, and
   167  		// all we need here is consistency. We respect this
   168  		// sorting below.
   169  		sort.Slice(cc, func(i, j int) bool {
   170  			si := ir.StringVal(cc[i].lo)
   171  			sj := ir.StringVal(cc[j].lo)
   172  			if len(si) != len(sj) {
   173  				return len(si) < len(sj)
   174  			}
   175  			return si < sj
   176  		})
   177  
   178  		// runLen returns the string length associated with a
   179  		// particular run of exprClauses.
   180  		runLen := func(run []exprClause) int64 { return int64(len(ir.StringVal(run[0].lo))) }
   181  
   182  		// Collapse runs of consecutive strings with the same length.
   183  		var runs [][]exprClause
   184  		start := 0
   185  		for i := 1; i < len(cc); i++ {
   186  			if runLen(cc[start:]) != runLen(cc[i:]) {
   187  				runs = append(runs, cc[start:i])
   188  				start = i
   189  			}
   190  		}
   191  		runs = append(runs, cc[start:])
   192  
   193  		// We have strings of more than one length. Generate an
   194  		// outer switch which switches on the length of the string
   195  		// and an inner switch in each case which resolves all the
   196  		// strings of the same length. The code looks something like this:
   197  
   198  		// goto outerLabel
   199  		// len5:
   200  		//   ... search among length 5 strings ...
   201  		//   goto endLabel
   202  		// len8:
   203  		//   ... search among length 8 strings ...
   204  		//   goto endLabel
   205  		// ... other lengths ...
   206  		// outerLabel:
   207  		// switch len(s) {
   208  		//   case 5: goto len5
   209  		//   case 8: goto len8
   210  		//   ... other lengths ...
   211  		// }
   212  		// endLabel:
   213  
   214  		outerLabel := typecheck.AutoLabel(".s")
   215  		endLabel := typecheck.AutoLabel(".s")
   216  
   217  		// Jump around all the individual switches for each length.
   218  		s.done.Append(ir.NewBranchStmt(s.pos, ir.OGOTO, outerLabel))
   219  
   220  		var outer exprSwitch
   221  		outer.exprname = ir.NewUnaryExpr(s.pos, ir.OLEN, s.exprname)
   222  		outer.exprname.SetType(types.Types[types.TINT])
   223  
   224  		for _, run := range runs {
   225  			// Target label to jump to when we match this length.
   226  			label := typecheck.AutoLabel(".s")
   227  
   228  			// Search within this run of same-length strings.
   229  			pos := run[0].pos
   230  			s.done.Append(ir.NewLabelStmt(pos, label))
   231  			stringSearch(s.exprname, run, &s.done)
   232  			s.done.Append(ir.NewBranchStmt(pos, ir.OGOTO, endLabel))
   233  
   234  			// Add length case to outer switch.
   235  			cas := ir.NewBasicLit(pos, constant.MakeInt64(runLen(run)))
   236  			jmp := ir.NewBranchStmt(pos, ir.OGOTO, label)
   237  			outer.Add(pos, cas, nil, jmp)
   238  		}
   239  		s.done.Append(ir.NewLabelStmt(s.pos, outerLabel))
   240  		outer.Emit(&s.done)
   241  		s.done.Append(ir.NewLabelStmt(s.pos, endLabel))
   242  		return
   243  	}
   244  
   245  	sort.Slice(cc, func(i, j int) bool {
   246  		return constant.Compare(cc[i].lo.Val(), token.LSS, cc[j].lo.Val())
   247  	})
   248  
   249  	// Merge consecutive integer cases.
   250  	if s.exprname.Type().IsInteger() {
   251  		consecutive := func(last, next constant.Value) bool {
   252  			delta := constant.BinaryOp(next, token.SUB, last)
   253  			return constant.Compare(delta, token.EQL, constant.MakeInt64(1))
   254  		}
   255  
   256  		merged := cc[:1]
   257  		for _, c := range cc[1:] {
   258  			last := &merged[len(merged)-1]
   259  			if last.jmp == c.jmp && consecutive(last.hi.Val(), c.lo.Val()) {
   260  				last.hi = c.lo
   261  			} else {
   262  				merged = append(merged, c)
   263  			}
   264  		}
   265  		cc = merged
   266  	}
   267  
   268  	s.search(cc, &s.done)
   269  }
   270  
   271  func (s *exprSwitch) search(cc []exprClause, out *ir.Nodes) {
   272  	if s.tryJumpTable(cc, out) {
   273  		return
   274  	}
   275  	binarySearch(len(cc), out,
   276  		func(i int) ir.Node {
   277  			return ir.NewBinaryExpr(base.Pos, ir.OLE, s.exprname, cc[i-1].hi)
   278  		},
   279  		func(i int, nif *ir.IfStmt) {
   280  			c := &cc[i]
   281  			nif.Cond = c.test(s.exprname)
   282  			nif.Body = []ir.Node{c.jmp}
   283  		},
   284  	)
   285  }
   286  
   287  // Try to implement the clauses with a jump table. Returns true if successful.
   288  func (s *exprSwitch) tryJumpTable(cc []exprClause, out *ir.Nodes) bool {
   289  	const go119UseJumpTables = true
   290  	const minCases = 8   // have at least minCases cases in the switch
   291  	const minDensity = 4 // use at least 1 out of every minDensity entries
   292  
   293  	if !go119UseJumpTables || base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable || base.Ctxt.Retpoline {
   294  		return false
   295  	}
   296  	if len(cc) < minCases {
   297  		return false // not enough cases for it to be worth it
   298  	}
   299  	if cc[0].lo.Val().Kind() != constant.Int {
   300  		return false // e.g. float
   301  	}
   302  	if s.exprname.Type().Size() > int64(types.PtrSize) {
   303  		return false // 64-bit switches on 32-bit archs
   304  	}
   305  	min := cc[0].lo.Val()
   306  	max := cc[len(cc)-1].hi.Val()
   307  	width := constant.BinaryOp(constant.BinaryOp(max, token.SUB, min), token.ADD, constant.MakeInt64(1))
   308  	limit := constant.MakeInt64(int64(len(cc)) * minDensity)
   309  	if constant.Compare(width, token.GTR, limit) {
   310  		// We disable jump tables if we use less than a minimum fraction of the entries.
   311  		// i.e. for switch x {case 0: case 1000: case 2000:} we don't want to use a jump table.
   312  		return false
   313  	}
   314  	jt := ir.NewJumpTableStmt(base.Pos, s.exprname)
   315  	for _, c := range cc {
   316  		jmp := c.jmp.(*ir.BranchStmt)
   317  		if jmp.Op() != ir.OGOTO || jmp.Label == nil {
   318  			panic("bad switch case body")
   319  		}
   320  		for i := c.lo.Val(); constant.Compare(i, token.LEQ, c.hi.Val()); i = constant.BinaryOp(i, token.ADD, constant.MakeInt64(1)) {
   321  			jt.Cases = append(jt.Cases, i)
   322  			jt.Targets = append(jt.Targets, jmp.Label)
   323  		}
   324  	}
   325  	out.Append(jt)
   326  	return true
   327  }
   328  
   329  func (c *exprClause) test(exprname ir.Node) ir.Node {
   330  	// Integer range.
   331  	if c.hi != c.lo {
   332  		low := ir.NewBinaryExpr(c.pos, ir.OGE, exprname, c.lo)
   333  		high := ir.NewBinaryExpr(c.pos, ir.OLE, exprname, c.hi)
   334  		return ir.NewLogicalExpr(c.pos, ir.OANDAND, low, high)
   335  	}
   336  
   337  	// Optimize "switch true { ...}" and "switch false { ... }".
   338  	if ir.IsConst(exprname, constant.Bool) && !c.lo.Type().IsInterface() {
   339  		if ir.BoolVal(exprname) {
   340  			return c.lo
   341  		} else {
   342  			return ir.NewUnaryExpr(c.pos, ir.ONOT, c.lo)
   343  		}
   344  	}
   345  
   346  	n := ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo)
   347  	n.RType = c.rtype
   348  	return n
   349  }
   350  
   351  func allCaseExprsAreSideEffectFree(sw *ir.SwitchStmt) bool {
   352  	// In theory, we could be more aggressive, allowing any
   353  	// side-effect-free expressions in cases, but it's a bit
   354  	// tricky because some of that information is unavailable due
   355  	// to the introduction of temporaries during order.
   356  	// Restricting to constants is simple and probably powerful
   357  	// enough.
   358  
   359  	for _, ncase := range sw.Cases {
   360  		for _, v := range ncase.List {
   361  			if v.Op() != ir.OLITERAL {
   362  				return false
   363  			}
   364  		}
   365  	}
   366  	return true
   367  }
   368  
   369  // endsInFallthrough reports whether stmts ends with a "fallthrough" statement.
   370  func endsInFallthrough(stmts []ir.Node) (bool, src.XPos) {
   371  	if len(stmts) == 0 {
   372  		return false, src.NoXPos
   373  	}
   374  	i := len(stmts) - 1
   375  	return stmts[i].Op() == ir.OFALL, stmts[i].Pos()
   376  }
   377  
   378  // walkSwitchType generates an AST that implements sw, where sw is a
   379  // type switch.
   380  func walkSwitchType(sw *ir.SwitchStmt) {
   381  	var s typeSwitch
   382  	s.facename = sw.Tag.(*ir.TypeSwitchGuard).X
   383  	sw.Tag = nil
   384  
   385  	s.facename = walkExpr(s.facename, sw.PtrInit())
   386  	s.facename = copyExpr(s.facename, s.facename.Type(), &sw.Compiled)
   387  	s.okname = typecheck.Temp(types.Types[types.TBOOL])
   388  
   389  	// Get interface descriptor word.
   390  	// For empty interfaces this will be the type.
   391  	// For non-empty interfaces this will be the itab.
   392  	itab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s.facename)
   393  
   394  	// For empty interfaces, do:
   395  	//     if e._type == nil {
   396  	//         do nil case if it exists, otherwise default
   397  	//     }
   398  	//     h := e._type.hash
   399  	// Use a similar strategy for non-empty interfaces.
   400  	ifNil := ir.NewIfStmt(base.Pos, nil, nil, nil)
   401  	ifNil.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, itab, typecheck.NodNil())
   402  	base.Pos = base.Pos.WithNotStmt() // disable statement marks after the first check.
   403  	ifNil.Cond = typecheck.Expr(ifNil.Cond)
   404  	ifNil.Cond = typecheck.DefaultLit(ifNil.Cond, nil)
   405  	// ifNil.Nbody assigned at end.
   406  	sw.Compiled.Append(ifNil)
   407  
   408  	// Load hash from type or itab.
   409  	dotHash := typeHashFieldOf(base.Pos, itab)
   410  	s.hashname = copyExpr(dotHash, dotHash.Type(), &sw.Compiled)
   411  
   412  	br := ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)
   413  	var defaultGoto, nilGoto ir.Node
   414  	var body ir.Nodes
   415  	for _, ncase := range sw.Cases {
   416  		caseVar := ncase.Var
   417  
   418  		// For single-type cases with an interface type,
   419  		// we initialize the case variable as part of the type assertion.
   420  		// In other cases, we initialize it in the body.
   421  		var singleType *types.Type
   422  		if len(ncase.List) == 1 && ncase.List[0].Op() == ir.OTYPE {
   423  			singleType = ncase.List[0].Type()
   424  		}
   425  		caseVarInitialized := false
   426  
   427  		label := typecheck.AutoLabel(".s")
   428  		jmp := ir.NewBranchStmt(ncase.Pos(), ir.OGOTO, label)
   429  
   430  		if len(ncase.List) == 0 { // default:
   431  			if defaultGoto != nil {
   432  				base.Fatalf("duplicate default case not detected during typechecking")
   433  			}
   434  			defaultGoto = jmp
   435  		}
   436  
   437  		for _, n1 := range ncase.List {
   438  			if ir.IsNil(n1) { // case nil:
   439  				if nilGoto != nil {
   440  					base.Fatalf("duplicate nil case not detected during typechecking")
   441  				}
   442  				nilGoto = jmp
   443  				continue
   444  			}
   445  
   446  			if singleType != nil && singleType.IsInterface() {
   447  				s.Add(ncase.Pos(), n1, caseVar, jmp)
   448  				caseVarInitialized = true
   449  			} else {
   450  				s.Add(ncase.Pos(), n1, nil, jmp)
   451  			}
   452  		}
   453  
   454  		body.Append(ir.NewLabelStmt(ncase.Pos(), label))
   455  		if caseVar != nil && !caseVarInitialized {
   456  			val := s.facename
   457  			if singleType != nil {
   458  				// We have a single concrete type. Extract the data.
   459  				if singleType.IsInterface() {
   460  					base.Fatalf("singleType interface should have been handled in Add")
   461  				}
   462  				val = ifaceData(ncase.Pos(), s.facename, singleType)
   463  			}
   464  			if len(ncase.List) == 1 && ncase.List[0].Op() == ir.ODYNAMICTYPE {
   465  				dt := ncase.List[0].(*ir.DynamicType)
   466  				x := ir.NewDynamicTypeAssertExpr(ncase.Pos(), ir.ODYNAMICDOTTYPE, val, dt.RType)
   467  				x.ITab = dt.ITab
   468  				x.SetType(caseVar.Type())
   469  				x.SetTypecheck(1)
   470  				val = x
   471  			}
   472  			l := []ir.Node{
   473  				ir.NewDecl(ncase.Pos(), ir.ODCL, caseVar),
   474  				ir.NewAssignStmt(ncase.Pos(), caseVar, val),
   475  			}
   476  			typecheck.Stmts(l)
   477  			body.Append(l...)
   478  		}
   479  		body.Append(ncase.Body...)
   480  		body.Append(br)
   481  	}
   482  	sw.Cases = nil
   483  
   484  	if defaultGoto == nil {
   485  		defaultGoto = br
   486  	}
   487  	if nilGoto == nil {
   488  		nilGoto = defaultGoto
   489  	}
   490  	ifNil.Body = []ir.Node{nilGoto}
   491  
   492  	s.Emit(&sw.Compiled)
   493  	sw.Compiled.Append(defaultGoto)
   494  	sw.Compiled.Append(body.Take()...)
   495  
   496  	walkStmtList(sw.Compiled)
   497  }
   498  
   499  // typeHashFieldOf returns an expression to select the type hash field
   500  // from an interface's descriptor word (whether a *runtime._type or
   501  // *runtime.itab pointer).
   502  func typeHashFieldOf(pos src.XPos, itab *ir.UnaryExpr) *ir.SelectorExpr {
   503  	if itab.Op() != ir.OITAB {
   504  		base.Fatalf("expected OITAB, got %v", itab.Op())
   505  	}
   506  	var hashField *types.Field
   507  	if itab.X.Type().IsEmptyInterface() {
   508  		// runtime._type's hash field
   509  		if rtypeHashField == nil {
   510  			rtypeHashField = runtimeField("hash", int64(2*types.PtrSize), types.Types[types.TUINT32])
   511  		}
   512  		hashField = rtypeHashField
   513  	} else {
   514  		// runtime.itab's hash field
   515  		if itabHashField == nil {
   516  			itabHashField = runtimeField("hash", int64(2*types.PtrSize), types.Types[types.TUINT32])
   517  		}
   518  		hashField = itabHashField
   519  	}
   520  	return boundedDotPtr(pos, itab, hashField)
   521  }
   522  
   523  var rtypeHashField, itabHashField *types.Field
   524  
   525  // A typeSwitch walks a type switch.
   526  type typeSwitch struct {
   527  	// Temporary variables (i.e., ONAMEs) used by type switch dispatch logic:
   528  	facename ir.Node // value being type-switched on
   529  	hashname ir.Node // type hash of the value being type-switched on
   530  	okname   ir.Node // boolean used for comma-ok type assertions
   531  
   532  	done    ir.Nodes
   533  	clauses []typeClause
   534  }
   535  
   536  type typeClause struct {
   537  	hash uint32
   538  	body ir.Nodes
   539  }
   540  
   541  func (s *typeSwitch) Add(pos src.XPos, n1 ir.Node, caseVar *ir.Name, jmp ir.Node) {
   542  	typ := n1.Type()
   543  	var body ir.Nodes
   544  	if caseVar != nil {
   545  		l := []ir.Node{
   546  			ir.NewDecl(pos, ir.ODCL, caseVar),
   547  			ir.NewAssignStmt(pos, caseVar, nil),
   548  		}
   549  		typecheck.Stmts(l)
   550  		body.Append(l...)
   551  	} else {
   552  		caseVar = ir.BlankNode.(*ir.Name)
   553  	}
   554  
   555  	// cv, ok = iface.(type)
   556  	as := ir.NewAssignListStmt(pos, ir.OAS2, nil, nil)
   557  	as.Lhs = []ir.Node{caseVar, s.okname} // cv, ok =
   558  	switch n1.Op() {
   559  	case ir.OTYPE:
   560  		// Static type assertion (non-generic)
   561  		dot := ir.NewTypeAssertExpr(pos, s.facename, typ) // iface.(type)
   562  		as.Rhs = []ir.Node{dot}
   563  	case ir.ODYNAMICTYPE:
   564  		// Dynamic type assertion (generic)
   565  		dt := n1.(*ir.DynamicType)
   566  		dot := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, s.facename, dt.RType)
   567  		dot.ITab = dt.ITab
   568  		dot.SetType(typ)
   569  		dot.SetTypecheck(1)
   570  		as.Rhs = []ir.Node{dot}
   571  	default:
   572  		base.Fatalf("unhandled type case %s", n1.Op())
   573  	}
   574  	appendWalkStmt(&body, as)
   575  
   576  	// if ok { goto label }
   577  	nif := ir.NewIfStmt(pos, nil, nil, nil)
   578  	nif.Cond = s.okname
   579  	nif.Body = []ir.Node{jmp}
   580  	body.Append(nif)
   581  
   582  	if n1.Op() == ir.OTYPE && !typ.IsInterface() {
   583  		// Defer static, noninterface cases so they can be binary searched by hash.
   584  		s.clauses = append(s.clauses, typeClause{
   585  			hash: types.TypeHash(n1.Type()),
   586  			body: body,
   587  		})
   588  		return
   589  	}
   590  
   591  	s.flush()
   592  	s.done.Append(body.Take()...)
   593  }
   594  
   595  func (s *typeSwitch) Emit(out *ir.Nodes) {
   596  	s.flush()
   597  	out.Append(s.done.Take()...)
   598  }
   599  
   600  func (s *typeSwitch) flush() {
   601  	cc := s.clauses
   602  	s.clauses = nil
   603  	if len(cc) == 0 {
   604  		return
   605  	}
   606  
   607  	sort.Slice(cc, func(i, j int) bool { return cc[i].hash < cc[j].hash })
   608  
   609  	// Combine adjacent cases with the same hash.
   610  	merged := cc[:1]
   611  	for _, c := range cc[1:] {
   612  		last := &merged[len(merged)-1]
   613  		if last.hash == c.hash {
   614  			last.body.Append(c.body.Take()...)
   615  		} else {
   616  			merged = append(merged, c)
   617  		}
   618  	}
   619  	cc = merged
   620  
   621  	// TODO: figure out if we could use a jump table using some low bits of the type hashes.
   622  	binarySearch(len(cc), &s.done,
   623  		func(i int) ir.Node {
   624  			return ir.NewBinaryExpr(base.Pos, ir.OLE, s.hashname, ir.NewInt(int64(cc[i-1].hash)))
   625  		},
   626  		func(i int, nif *ir.IfStmt) {
   627  			// TODO(mdempsky): Omit hash equality check if
   628  			// there's only one type.
   629  			c := cc[i]
   630  			nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, s.hashname, ir.NewInt(int64(c.hash)))
   631  			nif.Body.Append(c.body.Take()...)
   632  		},
   633  	)
   634  }
   635  
   636  // binarySearch constructs a binary search tree for handling n cases,
   637  // and appends it to out. It's used for efficiently implementing
   638  // switch statements.
   639  //
   640  // less(i) should return a boolean expression. If it evaluates true,
   641  // then cases before i will be tested; otherwise, cases i and later.
   642  //
   643  // leaf(i, nif) should setup nif (an OIF node) to test case i. In
   644  // particular, it should set nif.Cond and nif.Body.
   645  func binarySearch(n int, out *ir.Nodes, less func(i int) ir.Node, leaf func(i int, nif *ir.IfStmt)) {
   646  	const binarySearchMin = 4 // minimum number of cases for binary search
   647  
   648  	var do func(lo, hi int, out *ir.Nodes)
   649  	do = func(lo, hi int, out *ir.Nodes) {
   650  		n := hi - lo
   651  		if n < binarySearchMin {
   652  			for i := lo; i < hi; i++ {
   653  				nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
   654  				leaf(i, nif)
   655  				base.Pos = base.Pos.WithNotStmt()
   656  				nif.Cond = typecheck.Expr(nif.Cond)
   657  				nif.Cond = typecheck.DefaultLit(nif.Cond, nil)
   658  				out.Append(nif)
   659  				out = &nif.Else
   660  			}
   661  			return
   662  		}
   663  
   664  		half := lo + n/2
   665  		nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
   666  		nif.Cond = less(half)
   667  		base.Pos = base.Pos.WithNotStmt()
   668  		nif.Cond = typecheck.Expr(nif.Cond)
   669  		nif.Cond = typecheck.DefaultLit(nif.Cond, nil)
   670  		do(lo, half, &nif.Body)
   671  		do(half, hi, &nif.Else)
   672  		out.Append(nif)
   673  	}
   674  
   675  	do(0, n, out)
   676  }
   677  
   678  func stringSearch(expr ir.Node, cc []exprClause, out *ir.Nodes) {
   679  	if len(cc) < 4 {
   680  		// Short list, just do brute force equality checks.
   681  		for _, c := range cc {
   682  			nif := ir.NewIfStmt(base.Pos.WithNotStmt(), typecheck.DefaultLit(typecheck.Expr(c.test(expr)), nil), []ir.Node{c.jmp}, nil)
   683  			out.Append(nif)
   684  			out = &nif.Else
   685  		}
   686  		return
   687  	}
   688  
   689  	// The strategy here is to find a simple test to divide the set of possible strings
   690  	// that might match expr approximately in half.
   691  	// The test we're going to use is to do an ordered comparison of a single byte
   692  	// of expr to a constant. We will pick the index of that byte and the value we're
   693  	// comparing against to make the split as even as possible.
   694  	//   if expr[3] <= 'd' { ... search strings with expr[3] at 'd' or lower  ... }
   695  	//   else              { ... search strings with expr[3] at 'e' or higher ... }
   696  	//
   697  	// To add complication, we will do the ordered comparison in the signed domain.
   698  	// The reason for this is to prevent CSE from merging the load used for the
   699  	// ordered comparison with the load used for the later equality check.
   700  	//   if expr[3] <= 'd' { ... if expr[0] == 'f' && expr[1] == 'o' && expr[2] == 'o' && expr[3] == 'd' { ... } }
   701  	// If we did both expr[3] loads in the unsigned domain, they would be CSEd, and that
   702  	// would in turn defeat the combining of expr[0]...expr[3] into a single 4-byte load.
   703  	// See issue 48222.
   704  	// By using signed loads for the ordered comparison and unsigned loads for the
   705  	// equality comparison, they don't get CSEd and the equality comparisons will be
   706  	// done using wider loads.
   707  
   708  	n := len(ir.StringVal(cc[0].lo)) // Length of the constant strings.
   709  	bestScore := int64(0)            // measure of how good the split is.
   710  	bestIdx := 0                     // split using expr[bestIdx]
   711  	bestByte := int8(0)              // compare expr[bestIdx] against bestByte
   712  	for idx := 0; idx < n; idx++ {
   713  		for b := int8(-128); b < 127; b++ {
   714  			le := 0
   715  			for _, c := range cc {
   716  				s := ir.StringVal(c.lo)
   717  				if int8(s[idx]) <= b {
   718  					le++
   719  				}
   720  			}
   721  			score := int64(le) * int64(len(cc)-le)
   722  			if score > bestScore {
   723  				bestScore = score
   724  				bestIdx = idx
   725  				bestByte = b
   726  			}
   727  		}
   728  	}
   729  
   730  	// The split must be at least 1:n-1 because we have at least 2 distinct strings; they
   731  	// have to be different somewhere.
   732  	// TODO: what if the best split is still pretty bad?
   733  	if bestScore == 0 {
   734  		base.Fatalf("unable to split string set")
   735  	}
   736  
   737  	// Convert expr to a []int8
   738  	slice := ir.NewConvExpr(base.Pos, ir.OSTR2BYTESTMP, types.NewSlice(types.Types[types.TINT8]), expr)
   739  	slice.SetTypecheck(1) // legacy typechecker doesn't handle this op
   740  	// Load the byte we're splitting on.
   741  	load := ir.NewIndexExpr(base.Pos, slice, ir.NewInt(int64(bestIdx)))
   742  	// Compare with the value we're splitting on.
   743  	cmp := ir.Node(ir.NewBinaryExpr(base.Pos, ir.OLE, load, ir.NewInt(int64(bestByte))))
   744  	cmp = typecheck.DefaultLit(typecheck.Expr(cmp), nil)
   745  	nif := ir.NewIfStmt(base.Pos, cmp, nil, nil)
   746  
   747  	var le []exprClause
   748  	var gt []exprClause
   749  	for _, c := range cc {
   750  		s := ir.StringVal(c.lo)
   751  		if int8(s[bestIdx]) <= bestByte {
   752  			le = append(le, c)
   753  		} else {
   754  			gt = append(gt, c)
   755  		}
   756  	}
   757  	stringSearch(expr, le, &nif.Body)
   758  	stringSearch(expr, gt, &nif.Else)
   759  	out.Append(nif)
   760  
   761  	// TODO: if expr[bestIdx] has enough different possible values, use a jump table.
   762  }