1
2
3
4
5 package generic
6
7 import (
8 "fmt"
9 "math"
10 "math/bits"
11 "strings"
12
13 "cmd/compile/internal/base"
14 "cmd/compile/internal/ir"
15 "cmd/compile/internal/reflectdata"
16 "cmd/compile/internal/rttype"
17 "cmd/compile/internal/ssa"
18 "cmd/compile/internal/ssa/ssaop"
19 "cmd/compile/internal/typecheck"
20 "cmd/compile/internal/types"
21 "cmd/internal/obj"
22 "cmd/internal/objabi"
23 )
24
25 func addToSub(op ssaop.Op) ssaop.Op {
26 switch op {
27 case ssaop.OpAdd64:
28 return ssaop.OpSub64
29 case ssaop.OpAdd32:
30 return ssaop.OpSub32
31 case ssaop.OpAdd16:
32 return ssaop.OpSub16
33 case ssaop.OpAdd8:
34 return ssaop.OpSub8
35 default:
36 panic(fmt.Sprintf("unexpected op %v", op))
37 }
38 }
39
40 func bitsAdd64(x, y, carry int64) (r struct{ sum, carry int64 }) {
41 s, c := bits.Add64(uint64(x), uint64(y), uint64(carry))
42 r.sum, r.carry = int64(s), int64(c)
43 return
44 }
45
46 func bitsMulU32(x, y int32) (r struct{ hi, lo int32 }) {
47 hi, lo := bits.Mul32(uint32(x), uint32(y))
48 r.hi, r.lo = int32(hi), int32(lo)
49 return
50 }
51
52 func bitsMulU64(x, y int64) (r struct{ hi, lo int64 }) {
53 hi, lo := bits.Mul64(uint64(x), uint64(y))
54 r.hi, r.lo = int64(hi), int64(lo)
55 return
56 }
57
58 func bitsDiv128u(hi, lo, y int64) (r struct{ quo, rem int64 }) {
59 q, rem := bits.Div64(uint64(hi), uint64(lo), uint64(y))
60 r.quo, r.rem = int64(q), int64(rem)
61 return
62 }
63
64
65 func bool2int(x bool) int {
66 var b int
67 if x {
68 b = 1
69 }
70 return b
71 }
72
73
74 func canLoadUnaligned(c *ssa.Config) bool {
75 return c.Ctxt.Arch.Alignment == 1
76 }
77
78
79
80 func canRotate(c *ssa.Config, bits int64) bool {
81 if bits > c.PtrSize*8 {
82
83 return false
84 }
85 switch c.Arch {
86 case "386", "amd64", "arm64", "loong64", "riscv64":
87 return true
88 case "arm", "s390x", "ppc64", "ppc64le", "wasm":
89 return bits >= 32
90 default:
91 return false
92 }
93 }
94
95 func copyCompatibleType(t1, t2 *types.Type) bool {
96 if t1.Size() != t2.Size() {
97 return false
98 }
99 if t1.IsInteger() {
100 return t2.IsInteger()
101 }
102 if ssa.IsPtr(t1) {
103 return ssa.IsPtr(t2)
104 }
105 return t1.Compare(t2) == types.CMPeq
106 }
107
108 func devirtLECall(v *ssa.Value, sym *obj.LSym) *ssa.Value {
109 v.Op = ssaop.OpStaticLECall
110 auxcall := v.Aux.(*ssa.AuxCall)
111 auxcall.Fn = sym
112
113 v.Args[0].Uses--
114 copy(v.Args[0:], v.Args[1:])
115 v.Args[len(v.Args)-1] = nil
116 v.Args = v.Args[:len(v.Args)-1]
117 if f := v.Block.Func; f.Pass.Debug > 0 {
118 f.Warnl(v.Pos, "de-virtualizing call")
119 }
120 return v
121 }
122
123
124
125 func hasSmallRotate(c *ssa.Config) bool {
126 switch c.Arch {
127 case "amd64", "386":
128 return true
129 default:
130 return false
131 }
132 }
133
134 func invertibleBool(op ssaop.Op) bool {
135 switch op {
136 case ssaop.OpLess64, ssaop.OpLess32, ssaop.OpLess16, ssaop.OpLess8,
137 ssaop.OpLeq64, ssaop.OpLeq32, ssaop.OpLeq16, ssaop.OpLeq8,
138 ssaop.OpLess64U, ssaop.OpLess32U, ssaop.OpLess16U, ssaop.OpLess8U,
139 ssaop.OpLeq64U, ssaop.OpLeq32U, ssaop.OpLeq16U, ssaop.OpLeq8U,
140 ssaop.OpEq64, ssaop.OpEq32, ssaop.OpEq16, ssaop.OpEq8,
141 ssaop.OpNeq64, ssaop.OpNeq32, ssaop.OpNeq16, ssaop.OpNeq8,
142 ssaop.OpNot:
143 return true
144 default:
145 return false
146 }
147 }
148
149 func isDictArgSym(sym ssa.Sym) bool {
150 return sym.(*ir.Name).Sym().Name == typecheck.LocalDictName
151 }
152
153
154
155
156 func isDirectAndComparableIface(v *ssa.Value) bool {
157 return isDirectAndComparableIface1(v, 9)
158 }
159
160
161 func isDirectAndComparableIface1(v *ssa.Value, depth int) bool {
162 if depth == 0 {
163 return false
164 }
165 switch v.Op {
166 case ssaop.OpITab:
167 return isDirectAndComparableIface2(v.Args[0], depth-1)
168 case ssaop.OpAddr:
169 lsym := v.Aux.(*obj.LSym)
170 if ii := lsym.ItabInfo(); ii != nil {
171 t := ii.Type.(*types.Type)
172 return types.IsDirectIface(t) && types.IsComparable(t)
173 }
174 case ssaop.OpConstNil:
175
176
177 return true
178 }
179 return false
180 }
181
182
183 func isDirectAndComparableIface2(v *ssa.Value, depth int) bool {
184 if depth == 0 {
185 return false
186 }
187 switch v.Op {
188 case ssaop.OpIMake:
189 return isDirectAndComparableIface1(v.Args[0], depth-1)
190 case ssaop.OpPhi:
191 for _, a := range v.Args {
192 if !isDirectAndComparableIface2(a, depth-1) {
193 return false
194 }
195 }
196 return true
197 }
198 return false
199 }
200
201
202
203
204 func isDirectAndComparableType(v *ssa.Value) bool {
205 return isDirectAndComparableType1(v)
206 }
207
208
209 func isDirectAndComparableType1(v *ssa.Value) bool {
210 switch v.Op {
211 case ssaop.OpITab:
212 return isDirectAndComparableType2(v.Args[0])
213 case ssaop.OpAddr:
214 lsym := v.Aux.(*obj.LSym)
215 if ti := lsym.TypeInfo(); ti != nil {
216 t := ti.Type.(*types.Type)
217 return types.IsDirectIface(t) && types.IsComparable(t)
218 }
219 }
220 return false
221 }
222
223
224 func isDirectAndComparableType2(v *ssa.Value) bool {
225 switch v.Op {
226 case ssaop.OpIMake:
227 return isDirectAndComparableType1(v.Args[0])
228 }
229 return false
230 }
231
232
233
234 func isFixedLoad(v *ssa.Value, sym ssa.Sym, off int64) bool {
235 lsym := sym.(*obj.LSym)
236 if (v.Type.IsPtrShaped() || v.Type.IsUintptr()) && lsym.Type == objabi.SRODATA {
237 for _, r := range lsym.R {
238 if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 {
239 return true
240 }
241 }
242 return false
243 }
244
245 if ti := lsym.TypeInfo(); ti != nil {
246
247
248
249
250 t := ti.Type.(*types.Type)
251
252 for _, f := range rttype.Type.Fields() {
253 if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
254 switch f.Sym.Name {
255 case "Size_", "PtrBytes", "Hash", "Kind_", "GCData":
256 return true
257 case "TFlag":
258
259
260 return t.TFlagComputed() && !types.TypeHasNoAlg(t)
261 default:
262
263 return false
264 }
265 }
266 }
267
268 if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") {
269 return true
270 }
271
272 return false
273 }
274
275 return false
276 }
277
278 func isInlinableMemclr(c *ssa.Config, sz int64) bool {
279 if sz < 0 {
280 return false
281 }
282
283
284 switch c.Arch {
285 case "amd64", "arm64":
286 return true
287 case "ppc64le", "ppc64", "loong64":
288 return sz < 512
289 }
290 return false
291 }
292
293 func isMalloc(aux ssa.Aux) bool {
294 return ssa.IsNewObjectCall(aux) || ssa.IsSpecializedMalloc(aux)
295 }
296
297
298
299
300 func isNonNegative(v *ssa.Value) bool {
301 if !v.Type.IsInteger() {
302 v.Fatalf("isNonNegative bad type: %v", v.Type)
303 }
304
305
306
307
308 switch v.Op {
309 case ssaop.OpConst64:
310 return v.AuxInt >= 0
311
312 case ssaop.OpConst32:
313 return int32(v.AuxInt) >= 0
314
315 case ssaop.OpConst16:
316 return int16(v.AuxInt) >= 0
317
318 case ssaop.OpConst8:
319 return int8(v.AuxInt) >= 0
320
321 case ssaop.OpStringLen, ssaop.OpSliceLen, ssaop.OpSliceCap,
322 ssaop.OpZeroExt8to64, ssaop.OpZeroExt16to64, ssaop.OpZeroExt32to64,
323 ssaop.OpZeroExt8to32, ssaop.OpZeroExt16to32, ssaop.OpZeroExt8to16,
324 ssaop.OpCtz64, ssaop.OpCtz32, ssaop.OpCtz16, ssaop.OpCtz8,
325 ssaop.OpCtz64NonZero, ssaop.OpCtz32NonZero, ssaop.OpCtz16NonZero, ssaop.OpCtz8NonZero,
326 ssaop.OpBitLen64, ssaop.OpBitLen32, ssaop.OpBitLen16, ssaop.OpBitLen8:
327 return true
328
329 case ssaop.OpRsh64Ux64, ssaop.OpRsh32Ux64:
330 by := v.Args[1]
331 return by.Op == ssaop.OpConst64 && by.AuxInt > 0
332
333 case ssaop.OpRsh64x64, ssaop.OpRsh32x64, ssaop.OpRsh8x64, ssaop.OpRsh16x64, ssaop.OpRsh32x32, ssaop.OpRsh64x32,
334 ssaop.OpSignExt32to64, ssaop.OpSignExt16to64, ssaop.OpSignExt8to64, ssaop.OpSignExt16to32, ssaop.OpSignExt8to32:
335 return isNonNegative(v.Args[0])
336
337 case ssaop.OpAnd64, ssaop.OpAnd32, ssaop.OpAnd16, ssaop.OpAnd8:
338 return isNonNegative(v.Args[0]) || isNonNegative(v.Args[1])
339
340 case ssaop.OpMod64, ssaop.OpMod32, ssaop.OpMod16, ssaop.OpMod8,
341 ssaop.OpDiv64, ssaop.OpDiv32, ssaop.OpDiv16, ssaop.OpDiv8,
342 ssaop.OpOr64, ssaop.OpOr32, ssaop.OpOr16, ssaop.OpOr8,
343 ssaop.OpXor64, ssaop.OpXor32, ssaop.OpXor16, ssaop.OpXor8:
344 return isNonNegative(v.Args[0]) && isNonNegative(v.Args[1])
345
346
347
348 }
349 return false
350 }
351
352 func isStackPtr(v *ssa.Value) bool {
353 for v.Op == ssaop.OpOffPtr || v.Op == ssaop.OpAddPtr {
354 v = v.Args[0]
355 }
356 return v.Op == ssaop.OpSP || v.Op == ssaop.OpLocalAddr
357 }
358
359
360 func needRaceCleanup(sym *ssa.AuxCall, v *ssa.Value) bool {
361 f := v.Block.Func
362 if !f.Config.Race {
363 return false
364 }
365 if !ssa.IsSameCall(sym, "runtime.racefuncenter") && !ssa.IsSameCall(sym, "runtime.racefuncexit") {
366 return false
367 }
368 for _, b := range f.Blocks {
369 for _, v := range b.Values {
370 switch v.Op {
371 case ssaop.OpStaticCall, ssaop.OpStaticLECall:
372
373
374 s := v.Aux.(*ssa.AuxCall).Fn.String()
375 switch s {
376 case "runtime.racefuncenter", "runtime.racefuncexit",
377 "runtime.panicdivide", "runtime.panicwrap",
378 "runtime.panicshift":
379 continue
380 }
381
382
383 return false
384 case ssaop.OpPanicBounds, ssaop.OpPanicExtend:
385
386 case ssaop.OpClosureCall, ssaop.OpInterCall, ssaop.OpClosureLECall, ssaop.OpInterLECall:
387
388 return false
389 }
390 }
391 }
392 if ssa.IsSameCall(sym, "runtime.racefuncenter") {
393
394
395 if v.Args[0].Op != ssaop.OpStore {
396 if v.Op == ssaop.OpStaticLECall {
397
398 return true
399 }
400 return false
401 }
402 mem := v.Args[0].Args[2]
403 v.Args[0].Reset(ssaop.OpCopy)
404 v.Args[0].AddArg(mem)
405 }
406 return true
407 }
408
409 func nlz16(x int16) int { return bits.LeadingZeros16(uint16(x)) }
410
411 func nlz32(x int32) int { return bits.LeadingZeros32(uint32(x)) }
412
413
414 func nlz64(x int64) int { return bits.LeadingZeros64(uint64(x)) }
415
416 func nlz8(x int8) int { return bits.LeadingZeros8(uint8(x)) }
417
418 func ntz16(x int16) int { return bits.TrailingZeros16(uint16(x)) }
419
420 func ntz32(x int32) int { return bits.TrailingZeros32(uint32(x)) }
421
422 func ntz8(x int8) int { return bits.TrailingZeros8(uint8(x)) }
423
424
425 func reciprocalExact32(c float32) bool {
426 b := math.Float32bits(c)
427 man := b & (1<<23 - 1)
428 if man != 0 {
429 return false
430 }
431 exp := b >> 23 & (1<<8 - 1)
432
433
434 switch exp {
435 case 0:
436 return false
437 case 0xff:
438 return false
439 case 0xfe:
440 return false
441 default:
442 return true
443 }
444 }
445
446
447 func reciprocalExact64(c float64) bool {
448 b := math.Float64bits(c)
449 man := b & (1<<52 - 1)
450 if man != 0 {
451 return false
452 }
453 exp := b >> 52 & (1<<11 - 1)
454
455
456 switch exp {
457 case 0:
458 return false
459 case 0x7ff:
460 return false
461 case 0x7fe:
462 return false
463 default:
464 return true
465 }
466 }
467
468
469
470
471 func registerizable(b *ssa.Block, typ *types.Type) bool {
472 if typ.IsPtrShaped() || typ.IsFloat() || typ.IsBoolean() {
473 return true
474 }
475 if typ.IsInteger() {
476 return typ.Size() <= b.Func.Config.RegSize
477 }
478 return false
479 }
480
481
482
483 func resetCopy(v *ssa.Value, arg *ssa.Value) bool {
484 v.Reset(ssaop.OpCopy)
485 v.AddArg(arg)
486 return true
487 }
488
489
490
491 func rewriteCondSelectIntoMath(config *ssa.Config, op ssaop.Op, constant int64) bool {
492
493
494
495
496 switch config.Arch {
497 case "arm64":
498 switch op {
499 case ssaop.OpAdd64, ssaop.OpAdd32, ssaop.OpAdd16, ssaop.OpAdd8:
500 if constant == 1 {
501 return false
502 }
503 fallthrough
504 default:
505
506
507 return ssa.IsPowerOfTwo(uint64(constant))
508 }
509 default:
510 return ssa.IsPowerOfTwo(uint64(constant))
511 }
512 }
513
514
515 func rewriteFixedLoad(v *ssa.Value, sym ssa.Sym, sb *ssa.Value, off int64) *ssa.Value {
516 b := v.Block
517 f := b.Func
518
519 lsym := sym.(*obj.LSym)
520 if (v.Type.IsPtrShaped() || v.Type.IsUintptr()) && lsym.Type == objabi.SRODATA {
521 for _, r := range lsym.R {
522 if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 {
523 if r.Sym.IsGoType() {
524
525
526
527
528
529 reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.Fe.Func().Linksym())
530 } else if strings.HasPrefix(r.Sym.Name, "go:itab") {
531
532
533 reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.Fe.Func().Linksym())
534 }
535 v.Reset(ssaop.OpAddr)
536 v.Aux = ssa.SymToAux(r.Sym)
537 v.AddArg(sb)
538 return v
539 }
540 }
541 base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
542 }
543
544 if ti := lsym.TypeInfo(); ti != nil {
545
546
547
548
549 t := ti.Type.(*types.Type)
550
551 ptrSizedOpConst := ssaop.OpConst64
552 if f.Config.PtrSize == 4 {
553 ptrSizedOpConst = ssaop.OpConst32
554 }
555
556 for _, f := range rttype.Type.Fields() {
557 if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
558 switch f.Sym.Name {
559 case "Size_":
560 v.Reset(ptrSizedOpConst)
561 v.AuxInt = t.Size()
562 return v
563 case "PtrBytes":
564 v.Reset(ptrSizedOpConst)
565 v.AuxInt = types.PtrDataSize(t)
566 return v
567 case "Hash":
568 v.Reset(ssaop.OpConst32)
569 v.AuxInt = int64(int32(types.TypeHash(t)))
570 return v
571 case "TFlag":
572 v.Reset(ssaop.OpConst8)
573 v.AuxInt = int64(t.TFlag())
574 return v
575 case "Kind_":
576 v.Reset(ssaop.OpConst8)
577 v.AuxInt = int64(int8(reflectdata.ABIKindOfType(t)))
578 return v
579 case "GCData":
580 gcdata, _ := reflectdata.GCSym(t, true)
581 v.Reset(ssaop.OpAddr)
582 v.Aux = ssa.SymToAux(gcdata)
583 v.AddArg(sb)
584 return v
585 default:
586 base.Fatalf("unknown field %s for fixedLoad of %s at offset %d", f.Sym.Name, lsym.Name, off)
587 }
588 }
589 }
590
591 if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") {
592 elemSym := reflectdata.TypeLinksym(t.Elem())
593 reflectdata.MarkTypeSymUsedInInterface(elemSym, f.Fe.Func().Linksym())
594 v.Reset(ssaop.OpAddr)
595 v.Aux = ssa.SymToAux(elemSym)
596 v.AddArg(sb)
597 return v
598 }
599
600 base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
601 }
602
603 base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
604 return nil
605 }
606
607 func rewriteStructLoad(v *ssa.Value) *ssa.Value {
608 b := v.Block
609 ptr := v.Args[0]
610 mem := v.Args[1]
611
612 t := v.Type
613 args := make([]*ssa.Value, t.NumFields())
614 for i := range args {
615 ft := t.FieldType(i)
616 addr := b.NewValue1I(v.Pos, ssaop.OpOffPtr, ft.PtrTo(), t.FieldOff(i), ptr)
617 args[i] = b.NewValue2(v.Pos, ssaop.OpLoad, ft, addr, mem)
618 }
619
620 v.Reset(ssaop.OpStructMake)
621 v.AddArgs(args...)
622 return v
623 }
624
625
626 func symIsROZero(sym ssa.Sym) bool {
627 lsym := sym.(*obj.LSym)
628 if lsym.Type != objabi.SRODATA || len(lsym.R) != 0 {
629 return false
630 }
631 for _, b := range lsym.P {
632 if b != 0 {
633 return false
634 }
635 }
636 return true
637 }
638
639
640 func uaddOvf(a, b int64) bool {
641 return uint64(a)+uint64(b) < uint64(a)
642 }
643
644
645
646 func warnRule(cond bool, v *ssa.Value, s string) bool {
647 if pos := v.Pos; pos.Line() > 1 && cond {
648 v.Block.Func.Warnl(pos, s)
649 }
650 return true
651 }
652
653 func bitsSub64(x, y, borrow int64) (r struct{ diff, borrow int64 }) {
654 d, b := bits.Sub64(uint64(x), uint64(y), uint64(borrow))
655 r.diff, r.borrow = int64(d), int64(b)
656 return
657 }
658
659 func modularMultiplicativeInverse(x uint64) (y uint64) {
660 if x%2 != 1 {
661 panic("even numbers in a power-of-two modulus do not have a multiplicative inverse")
662 }
663
664 y = x
665
666
667 y *= 2 - x*y
668 y *= 2 - x*y
669 y *= 2 - x*y
670 y *= 2 - x*y
671 y *= 2 - x*y
672 return
673 }
674
View as plain text