1
2
3
4
5 package ssa
6
7 import (
8 "cmd/compile/internal/types"
9 "cmd/internal/src"
10 "cmp"
11 "fmt"
12 "math"
13 "math/bits"
14 "slices"
15 "strings"
16 )
17
18 type branch int
19
20 const (
21 unknown branch = iota
22 positive
23 negative
24
25
26
27 jumpTable0
28 )
29
30 func (b branch) String() string {
31 switch b {
32 case unknown:
33 return "unk"
34 case positive:
35 return "pos"
36 case negative:
37 return "neg"
38 default:
39 return fmt.Sprintf("jmp%d", b-jumpTable0)
40 }
41 }
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 type relation uint
64
65 const (
66 lt relation = 1 << iota
67 eq
68 gt
69 )
70
71 var relationStrings = [...]string{
72 0: "none", lt: "<", eq: "==", lt | eq: "<=",
73 gt: ">", gt | lt: "!=", gt | eq: ">=", gt | eq | lt: "any",
74 }
75
76 func (r relation) String() string {
77 if r < relation(len(relationStrings)) {
78 return relationStrings[r]
79 }
80 return fmt.Sprintf("relation(%d)", uint(r))
81 }
82
83
84
85
86
87 type domain uint
88
89 const (
90 signed domain = 1 << iota
91 unsigned
92 pointer
93 boolean
94 )
95
96 var domainStrings = [...]string{
97 "signed", "unsigned", "pointer", "boolean",
98 }
99
100 func (d domain) String() string {
101 s := ""
102 for i, ds := range domainStrings {
103 if d&(1<<uint(i)) != 0 {
104 if len(s) != 0 {
105 s += "|"
106 }
107 s += ds
108 d &^= 1 << uint(i)
109 }
110 }
111 if d != 0 {
112 if len(s) != 0 {
113 s += "|"
114 }
115 s += fmt.Sprintf("0x%x", uint(d))
116 }
117 return s
118 }
119
120
121
122
123
124
125
126
127
128 type limit struct {
129 min, max int64
130 umin, umax uint64
131
132
133 }
134
135 func (l limit) String() string {
136 return fmt.Sprintf("sm,SM=%d,%d um,UM=%d,%d", l.min, l.max, l.umin, l.umax)
137 }
138
139 func (l limit) intersect(l2 limit) limit {
140 l.min = max(l.min, l2.min)
141 l.umin = max(l.umin, l2.umin)
142 l.max = min(l.max, l2.max)
143 l.umax = min(l.umax, l2.umax)
144 return l
145 }
146
147 func (l limit) signedMin(m int64) limit {
148 l.min = max(l.min, m)
149 return l
150 }
151
152 func (l limit) signedMinMax(minimum, maximum int64) limit {
153 l.min = max(l.min, minimum)
154 l.max = min(l.max, maximum)
155 return l
156 }
157
158 func (l limit) unsignedMin(m uint64) limit {
159 l.umin = max(l.umin, m)
160 return l
161 }
162 func (l limit) unsignedMax(m uint64) limit {
163 l.umax = min(l.umax, m)
164 return l
165 }
166 func (l limit) unsignedMinMax(minimum, maximum uint64) limit {
167 l.umin = max(l.umin, minimum)
168 l.umax = min(l.umax, maximum)
169 return l
170 }
171
172 func (l limit) nonzero() bool {
173 return l.min > 0 || l.umin > 0 || l.max < 0
174 }
175 func (l limit) maybeZero() bool {
176 return !l.nonzero()
177 }
178 func (l limit) nonnegative() bool {
179 return l.min >= 0
180 }
181 func (l limit) unsat() bool {
182 return l.min > l.max || l.umin > l.umax
183 }
184
185
186
187
188 func safeAdd(x, y int64, b uint) (int64, bool) {
189 s := x + y
190 if x >= 0 && y >= 0 && s < 0 {
191 return 0, false
192 }
193 if x < 0 && y < 0 && s >= 0 {
194 return 0, false
195 }
196 if !fitsInBits(s, b) {
197 return 0, false
198 }
199 return s, true
200 }
201
202
203 func safeAddU(x, y uint64, b uint) (uint64, bool) {
204 s := x + y
205 if s < x || s < y {
206 return 0, false
207 }
208 if !fitsInBitsU(s, b) {
209 return 0, false
210 }
211 return s, true
212 }
213
214
215 func safeSub(x, y int64, b uint) (int64, bool) {
216 if y == math.MinInt64 {
217 if x == math.MaxInt64 {
218 return 0, false
219 }
220 x++
221 y++
222 }
223 return safeAdd(x, -y, b)
224 }
225
226
227 func safeSubU(x, y uint64, b uint) (uint64, bool) {
228 if x < y {
229 return 0, false
230 }
231 s := x - y
232 if !fitsInBitsU(s, b) {
233 return 0, false
234 }
235 return s, true
236 }
237
238
239 func fitsInBits(x int64, b uint) bool {
240 if b == 64 {
241 return true
242 }
243 m := int64(-1) << (b - 1)
244 M := -m - 1
245 return x >= m && x <= M
246 }
247
248
249 func fitsInBitsU(x uint64, b uint) bool {
250 return x>>b == 0
251 }
252
253 func noLimit() limit {
254 return noLimitForBitsize(64)
255 }
256
257 func noLimitForBitsize(bitsize uint) limit {
258 return limit{min: -(1 << (bitsize - 1)), max: 1<<(bitsize-1) - 1, umin: 0, umax: 1<<bitsize - 1}
259 }
260
261 func convertIntWithBitsize[Target uint64 | int64, Source uint64 | int64](x Source, bitsize uint) Target {
262 switch bitsize {
263 case 64:
264 return Target(x)
265 case 32:
266 return Target(int32(x))
267 case 16:
268 return Target(int16(x))
269 case 8:
270 return Target(int8(x))
271 default:
272 panic("unreachable")
273 }
274 }
275
276 func (l limit) unsignedFixedLeadingBits() (fixed uint64, count uint) {
277 varying := uint(bits.Len64(l.umin ^ l.umax))
278 count = uint(bits.LeadingZeros64(l.umin ^ l.umax))
279 fixed = l.umin &^ (1<<varying - 1)
280 return
281 }
282
283
284
285 func (l limit) add(l2 limit, b uint) limit {
286 var isLConst, isL2Const bool
287 var lConst, l2Const uint64
288 if l.min == l.max {
289 isLConst = true
290 lConst = convertIntWithBitsize[uint64](l.min, b)
291 } else if l.umin == l.umax {
292 isLConst = true
293 lConst = l.umin
294 }
295 if l2.min == l2.max {
296 isL2Const = true
297 l2Const = convertIntWithBitsize[uint64](l2.min, b)
298 } else if l2.umin == l2.umax {
299 isL2Const = true
300 l2Const = l2.umin
301 }
302 if isLConst && isL2Const {
303 r := lConst + l2Const
304 r &= (uint64(1) << b) - 1
305 int64r := convertIntWithBitsize[int64](r, b)
306 return limit{min: int64r, max: int64r, umin: r, umax: r}
307 }
308
309 r := noLimit()
310 min, minOk := safeAdd(l.min, l2.min, b)
311 max, maxOk := safeAdd(l.max, l2.max, b)
312 if minOk && maxOk {
313 r.min = min
314 r.max = max
315 }
316 umin, uminOk := safeAddU(l.umin, l2.umin, b)
317 umax, umaxOk := safeAddU(l.umax, l2.umax, b)
318 if uminOk && umaxOk {
319 r.umin = umin
320 r.umax = umax
321 }
322 return r
323 }
324
325
326 func (l limit) sub(l2 limit, b uint) limit {
327 r := noLimit()
328 min, minOk := safeSub(l.min, l2.max, b)
329 max, maxOk := safeSub(l.max, l2.min, b)
330 if minOk && maxOk {
331 r.min = min
332 r.max = max
333 }
334 umin, uminOk := safeSubU(l.umin, l2.umax, b)
335 umax, umaxOk := safeSubU(l.umax, l2.umin, b)
336 if uminOk && umaxOk {
337 r.umin = umin
338 r.umax = umax
339 }
340 return r
341 }
342
343
344 func (l limit) mul(l2 limit, b uint) limit {
345 r := noLimit()
346 umaxhi, umaxlo := bits.Mul64(l.umax, l2.umax)
347 if umaxhi == 0 && fitsInBitsU(umaxlo, b) {
348 r.umax = umaxlo
349 r.umin = l.umin * l2.umin
350
351
352
353
354
355
356 }
357
358
359
360
361
362 return r
363 }
364
365
366 func (l limit) exp2(b uint) limit {
367 r := noLimit()
368 if l.umax < uint64(b) {
369 r.umin = 1 << l.umin
370 r.umax = 1 << l.umax
371
372
373 }
374 return r
375 }
376
377
378 func (l limit) com(b uint) limit {
379 switch b {
380 case 64:
381 return limit{
382 min: ^l.max,
383 max: ^l.min,
384 umin: ^l.umax,
385 umax: ^l.umin,
386 }
387 case 32:
388 return limit{
389 min: int64(^int32(l.max)),
390 max: int64(^int32(l.min)),
391 umin: uint64(^uint32(l.umax)),
392 umax: uint64(^uint32(l.umin)),
393 }
394 case 16:
395 return limit{
396 min: int64(^int16(l.max)),
397 max: int64(^int16(l.min)),
398 umin: uint64(^uint16(l.umax)),
399 umax: uint64(^uint16(l.umin)),
400 }
401 case 8:
402 return limit{
403 min: int64(^int8(l.max)),
404 max: int64(^int8(l.min)),
405 umin: uint64(^uint8(l.umax)),
406 umax: uint64(^uint8(l.umin)),
407 }
408 default:
409 panic("unreachable")
410 }
411 }
412
413
414 func (l limit) neg(b uint) limit {
415 return l.com(b).add(limit{min: 1, max: 1, umin: 1, umax: 1}, b)
416 }
417
418
419 func (l limit) ctz(b uint) limit {
420 fixed, fixedCount := l.unsignedFixedLeadingBits()
421 if fixedCount == 64 {
422 constResult := min(uint(bits.TrailingZeros64(fixed)), b)
423 return limit{min: int64(constResult), max: int64(constResult), umin: uint64(constResult), umax: uint64(constResult)}
424 }
425
426 varying := 64 - fixedCount
427 if l.umin&((1<<varying)-1) != 0 {
428
429 varying--
430 return noLimit().unsignedMax(uint64(varying))
431 }
432 return noLimit().unsignedMax(uint64(min(uint(bits.TrailingZeros64(fixed)), b)))
433 }
434
435
436 func (l limit) bitlen(b uint) limit {
437 return noLimit().unsignedMinMax(
438 uint64(bits.Len64(l.umin)),
439 uint64(bits.Len64(l.umax)),
440 )
441 }
442
443
444 func (l limit) popcount(b uint) limit {
445 fixed, fixedCount := l.unsignedFixedLeadingBits()
446 varying := 64 - fixedCount
447 fixedContribution := uint64(bits.OnesCount64(fixed))
448
449 min := fixedContribution
450 max := fixedContribution + uint64(varying)
451
452 varyingMask := uint64(1)<<varying - 1
453
454 if varyingPartOfUmax := l.umax & varyingMask; uint(bits.OnesCount64(varyingPartOfUmax)) != varying {
455
456 max--
457 }
458 if varyingPartOfUmin := l.umin & varyingMask; varyingPartOfUmin != 0 {
459
460 min++
461 }
462
463 return noLimit().unsignedMinMax(min, max)
464 }
465
466
467 type limitFact struct {
468 vid ID
469 limit limit
470 }
471
472
473 type ordering struct {
474 next *ordering
475
476 w *Value
477 d domain
478 r relation
479
480 }
481
482
483
484
485
486
487
488
489 type factsTable struct {
490
491
492
493
494
495 unsat bool
496 unsatDepth int
497
498
499
500
501 orderS *poset
502 orderU *poset
503
504
505
506
507
508
509 orderings map[ID]*ordering
510
511
512 orderingsStack []ID
513 orderingCache *ordering
514
515
516 limits []limit
517 limitStack []limitFact
518 recurseCheck []bool
519
520
521
522
523 lens map[ID]*Value
524 caps map[ID]*Value
525
526
527 reusedTopoSortScoresTable []uint
528 }
529
530
531
532 var checkpointBound = limitFact{}
533
534 func newFactsTable(f *Func) *factsTable {
535 ft := &factsTable{}
536 ft.orderS = f.newPoset()
537 ft.orderU = f.newPoset()
538 ft.orderS.SetUnsigned(false)
539 ft.orderU.SetUnsigned(true)
540 ft.orderings = make(map[ID]*ordering)
541 ft.limits = f.Cache.allocLimitSlice(f.NumValues())
542 for _, b := range f.Blocks {
543 for _, v := range b.Values {
544 ft.limits[v.ID] = initLimit(v)
545 }
546 }
547 ft.limitStack = make([]limitFact, 4)
548 ft.recurseCheck = f.Cache.allocBoolSlice(f.NumValues())
549 return ft
550 }
551
552
553
554
555 func (ft *factsTable) initLimitForNewValue(v *Value) {
556 if int(v.ID) >= len(ft.limits) {
557 f := v.Block.Func
558 n := f.NumValues()
559 if cap(ft.limits) >= n {
560 ft.limits = ft.limits[:n]
561 } else {
562 old := ft.limits
563 ft.limits = f.Cache.allocLimitSlice(n)
564 copy(ft.limits, old)
565 f.Cache.freeLimitSlice(old)
566 }
567 }
568 ft.limits[v.ID] = initLimit(v)
569 }
570
571
572
573 func (ft *factsTable) signedMin(v *Value, min int64) {
574 ft.newLimit(v, limit{min: min, max: math.MaxInt64, umin: 0, umax: math.MaxUint64})
575 }
576
577
578
579 func (ft *factsTable) signedMax(v *Value, max int64) {
580 ft.newLimit(v, limit{min: math.MinInt64, max: max, umin: 0, umax: math.MaxUint64})
581 }
582 func (ft *factsTable) signedMinMax(v *Value, min, max int64) {
583 ft.newLimit(v, limit{min: min, max: max, umin: 0, umax: math.MaxUint64})
584 }
585
586
587 func (ft *factsTable) setNonNegative(v *Value) {
588 ft.signedMin(v, 0)
589 }
590
591
592
593 func (ft *factsTable) unsignedMin(v *Value, min uint64) {
594 ft.newLimit(v, limit{min: math.MinInt64, max: math.MaxInt64, umin: min, umax: math.MaxUint64})
595 }
596
597
598
599 func (ft *factsTable) unsignedMax(v *Value, max uint64) {
600 ft.newLimit(v, limit{min: math.MinInt64, max: math.MaxInt64, umin: 0, umax: max})
601 }
602 func (ft *factsTable) unsignedMinMax(v *Value, min, max uint64) {
603 ft.newLimit(v, limit{min: math.MinInt64, max: math.MaxInt64, umin: min, umax: max})
604 }
605
606 func (ft *factsTable) booleanFalse(v *Value) {
607 ft.newLimit(v, limit{min: 0, max: 0, umin: 0, umax: 0})
608 }
609 func (ft *factsTable) booleanTrue(v *Value) {
610 ft.newLimit(v, limit{min: 1, max: 1, umin: 1, umax: 1})
611 }
612 func (ft *factsTable) pointerNil(v *Value) {
613 ft.newLimit(v, limit{min: 0, max: 0, umin: 0, umax: 0})
614 }
615 func (ft *factsTable) pointerNonNil(v *Value) {
616 l := noLimit()
617 l.umin = 1
618 ft.newLimit(v, l)
619 }
620
621
622 func (ft *factsTable) newLimit(v *Value, newLim limit) {
623 oldLim := ft.limits[v.ID]
624
625
626 lim := oldLim.intersect(newLim)
627
628
629 if lim.min >= 0 {
630 lim = lim.unsignedMinMax(uint64(lim.min), uint64(lim.max))
631 }
632 if fitsInBitsU(lim.umax, uint(8*v.Type.Size()-1)) {
633 lim = lim.signedMinMax(int64(lim.umin), int64(lim.umax))
634 }
635
636 if lim == oldLim {
637 return
638 }
639
640 if lim.unsat() {
641 ft.unsat = true
642 return
643 }
644
645
646
647
648
649
650
651 if ft.recurseCheck[v.ID] {
652
653 return
654 }
655 ft.recurseCheck[v.ID] = true
656 defer func() {
657 ft.recurseCheck[v.ID] = false
658 }()
659
660
661 ft.limitStack = append(ft.limitStack, limitFact{v.ID, oldLim})
662
663 ft.limits[v.ID] = lim
664 if v.Block.Func.pass.debug > 2 {
665
666
667
668 v.Block.Func.Warnl(v.Pos, "new limit %s %s unsat=%v", v, lim.String(), ft.unsat)
669 }
670
671
672
673
674
675 for o := ft.orderings[v.ID]; o != nil; o = o.next {
676 switch o.d {
677 case signed:
678 switch o.r {
679 case eq:
680 ft.signedMinMax(o.w, lim.min, lim.max)
681 case lt | eq:
682 ft.signedMin(o.w, lim.min)
683 case lt:
684 ft.signedMin(o.w, lim.min+1)
685 case gt | eq:
686 ft.signedMax(o.w, lim.max)
687 case gt:
688 ft.signedMax(o.w, lim.max-1)
689 case lt | gt:
690 if lim.min == lim.max {
691 c := lim.min
692 if ft.limits[o.w.ID].min == c {
693 ft.signedMin(o.w, c+1)
694 }
695 if ft.limits[o.w.ID].max == c {
696 ft.signedMax(o.w, c-1)
697 }
698 }
699 }
700 case unsigned:
701 switch o.r {
702 case eq:
703 ft.unsignedMinMax(o.w, lim.umin, lim.umax)
704 case lt | eq:
705 ft.unsignedMin(o.w, lim.umin)
706 case lt:
707 ft.unsignedMin(o.w, lim.umin+1)
708 case gt | eq:
709 ft.unsignedMax(o.w, lim.umax)
710 case gt:
711 ft.unsignedMax(o.w, lim.umax-1)
712 case lt | gt:
713 if lim.umin == lim.umax {
714 c := lim.umin
715 if ft.limits[o.w.ID].umin == c {
716 ft.unsignedMin(o.w, c+1)
717 }
718 if ft.limits[o.w.ID].umax == c {
719 ft.unsignedMax(o.w, c-1)
720 }
721 }
722 }
723 case boolean:
724 switch o.r {
725 case eq:
726 if lim.min == 0 && lim.max == 0 {
727 ft.booleanFalse(o.w)
728 }
729 if lim.min == 1 && lim.max == 1 {
730 ft.booleanTrue(o.w)
731 }
732 case lt | gt:
733 if lim.min == 0 && lim.max == 0 {
734 ft.booleanTrue(o.w)
735 }
736 if lim.min == 1 && lim.max == 1 {
737 ft.booleanFalse(o.w)
738 }
739 }
740 case pointer:
741 switch o.r {
742 case eq:
743 if lim.umax == 0 {
744 ft.pointerNil(o.w)
745 }
746 if lim.umin > 0 {
747 ft.pointerNonNil(o.w)
748 }
749 case lt | gt:
750 if lim.umax == 0 {
751 ft.pointerNonNil(o.w)
752 }
753
754 }
755 }
756 }
757
758
759
760
761 if v.Type.IsBoolean() {
762
763
764
765 if lim.min != lim.max {
766 v.Block.Func.Fatalf("boolean not constant %v", v)
767 }
768 isTrue := lim.min == 1
769 if dr, ok := domainRelationTable[v.Op]; ok && v.Op != OpIsInBounds && v.Op != OpIsSliceInBounds {
770 d := dr.d
771 r := dr.r
772 if d == signed && ft.isNonNegative(v.Args[0]) && ft.isNonNegative(v.Args[1]) {
773 d |= unsigned
774 }
775 if !isTrue {
776 r ^= lt | gt | eq
777 }
778
779 addRestrictions(v.Block, ft, d, v.Args[0], v.Args[1], r)
780 }
781 switch v.Op {
782 case OpIsNonNil:
783 if isTrue {
784 ft.pointerNonNil(v.Args[0])
785 } else {
786 ft.pointerNil(v.Args[0])
787 }
788 case OpIsInBounds, OpIsSliceInBounds:
789
790 r := lt
791 if v.Op == OpIsSliceInBounds {
792 r |= eq
793 }
794 if isTrue {
795
796
797
798 ft.setNonNegative(v.Args[0])
799 ft.update(v.Block, v.Args[0], v.Args[1], signed, r)
800 ft.update(v.Block, v.Args[0], v.Args[1], unsigned, r)
801 } else {
802
803
804
805
806
807
808
809 r ^= lt | gt | eq
810 if ft.isNonNegative(v.Args[0]) {
811 ft.update(v.Block, v.Args[0], v.Args[1], signed, r)
812 }
813 ft.update(v.Block, v.Args[0], v.Args[1], unsigned, r)
814
815 }
816 }
817 }
818 }
819
820 func (ft *factsTable) addOrdering(v, w *Value, d domain, r relation) {
821 o := ft.orderingCache
822 if o == nil {
823 o = &ordering{}
824 } else {
825 ft.orderingCache = o.next
826 }
827 o.w = w
828 o.d = d
829 o.r = r
830 o.next = ft.orderings[v.ID]
831 ft.orderings[v.ID] = o
832 ft.orderingsStack = append(ft.orderingsStack, v.ID)
833 }
834
835
836
837 func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
838 if parent.Func.pass.debug > 2 {
839 parent.Func.Warnl(parent.Pos, "parent=%s, update %s %s %s", parent, v, w, r)
840 }
841
842 if ft.unsat {
843 return
844 }
845
846
847
848 if v == w {
849 if r&eq == 0 {
850 ft.unsat = true
851 }
852 return
853 }
854
855 if d == signed || d == unsigned {
856 var ok bool
857 order := ft.orderS
858 if d == unsigned {
859 order = ft.orderU
860 }
861 switch r {
862 case lt:
863 ok = order.SetOrder(v, w)
864 case gt:
865 ok = order.SetOrder(w, v)
866 case lt | eq:
867 ok = order.SetOrderOrEqual(v, w)
868 case gt | eq:
869 ok = order.SetOrderOrEqual(w, v)
870 case eq:
871 ok = order.SetEqual(v, w)
872 case lt | gt:
873 ok = order.SetNonEqual(v, w)
874 default:
875 panic("unknown relation")
876 }
877 ft.addOrdering(v, w, d, r)
878 ft.addOrdering(w, v, d, reverseBits[r])
879
880 if !ok {
881 if parent.Func.pass.debug > 2 {
882 parent.Func.Warnl(parent.Pos, "unsat %s %s %s", v, w, r)
883 }
884 ft.unsat = true
885 return
886 }
887 }
888 if d == boolean || d == pointer {
889 for o := ft.orderings[v.ID]; o != nil; o = o.next {
890 if o.d == d && o.w == w {
891
892
893
894 if o.r != r {
895 ft.unsat = true
896 }
897 return
898 }
899 }
900
901
902 ft.addOrdering(v, w, d, r)
903 ft.addOrdering(w, v, d, r)
904 }
905
906
907 vLimit := ft.limits[v.ID]
908 wLimit := ft.limits[w.ID]
909
910
911
912
913
914 switch d {
915 case signed:
916 switch r {
917 case eq:
918 ft.signedMinMax(v, wLimit.min, wLimit.max)
919 ft.signedMinMax(w, vLimit.min, vLimit.max)
920 case lt:
921 ft.signedMax(v, wLimit.max-1)
922 ft.signedMin(w, vLimit.min+1)
923 case lt | eq:
924 ft.signedMax(v, wLimit.max)
925 ft.signedMin(w, vLimit.min)
926 case gt:
927 ft.signedMin(v, wLimit.min+1)
928 ft.signedMax(w, vLimit.max-1)
929 case gt | eq:
930 ft.signedMin(v, wLimit.min)
931 ft.signedMax(w, vLimit.max)
932 case lt | gt:
933 if vLimit.min == vLimit.max {
934 c := vLimit.min
935 if wLimit.min == c {
936 ft.signedMin(w, c+1)
937 }
938 if wLimit.max == c {
939 ft.signedMax(w, c-1)
940 }
941 }
942 if wLimit.min == wLimit.max {
943 c := wLimit.min
944 if vLimit.min == c {
945 ft.signedMin(v, c+1)
946 }
947 if vLimit.max == c {
948 ft.signedMax(v, c-1)
949 }
950 }
951 }
952 case unsigned:
953 switch r {
954 case eq:
955 ft.unsignedMinMax(v, wLimit.umin, wLimit.umax)
956 ft.unsignedMinMax(w, vLimit.umin, vLimit.umax)
957 case lt:
958 ft.unsignedMax(v, wLimit.umax-1)
959 ft.unsignedMin(w, vLimit.umin+1)
960 case lt | eq:
961 ft.unsignedMax(v, wLimit.umax)
962 ft.unsignedMin(w, vLimit.umin)
963 case gt:
964 ft.unsignedMin(v, wLimit.umin+1)
965 ft.unsignedMax(w, vLimit.umax-1)
966 case gt | eq:
967 ft.unsignedMin(v, wLimit.umin)
968 ft.unsignedMax(w, vLimit.umax)
969 case lt | gt:
970 if vLimit.umin == vLimit.umax {
971 c := vLimit.umin
972 if wLimit.umin == c {
973 ft.unsignedMin(w, c+1)
974 }
975 if wLimit.umax == c {
976 ft.unsignedMax(w, c-1)
977 }
978 }
979 if wLimit.umin == wLimit.umax {
980 c := wLimit.umin
981 if vLimit.umin == c {
982 ft.unsignedMin(v, c+1)
983 }
984 if vLimit.umax == c {
985 ft.unsignedMax(v, c-1)
986 }
987 }
988 }
989 case boolean:
990 switch r {
991 case eq:
992 if vLimit.min == 1 {
993 ft.booleanTrue(w)
994 }
995 if vLimit.max == 0 {
996 ft.booleanFalse(w)
997 }
998 if wLimit.min == 1 {
999 ft.booleanTrue(v)
1000 }
1001 if wLimit.max == 0 {
1002 ft.booleanFalse(v)
1003 }
1004 case lt | gt:
1005 if vLimit.min == 1 {
1006 ft.booleanFalse(w)
1007 }
1008 if vLimit.max == 0 {
1009 ft.booleanTrue(w)
1010 }
1011 if wLimit.min == 1 {
1012 ft.booleanFalse(v)
1013 }
1014 if wLimit.max == 0 {
1015 ft.booleanTrue(v)
1016 }
1017 }
1018 case pointer:
1019 switch r {
1020 case eq:
1021 if vLimit.umax == 0 {
1022 ft.pointerNil(w)
1023 }
1024 if vLimit.umin > 0 {
1025 ft.pointerNonNil(w)
1026 }
1027 if wLimit.umax == 0 {
1028 ft.pointerNil(v)
1029 }
1030 if wLimit.umin > 0 {
1031 ft.pointerNonNil(v)
1032 }
1033 case lt | gt:
1034 if vLimit.umax == 0 {
1035 ft.pointerNonNil(w)
1036 }
1037 if wLimit.umax == 0 {
1038 ft.pointerNonNil(v)
1039 }
1040
1041
1042
1043 }
1044 }
1045
1046
1047 if d != signed && d != unsigned {
1048 return
1049 }
1050
1051
1052
1053
1054
1055
1056 if v.Op == OpSliceLen && r< == 0 && ft.caps[v.Args[0].ID] != nil {
1057
1058
1059
1060 ft.update(parent, ft.caps[v.Args[0].ID], w, d, r|gt)
1061 }
1062 if w.Op == OpSliceLen && r> == 0 && ft.caps[w.Args[0].ID] != nil {
1063
1064 ft.update(parent, v, ft.caps[w.Args[0].ID], d, r|lt)
1065 }
1066 if v.Op == OpSliceCap && r> == 0 && ft.lens[v.Args[0].ID] != nil {
1067
1068
1069
1070 ft.update(parent, ft.lens[v.Args[0].ID], w, d, r|lt)
1071 }
1072 if w.Op == OpSliceCap && r< == 0 && ft.lens[w.Args[0].ID] != nil {
1073
1074 ft.update(parent, v, ft.lens[w.Args[0].ID], d, r|gt)
1075 }
1076
1077
1078
1079
1080 if r == lt || r == lt|eq {
1081 v, w = w, v
1082 r = reverseBits[r]
1083 }
1084 switch r {
1085 case gt:
1086 if x, delta := isConstDelta(v); x != nil && delta == 1 {
1087
1088
1089
1090
1091 ft.update(parent, x, w, d, gt|eq)
1092 } else if x, delta := isConstDelta(w); x != nil && delta == -1 {
1093
1094 ft.update(parent, v, x, d, gt|eq)
1095 }
1096 case gt | eq:
1097 if x, delta := isConstDelta(v); x != nil && delta == -1 {
1098
1099
1100
1101 lim := ft.limits[x.ID]
1102 if (d == signed && lim.min > opMin[v.Op]) || (d == unsigned && lim.umin > 0) {
1103 ft.update(parent, x, w, d, gt)
1104 }
1105 } else if x, delta := isConstDelta(w); x != nil && delta == 1 {
1106
1107 lim := ft.limits[x.ID]
1108 if (d == signed && lim.max < opMax[w.Op]) || (d == unsigned && lim.umax < opUMax[w.Op]) {
1109 ft.update(parent, v, x, d, gt)
1110 }
1111 }
1112 }
1113
1114
1115
1116 if r == gt || r == gt|eq {
1117 if x, delta := isConstDelta(v); x != nil && d == signed {
1118 if parent.Func.pass.debug > 1 {
1119 parent.Func.Warnl(parent.Pos, "x+d %s w; x:%v %v delta:%v w:%v d:%v", r, x, parent.String(), delta, w.AuxInt, d)
1120 }
1121 underflow := true
1122 if delta < 0 {
1123 l := ft.limits[x.ID]
1124 if (x.Type.Size() == 8 && l.min >= math.MinInt64-delta) ||
1125 (x.Type.Size() == 4 && l.min >= math.MinInt32-delta) {
1126 underflow = false
1127 }
1128 }
1129 if delta < 0 && !underflow {
1130
1131 ft.update(parent, x, v, signed, gt)
1132 }
1133 if !w.isGenericIntConst() {
1134
1135
1136
1137 if delta < 0 && !underflow {
1138 ft.update(parent, x, w, signed, r)
1139 }
1140 } else {
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158 var min, max int64
1159 switch x.Type.Size() {
1160 case 8:
1161 min = w.AuxInt - delta
1162 max = int64(^uint64(0)>>1) - delta
1163 case 4:
1164 min = int64(int32(w.AuxInt) - int32(delta))
1165 max = int64(int32(^uint32(0)>>1) - int32(delta))
1166 case 2:
1167 min = int64(int16(w.AuxInt) - int16(delta))
1168 max = int64(int16(^uint16(0)>>1) - int16(delta))
1169 case 1:
1170 min = int64(int8(w.AuxInt) - int8(delta))
1171 max = int64(int8(^uint8(0)>>1) - int8(delta))
1172 default:
1173 panic("unimplemented")
1174 }
1175
1176 if min < max {
1177
1178 if r == gt {
1179 min++
1180 }
1181 ft.signedMinMax(x, min, max)
1182 } else {
1183
1184
1185
1186 l := ft.limits[x.ID]
1187 if l.max <= min {
1188 if r&eq == 0 || l.max < min {
1189
1190 ft.signedMax(x, max)
1191 }
1192 } else if l.min > max {
1193
1194 if r == gt {
1195 min++
1196 }
1197 ft.signedMin(x, min)
1198 }
1199 }
1200 }
1201 }
1202 }
1203
1204
1205
1206
1207 if isCleanExt(v) {
1208 switch {
1209 case d == signed && v.Args[0].Type.IsSigned():
1210 fallthrough
1211 case d == unsigned && !v.Args[0].Type.IsSigned():
1212 ft.update(parent, v.Args[0], w, d, r)
1213 }
1214 }
1215 if isCleanExt(w) {
1216 switch {
1217 case d == signed && w.Args[0].Type.IsSigned():
1218 fallthrough
1219 case d == unsigned && !w.Args[0].Type.IsSigned():
1220 ft.update(parent, v, w.Args[0], d, r)
1221 }
1222 }
1223 }
1224
1225 var opMin = map[Op]int64{
1226 OpAdd64: math.MinInt64, OpSub64: math.MinInt64,
1227 OpAdd32: math.MinInt32, OpSub32: math.MinInt32,
1228 }
1229
1230 var opMax = map[Op]int64{
1231 OpAdd64: math.MaxInt64, OpSub64: math.MaxInt64,
1232 OpAdd32: math.MaxInt32, OpSub32: math.MaxInt32,
1233 }
1234
1235 var opUMax = map[Op]uint64{
1236 OpAdd64: math.MaxUint64, OpSub64: math.MaxUint64,
1237 OpAdd32: math.MaxUint32, OpSub32: math.MaxUint32,
1238 }
1239
1240
1241 func (ft *factsTable) isNonNegative(v *Value) bool {
1242 return ft.limits[v.ID].min >= 0
1243 }
1244
1245
1246
1247 func (ft *factsTable) checkpoint() {
1248 if ft.unsat {
1249 ft.unsatDepth++
1250 }
1251 ft.limitStack = append(ft.limitStack, checkpointBound)
1252 ft.orderS.Checkpoint()
1253 ft.orderU.Checkpoint()
1254 ft.orderingsStack = append(ft.orderingsStack, 0)
1255 }
1256
1257
1258
1259
1260 func (ft *factsTable) restore() {
1261 if ft.unsatDepth > 0 {
1262 ft.unsatDepth--
1263 } else {
1264 ft.unsat = false
1265 }
1266 for {
1267 old := ft.limitStack[len(ft.limitStack)-1]
1268 ft.limitStack = ft.limitStack[:len(ft.limitStack)-1]
1269 if old.vid == 0 {
1270 break
1271 }
1272 ft.limits[old.vid] = old.limit
1273 }
1274 ft.orderS.Undo()
1275 ft.orderU.Undo()
1276 for {
1277 id := ft.orderingsStack[len(ft.orderingsStack)-1]
1278 ft.orderingsStack = ft.orderingsStack[:len(ft.orderingsStack)-1]
1279 if id == 0 {
1280 break
1281 }
1282 o := ft.orderings[id]
1283 ft.orderings[id] = o.next
1284 o.next = ft.orderingCache
1285 ft.orderingCache = o
1286 }
1287 }
1288
1289 var (
1290 reverseBits = [...]relation{0, 4, 2, 6, 1, 5, 3, 7}
1291
1292
1293
1294
1295
1296
1297
1298 domainRelationTable = map[Op]struct {
1299 d domain
1300 r relation
1301 }{
1302 OpEq8: {signed | unsigned, eq},
1303 OpEq16: {signed | unsigned, eq},
1304 OpEq32: {signed | unsigned, eq},
1305 OpEq64: {signed | unsigned, eq},
1306 OpEqPtr: {pointer, eq},
1307 OpEqB: {boolean, eq},
1308
1309 OpNeq8: {signed | unsigned, lt | gt},
1310 OpNeq16: {signed | unsigned, lt | gt},
1311 OpNeq32: {signed | unsigned, lt | gt},
1312 OpNeq64: {signed | unsigned, lt | gt},
1313 OpNeqPtr: {pointer, lt | gt},
1314 OpNeqB: {boolean, lt | gt},
1315
1316 OpLess8: {signed, lt},
1317 OpLess8U: {unsigned, lt},
1318 OpLess16: {signed, lt},
1319 OpLess16U: {unsigned, lt},
1320 OpLess32: {signed, lt},
1321 OpLess32U: {unsigned, lt},
1322 OpLess64: {signed, lt},
1323 OpLess64U: {unsigned, lt},
1324
1325 OpLeq8: {signed, lt | eq},
1326 OpLeq8U: {unsigned, lt | eq},
1327 OpLeq16: {signed, lt | eq},
1328 OpLeq16U: {unsigned, lt | eq},
1329 OpLeq32: {signed, lt | eq},
1330 OpLeq32U: {unsigned, lt | eq},
1331 OpLeq64: {signed, lt | eq},
1332 OpLeq64U: {unsigned, lt | eq},
1333 }
1334 )
1335
1336
1337 func (ft *factsTable) cleanup(f *Func) {
1338 for _, po := range []*poset{ft.orderS, ft.orderU} {
1339
1340
1341 if checkEnabled {
1342 if err := po.CheckEmpty(); err != nil {
1343 f.Fatalf("poset not empty after function %s: %v", f.Name, err)
1344 }
1345 }
1346 f.retPoset(po)
1347 }
1348 f.Cache.freeLimitSlice(ft.limits)
1349 f.Cache.freeBoolSlice(ft.recurseCheck)
1350 if cap(ft.reusedTopoSortScoresTable) > 0 {
1351 f.Cache.freeUintSlice(ft.reusedTopoSortScoresTable)
1352 }
1353 }
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386 func addSlicesOfSameLen(ft *factsTable, b *Block) {
1387
1388
1389
1390
1391 var u, w *Value
1392 var i, j, k sliceInfo
1393 isInterested := func(v *Value) bool {
1394 j = getSliceInfo(v)
1395 return j.sliceWhere != sliceUnknown
1396 }
1397 for _, v := range b.Values {
1398 if v.Uses == 0 {
1399 continue
1400 }
1401 if v.Op == OpPhi && len(v.Args) == 2 && ft.lens[v.ID] != nil && isInterested(v) {
1402 if j.predIndex == 1 && ft.lens[v.Args[0].ID] != nil {
1403
1404
1405 if w == nil {
1406 k = j
1407 w = v
1408 continue
1409 }
1410
1411 if j == k && ft.orderS.Equal(ft.lens[v.Args[0].ID], ft.lens[w.Args[0].ID]) {
1412 ft.update(b, ft.lens[v.ID], ft.lens[w.ID], signed, eq)
1413 }
1414 } else if j.predIndex == 0 && ft.lens[v.Args[1].ID] != nil {
1415
1416
1417 if u == nil {
1418 i = j
1419 u = v
1420 continue
1421 }
1422
1423 if j == i && ft.orderS.Equal(ft.lens[v.Args[1].ID], ft.lens[u.Args[1].ID]) {
1424 ft.update(b, ft.lens[v.ID], ft.lens[u.ID], signed, eq)
1425 }
1426 }
1427 }
1428 }
1429 }
1430
1431 type sliceWhere int
1432
1433 const (
1434 sliceUnknown sliceWhere = iota
1435 sliceInFor
1436 sliceInIf
1437 )
1438
1439
1440
1441 type predIndex int
1442
1443 type sliceInfo struct {
1444 lengthDiff int64
1445 sliceWhere
1446 predIndex
1447 }
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483 func getSliceInfo(vp *Value) (inf sliceInfo) {
1484 if vp.Op != OpPhi || len(vp.Args) != 2 {
1485 return
1486 }
1487 var i predIndex
1488 var l *Value
1489 if vp.Args[0].Op != OpSliceMake && vp.Args[1].Op == OpSliceMake {
1490 l = vp.Args[1].Args[1]
1491 i = 1
1492 } else if vp.Args[0].Op == OpSliceMake && vp.Args[1].Op != OpSliceMake {
1493 l = vp.Args[0].Args[1]
1494 i = 0
1495 } else {
1496 return
1497 }
1498 var op Op
1499 switch l.Op {
1500 case OpAdd64:
1501 op = OpConst64
1502 case OpAdd32:
1503 op = OpConst32
1504 default:
1505 return
1506 }
1507 if l.Args[0].Op == op && l.Args[1].Op == OpSliceLen && l.Args[1].Args[0] == vp {
1508 return sliceInfo{l.Args[0].AuxInt, sliceInFor, i}
1509 }
1510 if l.Args[1].Op == op && l.Args[0].Op == OpSliceLen && l.Args[0].Args[0] == vp {
1511 return sliceInfo{l.Args[1].AuxInt, sliceInFor, i}
1512 }
1513 if l.Args[0].Op == op && l.Args[1].Op == OpSliceLen && l.Args[1].Args[0] == vp.Args[1-i] {
1514 return sliceInfo{l.Args[0].AuxInt, sliceInIf, i}
1515 }
1516 if l.Args[1].Op == op && l.Args[0].Op == OpSliceLen && l.Args[0].Args[0] == vp.Args[1-i] {
1517 return sliceInfo{l.Args[1].AuxInt, sliceInIf, i}
1518 }
1519 return
1520 }
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553 func prove(f *Func) {
1554
1555
1556 var indVars map[*Block]indVar
1557 for _, v := range findIndVar(f) {
1558 ind := v.ind
1559 if len(ind.Args) != 2 {
1560
1561 panic("unexpected induction with too many parents")
1562 }
1563
1564 nxt := v.nxt
1565 if !(ind.Uses == 2 &&
1566 nxt.Uses == 1) {
1567
1568 if indVars == nil {
1569 indVars = make(map[*Block]indVar)
1570 }
1571 indVars[v.entry] = v
1572 continue
1573 } else {
1574
1575
1576 }
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614 start, end := v.min, v.max
1615 if v.flags&indVarCountDown != 0 {
1616 start, end = end, start
1617 }
1618
1619 if !start.isGenericIntConst() {
1620
1621 continue
1622 }
1623 if end.isGenericIntConst() {
1624
1625
1626
1627
1628
1629
1630 continue
1631 }
1632
1633 if end.Block == ind.Block {
1634
1635
1636 continue
1637 }
1638
1639 check := ind.Block.Controls[0]
1640
1641 check.Args[0], check.Args[1] = check.Args[1], check.Args[0]
1642
1643
1644 for i, v := range check.Args {
1645 if v != end {
1646 continue
1647 }
1648
1649 check.SetArg(i, start)
1650 goto replacedEnd
1651 }
1652 panic(fmt.Sprintf("unreachable, ind: %v, start: %v, end: %v", ind, start, end))
1653 replacedEnd:
1654
1655 for i, v := range ind.Args {
1656 if v != start {
1657 continue
1658 }
1659
1660 ind.SetArg(i, end)
1661 goto replacedStart
1662 }
1663 panic(fmt.Sprintf("unreachable, ind: %v, start: %v, end: %v", ind, start, end))
1664 replacedStart:
1665
1666 if nxt.Args[0] != ind {
1667
1668 nxt.Args[0], nxt.Args[1] = nxt.Args[1], nxt.Args[0]
1669 }
1670
1671 switch nxt.Op {
1672 case OpAdd8:
1673 nxt.Op = OpSub8
1674 case OpAdd16:
1675 nxt.Op = OpSub16
1676 case OpAdd32:
1677 nxt.Op = OpSub32
1678 case OpAdd64:
1679 nxt.Op = OpSub64
1680 case OpSub8:
1681 nxt.Op = OpAdd8
1682 case OpSub16:
1683 nxt.Op = OpAdd16
1684 case OpSub32:
1685 nxt.Op = OpAdd32
1686 case OpSub64:
1687 nxt.Op = OpAdd64
1688 default:
1689 panic("unreachable")
1690 }
1691
1692 if f.pass.debug > 0 {
1693 f.Warnl(ind.Pos, "Inverted loop iteration")
1694 }
1695 }
1696
1697 ft := newFactsTable(f)
1698 ft.checkpoint()
1699
1700
1701 for _, b := range f.Blocks {
1702 for _, v := range b.Values {
1703 if v.Uses == 0 {
1704
1705
1706 continue
1707 }
1708 switch v.Op {
1709 case OpSliceLen:
1710 if ft.lens == nil {
1711 ft.lens = map[ID]*Value{}
1712 }
1713
1714
1715
1716 if l, ok := ft.lens[v.Args[0].ID]; ok {
1717 ft.update(b, v, l, signed, eq)
1718 } else {
1719 ft.lens[v.Args[0].ID] = v
1720 }
1721 case OpSliceCap:
1722 if ft.caps == nil {
1723 ft.caps = map[ID]*Value{}
1724 }
1725
1726 if c, ok := ft.caps[v.Args[0].ID]; ok {
1727 ft.update(b, v, c, signed, eq)
1728 } else {
1729 ft.caps[v.Args[0].ID] = v
1730 }
1731 }
1732 }
1733 }
1734
1735
1736 type walkState int
1737 const (
1738 descend walkState = iota
1739 simplify
1740 )
1741
1742 type bp struct {
1743 block *Block
1744 state walkState
1745 }
1746 work := make([]bp, 0, 256)
1747 work = append(work, bp{
1748 block: f.Entry,
1749 state: descend,
1750 })
1751
1752 idom := f.Idom()
1753 sdom := f.Sdom()
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765 for len(work) > 0 {
1766 node := work[len(work)-1]
1767 work = work[:len(work)-1]
1768 parent := idom[node.block.ID]
1769 branch := getBranch(sdom, parent, node.block)
1770
1771 switch node.state {
1772 case descend:
1773 ft.checkpoint()
1774
1775
1776
1777 if iv, ok := indVars[node.block]; ok {
1778 addIndVarRestrictions(ft, parent, iv)
1779 }
1780
1781
1782
1783 if branch != unknown {
1784 addBranchRestrictions(ft, parent, branch)
1785 }
1786
1787
1788 addSlicesOfSameLen(ft, node.block)
1789
1790 if ft.unsat {
1791
1792
1793
1794 removeBranch(parent, branch)
1795 ft.restore()
1796 break
1797 }
1798
1799
1800
1801
1802
1803 addLocalFacts(ft, node.block)
1804
1805 work = append(work, bp{
1806 block: node.block,
1807 state: simplify,
1808 })
1809 for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) {
1810 work = append(work, bp{
1811 block: s,
1812 state: descend,
1813 })
1814 }
1815
1816 case simplify:
1817 simplifyBlock(sdom, ft, node.block)
1818 ft.restore()
1819 }
1820 }
1821
1822 ft.restore()
1823
1824 ft.cleanup(f)
1825 }
1826
1827
1828
1829
1830
1831
1832
1833 func initLimit(v *Value) limit {
1834 if v.Type.IsBoolean() {
1835 switch v.Op {
1836 case OpConstBool:
1837 b := v.AuxInt
1838 return limit{min: b, max: b, umin: uint64(b), umax: uint64(b)}
1839 default:
1840 return limit{min: 0, max: 1, umin: 0, umax: 1}
1841 }
1842 }
1843 if v.Type.IsPtrShaped() {
1844 switch v.Op {
1845 case OpConstNil:
1846 return limit{min: 0, max: 0, umin: 0, umax: 0}
1847 case OpAddr, OpLocalAddr:
1848 l := noLimit()
1849 l.umin = 1
1850 return l
1851 default:
1852 return noLimit()
1853 }
1854 }
1855 if !v.Type.IsInteger() {
1856 return noLimit()
1857 }
1858
1859
1860 lim := noLimitForBitsize(uint(v.Type.Size()) * 8)
1861
1862
1863 switch v.Op {
1864
1865 case OpConst64:
1866 lim = limit{min: v.AuxInt, max: v.AuxInt, umin: uint64(v.AuxInt), umax: uint64(v.AuxInt)}
1867 case OpConst32:
1868 lim = limit{min: v.AuxInt, max: v.AuxInt, umin: uint64(uint32(v.AuxInt)), umax: uint64(uint32(v.AuxInt))}
1869 case OpConst16:
1870 lim = limit{min: v.AuxInt, max: v.AuxInt, umin: uint64(uint16(v.AuxInt)), umax: uint64(uint16(v.AuxInt))}
1871 case OpConst8:
1872 lim = limit{min: v.AuxInt, max: v.AuxInt, umin: uint64(uint8(v.AuxInt)), umax: uint64(uint8(v.AuxInt))}
1873
1874
1875 case OpZeroExt8to64, OpZeroExt8to32, OpZeroExt8to16:
1876 lim = lim.signedMinMax(0, 1<<8-1)
1877 lim = lim.unsignedMax(1<<8 - 1)
1878 case OpZeroExt16to64, OpZeroExt16to32:
1879 lim = lim.signedMinMax(0, 1<<16-1)
1880 lim = lim.unsignedMax(1<<16 - 1)
1881 case OpZeroExt32to64:
1882 lim = lim.signedMinMax(0, 1<<32-1)
1883 lim = lim.unsignedMax(1<<32 - 1)
1884 case OpSignExt8to64, OpSignExt8to32, OpSignExt8to16:
1885 lim = lim.signedMinMax(math.MinInt8, math.MaxInt8)
1886 case OpSignExt16to64, OpSignExt16to32:
1887 lim = lim.signedMinMax(math.MinInt16, math.MaxInt16)
1888 case OpSignExt32to64:
1889 lim = lim.signedMinMax(math.MinInt32, math.MaxInt32)
1890
1891
1892 case OpCtz64, OpBitLen64, OpPopCount64,
1893 OpCtz32, OpBitLen32, OpPopCount32,
1894 OpCtz16, OpBitLen16, OpPopCount16,
1895 OpCtz8, OpBitLen8, OpPopCount8:
1896 lim = lim.unsignedMax(uint64(v.Args[0].Type.Size() * 8))
1897
1898
1899 case OpCvtBoolToUint8:
1900 lim = lim.unsignedMax(1)
1901
1902
1903 case OpSliceLen, OpSliceCap:
1904 f := v.Block.Func
1905 elemSize := uint64(v.Args[0].Type.Elem().Size())
1906 if elemSize > 0 {
1907 heapSize := uint64(1)<<(uint64(f.Config.PtrSize)*8) - 1
1908 maximumElementsFittingInHeap := heapSize / elemSize
1909 lim = lim.unsignedMax(maximumElementsFittingInHeap)
1910 }
1911 fallthrough
1912 case OpStringLen:
1913 lim = lim.signedMin(0)
1914 }
1915
1916
1917 if lim.min >= 0 {
1918 lim = lim.unsignedMinMax(uint64(lim.min), uint64(lim.max))
1919 }
1920 if fitsInBitsU(lim.umax, uint(8*v.Type.Size()-1)) {
1921 lim = lim.signedMinMax(int64(lim.umin), int64(lim.umax))
1922 }
1923
1924 return lim
1925 }
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940 func (ft *factsTable) flowLimit(v *Value) {
1941 if !v.Type.IsInteger() {
1942
1943 return
1944 }
1945
1946
1947
1948 switch v.Op {
1949
1950
1951 case OpZeroExt8to64, OpZeroExt8to32, OpZeroExt8to16, OpZeroExt16to64, OpZeroExt16to32, OpZeroExt32to64:
1952 a := ft.limits[v.Args[0].ID]
1953 ft.unsignedMinMax(v, a.umin, a.umax)
1954 case OpSignExt8to64, OpSignExt8to32, OpSignExt8to16, OpSignExt16to64, OpSignExt16to32, OpSignExt32to64:
1955 a := ft.limits[v.Args[0].ID]
1956 ft.signedMinMax(v, a.min, a.max)
1957 case OpTrunc64to8, OpTrunc64to16, OpTrunc64to32, OpTrunc32to8, OpTrunc32to16, OpTrunc16to8:
1958 a := ft.limits[v.Args[0].ID]
1959 if a.umax <= 1<<(uint64(v.Type.Size())*8)-1 {
1960 ft.unsignedMinMax(v, a.umin, a.umax)
1961 }
1962
1963
1964 case OpCtz64, OpCtz32, OpCtz16, OpCtz8:
1965 a := v.Args[0]
1966 al := ft.limits[a.ID]
1967 ft.newLimit(v, al.ctz(uint(a.Type.Size())*8))
1968
1969 case OpPopCount64, OpPopCount32, OpPopCount16, OpPopCount8:
1970 a := v.Args[0]
1971 al := ft.limits[a.ID]
1972 ft.newLimit(v, al.popcount(uint(a.Type.Size())*8))
1973
1974 case OpBitLen64, OpBitLen32, OpBitLen16, OpBitLen8:
1975 a := v.Args[0]
1976 al := ft.limits[a.ID]
1977 ft.newLimit(v, al.bitlen(uint(a.Type.Size())*8))
1978
1979
1980
1981
1982
1983
1984 case OpAnd64, OpAnd32, OpAnd16, OpAnd8:
1985
1986 a := ft.limits[v.Args[0].ID]
1987 b := ft.limits[v.Args[1].ID]
1988 ft.unsignedMax(v, min(a.umax, b.umax))
1989 case OpOr64, OpOr32, OpOr16, OpOr8:
1990
1991 a := ft.limits[v.Args[0].ID]
1992 b := ft.limits[v.Args[1].ID]
1993 ft.unsignedMinMax(v,
1994 max(a.umin, b.umin),
1995 1<<bits.Len64(a.umax|b.umax)-1)
1996 case OpXor64, OpXor32, OpXor16, OpXor8:
1997
1998 a := ft.limits[v.Args[0].ID]
1999 b := ft.limits[v.Args[1].ID]
2000 ft.unsignedMax(v, 1<<bits.Len64(a.umax|b.umax)-1)
2001 case OpCom64, OpCom32, OpCom16, OpCom8:
2002 a := ft.limits[v.Args[0].ID]
2003 ft.newLimit(v, a.com(uint(v.Type.Size())*8))
2004
2005
2006 case OpAdd64, OpAdd32, OpAdd16, OpAdd8:
2007 a := ft.limits[v.Args[0].ID]
2008 b := ft.limits[v.Args[1].ID]
2009 ft.newLimit(v, a.add(b, uint(v.Type.Size())*8))
2010 case OpSub64, OpSub32, OpSub16, OpSub8:
2011 a := ft.limits[v.Args[0].ID]
2012 b := ft.limits[v.Args[1].ID]
2013 ft.newLimit(v, a.sub(b, uint(v.Type.Size())*8))
2014 ft.detectMod(v)
2015 ft.detectSliceLenRelation(v)
2016 ft.detectSubRelations(v)
2017 case OpNeg64, OpNeg32, OpNeg16, OpNeg8:
2018 a := ft.limits[v.Args[0].ID]
2019 bitsize := uint(v.Type.Size()) * 8
2020 ft.newLimit(v, a.neg(bitsize))
2021 case OpMul64, OpMul32, OpMul16, OpMul8:
2022 a := ft.limits[v.Args[0].ID]
2023 b := ft.limits[v.Args[1].ID]
2024 ft.newLimit(v, a.mul(b, uint(v.Type.Size())*8))
2025 case OpLsh64x64, OpLsh64x32, OpLsh64x16, OpLsh64x8,
2026 OpLsh32x64, OpLsh32x32, OpLsh32x16, OpLsh32x8,
2027 OpLsh16x64, OpLsh16x32, OpLsh16x16, OpLsh16x8,
2028 OpLsh8x64, OpLsh8x32, OpLsh8x16, OpLsh8x8:
2029 a := ft.limits[v.Args[0].ID]
2030 b := ft.limits[v.Args[1].ID]
2031 bitsize := uint(v.Type.Size()) * 8
2032 ft.newLimit(v, a.mul(b.exp2(bitsize), bitsize))
2033 case OpRsh64x64, OpRsh64x32, OpRsh64x16, OpRsh64x8,
2034 OpRsh32x64, OpRsh32x32, OpRsh32x16, OpRsh32x8,
2035 OpRsh16x64, OpRsh16x32, OpRsh16x16, OpRsh16x8,
2036 OpRsh8x64, OpRsh8x32, OpRsh8x16, OpRsh8x8:
2037 a := ft.limits[v.Args[0].ID]
2038 b := ft.limits[v.Args[1].ID]
2039 if b.min >= 0 {
2040
2041
2042
2043
2044 vmin := min(a.min>>b.min, a.min>>b.max)
2045 vmax := max(a.max>>b.min, a.max>>b.max)
2046 ft.signedMinMax(v, vmin, vmax)
2047 }
2048 case OpRsh64Ux64, OpRsh64Ux32, OpRsh64Ux16, OpRsh64Ux8,
2049 OpRsh32Ux64, OpRsh32Ux32, OpRsh32Ux16, OpRsh32Ux8,
2050 OpRsh16Ux64, OpRsh16Ux32, OpRsh16Ux16, OpRsh16Ux8,
2051 OpRsh8Ux64, OpRsh8Ux32, OpRsh8Ux16, OpRsh8Ux8:
2052 a := ft.limits[v.Args[0].ID]
2053 b := ft.limits[v.Args[1].ID]
2054 if b.min >= 0 {
2055 ft.unsignedMinMax(v, a.umin>>b.max, a.umax>>b.min)
2056 }
2057 case OpDiv64, OpDiv32, OpDiv16, OpDiv8:
2058 a := ft.limits[v.Args[0].ID]
2059 b := ft.limits[v.Args[1].ID]
2060 if !(a.nonnegative() && b.nonnegative()) {
2061
2062 break
2063 }
2064 fallthrough
2065 case OpDiv64u, OpDiv32u, OpDiv16u, OpDiv8u:
2066 a := ft.limits[v.Args[0].ID]
2067 b := ft.limits[v.Args[1].ID]
2068 lim := noLimit()
2069 if b.umax > 0 {
2070 lim = lim.unsignedMin(a.umin / b.umax)
2071 }
2072 if b.umin > 0 {
2073 lim = lim.unsignedMax(a.umax / b.umin)
2074 }
2075 ft.newLimit(v, lim)
2076 case OpMod64, OpMod32, OpMod16, OpMod8:
2077 ft.modLimit(true, v, v.Args[0], v.Args[1])
2078 case OpMod64u, OpMod32u, OpMod16u, OpMod8u:
2079 ft.modLimit(false, v, v.Args[0], v.Args[1])
2080
2081 case OpPhi:
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091 l := ft.limits[v.Args[0].ID]
2092 for _, a := range v.Args[1:] {
2093 l2 := ft.limits[a.ID]
2094 l.min = min(l.min, l2.min)
2095 l.max = max(l.max, l2.max)
2096 l.umin = min(l.umin, l2.umin)
2097 l.umax = max(l.umax, l2.umax)
2098 }
2099 ft.newLimit(v, l)
2100 }
2101 }
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113 func (ft *factsTable) detectSliceLenRelation(v *Value) {
2114 if v.Op != OpSub64 {
2115 return
2116 }
2117
2118 if !(v.Args[0].Op == OpSliceLen || v.Args[0].Op == OpStringLen || v.Args[0].Op == OpSliceCap) {
2119 return
2120 }
2121
2122 index := v.Args[1]
2123 if !ft.isNonNegative(index) {
2124 return
2125 }
2126 slice := v.Args[0].Args[0]
2127
2128 for o := ft.orderings[index.ID]; o != nil; o = o.next {
2129 if o.d != signed {
2130 continue
2131 }
2132 or := o.r
2133 if or != lt && or != lt|eq {
2134 continue
2135 }
2136 ow := o.w
2137 if ow.Op != OpAdd64 && ow.Op != OpSub64 {
2138 continue
2139 }
2140 var lenOffset *Value
2141 if bound := ow.Args[0]; (bound.Op == OpSliceLen || bound.Op == OpStringLen) && bound.Args[0] == slice {
2142 lenOffset = ow.Args[1]
2143 } else if bound := ow.Args[1]; (bound.Op == OpSliceLen || bound.Op == OpStringLen) && bound.Args[0] == slice {
2144
2145 if ow.Op == OpAdd64 {
2146 lenOffset = ow.Args[0]
2147 }
2148 }
2149 if lenOffset == nil || lenOffset.Op != OpConst64 {
2150 continue
2151 }
2152 K := lenOffset.AuxInt
2153 if ow.Op == OpAdd64 {
2154 K = -K
2155 }
2156 if K < 0 {
2157 continue
2158 }
2159 if or == lt {
2160 K++
2161 }
2162 if K < 0 {
2163 continue
2164 }
2165 ft.signedMin(v, K)
2166 }
2167 }
2168
2169
2170 func (ft *factsTable) detectSubRelations(v *Value) {
2171
2172 x := v.Args[0]
2173 y := v.Args[1]
2174 if x == y {
2175 ft.signedMinMax(v, 0, 0)
2176 return
2177 }
2178 xLim := ft.limits[x.ID]
2179 yLim := ft.limits[y.ID]
2180
2181
2182 width := uint(v.Type.Size()) * 8
2183 if _, ok := safeSub(xLim.min, yLim.max, width); !ok {
2184 return
2185 }
2186 if _, ok := safeSub(xLim.max, yLim.min, width); !ok {
2187 return
2188 }
2189
2190
2191
2192
2193 if yLim.min > 0 {
2194 ft.update(v.Block, v, x, signed, lt)
2195 } else if yLim.min == 0 {
2196 ft.update(v.Block, v, x, signed, lt|eq)
2197 }
2198
2199
2200
2201
2202 if ft.orderS.Ordered(y, x) {
2203 ft.signedMin(v, 1)
2204 } else if ft.orderS.OrderedOrEqual(y, x) {
2205 ft.setNonNegative(v)
2206 }
2207 }
2208
2209
2210 func (ft *factsTable) detectMod(v *Value) {
2211 var opDiv, opDivU, opMul, opConst Op
2212 switch v.Op {
2213 case OpSub64:
2214 opDiv = OpDiv64
2215 opDivU = OpDiv64u
2216 opMul = OpMul64
2217 opConst = OpConst64
2218 case OpSub32:
2219 opDiv = OpDiv32
2220 opDivU = OpDiv32u
2221 opMul = OpMul32
2222 opConst = OpConst32
2223 case OpSub16:
2224 opDiv = OpDiv16
2225 opDivU = OpDiv16u
2226 opMul = OpMul16
2227 opConst = OpConst16
2228 case OpSub8:
2229 opDiv = OpDiv8
2230 opDivU = OpDiv8u
2231 opMul = OpMul8
2232 opConst = OpConst8
2233 }
2234
2235 mul := v.Args[1]
2236 if mul.Op != opMul {
2237 return
2238 }
2239 div, con := mul.Args[0], mul.Args[1]
2240 if div.Op == opConst {
2241 div, con = con, div
2242 }
2243 if con.Op != opConst || (div.Op != opDiv && div.Op != opDivU) || div.Args[0] != v.Args[0] || div.Args[1].Op != opConst || div.Args[1].AuxInt != con.AuxInt {
2244 return
2245 }
2246 ft.modLimit(div.Op == opDiv, v, v.Args[0], con)
2247 }
2248
2249
2250 func (ft *factsTable) modLimit(signed bool, v, p, q *Value) {
2251 a := ft.limits[p.ID]
2252 b := ft.limits[q.ID]
2253 if signed {
2254 if a.min < 0 && b.min > 0 {
2255 ft.signedMinMax(v, -(b.max - 1), b.max-1)
2256 return
2257 }
2258 if !(a.nonnegative() && b.nonnegative()) {
2259
2260 return
2261 }
2262 if a.min >= 0 && b.min > 0 {
2263 ft.setNonNegative(v)
2264 }
2265 }
2266
2267 ft.unsignedMax(v, min(a.umax, b.umax-1))
2268 }
2269
2270
2271
2272 func getBranch(sdom SparseTree, p *Block, b *Block) branch {
2273 if p == nil {
2274 return unknown
2275 }
2276 switch p.Kind {
2277 case BlockIf:
2278
2279
2280
2281
2282
2283
2284 if sdom.IsAncestorEq(p.Succs[0].b, b) && len(p.Succs[0].b.Preds) == 1 {
2285 return positive
2286 }
2287 if sdom.IsAncestorEq(p.Succs[1].b, b) && len(p.Succs[1].b.Preds) == 1 {
2288 return negative
2289 }
2290 case BlockJumpTable:
2291
2292
2293 for i, e := range p.Succs {
2294 if sdom.IsAncestorEq(e.b, b) && len(e.b.Preds) == 1 {
2295 return jumpTable0 + branch(i)
2296 }
2297 }
2298 }
2299 return unknown
2300 }
2301
2302
2303
2304
2305 func addIndVarRestrictions(ft *factsTable, b *Block, iv indVar) {
2306 d := signed
2307 if ft.isNonNegative(iv.min) && ft.isNonNegative(iv.max) {
2308 d |= unsigned
2309 }
2310
2311 if iv.flags&indVarMinExc == 0 {
2312 addRestrictions(b, ft, d, iv.min, iv.ind, lt|eq)
2313 } else {
2314 addRestrictions(b, ft, d, iv.min, iv.ind, lt)
2315 }
2316
2317 if iv.flags&indVarMaxInc == 0 {
2318 addRestrictions(b, ft, d, iv.ind, iv.max, lt)
2319 } else {
2320 addRestrictions(b, ft, d, iv.ind, iv.max, lt|eq)
2321 }
2322 }
2323
2324
2325
2326 func addBranchRestrictions(ft *factsTable, b *Block, br branch) {
2327 c := b.Controls[0]
2328 switch {
2329 case br == negative:
2330 ft.booleanFalse(c)
2331 case br == positive:
2332 ft.booleanTrue(c)
2333 case br >= jumpTable0:
2334 idx := br - jumpTable0
2335 val := int64(idx)
2336 if v, off := isConstDelta(c); v != nil {
2337
2338
2339 c = v
2340 val -= off
2341 }
2342 ft.newLimit(c, limit{min: val, max: val, umin: uint64(val), umax: uint64(val)})
2343 default:
2344 panic("unknown branch")
2345 }
2346 }
2347
2348
2349
2350 func addRestrictions(parent *Block, ft *factsTable, t domain, v, w *Value, r relation) {
2351 if t == 0 {
2352
2353
2354 return
2355 }
2356 for i := domain(1); i <= t; i <<= 1 {
2357 if t&i == 0 {
2358 continue
2359 }
2360 ft.update(parent, v, w, i, r)
2361 }
2362 }
2363
2364 func unsignedAddOverflows(a, b uint64, t *types.Type) bool {
2365 switch t.Size() {
2366 case 8:
2367 return a+b < a
2368 case 4:
2369 return a+b > math.MaxUint32
2370 case 2:
2371 return a+b > math.MaxUint16
2372 case 1:
2373 return a+b > math.MaxUint8
2374 default:
2375 panic("unreachable")
2376 }
2377 }
2378
2379 func signedAddOverflowsOrUnderflows(a, b int64, t *types.Type) bool {
2380 r := a + b
2381 switch t.Size() {
2382 case 8:
2383 return (a >= 0 && b >= 0 && r < 0) || (a < 0 && b < 0 && r >= 0)
2384 case 4:
2385 return r < math.MinInt32 || math.MaxInt32 < r
2386 case 2:
2387 return r < math.MinInt16 || math.MaxInt16 < r
2388 case 1:
2389 return r < math.MinInt8 || math.MaxInt8 < r
2390 default:
2391 panic("unreachable")
2392 }
2393 }
2394
2395 func unsignedSubUnderflows(a, b uint64) bool {
2396 return a < b
2397 }
2398
2399
2400
2401
2402
2403 func checkForChunkedIndexBounds(ft *factsTable, b *Block, index, bound *Value, isReslice bool) bool {
2404 if bound.Op != OpSliceLen && bound.Op != OpStringLen && bound.Op != OpSliceCap {
2405 return false
2406 }
2407
2408
2409
2410
2411
2412
2413 slice := bound.Args[0]
2414 lim := ft.limits[index.ID]
2415 if lim.min < 0 {
2416 return false
2417 }
2418 i, delta := isConstDelta(index)
2419 if i == nil {
2420 return false
2421 }
2422 if delta < 0 {
2423 return false
2424 }
2425
2426
2427
2428
2429
2430
2431
2432
2433 for o := ft.orderings[i.ID]; o != nil; o = o.next {
2434 if o.d != signed {
2435 continue
2436 }
2437 if ow := o.w; ow.Op == OpAdd64 {
2438 var lenOffset *Value
2439 if bound := ow.Args[0]; (bound.Op == OpSliceLen || bound.Op == OpStringLen) && bound.Args[0] == slice {
2440 lenOffset = ow.Args[1]
2441 } else if bound := ow.Args[1]; (bound.Op == OpSliceLen || bound.Op == OpStringLen) && bound.Args[0] == slice {
2442 lenOffset = ow.Args[0]
2443 }
2444 if lenOffset == nil || lenOffset.Op != OpConst64 {
2445 continue
2446 }
2447 if K := -lenOffset.AuxInt; K >= 0 {
2448 or := o.r
2449 if isReslice {
2450 K++
2451 }
2452 if or == lt {
2453 or = lt | eq
2454 K++
2455 }
2456 if K < 0 {
2457 continue
2458 }
2459
2460 if delta < K && or == lt|eq {
2461 return true
2462 }
2463 }
2464 }
2465 }
2466 return false
2467 }
2468
2469 func addLocalFacts(ft *factsTable, b *Block) {
2470 ft.topoSortValuesInBlock(b)
2471
2472 for _, v := range b.Values {
2473
2474
2475 ft.flowLimit(v)
2476
2477 switch v.Op {
2478 case OpAdd64, OpAdd32, OpAdd16, OpAdd8:
2479 x := ft.limits[v.Args[0].ID]
2480 y := ft.limits[v.Args[1].ID]
2481 if !unsignedAddOverflows(x.umax, y.umax, v.Type) {
2482 r := gt
2483 if x.maybeZero() {
2484 r |= eq
2485 }
2486 ft.update(b, v, v.Args[1], unsigned, r)
2487 r = gt
2488 if y.maybeZero() {
2489 r |= eq
2490 }
2491 ft.update(b, v, v.Args[0], unsigned, r)
2492 }
2493 if x.min >= 0 && !signedAddOverflowsOrUnderflows(x.max, y.max, v.Type) {
2494 r := gt
2495 if x.maybeZero() {
2496 r |= eq
2497 }
2498 ft.update(b, v, v.Args[1], signed, r)
2499 }
2500 if y.min >= 0 && !signedAddOverflowsOrUnderflows(x.max, y.max, v.Type) {
2501 r := gt
2502 if y.maybeZero() {
2503 r |= eq
2504 }
2505 ft.update(b, v, v.Args[0], signed, r)
2506 }
2507 if x.max <= 0 && !signedAddOverflowsOrUnderflows(x.min, y.min, v.Type) {
2508 r := lt
2509 if x.maybeZero() {
2510 r |= eq
2511 }
2512 ft.update(b, v, v.Args[1], signed, r)
2513 }
2514 if y.max <= 0 && !signedAddOverflowsOrUnderflows(x.min, y.min, v.Type) {
2515 r := lt
2516 if y.maybeZero() {
2517 r |= eq
2518 }
2519 ft.update(b, v, v.Args[0], signed, r)
2520 }
2521 case OpSub64, OpSub32, OpSub16, OpSub8:
2522 x := ft.limits[v.Args[0].ID]
2523 y := ft.limits[v.Args[1].ID]
2524 if !unsignedSubUnderflows(x.umin, y.umax) {
2525 r := lt
2526 if y.maybeZero() {
2527 r |= eq
2528 }
2529 ft.update(b, v, v.Args[0], unsigned, r)
2530 }
2531
2532 case OpAnd64, OpAnd32, OpAnd16, OpAnd8:
2533 ft.update(b, v, v.Args[0], unsigned, lt|eq)
2534 ft.update(b, v, v.Args[1], unsigned, lt|eq)
2535 if ft.isNonNegative(v.Args[0]) {
2536 ft.update(b, v, v.Args[0], signed, lt|eq)
2537 }
2538 if ft.isNonNegative(v.Args[1]) {
2539 ft.update(b, v, v.Args[1], signed, lt|eq)
2540 }
2541 case OpOr64, OpOr32, OpOr16, OpOr8:
2542
2543
2544
2545 case OpDiv64, OpDiv32, OpDiv16, OpDiv8:
2546 if !ft.isNonNegative(v.Args[1]) {
2547 break
2548 }
2549 fallthrough
2550 case OpRsh8x64, OpRsh8x32, OpRsh8x16, OpRsh8x8,
2551 OpRsh16x64, OpRsh16x32, OpRsh16x16, OpRsh16x8,
2552 OpRsh32x64, OpRsh32x32, OpRsh32x16, OpRsh32x8,
2553 OpRsh64x64, OpRsh64x32, OpRsh64x16, OpRsh64x8:
2554 if !ft.isNonNegative(v.Args[0]) {
2555 break
2556 }
2557 fallthrough
2558 case OpDiv64u, OpDiv32u, OpDiv16u, OpDiv8u,
2559 OpRsh8Ux64, OpRsh8Ux32, OpRsh8Ux16, OpRsh8Ux8,
2560 OpRsh16Ux64, OpRsh16Ux32, OpRsh16Ux16, OpRsh16Ux8,
2561 OpRsh32Ux64, OpRsh32Ux32, OpRsh32Ux16, OpRsh32Ux8,
2562 OpRsh64Ux64, OpRsh64Ux32, OpRsh64Ux16, OpRsh64Ux8:
2563 switch add := v.Args[0]; add.Op {
2564
2565
2566
2567 case OpAdd64, OpAdd32, OpAdd16, OpAdd8:
2568 z := v.Args[1]
2569 zl := ft.limits[z.ID]
2570 var uminDivisor uint64
2571 switch v.Op {
2572 case OpDiv64u, OpDiv32u, OpDiv16u, OpDiv8u,
2573 OpDiv64, OpDiv32, OpDiv16, OpDiv8:
2574 uminDivisor = zl.umin
2575 case OpRsh8Ux64, OpRsh8Ux32, OpRsh8Ux16, OpRsh8Ux8,
2576 OpRsh16Ux64, OpRsh16Ux32, OpRsh16Ux16, OpRsh16Ux8,
2577 OpRsh32Ux64, OpRsh32Ux32, OpRsh32Ux16, OpRsh32Ux8,
2578 OpRsh64Ux64, OpRsh64Ux32, OpRsh64Ux16, OpRsh64Ux8,
2579 OpRsh8x64, OpRsh8x32, OpRsh8x16, OpRsh8x8,
2580 OpRsh16x64, OpRsh16x32, OpRsh16x16, OpRsh16x8,
2581 OpRsh32x64, OpRsh32x32, OpRsh32x16, OpRsh32x8,
2582 OpRsh64x64, OpRsh64x32, OpRsh64x16, OpRsh64x8:
2583 uminDivisor = 1 << zl.umin
2584 default:
2585 panic("unreachable")
2586 }
2587
2588 x := add.Args[0]
2589 xl := ft.limits[x.ID]
2590 y := add.Args[1]
2591 yl := ft.limits[y.ID]
2592 if !unsignedAddOverflows(xl.umax, yl.umax, add.Type) {
2593 if xl.umax < uminDivisor {
2594 ft.update(b, v, y, unsigned, lt|eq)
2595 }
2596 if yl.umax < uminDivisor {
2597 ft.update(b, v, x, unsigned, lt|eq)
2598 }
2599 }
2600 }
2601 ft.update(b, v, v.Args[0], unsigned, lt|eq)
2602 case OpMod64, OpMod32, OpMod16, OpMod8:
2603 if !ft.isNonNegative(v.Args[0]) || !ft.isNonNegative(v.Args[1]) {
2604 break
2605 }
2606 fallthrough
2607 case OpMod64u, OpMod32u, OpMod16u, OpMod8u:
2608 ft.update(b, v, v.Args[0], unsigned, lt|eq)
2609
2610
2611
2612
2613 ft.update(b, v, v.Args[1], unsigned, lt)
2614 case OpStringLen:
2615 if v.Args[0].Op == OpStringMake {
2616 ft.update(b, v, v.Args[0].Args[1], signed, eq)
2617 }
2618 case OpSliceLen:
2619 if v.Args[0].Op == OpSliceMake {
2620 ft.update(b, v, v.Args[0].Args[1], signed, eq)
2621 }
2622 case OpSliceCap:
2623 if v.Args[0].Op == OpSliceMake {
2624 ft.update(b, v, v.Args[0].Args[2], signed, eq)
2625 }
2626 case OpIsInBounds:
2627 if checkForChunkedIndexBounds(ft, b, v.Args[0], v.Args[1], false) {
2628 if b.Func.pass.debug > 0 {
2629 b.Func.Warnl(v.Pos, "Proved %s for blocked indexing", v.Op)
2630 }
2631 ft.booleanTrue(v)
2632 }
2633 case OpIsSliceInBounds:
2634 if checkForChunkedIndexBounds(ft, b, v.Args[0], v.Args[1], true) {
2635 if b.Func.pass.debug > 0 {
2636 b.Func.Warnl(v.Pos, "Proved %s for blocked reslicing", v.Op)
2637 }
2638 ft.booleanTrue(v)
2639 }
2640 case OpPhi:
2641 addLocalFactsPhi(ft, v)
2642 }
2643 }
2644 }
2645
2646 func addLocalFactsPhi(ft *factsTable, v *Value) {
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661 if len(v.Args) != 2 {
2662 return
2663 }
2664 b := v.Block
2665 x := v.Args[0]
2666 y := v.Args[1]
2667 bx := b.Preds[0].b
2668 by := b.Preds[1].b
2669 var z *Block
2670 switch {
2671 case bx == by:
2672 z = bx
2673 case by.uniquePred() == bx:
2674 z = bx
2675 case bx.uniquePred() == by:
2676 z = by
2677 case bx.uniquePred() == by.uniquePred():
2678 z = bx.uniquePred()
2679 }
2680 if z == nil || z.Kind != BlockIf {
2681 return
2682 }
2683 c := z.Controls[0]
2684 if len(c.Args) != 2 {
2685 return
2686 }
2687 var isMin bool
2688 if bx == z {
2689 isMin = b.Preds[0].i == 0
2690 } else {
2691 isMin = bx.Preds[0].i == 0
2692 }
2693 if c.Args[0] == x && c.Args[1] == y {
2694
2695 } else if c.Args[0] == y && c.Args[1] == x {
2696
2697 isMin = !isMin
2698 } else {
2699
2700 return
2701 }
2702 var dom domain
2703 switch c.Op {
2704 case OpLess64, OpLess32, OpLess16, OpLess8, OpLeq64, OpLeq32, OpLeq16, OpLeq8:
2705 dom = signed
2706 case OpLess64U, OpLess32U, OpLess16U, OpLess8U, OpLeq64U, OpLeq32U, OpLeq16U, OpLeq8U:
2707 dom = unsigned
2708 default:
2709 return
2710 }
2711 var rel relation
2712 if isMin {
2713 rel = lt | eq
2714 } else {
2715 rel = gt | eq
2716 }
2717 ft.update(b, v, x, dom, rel)
2718 ft.update(b, v, y, dom, rel)
2719 }
2720
2721 var ctzNonZeroOp = map[Op]Op{
2722 OpCtz8: OpCtz8NonZero,
2723 OpCtz16: OpCtz16NonZero,
2724 OpCtz32: OpCtz32NonZero,
2725 OpCtz64: OpCtz64NonZero,
2726 }
2727 var mostNegativeDividend = map[Op]int64{
2728 OpDiv16: -1 << 15,
2729 OpMod16: -1 << 15,
2730 OpDiv32: -1 << 31,
2731 OpMod32: -1 << 31,
2732 OpDiv64: -1 << 63,
2733 OpMod64: -1 << 63,
2734 }
2735 var unsignedOp = map[Op]Op{
2736 OpDiv8: OpDiv8u,
2737 OpDiv16: OpDiv16u,
2738 OpDiv32: OpDiv32u,
2739 OpDiv64: OpDiv64u,
2740 OpMod8: OpMod8u,
2741 OpMod16: OpMod16u,
2742 OpMod32: OpMod32u,
2743 OpMod64: OpMod64u,
2744 OpRsh8x8: OpRsh8Ux8,
2745 OpRsh8x16: OpRsh8Ux16,
2746 OpRsh8x32: OpRsh8Ux32,
2747 OpRsh8x64: OpRsh8Ux64,
2748 OpRsh16x8: OpRsh16Ux8,
2749 OpRsh16x16: OpRsh16Ux16,
2750 OpRsh16x32: OpRsh16Ux32,
2751 OpRsh16x64: OpRsh16Ux64,
2752 OpRsh32x8: OpRsh32Ux8,
2753 OpRsh32x16: OpRsh32Ux16,
2754 OpRsh32x32: OpRsh32Ux32,
2755 OpRsh32x64: OpRsh32Ux64,
2756 OpRsh64x8: OpRsh64Ux8,
2757 OpRsh64x16: OpRsh64Ux16,
2758 OpRsh64x32: OpRsh64Ux32,
2759 OpRsh64x64: OpRsh64Ux64,
2760 }
2761
2762 var bytesizeToConst = [...]Op{
2763 8 / 8: OpConst8,
2764 16 / 8: OpConst16,
2765 32 / 8: OpConst32,
2766 64 / 8: OpConst64,
2767 }
2768 var bytesizeToNeq = [...]Op{
2769 8 / 8: OpNeq8,
2770 16 / 8: OpNeq16,
2771 32 / 8: OpNeq32,
2772 64 / 8: OpNeq64,
2773 }
2774 var bytesizeToAnd = [...]Op{
2775 8 / 8: OpAnd8,
2776 16 / 8: OpAnd16,
2777 32 / 8: OpAnd32,
2778 64 / 8: OpAnd64,
2779 }
2780
2781
2782
2783 func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
2784 for iv, v := range b.Values {
2785 switch v.Op {
2786 case OpStaticLECall:
2787 if b.Func.pass.debug > 0 && len(v.Args) == 2 {
2788 fn := auxToCall(v.Aux).Fn
2789 if fn != nil && strings.Contains(fn.String(), "prove") {
2790
2791
2792
2793 x := v.Args[0]
2794 b.Func.Warnl(v.Pos, "Proved %v (%v)", ft.limits[x.ID], x)
2795 }
2796 }
2797 case OpSlicemask:
2798
2799 cap := v.Args[0]
2800 x, delta := isConstDelta(cap)
2801 if x != nil {
2802
2803
2804 lim := ft.limits[x.ID]
2805 if lim.umin > uint64(-delta) {
2806 if cap.Op == OpAdd64 {
2807 v.reset(OpConst64)
2808 } else {
2809 v.reset(OpConst32)
2810 }
2811 if b.Func.pass.debug > 0 {
2812 b.Func.Warnl(v.Pos, "Proved slicemask not needed")
2813 }
2814 v.AuxInt = -1
2815 }
2816 break
2817 }
2818 lim := ft.limits[cap.ID]
2819 if lim.umin > 0 {
2820 if cap.Type.Size() == 8 {
2821 v.reset(OpConst64)
2822 } else {
2823 v.reset(OpConst32)
2824 }
2825 if b.Func.pass.debug > 0 {
2826 b.Func.Warnl(v.Pos, "Proved slicemask not needed (by limit)")
2827 }
2828 v.AuxInt = -1
2829 }
2830
2831 case OpCtz8, OpCtz16, OpCtz32, OpCtz64:
2832
2833
2834
2835 x := v.Args[0]
2836 lim := ft.limits[x.ID]
2837 if lim.umin > 0 || lim.min > 0 || lim.max < 0 {
2838 if b.Func.pass.debug > 0 {
2839 b.Func.Warnl(v.Pos, "Proved %v non-zero", v.Op)
2840 }
2841 v.Op = ctzNonZeroOp[v.Op]
2842 }
2843 case OpRsh8x8, OpRsh8x16, OpRsh8x32, OpRsh8x64,
2844 OpRsh16x8, OpRsh16x16, OpRsh16x32, OpRsh16x64,
2845 OpRsh32x8, OpRsh32x16, OpRsh32x32, OpRsh32x64,
2846 OpRsh64x8, OpRsh64x16, OpRsh64x32, OpRsh64x64:
2847 if ft.isNonNegative(v.Args[0]) {
2848 if b.Func.pass.debug > 0 {
2849 b.Func.Warnl(v.Pos, "Proved %v is unsigned", v.Op)
2850 }
2851 v.Op = unsignedOp[v.Op]
2852 }
2853 fallthrough
2854 case OpLsh8x8, OpLsh8x16, OpLsh8x32, OpLsh8x64,
2855 OpLsh16x8, OpLsh16x16, OpLsh16x32, OpLsh16x64,
2856 OpLsh32x8, OpLsh32x16, OpLsh32x32, OpLsh32x64,
2857 OpLsh64x8, OpLsh64x16, OpLsh64x32, OpLsh64x64,
2858 OpRsh8Ux8, OpRsh8Ux16, OpRsh8Ux32, OpRsh8Ux64,
2859 OpRsh16Ux8, OpRsh16Ux16, OpRsh16Ux32, OpRsh16Ux64,
2860 OpRsh32Ux8, OpRsh32Ux16, OpRsh32Ux32, OpRsh32Ux64,
2861 OpRsh64Ux8, OpRsh64Ux16, OpRsh64Ux32, OpRsh64Ux64:
2862
2863
2864 by := v.Args[1]
2865 lim := ft.limits[by.ID]
2866 bits := 8 * v.Args[0].Type.Size()
2867 if lim.umax < uint64(bits) || (lim.max < bits && ft.isNonNegative(by)) {
2868 v.AuxInt = 1
2869 if b.Func.pass.debug > 0 && !by.isGenericIntConst() {
2870 b.Func.Warnl(v.Pos, "Proved %v bounded", v.Op)
2871 }
2872 }
2873 case OpDiv8, OpDiv16, OpDiv32, OpDiv64, OpMod8, OpMod16, OpMod32, OpMod64:
2874 p, q := ft.limits[v.Args[0].ID], ft.limits[v.Args[1].ID]
2875 if p.nonnegative() && q.nonnegative() {
2876 if b.Func.pass.debug > 0 {
2877 b.Func.Warnl(v.Pos, "Proved %v is unsigned", v.Op)
2878 }
2879 v.Op = unsignedOp[v.Op]
2880 v.AuxInt = 0
2881 break
2882 }
2883
2884
2885 if v.Op != OpDiv8 && v.Op != OpMod8 && (q.max < -1 || q.min > -1 || p.min > mostNegativeDividend[v.Op]) {
2886
2887
2888
2889
2890 if b.Func.pass.debug > 0 {
2891 b.Func.Warnl(v.Pos, "Proved %v does not need fix-up", v.Op)
2892 }
2893
2894
2895
2896
2897
2898 if b.Func.Config.arch == "386" || b.Func.Config.arch == "amd64" {
2899 v.AuxInt = 1
2900 }
2901 }
2902 case OpMul64, OpMul32, OpMul16, OpMul8:
2903 if vl := ft.limits[v.ID]; vl.min == vl.max || vl.umin == vl.umax {
2904
2905 break
2906 }
2907 x := v.Args[0]
2908 xl := ft.limits[x.ID]
2909 y := v.Args[1]
2910 yl := ft.limits[y.ID]
2911 if xl.umin == xl.umax && isPowerOfTwo(xl.umin) ||
2912 xl.min == xl.max && isPowerOfTwo(xl.min) ||
2913 yl.umin == yl.umax && isPowerOfTwo(yl.umin) ||
2914 yl.min == yl.max && isPowerOfTwo(yl.min) {
2915
2916 break
2917 }
2918 switch xOne, yOne := xl.umax <= 1, yl.umax <= 1; {
2919 case xOne && yOne:
2920 v.Op = bytesizeToAnd[v.Type.Size()]
2921 if b.Func.pass.debug > 0 {
2922 b.Func.Warnl(v.Pos, "Rewrote Mul %v into And", v)
2923 }
2924 case yOne && b.Func.Config.haveCondSelect:
2925 x, y = y, x
2926 fallthrough
2927 case xOne && b.Func.Config.haveCondSelect:
2928 if !canCondSelect(v, b.Func.Config.arch, nil) {
2929 break
2930 }
2931 zero := b.Func.constVal(bytesizeToConst[v.Type.Size()], v.Type, 0, true)
2932 ft.initLimitForNewValue(zero)
2933 check := b.NewValue2(v.Pos, bytesizeToNeq[v.Type.Size()], types.Types[types.TBOOL], zero, x)
2934 ft.initLimitForNewValue(check)
2935 v.reset(OpCondSelect)
2936 v.AddArg3(y, zero, check)
2937
2938
2939
2940
2941 if b.Values[len(b.Values)-1] != check {
2942 panic("unreachable; failed sanity check, new value isn't at the end of the block")
2943 }
2944 b.Values[iv], b.Values[len(b.Values)-1] = b.Values[len(b.Values)-1], b.Values[iv]
2945
2946 if b.Func.pass.debug > 0 {
2947 b.Func.Warnl(v.Pos, "Rewrote Mul %v into CondSelect; %v is bool", v, x)
2948 }
2949 }
2950 }
2951
2952
2953
2954 for i, arg := range v.Args {
2955 lim := ft.limits[arg.ID]
2956 var constValue int64
2957 switch {
2958 case lim.min == lim.max:
2959 constValue = lim.min
2960 case lim.umin == lim.umax:
2961 constValue = int64(lim.umin)
2962 default:
2963 continue
2964 }
2965 switch arg.Op {
2966 case OpConst64, OpConst32, OpConst16, OpConst8, OpConstBool, OpConstNil:
2967 continue
2968 }
2969 typ := arg.Type
2970 f := b.Func
2971 var c *Value
2972 switch {
2973 case typ.IsBoolean():
2974 c = f.ConstBool(typ, constValue != 0)
2975 case typ.IsInteger() && typ.Size() == 1:
2976 c = f.ConstInt8(typ, int8(constValue))
2977 case typ.IsInteger() && typ.Size() == 2:
2978 c = f.ConstInt16(typ, int16(constValue))
2979 case typ.IsInteger() && typ.Size() == 4:
2980 c = f.ConstInt32(typ, int32(constValue))
2981 case typ.IsInteger() && typ.Size() == 8:
2982 c = f.ConstInt64(typ, constValue)
2983 case typ.IsPtrShaped():
2984 if constValue == 0 {
2985 c = f.ConstNil(typ)
2986 } else {
2987
2988
2989 continue
2990 }
2991 default:
2992
2993
2994 continue
2995 }
2996 v.SetArg(i, c)
2997 ft.initLimitForNewValue(c)
2998 if b.Func.pass.debug > 1 {
2999 b.Func.Warnl(v.Pos, "Proved %v's arg %d (%v) is constant %d", v, i, arg, constValue)
3000 }
3001 }
3002 }
3003
3004 if b.Kind != BlockIf {
3005 return
3006 }
3007
3008
3009 parent := b
3010 for i, branch := range [...]branch{positive, negative} {
3011 child := parent.Succs[i].b
3012 if getBranch(sdom, parent, child) != unknown {
3013
3014
3015 continue
3016 }
3017
3018
3019 ft.checkpoint()
3020 addBranchRestrictions(ft, parent, branch)
3021 unsat := ft.unsat
3022 ft.restore()
3023 if unsat {
3024
3025
3026 removeBranch(parent, branch)
3027
3028
3029
3030
3031
3032 break
3033 }
3034 }
3035 }
3036
3037 func removeBranch(b *Block, branch branch) {
3038 c := b.Controls[0]
3039 if b.Func.pass.debug > 0 {
3040 verb := "Proved"
3041 if branch == positive {
3042 verb = "Disproved"
3043 }
3044 if b.Func.pass.debug > 1 {
3045 b.Func.Warnl(b.Pos, "%s %s (%s)", verb, c.Op, c)
3046 } else {
3047 b.Func.Warnl(b.Pos, "%s %s", verb, c.Op)
3048 }
3049 }
3050 if c != nil && c.Pos.IsStmt() == src.PosIsStmt && c.Pos.SameFileAndLine(b.Pos) {
3051
3052 b.Pos = b.Pos.WithIsStmt()
3053 }
3054 if branch == positive || branch == negative {
3055 b.Kind = BlockFirst
3056 b.ResetControls()
3057 if branch == positive {
3058 b.swapSuccessors()
3059 }
3060 } else {
3061
3062 }
3063 }
3064
3065
3066 func isConstDelta(v *Value) (w *Value, delta int64) {
3067 cop := OpConst64
3068 switch v.Op {
3069 case OpAdd32, OpSub32:
3070 cop = OpConst32
3071 case OpAdd16, OpSub16:
3072 cop = OpConst16
3073 case OpAdd8, OpSub8:
3074 cop = OpConst8
3075 }
3076 switch v.Op {
3077 case OpAdd64, OpAdd32, OpAdd16, OpAdd8:
3078 if v.Args[0].Op == cop {
3079 return v.Args[1], v.Args[0].AuxInt
3080 }
3081 if v.Args[1].Op == cop {
3082 return v.Args[0], v.Args[1].AuxInt
3083 }
3084 case OpSub64, OpSub32, OpSub16, OpSub8:
3085 if v.Args[1].Op == cop {
3086 aux := v.Args[1].AuxInt
3087 if aux != -aux {
3088 return v.Args[0], -aux
3089 }
3090 }
3091 }
3092 return nil, 0
3093 }
3094
3095
3096
3097 func isCleanExt(v *Value) bool {
3098 switch v.Op {
3099 case OpSignExt8to16, OpSignExt8to32, OpSignExt8to64,
3100 OpSignExt16to32, OpSignExt16to64, OpSignExt32to64:
3101
3102 return v.Args[0].Type.IsSigned() && v.Type.IsSigned()
3103
3104 case OpZeroExt8to16, OpZeroExt8to32, OpZeroExt8to64,
3105 OpZeroExt16to32, OpZeroExt16to64, OpZeroExt32to64:
3106
3107 return !v.Args[0].Type.IsSigned()
3108 }
3109 return false
3110 }
3111
3112 func getDependencyScore(scores []uint, v *Value) (score uint) {
3113 if score = scores[v.ID]; score != 0 {
3114 return score
3115 }
3116 defer func() {
3117 scores[v.ID] = score
3118 }()
3119 if v.Op == OpPhi {
3120 return 1
3121 }
3122 score = 2
3123 for _, a := range v.Args {
3124 if a.Block != v.Block {
3125 continue
3126 }
3127 score = max(score, getDependencyScore(scores, a)+1)
3128 }
3129 return score
3130 }
3131
3132
3133
3134
3135 func (ft *factsTable) topoSortValuesInBlock(b *Block) {
3136 f := b.Func
3137 want := f.NumValues()
3138
3139 scores := ft.reusedTopoSortScoresTable
3140 if want <= cap(scores) {
3141 scores = scores[:want]
3142 } else {
3143 if cap(scores) > 0 {
3144 f.Cache.freeUintSlice(scores)
3145 }
3146 scores = f.Cache.allocUintSlice(want)
3147 ft.reusedTopoSortScoresTable = scores
3148 }
3149
3150 for _, v := range b.Values {
3151 scores[v.ID] = 0
3152 }
3153
3154 slices.SortFunc(b.Values, func(a, b *Value) int {
3155 dependencyScoreA := getDependencyScore(scores, a)
3156 dependencyScoreB := getDependencyScore(scores, b)
3157 if dependencyScoreA != dependencyScoreB {
3158 return cmp.Compare(dependencyScoreA, dependencyScoreB)
3159 }
3160 return cmp.Compare(a.ID, b.ID)
3161 })
3162 }
3163
View as plain text