1
2
3
4
5 package rewritegeneric
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", "TFlag":
256 return true
257 default:
258
259 return false
260 }
261 }
262 }
263
264 if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") {
265 return true
266 }
267
268 return false
269 }
270
271 return false
272 }
273
274 func isInlinableMemclr(c *ssa.Config, sz int64) bool {
275 if sz < 0 {
276 return false
277 }
278
279
280 switch c.Arch {
281 case "amd64", "arm64":
282 return true
283 case "ppc64le", "ppc64", "loong64":
284 return sz < 512
285 }
286 return false
287 }
288
289 func isMalloc(aux ssa.Aux) bool {
290 return ssa.IsNewObjectCall(aux) || ssa.IsSpecializedMalloc(aux)
291 }
292
293
294
295
296 func isNonNegative(v *ssa.Value) bool {
297 if !v.Type.IsInteger() {
298 v.Fatalf("isNonNegative bad type: %v", v.Type)
299 }
300
301
302
303
304 switch v.Op {
305 case ssaop.OpConst64:
306 return v.AuxInt >= 0
307
308 case ssaop.OpConst32:
309 return int32(v.AuxInt) >= 0
310
311 case ssaop.OpConst16:
312 return int16(v.AuxInt) >= 0
313
314 case ssaop.OpConst8:
315 return int8(v.AuxInt) >= 0
316
317 case ssaop.OpStringLen, ssaop.OpSliceLen, ssaop.OpSliceCap,
318 ssaop.OpZeroExt8to64, ssaop.OpZeroExt16to64, ssaop.OpZeroExt32to64,
319 ssaop.OpZeroExt8to32, ssaop.OpZeroExt16to32, ssaop.OpZeroExt8to16,
320 ssaop.OpCtz64, ssaop.OpCtz32, ssaop.OpCtz16, ssaop.OpCtz8,
321 ssaop.OpCtz64NonZero, ssaop.OpCtz32NonZero, ssaop.OpCtz16NonZero, ssaop.OpCtz8NonZero,
322 ssaop.OpBitLen64, ssaop.OpBitLen32, ssaop.OpBitLen16, ssaop.OpBitLen8:
323 return true
324
325 case ssaop.OpRsh64Ux64, ssaop.OpRsh32Ux64:
326 by := v.Args[1]
327 return by.Op == ssaop.OpConst64 && by.AuxInt > 0
328
329 case ssaop.OpRsh64x64, ssaop.OpRsh32x64, ssaop.OpRsh8x64, ssaop.OpRsh16x64, ssaop.OpRsh32x32, ssaop.OpRsh64x32,
330 ssaop.OpSignExt32to64, ssaop.OpSignExt16to64, ssaop.OpSignExt8to64, ssaop.OpSignExt16to32, ssaop.OpSignExt8to32:
331 return isNonNegative(v.Args[0])
332
333 case ssaop.OpAnd64, ssaop.OpAnd32, ssaop.OpAnd16, ssaop.OpAnd8:
334 return isNonNegative(v.Args[0]) || isNonNegative(v.Args[1])
335
336 case ssaop.OpMod64, ssaop.OpMod32, ssaop.OpMod16, ssaop.OpMod8,
337 ssaop.OpDiv64, ssaop.OpDiv32, ssaop.OpDiv16, ssaop.OpDiv8,
338 ssaop.OpOr64, ssaop.OpOr32, ssaop.OpOr16, ssaop.OpOr8,
339 ssaop.OpXor64, ssaop.OpXor32, ssaop.OpXor16, ssaop.OpXor8:
340 return isNonNegative(v.Args[0]) && isNonNegative(v.Args[1])
341
342
343
344 }
345 return false
346 }
347
348 func isStackPtr(v *ssa.Value) bool {
349 for v.Op == ssaop.OpOffPtr || v.Op == ssaop.OpAddPtr {
350 v = v.Args[0]
351 }
352 return v.Op == ssaop.OpSP || v.Op == ssaop.OpLocalAddr
353 }
354
355
356 func needRaceCleanup(sym *ssa.AuxCall, v *ssa.Value) bool {
357 f := v.Block.Func
358 if !f.Config.Race {
359 return false
360 }
361 if !ssa.IsSameCall(sym, "runtime.racefuncenter") && !ssa.IsSameCall(sym, "runtime.racefuncexit") {
362 return false
363 }
364 for _, b := range f.Blocks {
365 for _, v := range b.Values {
366 switch v.Op {
367 case ssaop.OpStaticCall, ssaop.OpStaticLECall:
368
369
370 s := v.Aux.(*ssa.AuxCall).Fn.String()
371 switch s {
372 case "runtime.racefuncenter", "runtime.racefuncexit",
373 "runtime.panicdivide", "runtime.panicwrap",
374 "runtime.panicshift":
375 continue
376 }
377
378
379 return false
380 case ssaop.OpPanicBounds, ssaop.OpPanicExtend:
381
382 case ssaop.OpClosureCall, ssaop.OpInterCall, ssaop.OpClosureLECall, ssaop.OpInterLECall:
383
384 return false
385 }
386 }
387 }
388 if ssa.IsSameCall(sym, "runtime.racefuncenter") {
389
390
391 if v.Args[0].Op != ssaop.OpStore {
392 if v.Op == ssaop.OpStaticLECall {
393
394 return true
395 }
396 return false
397 }
398 mem := v.Args[0].Args[2]
399 v.Args[0].Reset(ssaop.OpCopy)
400 v.Args[0].AddArg(mem)
401 }
402 return true
403 }
404
405 func nlz16(x int16) int { return bits.LeadingZeros16(uint16(x)) }
406
407 func nlz32(x int32) int { return bits.LeadingZeros32(uint32(x)) }
408
409
410 func nlz64(x int64) int { return bits.LeadingZeros64(uint64(x)) }
411
412 func nlz8(x int8) int { return bits.LeadingZeros8(uint8(x)) }
413
414 func ntz16(x int16) int { return bits.TrailingZeros16(uint16(x)) }
415
416 func ntz32(x int32) int { return bits.TrailingZeros32(uint32(x)) }
417
418 func ntz8(x int8) int { return bits.TrailingZeros8(uint8(x)) }
419
420
421 func reciprocalExact32(c float32) bool {
422 b := math.Float32bits(c)
423 man := b & (1<<23 - 1)
424 if man != 0 {
425 return false
426 }
427 exp := b >> 23 & (1<<8 - 1)
428
429
430 switch exp {
431 case 0:
432 return false
433 case 0xff:
434 return false
435 case 0xfe:
436 return false
437 default:
438 return true
439 }
440 }
441
442
443 func reciprocalExact64(c float64) bool {
444 b := math.Float64bits(c)
445 man := b & (1<<52 - 1)
446 if man != 0 {
447 return false
448 }
449 exp := b >> 52 & (1<<11 - 1)
450
451
452 switch exp {
453 case 0:
454 return false
455 case 0x7ff:
456 return false
457 case 0x7fe:
458 return false
459 default:
460 return true
461 }
462 }
463
464
465
466
467 func registerizable(b *ssa.Block, typ *types.Type) bool {
468 if typ.IsPtrShaped() || typ.IsFloat() || typ.IsBoolean() {
469 return true
470 }
471 if typ.IsInteger() {
472 return typ.Size() <= b.Func.Config.RegSize
473 }
474 return false
475 }
476
477
478
479 func resetCopy(v *ssa.Value, arg *ssa.Value) bool {
480 v.Reset(ssaop.OpCopy)
481 v.AddArg(arg)
482 return true
483 }
484
485
486
487 func rewriteCondSelectIntoMath(config *ssa.Config, op ssaop.Op, constant int64) bool {
488
489
490
491
492 switch config.Arch {
493 case "arm64":
494 switch op {
495 case ssaop.OpAdd64, ssaop.OpAdd32, ssaop.OpAdd16, ssaop.OpAdd8:
496 if constant == 1 {
497 return false
498 }
499 fallthrough
500 default:
501
502
503 return ssa.IsPowerOfTwo(uint64(constant))
504 }
505 default:
506 return ssa.IsPowerOfTwo(uint64(constant))
507 }
508 }
509
510
511 func rewriteFixedLoad(v *ssa.Value, sym ssa.Sym, sb *ssa.Value, off int64) *ssa.Value {
512 b := v.Block
513 f := b.Func
514
515 lsym := sym.(*obj.LSym)
516 if (v.Type.IsPtrShaped() || v.Type.IsUintptr()) && lsym.Type == objabi.SRODATA {
517 for _, r := range lsym.R {
518 if (r.Type == objabi.R_ADDR || r.Type == objabi.R_WEAKADDR) && int64(r.Off) == off && r.Add == 0 {
519 if strings.HasPrefix(r.Sym.Name, "type:") {
520
521
522
523
524
525 reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.Fe.Func().Linksym())
526 } else if strings.HasPrefix(r.Sym.Name, "go:itab") {
527
528
529 reflectdata.MarkTypeSymUsedInInterface(r.Sym, f.Fe.Func().Linksym())
530 }
531 v.Reset(ssaop.OpAddr)
532 v.Aux = ssa.SymToAux(r.Sym)
533 v.AddArg(sb)
534 return v
535 }
536 }
537 base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
538 }
539
540 if ti := lsym.TypeInfo(); ti != nil {
541
542
543
544
545 t := ti.Type.(*types.Type)
546
547 ptrSizedOpConst := ssaop.OpConst64
548 if f.Config.PtrSize == 4 {
549 ptrSizedOpConst = ssaop.OpConst32
550 }
551
552 for _, f := range rttype.Type.Fields() {
553 if f.Offset == off && copyCompatibleType(v.Type, f.Type) {
554 switch f.Sym.Name {
555 case "Size_":
556 v.Reset(ptrSizedOpConst)
557 v.AuxInt = t.Size()
558 return v
559 case "PtrBytes":
560 v.Reset(ptrSizedOpConst)
561 v.AuxInt = types.PtrDataSize(t)
562 return v
563 case "Hash":
564 v.Reset(ssaop.OpConst32)
565 v.AuxInt = int64(int32(types.TypeHash(t)))
566 return v
567 case "TFlag":
568 v.Reset(ssaop.OpConst8)
569 v.AuxInt = int64(t.TFlag())
570 return v
571 case "Kind_":
572 v.Reset(ssaop.OpConst8)
573 v.AuxInt = int64(int8(reflectdata.ABIKindOfType(t)))
574 return v
575 case "GCData":
576 gcdata, _ := reflectdata.GCSym(t, true)
577 v.Reset(ssaop.OpAddr)
578 v.Aux = ssa.SymToAux(gcdata)
579 v.AddArg(sb)
580 return v
581 default:
582 base.Fatalf("unknown field %s for fixedLoad of %s at offset %d", f.Sym.Name, lsym.Name, off)
583 }
584 }
585 }
586
587 if t.IsPtr() && off == rttype.PtrType.OffsetOf("Elem") {
588 elemSym := reflectdata.TypeLinksym(t.Elem())
589 reflectdata.MarkTypeSymUsedInInterface(elemSym, f.Fe.Func().Linksym())
590 v.Reset(ssaop.OpAddr)
591 v.Aux = ssa.SymToAux(elemSym)
592 v.AddArg(sb)
593 return v
594 }
595
596 base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
597 }
598
599 base.Fatalf("fixedLoad data not known for %s:%d", sym, off)
600 return nil
601 }
602
603 func rewriteStructLoad(v *ssa.Value) *ssa.Value {
604 b := v.Block
605 ptr := v.Args[0]
606 mem := v.Args[1]
607
608 t := v.Type
609 args := make([]*ssa.Value, t.NumFields())
610 for i := range args {
611 ft := t.FieldType(i)
612 addr := b.NewValue1I(v.Pos, ssaop.OpOffPtr, ft.PtrTo(), t.FieldOff(i), ptr)
613 args[i] = b.NewValue2(v.Pos, ssaop.OpLoad, ft, addr, mem)
614 }
615
616 v.Reset(ssaop.OpStructMake)
617 v.AddArgs(args...)
618 return v
619 }
620
621
622 func symIsROZero(sym ssa.Sym) bool {
623 lsym := sym.(*obj.LSym)
624 if lsym.Type != objabi.SRODATA || len(lsym.R) != 0 {
625 return false
626 }
627 for _, b := range lsym.P {
628 if b != 0 {
629 return false
630 }
631 }
632 return true
633 }
634
635
636 func uaddOvf(a, b int64) bool {
637 return uint64(a)+uint64(b) < uint64(a)
638 }
639
640
641
642 func warnRule(cond bool, v *ssa.Value, s string) bool {
643 if pos := v.Pos; pos.Line() > 1 && cond {
644 v.Block.Func.Warnl(pos, s)
645 }
646 return true
647 }
648
649 func bitsSub64(x, y, borrow int64) (r struct{ diff, borrow int64 }) {
650 d, b := bits.Sub64(uint64(x), uint64(y), uint64(borrow))
651 r.diff, r.borrow = int64(d), int64(b)
652 return
653 }
654
655 func modularMultiplicativeInverse(x uint64) (y uint64) {
656 if x%2 != 1 {
657 panic("even numbers in a power-of-two modulus do not have a multiplicative inverse")
658 }
659
660 y = x
661
662
663 y *= 2 - x*y
664 y *= 2 - x*y
665 y *= 2 - x*y
666 y *= 2 - x*y
667 y *= 2 - x*y
668 return
669 }
670
View as plain text