1
2
3
4
5 package ssa
6
7 import (
8 "fmt"
9 "math"
10 "math/bits"
11
12 "cmd/compile/internal/ssa/ssaop"
13 )
14
15
16 func FitsInBitsU(x uint64, b uint) bool {
17 return x>>b == 0
18 }
19
20
21
22
23
24
25
26 func InitLimit(v *Value) Limit {
27 if v.Type.IsBoolean() {
28 switch v.Op {
29 case ssaop.OpConstBool:
30 b := v.AuxInt
31 return Limit{Min: b, Max: b, Umin: uint64(b), Umax: uint64(b)}
32 default:
33 return Limit{Min: 0, Max: 1, Umin: 0, Umax: 1}
34 }
35 }
36 if v.Type.IsPtrShaped() {
37 switch v.Op {
38 case ssaop.OpConstNil:
39 return Limit{Min: 0, Max: 0, Umin: 0, Umax: 0}
40 case ssaop.OpAddr, ssaop.OpLocalAddr:
41 l := NoLimit()
42 l.Umin = 1
43 return l
44 default:
45 return NoLimit()
46 }
47 }
48 if !v.Type.IsInteger() {
49 return NoLimit()
50 }
51
52
53 lim := NoLimitForBitsize(uint(v.Type.Size()) * 8)
54
55
56 switch v.Op {
57
58 case ssaop.OpConst64:
59 lim = Limit{Min: v.AuxInt, Max: v.AuxInt, Umin: uint64(v.AuxInt), Umax: uint64(v.AuxInt)}
60 case ssaop.OpConst32:
61 lim = Limit{Min: v.AuxInt, Max: v.AuxInt, Umin: uint64(uint32(v.AuxInt)), Umax: uint64(uint32(v.AuxInt))}
62 case ssaop.OpConst16:
63 lim = Limit{Min: v.AuxInt, Max: v.AuxInt, Umin: uint64(uint16(v.AuxInt)), Umax: uint64(uint16(v.AuxInt))}
64 case ssaop.OpConst8:
65 lim = Limit{Min: v.AuxInt, Max: v.AuxInt, Umin: uint64(uint8(v.AuxInt)), Umax: uint64(uint8(v.AuxInt))}
66
67
68 case ssaop.OpZeroExt8to64, ssaop.OpZeroExt8to32, ssaop.OpZeroExt8to16:
69 lim = lim.SignedMinMax(0, 1<<8-1)
70 lim = lim.UnsignedMax(1<<8 - 1)
71 case ssaop.OpZeroExt16to64, ssaop.OpZeroExt16to32:
72 lim = lim.SignedMinMax(0, 1<<16-1)
73 lim = lim.UnsignedMax(1<<16 - 1)
74 case ssaop.OpZeroExt32to64:
75 lim = lim.SignedMinMax(0, 1<<32-1)
76 lim = lim.UnsignedMax(1<<32 - 1)
77 case ssaop.OpSignExt8to64, ssaop.OpSignExt8to32, ssaop.OpSignExt8to16:
78 lim = lim.SignedMinMax(math.MinInt8, math.MaxInt8)
79 case ssaop.OpSignExt16to64, ssaop.OpSignExt16to32:
80 lim = lim.SignedMinMax(math.MinInt16, math.MaxInt16)
81 case ssaop.OpSignExt32to64:
82 lim = lim.SignedMinMax(math.MinInt32, math.MaxInt32)
83
84
85 case ssaop.OpCtz64, ssaop.OpBitLen64, ssaop.OpPopCount64,
86 ssaop.OpCtz32, ssaop.OpBitLen32, ssaop.OpPopCount32,
87 ssaop.OpCtz16, ssaop.OpBitLen16, ssaop.OpPopCount16,
88 ssaop.OpCtz8, ssaop.OpBitLen8, ssaop.OpPopCount8:
89 lim = lim.UnsignedMax(uint64(v.Args[0].Type.Size() * 8))
90
91
92 case ssaop.OpCvtBoolToUint8:
93 lim = lim.UnsignedMax(1)
94
95
96 case ssaop.OpSliceLen, ssaop.OpSliceCap:
97 f := v.Block.Func
98 elemSize := uint64(v.Args[0].Type.Elem().Size())
99 if elemSize > 0 {
100 heapSize := uint64(1)<<(uint64(f.Config.PtrSize)*8) - 1
101 maximumElementsFittingInHeap := heapSize / elemSize
102 lim = lim.UnsignedMax(maximumElementsFittingInHeap)
103 }
104 fallthrough
105 case ssaop.OpStringLen:
106 lim = lim.signedMin(0)
107 }
108
109
110 if lim.Min >= 0 {
111 lim = lim.UnsignedMinMax(uint64(lim.Min), uint64(lim.Max))
112 }
113 if FitsInBitsU(lim.Umax, uint(8*v.Type.Size()-1)) {
114 lim = lim.SignedMinMax(int64(lim.Umin), int64(lim.Umax))
115 }
116
117 return lim
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 NoLimit() Limit {
136 return NoLimitForBitsize(64)
137 }
138
139
140
141
142 func SafeAdd(x, y int64, b uint) (int64, bool) {
143 s := x + y
144 if x >= 0 && y >= 0 && s < 0 {
145 return 0, false
146 }
147 if x < 0 && y < 0 && s >= 0 {
148 return 0, false
149 }
150 if !fitsInBits(s, b) {
151 return 0, false
152 }
153 return s, true
154 }
155
156
157 func SafeSub(x, y int64, b uint) (int64, bool) {
158 if y == math.MinInt64 {
159 if x == math.MaxInt64 {
160 return 0, false
161 }
162 x++
163 y++
164 }
165 return SafeAdd(x, -y, b)
166 }
167
168
169 func SafeSubU(x, y uint64, b uint) (uint64, bool) {
170 if x < y {
171 return 0, false
172 }
173 s := x - y
174 if !FitsInBitsU(s, b) {
175 return 0, false
176 }
177 return s, true
178 }
179
180 func ConvertIntWithBitsize[Target uint64 | int64, Source uint64 | int64](x Source, bitsize uint) Target {
181 if Target(0)-1 < 0 {
182
183 switch bitsize {
184 case 64:
185 return Target(int64(x))
186 case 32:
187 return Target(int32(x))
188 case 16:
189 return Target(int16(x))
190 case 8:
191 return Target(int8(x))
192 }
193 } else {
194
195 switch bitsize {
196 case 64:
197 return Target(uint64(x))
198 case 32:
199 return Target(uint32(x))
200 case 16:
201 return Target(uint16(x))
202 case 8:
203 return Target(uint8(x))
204 }
205 }
206 panic("unreachable")
207 }
208
209
210 func fitsInBits(x int64, b uint) bool {
211 if b == 64 {
212 return true
213 }
214 m := int64(-1) << (b - 1)
215 M := -m - 1
216 return x >= m && x <= M
217 }
218
219 func NoLimitForBitsize(bitsize uint) Limit {
220 return Limit{Min: -(1 << (bitsize - 1)), Max: 1<<(bitsize-1) - 1, Umin: 0, Umax: 1<<bitsize - 1}
221 }
222
223
224 func safeAddU(x, y uint64, b uint) (uint64, bool) {
225 s := x + y
226 if s < x || s < y {
227 return 0, false
228 }
229 if !FitsInBitsU(s, b) {
230 return 0, false
231 }
232 return s, true
233 }
234
235 func (l Limit) String() string {
236 return fmt.Sprintf("sm,SM=%d,%d um,UM=%d,%d", l.Min, l.Max, l.Umin, l.Umax)
237 }
238
239 func (l Limit) Intersect(l2 Limit) Limit {
240 l.Min = max(l.Min, l2.Min)
241 l.Umin = max(l.Umin, l2.Umin)
242 l.Max = min(l.Max, l2.Max)
243 l.Umax = min(l.Umax, l2.Umax)
244 return l
245 }
246
247 func (l Limit) signedMin(m int64) Limit {
248 l.Min = max(l.Min, m)
249 return l
250 }
251
252 func (l Limit) SignedMinMax(minimum, maximum int64) Limit {
253 l.Min = max(l.Min, minimum)
254 l.Max = min(l.Max, maximum)
255 return l
256 }
257
258 func (l Limit) UnsignedMin(m uint64) Limit {
259 l.Umin = max(l.Umin, m)
260 return l
261 }
262
263 func (l Limit) UnsignedMax(m uint64) Limit {
264 l.Umax = min(l.Umax, m)
265 return l
266 }
267
268 func (l Limit) UnsignedMinMax(minimum, maximum uint64) Limit {
269 l.Umin = max(l.Umin, minimum)
270 l.Umax = min(l.Umax, maximum)
271 return l
272 }
273
274 func (l Limit) nonzero() bool {
275 return l.Min > 0 || l.Umin > 0 || l.Max < 0
276 }
277
278 func (l Limit) MaybeZero() bool {
279 return !l.nonzero()
280 }
281
282 func (l Limit) Nonnegative() bool {
283 return l.Min >= 0
284 }
285
286 func (l Limit) Unsat() bool {
287 return l.Min > l.Max || l.Umin > l.Umax
288 }
289
290
291
292
293
294
295
296
297
298
299 func (l Limit) UnsignedFixedLeadingBits() (fixed uint64, count uint) {
300 varying := uint(bits.Len64(l.Umin ^ l.Umax))
301 count = uint(bits.LeadingZeros64(l.Umin ^ l.Umax))
302 fixed = l.Umin &^ (1<<varying - 1)
303 return
304 }
305
306
307
308 func (l Limit) Add(l2 Limit, b uint) Limit {
309 var isLConst, isL2Const bool
310 var lConst, l2Const uint64
311 if l.Min == l.Max {
312 isLConst = true
313 lConst = ConvertIntWithBitsize[uint64](l.Min, b)
314 } else if l.Umin == l.Umax {
315 isLConst = true
316 lConst = l.Umin
317 }
318 if l2.Min == l2.Max {
319 isL2Const = true
320 l2Const = ConvertIntWithBitsize[uint64](l2.Min, b)
321 } else if l2.Umin == l2.Umax {
322 isL2Const = true
323 l2Const = l2.Umin
324 }
325 if isLConst && isL2Const {
326 r := lConst + l2Const
327 r &= (uint64(1) << b) - 1
328 int64r := ConvertIntWithBitsize[int64](r, b)
329 return Limit{Min: int64r, Max: int64r, Umin: r, Umax: r}
330 }
331
332 r := NoLimit()
333 min, minOk := SafeAdd(l.Min, l2.Min, b)
334 max, maxOk := SafeAdd(l.Max, l2.Max, b)
335 if minOk && maxOk {
336 r.Min = min
337 r.Max = max
338 }
339 umin, uminOk := safeAddU(l.Umin, l2.Umin, b)
340 umax, umaxOk := safeAddU(l.Umax, l2.Umax, b)
341 if uminOk && umaxOk {
342 r.Umin = umin
343 r.Umax = umax
344 }
345 return r
346 }
347
348
349 func (l Limit) Sub(l2 Limit, b uint) Limit {
350 r := NoLimit()
351 min, minOk := SafeSub(l.Min, l2.Max, b)
352 max, maxOk := SafeSub(l.Max, l2.Min, b)
353 if minOk && maxOk {
354 r.Min = min
355 r.Max = max
356 }
357 umin, uminOk := SafeSubU(l.Umin, l2.Umax, b)
358 umax, umaxOk := SafeSubU(l.Umax, l2.Umin, b)
359 if uminOk && umaxOk {
360 r.Umin = umin
361 r.Umax = umax
362 }
363 return r
364 }
365
366
367 func (l Limit) Mul(l2 Limit, b uint) Limit {
368 r := NoLimit()
369 umaxhi, umaxlo := bits.Mul64(l.Umax, l2.Umax)
370 if umaxhi == 0 && FitsInBitsU(umaxlo, b) {
371 r.Umax = umaxlo
372 r.Umin = l.Umin * l2.Umin
373
374
375
376
377
378
379 }
380
381
382
383
384
385 return r
386 }
387
388
389 func (l Limit) Exp2(b uint) Limit {
390 r := NoLimit()
391 if l.Umax < uint64(b) {
392 r.Umin = 1 << l.Umin
393 r.Umax = 1 << l.Umax
394
395
396 }
397 return r
398 }
399
400
401 func (l Limit) Com(b uint) Limit {
402 switch b {
403 case 64:
404 return Limit{
405 Min: ^l.Max,
406 Max: ^l.Min,
407 Umin: ^l.Umax,
408 Umax: ^l.Umin,
409 }
410 case 32:
411 return Limit{
412 Min: int64(^int32(l.Max)),
413 Max: int64(^int32(l.Min)),
414 Umin: uint64(^uint32(l.Umax)),
415 Umax: uint64(^uint32(l.Umin)),
416 }
417 case 16:
418 return Limit{
419 Min: int64(^int16(l.Max)),
420 Max: int64(^int16(l.Min)),
421 Umin: uint64(^uint16(l.Umax)),
422 Umax: uint64(^uint16(l.Umin)),
423 }
424 case 8:
425 return Limit{
426 Min: int64(^int8(l.Max)),
427 Max: int64(^int8(l.Min)),
428 Umin: uint64(^uint8(l.Umax)),
429 Umax: uint64(^uint8(l.Umin)),
430 }
431 default:
432 panic("unreachable")
433 }
434 }
435
436
437 func (l Limit) Neg(b uint) Limit {
438 return l.Com(b).Add(Limit{Min: 1, Max: 1, Umin: 1, Umax: 1}, b)
439 }
440
441
442 func (l Limit) Ctz(b uint) Limit {
443 fixed, fixedCount := l.UnsignedFixedLeadingBits()
444 if fixedCount == 64 {
445 constResult := min(uint(bits.TrailingZeros64(fixed)), b)
446 return Limit{Min: int64(constResult), Max: int64(constResult), Umin: uint64(constResult), Umax: uint64(constResult)}
447 }
448
449 varying := 64 - fixedCount
450 if l.Umin&((1<<varying)-1) != 0 {
451
452 varying--
453 return NoLimit().UnsignedMax(uint64(varying))
454 }
455 return NoLimit().UnsignedMax(uint64(min(uint(bits.TrailingZeros64(fixed)), b)))
456 }
457
458
459 func (l Limit) Bitlen(b uint) Limit {
460 return NoLimit().UnsignedMinMax(
461 uint64(bits.Len64(l.Umin)),
462 uint64(bits.Len64(l.Umax)),
463 )
464 }
465
466
467 func (l Limit) Popcount(b uint) Limit {
468 fixed, fixedCount := l.UnsignedFixedLeadingBits()
469 varying := 64 - fixedCount
470 fixedContribution := uint64(bits.OnesCount64(fixed))
471
472 min := fixedContribution
473 max := fixedContribution + uint64(varying)
474
475 varyingMask := uint64(1)<<varying - 1
476
477 if varyingPartOfUmax := l.Umax & varyingMask; uint(bits.OnesCount64(varyingPartOfUmax)) != varying {
478
479 max--
480 }
481 if varyingPartOfUmin := l.Umin & varyingMask; varyingPartOfUmin != 0 {
482
483 min++
484 }
485
486 return NoLimit().UnsignedMinMax(min, max)
487 }
488
489 func (l Limit) ConstValue() (_ int64, ok bool) {
490 switch {
491 case l.Min == l.Max:
492 return l.Min, true
493 case l.Umin == l.Umax:
494 return int64(l.Umin), true
495 default:
496 return 0, false
497 }
498 }
499
View as plain text