1
2
3
4
5 package walk
6
7 import (
8 "fmt"
9 "go/constant"
10 "go/token"
11 "math/bits"
12 "sort"
13
14 "cmd/compile/internal/base"
15 "cmd/compile/internal/ir"
16 "cmd/compile/internal/objw"
17 "cmd/compile/internal/reflectdata"
18 "cmd/compile/internal/rttype"
19 "cmd/compile/internal/ssagen"
20 "cmd/compile/internal/typecheck"
21 "cmd/compile/internal/types"
22 "cmd/internal/obj"
23 "cmd/internal/src"
24 )
25
26
27 func walkSwitch(sw *ir.SwitchStmt) {
28
29 if sw.Walked() {
30 return
31 }
32 sw.SetWalked(true)
33
34 if sw.Tag != nil && sw.Tag.Op() == ir.OTYPESW {
35 walkSwitchType(sw)
36 } else {
37 walkSwitchExpr(sw)
38 }
39 }
40
41
42
43 func walkSwitchExpr(sw *ir.SwitchStmt) {
44 lno := ir.SetPos(sw)
45
46 cond := sw.Tag
47 sw.Tag = nil
48
49
50 if cond == nil {
51 cond = ir.NewBool(base.Pos, true)
52 cond = typecheck.Expr(cond)
53 cond = typecheck.DefaultLit(cond, nil)
54 }
55
56
57
58
59
60
61
62
63 if cond.Op() == ir.OBYTES2STR && allCaseExprsAreSideEffectFree(sw) {
64 cond := cond.(*ir.ConvExpr)
65 cond.SetOp(ir.OBYTES2STRTMP)
66 }
67
68 cond = walkExpr(cond, sw.PtrInit())
69 if cond.Op() != ir.OLITERAL && cond.Op() != ir.ONIL {
70 cond = copyExpr(cond, cond.Type(), &sw.Compiled)
71 }
72
73 base.Pos = lno
74
75 s := exprSwitch{
76 pos: lno,
77 exprname: cond,
78 }
79
80 var defaultGoto ir.Node
81 var body ir.Nodes
82 for _, ncase := range sw.Cases {
83 label := typecheck.AutoLabel(".s")
84 jmp := ir.NewBranchStmt(ncase.Pos(), ir.OGOTO, label)
85
86
87 if len(ncase.List) == 0 {
88 if defaultGoto != nil {
89 base.Fatalf("duplicate default case not detected during typechecking")
90 }
91 defaultGoto = jmp
92 }
93
94 for i, n1 := range ncase.List {
95 var rtype ir.Node
96 if i < len(ncase.RTypes) {
97 rtype = ncase.RTypes[i]
98 }
99 s.Add(ncase.Pos(), n1, rtype, jmp)
100 }
101
102
103 body.Append(ir.NewLabelStmt(ncase.Pos(), label))
104 body.Append(ncase.Body...)
105 if fall, pos := endsInFallthrough(ncase.Body); !fall {
106 br := ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)
107 br.SetPos(pos)
108 body.Append(br)
109 }
110 }
111 sw.Cases = nil
112
113 if defaultGoto == nil {
114 br := ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)
115 br.SetPos(br.Pos().WithNotStmt())
116 defaultGoto = br
117 }
118
119 s.Emit(&sw.Compiled)
120 sw.Compiled.Append(defaultGoto)
121 sw.Compiled.Append(body.Take()...)
122 walkStmtList(sw.Compiled)
123 }
124
125
126 type exprSwitch struct {
127 pos src.XPos
128 exprname ir.Node
129
130 done ir.Nodes
131 clauses []exprClause
132 }
133
134 type exprClause struct {
135 pos src.XPos
136 lo, hi ir.Node
137 rtype ir.Node
138 jmp ir.Node
139 }
140
141 func (s *exprSwitch) Add(pos src.XPos, expr, rtype, jmp ir.Node) {
142 c := exprClause{pos: pos, lo: expr, hi: expr, rtype: rtype, jmp: jmp}
143 if types.IsOrdered[s.exprname.Type().Kind()] && expr.Op() == ir.OLITERAL {
144 s.clauses = append(s.clauses, c)
145 return
146 }
147
148 s.flush()
149 s.clauses = append(s.clauses, c)
150 s.flush()
151 }
152
153 func (s *exprSwitch) Emit(out *ir.Nodes) {
154 s.flush()
155 out.Append(s.done.Take()...)
156 }
157
158 func (s *exprSwitch) flush() {
159 cc := s.clauses
160 s.clauses = nil
161 if len(cc) == 0 {
162 return
163 }
164
165
166
167
168
169
170 if s.exprname.Type().IsString() && len(cc) >= 2 {
171
172
173
174
175 sort.Slice(cc, func(i, j int) bool {
176 si := ir.StringVal(cc[i].lo)
177 sj := ir.StringVal(cc[j].lo)
178 if len(si) != len(sj) {
179 return len(si) < len(sj)
180 }
181 return si < sj
182 })
183
184
185
186 runLen := func(run []exprClause) int64 { return int64(len(ir.StringVal(run[0].lo))) }
187
188
189 var runs [][]exprClause
190 start := 0
191 for i := 1; i < len(cc); i++ {
192 if runLen(cc[start:]) != runLen(cc[i:]) {
193 runs = append(runs, cc[start:i])
194 start = i
195 }
196 }
197 runs = append(runs, cc[start:])
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220 outerLabel := typecheck.AutoLabel(".s")
221 endLabel := typecheck.AutoLabel(".s")
222
223
224 s.done.Append(ir.NewBranchStmt(s.pos, ir.OGOTO, outerLabel))
225
226 var outer exprSwitch
227 outer.exprname = ir.NewUnaryExpr(s.pos, ir.OLEN, s.exprname)
228 outer.exprname.SetType(types.Types[types.TINT])
229
230 for _, run := range runs {
231
232 label := typecheck.AutoLabel(".s")
233
234
235 pos := run[0].pos
236 s.done.Append(ir.NewLabelStmt(pos, label))
237 stringSearch(s.exprname, run, &s.done)
238 s.done.Append(ir.NewBranchStmt(pos, ir.OGOTO, endLabel))
239
240
241 cas := ir.NewInt(pos, runLen(run))
242 jmp := ir.NewBranchStmt(pos, ir.OGOTO, label)
243 outer.Add(pos, cas, nil, jmp)
244 }
245 s.done.Append(ir.NewLabelStmt(s.pos, outerLabel))
246 outer.Emit(&s.done)
247 s.done.Append(ir.NewLabelStmt(s.pos, endLabel))
248 return
249 }
250
251 sort.Slice(cc, func(i, j int) bool {
252 return constant.Compare(cc[i].lo.Val(), token.LSS, cc[j].lo.Val())
253 })
254
255
256 if s.exprname.Type().IsInteger() {
257 consecutive := func(last, next constant.Value) bool {
258 delta := constant.BinaryOp(next, token.SUB, last)
259 return constant.Compare(delta, token.EQL, constant.MakeInt64(1))
260 }
261
262 merged := cc[:1]
263 for _, c := range cc[1:] {
264 last := &merged[len(merged)-1]
265 if last.jmp == c.jmp && consecutive(last.hi.Val(), c.lo.Val()) {
266 last.hi = c.lo
267 } else {
268 merged = append(merged, c)
269 }
270 }
271 cc = merged
272 }
273
274 s.search(cc, &s.done)
275 }
276
277 func (s *exprSwitch) search(cc []exprClause, out *ir.Nodes) {
278 if s.tryJumpTable(cc, out) {
279 return
280 }
281 binarySearch(len(cc), out,
282 func(i int) ir.Node {
283 return ir.NewBinaryExpr(base.Pos, ir.OLE, s.exprname, cc[i-1].hi)
284 },
285 func(i int, nif *ir.IfStmt) {
286 c := &cc[i]
287 nif.Cond = c.test(s.exprname)
288 nif.Body = []ir.Node{c.jmp}
289 },
290 )
291 }
292
293
294 func (s *exprSwitch) tryJumpTable(cc []exprClause, out *ir.Nodes) bool {
295 const minCases = 8
296 const minDensity = 4
297
298 if base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable || base.Ctxt.Retpoline {
299 return false
300 }
301 if len(cc) < minCases {
302 return false
303 }
304 if cc[0].lo.Val().Kind() != constant.Int {
305 return false
306 }
307 if s.exprname.Type().Size() > int64(types.PtrSize) {
308 return false
309 }
310 min := cc[0].lo.Val()
311 max := cc[len(cc)-1].hi.Val()
312 width := constant.BinaryOp(constant.BinaryOp(max, token.SUB, min), token.ADD, constant.MakeInt64(1))
313 limit := constant.MakeInt64(int64(len(cc)) * minDensity)
314 if constant.Compare(width, token.GTR, limit) {
315
316
317 return false
318 }
319 jt := ir.NewJumpTableStmt(base.Pos, s.exprname)
320 for _, c := range cc {
321 jmp := c.jmp.(*ir.BranchStmt)
322 if jmp.Op() != ir.OGOTO || jmp.Label == nil {
323 panic("bad switch case body")
324 }
325 for i := c.lo.Val(); constant.Compare(i, token.LEQ, c.hi.Val()); i = constant.BinaryOp(i, token.ADD, constant.MakeInt64(1)) {
326 jt.Cases = append(jt.Cases, i)
327 jt.Targets = append(jt.Targets, jmp.Label)
328 }
329 }
330 out.Append(jt)
331 return true
332 }
333
334 func (c *exprClause) test(exprname ir.Node) ir.Node {
335
336 if c.hi != c.lo {
337 low := ir.NewBinaryExpr(c.pos, ir.OGE, exprname, c.lo)
338 high := ir.NewBinaryExpr(c.pos, ir.OLE, exprname, c.hi)
339 return ir.NewLogicalExpr(c.pos, ir.OANDAND, low, high)
340 }
341
342
343 if ir.IsConst(exprname, constant.Bool) && !c.lo.Type().IsInterface() {
344 if ir.BoolVal(exprname) {
345 return c.lo
346 } else {
347 return ir.NewUnaryExpr(c.pos, ir.ONOT, c.lo)
348 }
349 }
350
351 n := ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo)
352 n.RType = c.rtype
353 return n
354 }
355
356 func allCaseExprsAreSideEffectFree(sw *ir.SwitchStmt) bool {
357
358
359
360
361
362
363
364 for _, ncase := range sw.Cases {
365 for _, v := range ncase.List {
366 if v.Op() != ir.OLITERAL {
367 return false
368 }
369 }
370 }
371 return true
372 }
373
374
375 func endsInFallthrough(stmts []ir.Node) (bool, src.XPos) {
376 if len(stmts) == 0 {
377 return false, src.NoXPos
378 }
379 i := len(stmts) - 1
380 return stmts[i].Op() == ir.OFALL, stmts[i].Pos()
381 }
382
383
384
385 func walkSwitchType(sw *ir.SwitchStmt) {
386 var s typeSwitch
387 s.srcName = sw.Tag.(*ir.TypeSwitchGuard).X
388 s.srcName = walkExpr(s.srcName, sw.PtrInit())
389 s.srcName = copyExpr(s.srcName, s.srcName.Type(), &sw.Compiled)
390 s.okName = typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TBOOL])
391 s.itabName = typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TUINT8].PtrTo())
392
393
394
395
396 srcItab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s.srcName)
397 srcData := ir.NewUnaryExpr(base.Pos, ir.OIDATA, s.srcName)
398 srcData.SetType(types.Types[types.TUINT8].PtrTo())
399 srcData.SetTypecheck(1)
400
401
402
403
404
405
406
407 ifNil := ir.NewIfStmt(base.Pos, nil, nil, nil)
408 ifNil.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, srcItab, typecheck.NodNil())
409 base.Pos = base.Pos.WithNotStmt()
410 ifNil.Cond = typecheck.Expr(ifNil.Cond)
411 ifNil.Cond = typecheck.DefaultLit(ifNil.Cond, nil)
412
413 sw.Compiled.Append(ifNil)
414
415
416 dotHash := typeHashFieldOf(base.Pos, srcItab)
417 s.hashName = copyExpr(dotHash, dotHash.Type(), &sw.Compiled)
418
419
420 labels := make([]*types.Sym, len(sw.Cases))
421 for i := range sw.Cases {
422 labels[i] = typecheck.AutoLabel(".s")
423 }
424
425
426 br := ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)
427
428
429
430
431 type oneCase struct {
432 pos src.XPos
433 jmp ir.Node
434
435
436
437
438
439 typ ir.Node
440 }
441 var cases []oneCase
442 var defaultGoto, nilGoto ir.Node
443 for i, ncase := range sw.Cases {
444 jmp := ir.NewBranchStmt(ncase.Pos(), ir.OGOTO, labels[i])
445 if len(ncase.List) == 0 {
446 if defaultGoto != nil {
447 base.Fatalf("duplicate default case not detected during typechecking")
448 }
449 defaultGoto = jmp
450 }
451 for _, n1 := range ncase.List {
452 if ir.IsNil(n1) {
453 if nilGoto != nil {
454 base.Fatalf("duplicate nil case not detected during typechecking")
455 }
456 nilGoto = jmp
457 continue
458 }
459 if n1.Op() == ir.ODYNAMICTYPE {
460
461
462 dt := n1.(*ir.DynamicType)
463 if dt.RType != nil && dt.RType.Op() == ir.OADDR {
464 addr := dt.RType.(*ir.AddrExpr)
465 if addr.X.Op() == ir.OLINKSYMOFFSET {
466 n1 = ir.TypeNode(n1.Type())
467 }
468 }
469 if dt.ITab != nil && dt.ITab.Op() == ir.OADDR {
470 addr := dt.ITab.(*ir.AddrExpr)
471 if addr.X.Op() == ir.OLINKSYMOFFSET {
472 n1 = ir.TypeNode(n1.Type())
473 }
474 }
475 }
476 cases = append(cases, oneCase{
477 pos: ncase.Pos(),
478 typ: n1,
479 jmp: jmp,
480 })
481 }
482 }
483 if defaultGoto == nil {
484 defaultGoto = br
485 }
486 if nilGoto == nil {
487 nilGoto = defaultGoto
488 }
489 ifNil.Body = []ir.Node{nilGoto}
490
491
492 var concreteCases []oneCase
493 var interfaceCases []oneCase
494 flush := func() {
495
496
497
498
499 if len(concreteCases) > 0 {
500 var clauses []typeClause
501 for _, c := range concreteCases {
502 as := ir.NewAssignListStmt(c.pos, ir.OAS2,
503 []ir.Node{ir.BlankNode, s.okName},
504 []ir.Node{ir.NewTypeAssertExpr(c.pos, s.srcName, c.typ.Type())})
505 nif := ir.NewIfStmt(c.pos, s.okName, []ir.Node{c.jmp}, nil)
506 clauses = append(clauses, typeClause{
507 hash: types.TypeHash(c.typ.Type()),
508 body: []ir.Node{typecheck.Stmt(as), typecheck.Stmt(nif)},
509 })
510 }
511 s.flush(clauses, &sw.Compiled)
512 concreteCases = concreteCases[:0]
513 }
514
515
516
517
518 var anyGoto ir.Node
519 if len(interfaceCases) > 0 && interfaceCases[len(interfaceCases)-1].typ.Type().IsEmptyInterface() {
520 anyGoto = interfaceCases[len(interfaceCases)-1].jmp
521 interfaceCases = interfaceCases[:len(interfaceCases)-1]
522 }
523
524
525 if len(interfaceCases) > 0 {
526
527
528 lsym := types.LocalPkg.Lookup(fmt.Sprintf(".interfaceSwitch.%d", interfaceSwitchGen)).LinksymABI(obj.ABI0)
529 interfaceSwitchGen++
530 c := rttype.NewCursor(lsym, 0, rttype.InterfaceSwitch)
531 c.Field("Cache").WritePtr(typecheck.LookupRuntimeVar("emptyInterfaceSwitchCache"))
532 c.Field("NCases").WriteInt(int64(len(interfaceCases)))
533 array, sizeDelta := c.Field("Cases").ModifyArray(len(interfaceCases))
534 for i, c := range interfaceCases {
535 array.Elem(i).WritePtr(reflectdata.TypeLinksym(c.typ.Type()))
536 }
537 objw.Global(lsym, int32(rttype.InterfaceSwitch.Size()+sizeDelta), obj.LOCAL)
538
539
540
541 lsym.Gotype = reflectdata.TypeLinksym(rttype.InterfaceSwitch)
542
543
544
545 var typeArg ir.Node
546 if s.srcName.Type().IsEmptyInterface() {
547 typeArg = ir.NewConvExpr(base.Pos, ir.OCONVNOP, types.Types[types.TUINT8].PtrTo(), srcItab)
548 } else {
549 typeArg = itabType(srcItab)
550 }
551 caseVar := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT])
552 isw := ir.NewInterfaceSwitchStmt(base.Pos, caseVar, s.itabName, typeArg, dotHash, lsym)
553 sw.Compiled.Append(isw)
554
555
556 var newCases []*ir.CaseClause
557 for i, c := range interfaceCases {
558 newCases = append(newCases, &ir.CaseClause{
559 List: []ir.Node{ir.NewInt(base.Pos, int64(i))},
560 Body: []ir.Node{c.jmp},
561 })
562 }
563
564 sw2 := ir.NewSwitchStmt(base.Pos, caseVar, newCases)
565 sw.Compiled.Append(typecheck.Stmt(sw2))
566 interfaceCases = interfaceCases[:0]
567 }
568
569 if anyGoto != nil {
570
571
572 sw.Compiled.Append(anyGoto)
573 }
574 }
575 caseLoop:
576 for _, c := range cases {
577 if c.typ.Op() == ir.ODYNAMICTYPE {
578 flush()
579 dt := c.typ.(*ir.DynamicType)
580 dot := ir.NewDynamicTypeAssertExpr(c.pos, ir.ODYNAMICDOTTYPE, s.srcName, dt.RType)
581 dot.ITab = dt.ITab
582 dot.SetType(c.typ.Type())
583 dot.SetTypecheck(1)
584
585 as := ir.NewAssignListStmt(c.pos, ir.OAS2, nil, nil)
586 as.Lhs = []ir.Node{ir.BlankNode, s.okName}
587 as.Rhs = []ir.Node{dot}
588 typecheck.Stmt(as)
589
590 nif := ir.NewIfStmt(c.pos, s.okName, []ir.Node{c.jmp}, nil)
591 sw.Compiled.Append(as, nif)
592 continue
593 }
594
595
596
597
598
599 for _, ic := range interfaceCases {
600
601
602 if typecheck.Implements(c.typ.Type(), ic.typ.Type()) {
603 continue caseLoop
604 }
605
606
607
608
609
610
611
612
613 }
614
615 if c.typ.Type().IsInterface() {
616 interfaceCases = append(interfaceCases, c)
617 } else {
618 concreteCases = append(concreteCases, c)
619 }
620 }
621 flush()
622
623 sw.Compiled.Append(defaultGoto)
624
625
626 for i, ncase := range sw.Cases {
627 sw.Compiled.Append(ir.NewLabelStmt(ncase.Pos(), labels[i]))
628 if caseVar := ncase.Var; caseVar != nil {
629 val := s.srcName
630 if len(ncase.List) == 1 {
631
632 if ncase.List[0].Op() == ir.OTYPE {
633 t := ncase.List[0].Type()
634 if t.IsInterface() {
635
636
637 if t.IsEmptyInterface() {
638 var typ ir.Node
639 if s.srcName.Type().IsEmptyInterface() {
640
641 typ = srcItab
642 } else {
643
644 typ = itabType(srcItab)
645 typ.SetPos(ncase.Pos())
646 }
647 val = ir.NewBinaryExpr(ncase.Pos(), ir.OMAKEFACE, typ, srcData)
648 } else {
649
650 val = ir.NewBinaryExpr(ncase.Pos(), ir.OMAKEFACE, s.itabName, srcData)
651 }
652 } else {
653
654 val = ifaceData(ncase.Pos(), s.srcName, t)
655 }
656 } else if ncase.List[0].Op() == ir.ODYNAMICTYPE {
657 dt := ncase.List[0].(*ir.DynamicType)
658 x := ir.NewDynamicTypeAssertExpr(ncase.Pos(), ir.ODYNAMICDOTTYPE, val, dt.RType)
659 x.ITab = dt.ITab
660 val = x
661 } else if ir.IsNil(ncase.List[0]) {
662 } else {
663 base.Fatalf("unhandled type switch case %v", ncase.List[0])
664 }
665 val.SetType(caseVar.Type())
666 val.SetTypecheck(1)
667 }
668 l := []ir.Node{
669 ir.NewDecl(ncase.Pos(), ir.ODCL, caseVar),
670 ir.NewAssignStmt(ncase.Pos(), caseVar, val),
671 }
672 typecheck.Stmts(l)
673 sw.Compiled.Append(l...)
674 }
675 sw.Compiled.Append(ncase.Body...)
676 sw.Compiled.Append(br)
677 }
678
679 walkStmtList(sw.Compiled)
680 sw.Tag = nil
681 sw.Cases = nil
682 }
683
684 var interfaceSwitchGen int
685
686
687
688
689 func typeHashFieldOf(pos src.XPos, itab *ir.UnaryExpr) *ir.SelectorExpr {
690 if itab.Op() != ir.OITAB {
691 base.Fatalf("expected OITAB, got %v", itab.Op())
692 }
693 var hashField *types.Field
694 if itab.X.Type().IsEmptyInterface() {
695
696 if rtypeHashField == nil {
697 rtypeHashField = runtimeField("hash", rttype.Type.OffsetOf("Hash"), types.Types[types.TUINT32])
698 }
699 hashField = rtypeHashField
700 } else {
701
702 if itabHashField == nil {
703 itabHashField = runtimeField("hash", rttype.ITab.OffsetOf("Hash"), types.Types[types.TUINT32])
704 }
705 hashField = itabHashField
706 }
707 return boundedDotPtr(pos, itab, hashField)
708 }
709
710 var rtypeHashField, itabHashField *types.Field
711
712
713 type typeSwitch struct {
714
715 srcName ir.Node
716 hashName ir.Node
717 okName ir.Node
718 itabName ir.Node
719 }
720
721 type typeClause struct {
722 hash uint32
723 body ir.Nodes
724 }
725
726 func (s *typeSwitch) flush(cc []typeClause, compiled *ir.Nodes) {
727 if len(cc) == 0 {
728 return
729 }
730
731 sort.Slice(cc, func(i, j int) bool { return cc[i].hash < cc[j].hash })
732
733
734 merged := cc[:1]
735 for _, c := range cc[1:] {
736 last := &merged[len(merged)-1]
737 if last.hash == c.hash {
738 last.body.Append(c.body.Take()...)
739 } else {
740 merged = append(merged, c)
741 }
742 }
743 cc = merged
744
745 if s.tryJumpTable(cc, compiled) {
746 return
747 }
748 binarySearch(len(cc), compiled,
749 func(i int) ir.Node {
750 return ir.NewBinaryExpr(base.Pos, ir.OLE, s.hashName, ir.NewInt(base.Pos, int64(cc[i-1].hash)))
751 },
752 func(i int, nif *ir.IfStmt) {
753
754
755 c := cc[i]
756 nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, s.hashName, ir.NewInt(base.Pos, int64(c.hash)))
757 nif.Body.Append(c.body.Take()...)
758 },
759 )
760 }
761
762
763 func (s *typeSwitch) tryJumpTable(cc []typeClause, out *ir.Nodes) bool {
764 const minCases = 5
765 if base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable || base.Ctxt.Retpoline {
766 return false
767 }
768 if len(cc) < minCases {
769 return false
770 }
771 hashes := make([]uint32, len(cc))
772
773
774 b0 := bits.Len(uint(len(cc) - 1))
775 for b := b0; b < b0+3; b++ {
776 pickI:
777 for i := 0; i <= 32-b; i++ {
778
779
780 hashes = hashes[:0]
781 for _, c := range cc {
782 h := c.hash >> i & (1<<b - 1)
783 hashes = append(hashes, h)
784 }
785
786 sort.Slice(hashes, func(j, k int) bool {
787 return hashes[j] < hashes[k]
788 })
789 for j := 1; j < len(hashes); j++ {
790 if hashes[j] == hashes[j-1] {
791
792 continue pickI
793 }
794 }
795
796
797 h := s.hashName
798 if i != 0 {
799 h = ir.NewBinaryExpr(base.Pos, ir.ORSH, h, ir.NewInt(base.Pos, int64(i)))
800 }
801 h = ir.NewBinaryExpr(base.Pos, ir.OAND, h, ir.NewInt(base.Pos, int64(1<<b-1)))
802 h = typecheck.Expr(h)
803
804
805 jt := ir.NewJumpTableStmt(base.Pos, h)
806 jt.Cases = make([]constant.Value, 1<<b)
807 jt.Targets = make([]*types.Sym, 1<<b)
808 out.Append(jt)
809
810
811 noMatch := typecheck.AutoLabel(".s")
812 for j := 0; j < 1<<b; j++ {
813 jt.Cases[j] = constant.MakeInt64(int64(j))
814 jt.Targets[j] = noMatch
815 }
816
817
818 out.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, noMatch))
819
820
821 for _, c := range cc {
822 h := c.hash >> i & (1<<b - 1)
823 label := typecheck.AutoLabel(".s")
824 jt.Targets[h] = label
825 out.Append(ir.NewLabelStmt(base.Pos, label))
826 out.Append(c.body...)
827
828 out.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, noMatch))
829 }
830
831 out.Append(ir.NewLabelStmt(base.Pos, noMatch))
832 return true
833 }
834 }
835
836 return false
837 }
838
839
840
841
842
843
844
845
846
847
848 func binarySearch(n int, out *ir.Nodes, less func(i int) ir.Node, leaf func(i int, nif *ir.IfStmt)) {
849 const binarySearchMin = 4
850
851 var do func(lo, hi int, out *ir.Nodes)
852 do = func(lo, hi int, out *ir.Nodes) {
853 n := hi - lo
854 if n < binarySearchMin {
855 for i := lo; i < hi; i++ {
856 nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
857 leaf(i, nif)
858 base.Pos = base.Pos.WithNotStmt()
859 nif.Cond = typecheck.Expr(nif.Cond)
860 nif.Cond = typecheck.DefaultLit(nif.Cond, nil)
861 out.Append(nif)
862 out = &nif.Else
863 }
864 return
865 }
866
867 half := lo + n/2
868 nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
869 nif.Cond = less(half)
870 base.Pos = base.Pos.WithNotStmt()
871 nif.Cond = typecheck.Expr(nif.Cond)
872 nif.Cond = typecheck.DefaultLit(nif.Cond, nil)
873 do(lo, half, &nif.Body)
874 do(half, hi, &nif.Else)
875 out.Append(nif)
876 }
877
878 do(0, n, out)
879 }
880
881 func stringSearch(expr ir.Node, cc []exprClause, out *ir.Nodes) {
882 if len(cc) < 4 {
883
884 for _, c := range cc {
885 nif := ir.NewIfStmt(base.Pos.WithNotStmt(), typecheck.DefaultLit(typecheck.Expr(c.test(expr)), nil), []ir.Node{c.jmp}, nil)
886 out.Append(nif)
887 out = &nif.Else
888 }
889 return
890 }
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911 n := len(ir.StringVal(cc[0].lo))
912 bestScore := int64(0)
913 bestIdx := 0
914 bestByte := int8(0)
915 for idx := 0; idx < n; idx++ {
916 for b := int8(-128); b < 127; b++ {
917 le := 0
918 for _, c := range cc {
919 s := ir.StringVal(c.lo)
920 if int8(s[idx]) <= b {
921 le++
922 }
923 }
924 score := int64(le) * int64(len(cc)-le)
925 if score > bestScore {
926 bestScore = score
927 bestIdx = idx
928 bestByte = b
929 }
930 }
931 }
932
933
934
935
936 if bestScore == 0 {
937 base.Fatalf("unable to split string set")
938 }
939
940
941 slice := ir.NewConvExpr(base.Pos, ir.OSTR2BYTESTMP, types.NewSlice(types.Types[types.TINT8]), expr)
942 slice.SetTypecheck(1)
943 slice.MarkNonNil()
944
945 load := ir.NewIndexExpr(base.Pos, slice, ir.NewInt(base.Pos, int64(bestIdx)))
946
947 cmp := ir.Node(ir.NewBinaryExpr(base.Pos, ir.OLE, load, ir.NewInt(base.Pos, int64(bestByte))))
948 cmp = typecheck.DefaultLit(typecheck.Expr(cmp), nil)
949 nif := ir.NewIfStmt(base.Pos, cmp, nil, nil)
950
951 var le []exprClause
952 var gt []exprClause
953 for _, c := range cc {
954 s := ir.StringVal(c.lo)
955 if int8(s[bestIdx]) <= bestByte {
956 le = append(le, c)
957 } else {
958 gt = append(gt, c)
959 }
960 }
961 stringSearch(expr, le, &nif.Body)
962 stringSearch(expr, gt, &nif.Else)
963 out.Append(nif)
964
965
966 }
967
View as plain text