1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 package x86
32
33 import (
34 "cmd/internal/obj"
35 "cmd/internal/objabi"
36 "cmd/internal/src"
37 "cmd/internal/sys"
38 "internal/abi"
39 "log"
40 "math"
41 "path"
42 "strings"
43 )
44
45 func CanUse1InsnTLS(ctxt *obj.Link) bool {
46 if isAndroid {
47
48 return false
49 }
50
51 if ctxt.Arch.Family == sys.I386 {
52 switch ctxt.Headtype {
53 case objabi.Hlinux,
54 objabi.Hplan9,
55 objabi.Hwindows:
56 return false
57 }
58
59 return true
60 }
61
62 switch ctxt.Headtype {
63 case objabi.Hplan9, objabi.Hwindows:
64 return false
65 case objabi.Hlinux, objabi.Hfreebsd:
66 return !ctxt.Flag_shared
67 }
68
69 return true
70 }
71
72 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 if CanUse1InsnTLS(ctxt) {
114
115
116
117
118
119
120
121
122
123
124
125 if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 && ctxt.Headtype != objabi.Hsolaris {
126 obj.Nopout(p)
127 }
128 if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 {
129 p.From.Reg = REG_TLS
130 p.From.Scale = 0
131 p.From.Index = REG_NONE
132 }
133
134 if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
135 p.To.Reg = REG_TLS
136 p.To.Scale = 0
137 p.To.Index = REG_NONE
138 }
139 } else {
140
141
142
143
144
145
146 if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
147 q := obj.Appendp(p, newprog)
148 q.As = p.As
149 q.From = p.From
150 q.From.Type = obj.TYPE_MEM
151 q.From.Reg = p.To.Reg
152 q.From.Index = REG_TLS
153 q.From.Scale = 2
154 q.To = p.To
155 p.From.Type = obj.TYPE_REG
156 p.From.Reg = REG_TLS
157 p.From.Index = REG_NONE
158 p.From.Offset = 0
159 }
160 }
161
162
163
164
165
166 if (isAndroid || ctxt.Headtype == objabi.Hwindows) &&
167 (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
168 p.From.Type = obj.TYPE_MEM
169 p.From.Name = obj.NAME_EXTERN
170 p.From.Reg = REG_NONE
171 p.From.Sym = ctxt.Lookup("runtime.tls_g")
172 p.From.Index = REG_NONE
173 if ctxt.Headtype == objabi.Hwindows {
174
175
176
177
178
179
180
181 q := obj.Appendp(p, newprog)
182 q.As = p.As
183 q.From = obj.Addr{}
184 q.From.Type = obj.TYPE_MEM
185 q.From.Reg = p.To.Reg
186 if ctxt.Arch.Family == sys.AMD64 {
187 q.From.Index = REG_GS
188 } else {
189 q.From.Index = REG_FS
190 }
191 q.From.Scale = 1
192 q.From.Offset = 0
193 q.To = p.To
194 }
195 }
196
197
198 if ctxt.Headtype == objabi.Hwindows && ctxt.Arch.Family == sys.AMD64 || ctxt.Headtype == objabi.Hplan9 {
199 if p.From.Scale == 1 && p.From.Index == REG_TLS {
200 p.From.Scale = 2
201 }
202 if p.To.Scale == 1 && p.To.Index == REG_TLS {
203 p.To.Scale = 2
204 }
205 }
206
207
208
209 switch p.As {
210 case ACMPPD, ACMPPS, ACMPSD, ACMPSS:
211 if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE && p.To.Reg == REG_NONE && p.To.Index == REG_NONE && p.To.Sym == nil {
212 p.To.Type = obj.TYPE_CONST
213 }
214 }
215
216
217 switch p.As {
218 case obj.ACALL, obj.AJMP, obj.ARET:
219 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
220 p.To.Type = obj.TYPE_BRANCH
221 }
222 }
223
224
225 if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Family == sys.AMD64 || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) {
226 switch p.As {
227 case AMOVL:
228 p.As = ALEAL
229 p.From.Type = obj.TYPE_MEM
230 case AMOVQ:
231 p.As = ALEAQ
232 p.From.Type = obj.TYPE_MEM
233 }
234 }
235
236
237 switch p.As {
238
239 case AMOVSS, AVMOVSS:
240 if p.From.Type == obj.TYPE_FCONST {
241
242 if f := p.From.Val.(float64); math.Float64bits(f) == 0 {
243 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
244 p.As = AXORPS
245 p.From = p.To
246 break
247 }
248 }
249 }
250 fallthrough
251
252 case AFMOVF,
253 AFADDF,
254 AFSUBF,
255 AFSUBRF,
256 AFMULF,
257 AFDIVF,
258 AFDIVRF,
259 AFCOMF,
260 AFCOMFP,
261 AADDSS,
262 ASUBSS,
263 AMULSS,
264 ADIVSS,
265 ACOMISS,
266 AUCOMISS:
267 if p.From.Type == obj.TYPE_FCONST {
268 f32 := float32(p.From.Val.(float64))
269 p.From.Type = obj.TYPE_MEM
270 p.From.Name = obj.NAME_EXTERN
271 p.From.Sym = ctxt.Float32Sym(f32)
272 p.From.Offset = 0
273 }
274
275 case AMOVSD, AVMOVSD:
276
277 if p.From.Type == obj.TYPE_FCONST {
278
279 if f := p.From.Val.(float64); math.Float64bits(f) == 0 {
280 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
281 p.As = AXORPS
282 p.From = p.To
283 break
284 }
285 }
286 }
287 fallthrough
288
289 case AFMOVD,
290 AFADDD,
291 AFSUBD,
292 AFSUBRD,
293 AFMULD,
294 AFDIVD,
295 AFDIVRD,
296 AFCOMD,
297 AFCOMDP,
298 AADDSD,
299 ASUBSD,
300 AMULSD,
301 ADIVSD,
302 ACOMISD,
303 AUCOMISD:
304 if p.From.Type == obj.TYPE_FCONST {
305 f64 := p.From.Val.(float64)
306 p.From.Type = obj.TYPE_MEM
307 p.From.Name = obj.NAME_EXTERN
308 p.From.Sym = ctxt.Float64Sym(f64)
309 p.From.Offset = 0
310 }
311 }
312
313 if ctxt.Flag_dynlink {
314 rewriteToUseGot(ctxt, p, newprog)
315 }
316
317 if ctxt.Flag_shared && ctxt.Arch.Family == sys.I386 {
318 rewriteToPcrel(ctxt, p, newprog)
319 }
320 }
321
322
323 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
324 var lea, mov obj.As
325 var reg int16
326 if ctxt.Arch.Family == sys.AMD64 {
327 lea = ALEAQ
328 mov = AMOVQ
329 reg = REG_R15
330 } else {
331 lea = ALEAL
332 mov = AMOVL
333 reg = REG_CX
334 if p.As == ALEAL && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index {
335
336
337
338
339 reg = p.To.Reg
340 }
341 }
342
343 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
344
345
346
347
348
349
350
351 var sym *obj.LSym
352 if p.As == obj.ADUFFZERO {
353 sym = ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
354 } else {
355 sym = ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
356 }
357 offset := p.To.Offset
358 p.As = mov
359 p.From.Type = obj.TYPE_MEM
360 p.From.Name = obj.NAME_GOTREF
361 p.From.Sym = sym
362 p.To.Type = obj.TYPE_REG
363 p.To.Reg = reg
364 p.To.Offset = 0
365 p.To.Sym = nil
366 p1 := obj.Appendp(p, newprog)
367 p1.As = lea
368 p1.From.Type = obj.TYPE_MEM
369 p1.From.Offset = offset
370 p1.From.Reg = reg
371 p1.To.Type = obj.TYPE_REG
372 p1.To.Reg = reg
373 p2 := obj.Appendp(p1, newprog)
374 p2.As = obj.ACALL
375 p2.To.Type = obj.TYPE_REG
376 p2.To.Reg = reg
377 }
378
379
380
381
382 if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
383
384 p.As = mov
385 p.From.Type = obj.TYPE_ADDR
386 }
387 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
388
389
390
391 cmplxdest := false
392 pAs := p.As
393 var dest obj.Addr
394 if p.To.Type != obj.TYPE_REG || pAs != mov {
395 if ctxt.Arch.Family == sys.AMD64 {
396 ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
397 }
398 cmplxdest = true
399 dest = p.To
400 p.As = mov
401 p.To.Type = obj.TYPE_REG
402 p.To.Reg = reg
403 p.To.Sym = nil
404 p.To.Name = obj.NAME_NONE
405 }
406 p.From.Type = obj.TYPE_MEM
407 p.From.Name = obj.NAME_GOTREF
408 q := p
409 if p.From.Offset != 0 {
410 q = obj.Appendp(p, newprog)
411 q.As = lea
412 q.From.Type = obj.TYPE_MEM
413 q.From.Reg = p.To.Reg
414 q.From.Offset = p.From.Offset
415 q.To = p.To
416 p.From.Offset = 0
417 }
418 if cmplxdest {
419 q = obj.Appendp(q, newprog)
420 q.As = pAs
421 q.To = dest
422 q.From.Type = obj.TYPE_REG
423 q.From.Reg = reg
424 }
425 }
426 from3 := p.GetFrom3()
427 for i := range p.RestArgs {
428 a := &p.RestArgs[i].Addr
429 if a != from3 && a.Name == obj.NAME_EXTERN && !a.Sym.Local() {
430 ctxt.Diag("don't know how to handle %v with -dynlink", p)
431 }
432 }
433 var source *obj.Addr
434
435
436
437 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
438 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
439 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
440 }
441 if from3 != nil && from3.Name == obj.NAME_EXTERN && !from3.Sym.Local() {
442 ctxt.Diag("cannot handle NAME_EXTERN on multiple operands in %v with -dynlink", p)
443 }
444 source = &p.From
445 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
446 if from3 != nil && from3.Name == obj.NAME_EXTERN && !from3.Sym.Local() {
447 ctxt.Diag("cannot handle NAME_EXTERN on multiple operands in %v with -dynlink", p)
448 }
449 source = &p.To
450 } else if from3 != nil && from3.Name == obj.NAME_EXTERN && !from3.Sym.Local() {
451 source = from3
452 } else {
453 return
454 }
455 if p.As == obj.ACALL {
456
457
458
459
460
461
462
463
464
465
466 if ctxt.Arch.Family == sys.AMD64 || (p.To.Sym != nil && p.To.Sym.Local()) || p.RegTo2 != 0 {
467 return
468 }
469 p1 := obj.Appendp(p, newprog)
470 p2 := obj.Appendp(p1, newprog)
471
472 p1.As = ALEAL
473 p1.From.Type = obj.TYPE_MEM
474 p1.From.Name = obj.NAME_STATIC
475 p1.From.Sym = ctxt.Lookup("_GLOBAL_OFFSET_TABLE_")
476 p1.To.Type = obj.TYPE_REG
477 p1.To.Reg = REG_BX
478
479 p2.As = p.As
480 p2.Scond = p.Scond
481 p2.From = p.From
482 if p.RestArgs != nil {
483 p2.RestArgs = append(p2.RestArgs, p.RestArgs...)
484 }
485 p2.Reg = p.Reg
486 p2.To = p.To
487
488
489
490 p2.To.Type = obj.TYPE_MEM
491 p2.RegTo2 = 1
492
493 obj.Nopout(p)
494 return
495
496 }
497 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP {
498 return
499 }
500 if source.Type != obj.TYPE_MEM {
501 ctxt.Diag("don't know how to handle %v with -dynlink", p)
502 }
503 p1 := obj.Appendp(p, newprog)
504 p2 := obj.Appendp(p1, newprog)
505
506 p1.As = mov
507 p1.From.Type = obj.TYPE_MEM
508 p1.From.Sym = source.Sym
509 p1.From.Name = obj.NAME_GOTREF
510 p1.To.Type = obj.TYPE_REG
511 p1.To.Reg = reg
512
513 p2.As = p.As
514 p2.From = p.From
515 p2.To = p.To
516 p2.RestArgs = p.RestArgs
517 if p.From.Name == obj.NAME_EXTERN {
518 p2.From.Reg = reg
519 p2.From.Name = obj.NAME_NONE
520 p2.From.Sym = nil
521 } else if p.To.Name == obj.NAME_EXTERN {
522 p2.To.Reg = reg
523 p2.To.Name = obj.NAME_NONE
524 p2.To.Sym = nil
525 } else if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
526 from3 = p2.GetFrom3()
527 from3.Reg = reg
528 from3.Name = obj.NAME_NONE
529 from3.Sym = nil
530 } else {
531 return
532 }
533 obj.Nopout(p)
534 }
535
536 func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
537
538
539 if p.RegTo2 != 0 {
540 return
541 }
542 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
543 return
544 }
545
546
547
548 isName := func(a *obj.Addr) bool {
549 if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 {
550 return false
551 }
552 if a.Sym.Type == objabi.STLSBSS {
553 return false
554 }
555 return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF
556 }
557
558 if isName(&p.From) && p.From.Type == obj.TYPE_ADDR {
559
560
561
562 if p.To.Type != obj.TYPE_REG {
563 q := obj.Appendp(p, newprog)
564 q.As = p.As
565 q.From.Type = obj.TYPE_REG
566 q.From.Reg = REG_CX
567 q.To = p.To
568 p.As = AMOVL
569 p.To.Type = obj.TYPE_REG
570 p.To.Reg = REG_CX
571 p.To.Sym = nil
572 p.To.Name = obj.NAME_NONE
573 }
574 }
575
576 if !isName(&p.From) && !isName(&p.To) && (p.GetFrom3() == nil || !isName(p.GetFrom3())) {
577 return
578 }
579 var dst int16 = REG_CX
580 if (p.As == ALEAL || p.As == AMOVL) && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index {
581 dst = p.To.Reg
582
583
584 }
585 q := obj.Appendp(p, newprog)
586 q.RegTo2 = 1
587 r := obj.Appendp(q, newprog)
588 r.RegTo2 = 1
589 q.As = obj.ACALL
590 thunkname := "__x86.get_pc_thunk." + strings.ToLower(rconv(int(dst)))
591 q.To.Sym = ctxt.LookupInit(thunkname, func(s *obj.LSym) { s.Set(obj.AttrLocal, true) })
592 q.To.Type = obj.TYPE_MEM
593 q.To.Name = obj.NAME_EXTERN
594 r.As = p.As
595 r.Scond = p.Scond
596 r.From = p.From
597 r.RestArgs = p.RestArgs
598 r.Reg = p.Reg
599 r.To = p.To
600 if isName(&p.From) {
601 r.From.Reg = dst
602 }
603 if isName(&p.To) {
604 r.To.Reg = dst
605 }
606 if p.GetFrom3() != nil && isName(p.GetFrom3()) {
607 r.GetFrom3().Reg = dst
608 }
609 obj.Nopout(p)
610 }
611
612
613 const (
614 markBit = 1 << 0
615 )
616
617 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
618 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
619 return
620 }
621
622 p := cursym.Func().Text
623 autoffset := int32(p.To.Offset)
624 if autoffset < 0 {
625 autoffset = 0
626 }
627
628 var hasCall, mightCallABI0 bool
629 for q := p; q != nil && !(hasCall && mightCallABI0); q = q.Link {
630 switch q.As {
631 case obj.ACALL:
632 hasCall = true
633 if q.To.Sym != nil {
634 if q.To.Sym.ABI() == obj.ABI0 {
635 mightCallABI0 = true
636 }
637 } else {
638 if ctxt.IsAsm {
639
640 mightCallABI0 = true
641 } else {
642
643
644 }
645 }
646 case obj.ADUFFCOPY, obj.ADUFFZERO:
647 hasCall = true
648 }
649 }
650
651 var bpsize int
652 if ctxt.Arch.Family == sys.AMD64 &&
653 !p.From.Sym.NoFrame() &&
654 !(autoffset == 0 && !hasCall) {
655
656
657
658
659
660 bpsize = ctxt.Arch.PtrSize
661 autoffset += int32(bpsize)
662 p.To.Offset += int64(bpsize)
663 } else {
664 bpsize = 0
665 p.From.Sym.Set(obj.AttrNoFrame, true)
666 }
667
668 textarg := int64(p.To.Val.(int32))
669 cursym.Func().Args = int32(textarg)
670 cursym.Func().Locals = int32(p.To.Offset)
671
672
673 if ctxt.Arch.Family == sys.I386 && cursym.Func().Locals < 0 {
674 cursym.Func().Locals = 0
675 }
676
677
678 if ctxt.Arch.Family == sys.AMD64 && autoffset < abi.StackSmall && !p.From.Sym.NoSplit() {
679 leaf := true
680 LeafSearch:
681 for q := p; q != nil; q = q.Link {
682 switch q.As {
683 case obj.ACALL:
684
685
686
687
688
689
690
691 if !isZeroArgRuntimeCall(q.To.Sym) {
692 leaf = false
693 break LeafSearch
694 }
695 fallthrough
696 case obj.ADUFFCOPY, obj.ADUFFZERO:
697 if autoffset >= abi.StackSmall-8 {
698 leaf = false
699 break LeafSearch
700 }
701 }
702 }
703
704 if leaf {
705 p.From.Sym.Set(obj.AttrNoSplit, true)
706 }
707 }
708
709 if !p.From.Sym.NoSplit() {
710
711 p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg))
712 }
713
714 if bpsize > 0 {
715
716 p = obj.Appendp(p, newprog)
717
718 p.As = APUSHQ
719 p.From.Type = obj.TYPE_REG
720 p.From.Reg = REG_BP
721
722
723 p = obj.Appendp(p, newprog)
724
725 p.As = AMOVQ
726 p.From.Type = obj.TYPE_REG
727 p.From.Reg = REG_SP
728 p.To.Type = obj.TYPE_REG
729 p.To.Reg = REG_BP
730 }
731
732 if autoffset%int32(ctxt.Arch.RegSize) != 0 {
733 ctxt.Diag("unaligned stack size %d", autoffset)
734 }
735
736
737
738 localoffset := autoffset - int32(bpsize)
739 if localoffset != 0 {
740 p = obj.Appendp(p, newprog)
741 p.As = AADJSP
742 p.From.Type = obj.TYPE_CONST
743 p.From.Offset = int64(localoffset)
744 p.Spadj = localoffset
745 }
746
747
748
749 if autoffset != 0 {
750 p.Pos = p.Pos.WithXlogue(src.PosPrologueEnd)
751 }
752
753 var deltasp int32
754 for p = cursym.Func().Text; p != nil; p = p.Link {
755 pcsize := ctxt.Arch.RegSize
756 switch p.From.Name {
757 case obj.NAME_AUTO:
758 p.From.Offset += int64(deltasp) - int64(bpsize)
759 case obj.NAME_PARAM:
760 p.From.Offset += int64(deltasp) + int64(pcsize)
761 }
762 if p.GetFrom3() != nil {
763 switch p.GetFrom3().Name {
764 case obj.NAME_AUTO:
765 p.GetFrom3().Offset += int64(deltasp) - int64(bpsize)
766 case obj.NAME_PARAM:
767 p.GetFrom3().Offset += int64(deltasp) + int64(pcsize)
768 }
769 }
770 switch p.To.Name {
771 case obj.NAME_AUTO:
772 p.To.Offset += int64(deltasp) - int64(bpsize)
773 case obj.NAME_PARAM:
774 p.To.Offset += int64(deltasp) + int64(pcsize)
775 }
776
777 switch p.As {
778 default:
779 if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_SP && p.As != ACMPL && p.As != ACMPQ {
780 f := cursym.Func()
781 if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
782 f.FuncFlag |= abi.FuncFlagSPWrite
783 if ctxt.Debugvlog || !ctxt.IsAsm {
784 ctxt.Logf("auto-SPWRITE: %s %v\n", cursym.Name, p)
785 if !ctxt.IsAsm {
786 ctxt.Diag("invalid auto-SPWRITE in non-assembly")
787 ctxt.DiagFlush()
788 log.Fatalf("bad SPWRITE")
789 }
790 }
791 }
792 }
793 continue
794
795 case APUSHL, APUSHFL:
796 deltasp += 4
797 p.Spadj = 4
798 continue
799
800 case APUSHQ, APUSHFQ:
801 deltasp += 8
802 p.Spadj = 8
803 continue
804
805 case APUSHW, APUSHFW:
806 deltasp += 2
807 p.Spadj = 2
808 continue
809
810 case APOPL, APOPFL:
811 deltasp -= 4
812 p.Spadj = -4
813 continue
814
815 case APOPQ, APOPFQ:
816 deltasp -= 8
817 p.Spadj = -8
818 continue
819
820 case APOPW, APOPFW:
821 deltasp -= 2
822 p.Spadj = -2
823 continue
824
825 case AADJSP:
826 p.Spadj = int32(p.From.Offset)
827 deltasp += int32(p.From.Offset)
828 continue
829
830 case obj.ARET:
831
832 }
833
834 if autoffset != deltasp {
835 ctxt.Diag("%s: unbalanced PUSH/POP", cursym)
836 }
837
838 if autoffset != 0 {
839 to := p.To
840 p.To = obj.Addr{}
841
842 needSpRestore, needBpRestore := localoffset != 0, bpsize > 0
843
844
845
846
847
848 asmSafe := !ctxt.IsAsm || cursym.ABI() == obj.ABIInternal
849 if asmSafe && !mightCallABI0 && needSpRestore && needBpRestore {
850 p.As = ALEAVEQ
851 p.Spadj = -localoffset - int32(bpsize)
852 p = obj.Appendp(p, newprog)
853 } else {
854 if needSpRestore {
855 p.As = AADJSP
856 p.From.Type = obj.TYPE_CONST
857 p.From.Offset = int64(-localoffset)
858 p.Spadj = -localoffset
859 p = obj.Appendp(p, newprog)
860 }
861 if needBpRestore {
862 p.As = APOPQ
863 p.To.Type = obj.TYPE_REG
864 p.To.Reg = REG_BP
865 p.Spadj = -int32(bpsize)
866 p = obj.Appendp(p, newprog)
867 }
868 }
869
870 p.As = obj.ARET
871 p.To = to
872
873
874
875
876
877 p.Spadj = +autoffset
878 }
879
880 if p.As == obj.ARET && (p.To.Sym != nil || p.To.Type == obj.TYPE_REG) {
881 p.As = obj.AJMP
882 }
883 }
884 }
885
886 func isZeroArgRuntimeCall(s *obj.LSym) bool {
887 if s == nil {
888 return false
889 }
890 switch s.Name {
891 case "runtime.panicdivide", "runtime.panicwrap", "runtime.panicshift", "runtime.panicBounds", "runtime.panicExtend":
892 return true
893 }
894 return false
895 }
896
897
898
899
900 func loadG(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc) (*obj.Prog, int16) {
901 if ctxt.Arch.Family == sys.AMD64 && cursym.ABI() == obj.ABIInternal {
902
903 return p, REGG
904 }
905
906 var regg int16 = REG_CX
907 if ctxt.Arch.Family == sys.AMD64 {
908 regg = REGG
909 }
910
911 p = obj.Appendp(p, newprog)
912 p.As = AMOVQ
913 if ctxt.Arch.PtrSize == 4 {
914 p.As = AMOVL
915 }
916 p.From.Type = obj.TYPE_MEM
917 p.From.Reg = REG_TLS
918 p.From.Offset = 0
919 p.To.Type = obj.TYPE_REG
920 p.To.Reg = regg
921
922
923 next := p.Link
924 progedit(ctxt, p, newprog)
925 for p.Link != next {
926 p = p.Link
927 progedit(ctxt, p, newprog)
928 }
929
930 if p.From.Index == REG_TLS {
931 p.From.Scale = 2
932 }
933
934 return p, regg
935 }
936
937
938
939
940
941 func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32) *obj.Prog {
942 cmp := ACMPQ
943 lea := ALEAQ
944 mov := AMOVQ
945 sub := ASUBQ
946 push, pop := APUSHQ, APOPQ
947
948 if ctxt.Arch.Family == sys.I386 {
949 cmp = ACMPL
950 lea = ALEAL
951 mov = AMOVL
952 sub = ASUBL
953 push, pop = APUSHL, APOPL
954 }
955
956 tmp := int16(REG_AX)
957 if ctxt.Arch.Family == sys.AMD64 {
958
959 tmp = int16(REGENTRYTMP0)
960 }
961
962 if ctxt.Flag_maymorestack != "" {
963 p = cursym.Func().SpillRegisterArgs(p, newprog)
964
965 if cursym.Func().Text.From.Sym.NeedCtxt() {
966 p = obj.Appendp(p, newprog)
967 p.As = push
968 p.From.Type = obj.TYPE_REG
969 p.From.Reg = REGCTXT
970 }
971
972
973
974
975
976
977
978 p = obj.Appendp(p, newprog)
979 p.As = obj.ACALL
980 p.To.Type = obj.TYPE_BRANCH
981 p.To.Name = obj.NAME_EXTERN
982 p.To.Sym = ctxt.LookupABI(ctxt.Flag_maymorestack, cursym.ABI())
983
984 if cursym.Func().Text.From.Sym.NeedCtxt() {
985 p = obj.Appendp(p, newprog)
986 p.As = pop
987 p.To.Type = obj.TYPE_REG
988 p.To.Reg = REGCTXT
989 }
990
991 p = cursym.Func().UnspillRegisterArgs(p, newprog)
992 }
993
994
995 startPred := p
996
997
998 var rg int16
999 p, rg = loadG(ctxt, cursym, p, newprog)
1000
1001 var q1 *obj.Prog
1002 if framesize <= abi.StackSmall {
1003
1004
1005 p = obj.Appendp(p, newprog)
1006
1007 p.As = cmp
1008 p.From.Type = obj.TYPE_REG
1009 p.From.Reg = REG_SP
1010 p.To.Type = obj.TYPE_MEM
1011 p.To.Reg = rg
1012 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize)
1013 if cursym.CFunc() {
1014 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize)
1015 }
1016
1017
1018
1019
1020
1021 p = ctxt.StartUnsafePoint(p, newprog)
1022 } else if framesize <= abi.StackBig {
1023
1024
1025
1026 p = obj.Appendp(p, newprog)
1027
1028 p.As = lea
1029 p.From.Type = obj.TYPE_MEM
1030 p.From.Reg = REG_SP
1031 p.From.Offset = -(int64(framesize) - abi.StackSmall)
1032 p.To.Type = obj.TYPE_REG
1033 p.To.Reg = tmp
1034
1035 p = obj.Appendp(p, newprog)
1036 p.As = cmp
1037 p.From.Type = obj.TYPE_REG
1038 p.From.Reg = tmp
1039 p.To.Type = obj.TYPE_MEM
1040 p.To.Reg = rg
1041 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize)
1042 if cursym.CFunc() {
1043 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize)
1044 }
1045
1046 p = ctxt.StartUnsafePoint(p, newprog)
1047 } else {
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061 p = obj.Appendp(p, newprog)
1062
1063 p.As = mov
1064 p.From.Type = obj.TYPE_REG
1065 p.From.Reg = REG_SP
1066 p.To.Type = obj.TYPE_REG
1067 p.To.Reg = tmp
1068
1069 p = ctxt.StartUnsafePoint(p, newprog)
1070
1071 p = obj.Appendp(p, newprog)
1072 p.As = sub
1073 p.From.Type = obj.TYPE_CONST
1074 p.From.Offset = int64(framesize) - abi.StackSmall
1075 p.To.Type = obj.TYPE_REG
1076 p.To.Reg = tmp
1077
1078 p = obj.Appendp(p, newprog)
1079 p.As = AJCS
1080 p.To.Type = obj.TYPE_BRANCH
1081 q1 = p
1082
1083 p = obj.Appendp(p, newprog)
1084 p.As = cmp
1085 p.From.Type = obj.TYPE_REG
1086 p.From.Reg = tmp
1087 p.To.Type = obj.TYPE_MEM
1088 p.To.Reg = rg
1089 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize)
1090 if cursym.CFunc() {
1091 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize)
1092 }
1093 }
1094
1095
1096 jls := obj.Appendp(p, newprog)
1097 jls.As = AJLS
1098 jls.To.Type = obj.TYPE_BRANCH
1099
1100 end := ctxt.EndUnsafePoint(jls, newprog, -1)
1101
1102 var last *obj.Prog
1103 for last = cursym.Func().Text; last.Link != nil; last = last.Link {
1104 }
1105
1106
1107
1108
1109 spfix := obj.Appendp(last, newprog)
1110 spfix.As = obj.ANOP
1111 spfix.Spadj = -framesize
1112
1113 pcdata := ctxt.EmitEntryStackMap(cursym, spfix, newprog)
1114 spill := ctxt.StartUnsafePoint(pcdata, newprog)
1115 pcdata = cursym.Func().SpillRegisterArgs(spill, newprog)
1116
1117 call := obj.Appendp(pcdata, newprog)
1118 call.Pos = cursym.Func().Text.Pos
1119 call.As = obj.ACALL
1120 call.To.Type = obj.TYPE_BRANCH
1121 call.To.Name = obj.NAME_EXTERN
1122 morestack := "runtime.morestack"
1123 switch {
1124 case cursym.CFunc():
1125 morestack = "runtime.morestackc"
1126 case !cursym.Func().Text.From.Sym.NeedCtxt():
1127 morestack = "runtime.morestack_noctxt"
1128 }
1129 call.To.Sym = ctxt.Lookup(morestack)
1130
1131
1132
1133
1134 callend := call
1135 progedit(ctxt, callend, newprog)
1136 for ; callend.Link != nil; callend = callend.Link {
1137 progedit(ctxt, callend.Link, newprog)
1138 }
1139
1140
1141 pcdata = ctxt.EndUnsafePoint(callend, newprog, -1)
1142 unspill := cursym.Func().UnspillRegisterArgs(pcdata, newprog)
1143
1144 jmp := obj.Appendp(unspill, newprog)
1145 jmp.As = obj.AJMP
1146 jmp.To.Type = obj.TYPE_BRANCH
1147 jmp.To.SetTarget(startPred.Link)
1148 jmp.Spadj = +framesize
1149
1150 jls.To.SetTarget(spill)
1151 if q1 != nil {
1152 q1.To.SetTarget(spill)
1153 }
1154
1155 return end
1156 }
1157
1158 func isR15(r int16) bool {
1159 return r == REG_R15 || r == REG_R15B
1160 }
1161 func addrMentionsR15(a *obj.Addr) bool {
1162 if a == nil {
1163 return false
1164 }
1165 return isR15(a.Reg) || isR15(a.Index)
1166 }
1167 func progMentionsR15(p *obj.Prog) bool {
1168 return addrMentionsR15(&p.From) || addrMentionsR15(&p.To) || isR15(p.Reg) || addrMentionsR15(p.GetFrom3())
1169 }
1170
1171 func addrUsesGlobal(a *obj.Addr) bool {
1172 if a == nil {
1173 return false
1174 }
1175 return a.Name == obj.NAME_EXTERN && !a.Sym.Local()
1176 }
1177 func progUsesGlobal(p *obj.Prog) bool {
1178 if p.As == obj.ACALL || p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP {
1179
1180
1181 return false
1182 }
1183 if p.As == ALEAQ {
1184
1185 return false
1186 }
1187 return addrUsesGlobal(&p.From) || addrUsesGlobal(&p.To) || addrUsesGlobal(p.GetFrom3())
1188 }
1189
1190 type rwMask int
1191
1192 const (
1193 readFrom rwMask = 1 << iota
1194 readTo
1195 readReg
1196 readFrom3
1197 writeFrom
1198 writeTo
1199 writeReg
1200 writeFrom3
1201 )
1202
1203
1204
1205
1206
1207 func progRW(p *obj.Prog) rwMask {
1208 var m rwMask
1209
1210 if p.From.Type != obj.TYPE_NONE {
1211 m |= readFrom
1212 }
1213 if p.To.Type != obj.TYPE_NONE {
1214
1215 m |= readTo | writeTo
1216 }
1217 if p.Reg != 0 {
1218 m |= readReg
1219 }
1220 if p.GetFrom3() != nil {
1221 m |= readFrom3
1222 }
1223
1224
1225 name := p.As.String()
1226 if strings.HasPrefix(name, "MOV") || strings.HasPrefix(name, "PMOV") {
1227
1228 m &^= readTo
1229 }
1230 switch p.As {
1231 case APOPW, APOPL, APOPQ,
1232 ALEAL, ALEAQ,
1233 AIMUL3W, AIMUL3L, AIMUL3Q,
1234 APEXTRB, APEXTRW, APEXTRD, APEXTRQ, AVPEXTRB, AVPEXTRW, AVPEXTRD, AVPEXTRQ, AEXTRACTPS,
1235 ABSFW, ABSFL, ABSFQ, ABSRW, ABSRL, ABSRQ, APOPCNTW, APOPCNTL, APOPCNTQ, ALZCNTW, ALZCNTL, ALZCNTQ,
1236 ASHLXL, ASHLXQ, ASHRXL, ASHRXQ, ASARXL, ASARXQ:
1237
1238 m &^= readTo
1239 case AXORL, AXORQ:
1240
1241 if p.From.Type == obj.TYPE_REG && p.To.Type == obj.TYPE_REG && p.From.Reg == p.To.Reg {
1242 m &^= readFrom | readTo
1243 }
1244 case AMULXL, AMULXQ:
1245
1246 m &^= readTo | readFrom3
1247 m |= writeFrom3
1248 }
1249 return m
1250 }
1251
1252
1253 func progReadsR15(p *obj.Prog) bool {
1254 m := progRW(p)
1255 if m&readFrom != 0 && p.From.Type == obj.TYPE_REG && isR15(p.From.Reg) {
1256 return true
1257 }
1258 if m&readTo != 0 && p.To.Type == obj.TYPE_REG && isR15(p.To.Reg) {
1259 return true
1260 }
1261 if m&readReg != 0 && isR15(p.Reg) {
1262 return true
1263 }
1264 if m&readFrom3 != 0 && p.GetFrom3().Type == obj.TYPE_REG && isR15(p.GetFrom3().Reg) {
1265 return true
1266 }
1267
1268 if p.From.Type == obj.TYPE_MEM && (isR15(p.From.Reg) || isR15(p.From.Index)) {
1269 return true
1270 }
1271 if p.To.Type == obj.TYPE_MEM && (isR15(p.To.Reg) || isR15(p.To.Index)) {
1272 return true
1273 }
1274 if f3 := p.GetFrom3(); f3 != nil && f3.Type == obj.TYPE_MEM && (isR15(f3.Reg) || isR15(f3.Index)) {
1275 return true
1276 }
1277 return false
1278 }
1279
1280
1281 func progWritesR15(p *obj.Prog) bool {
1282 m := progRW(p)
1283 if m&writeFrom != 0 && p.From.Type == obj.TYPE_REG && isR15(p.From.Reg) {
1284 return true
1285 }
1286 if m&writeTo != 0 && p.To.Type == obj.TYPE_REG && isR15(p.To.Reg) {
1287 return true
1288 }
1289 if m&writeReg != 0 && isR15(p.Reg) {
1290 return true
1291 }
1292 if m&writeFrom3 != 0 && p.GetFrom3().Type == obj.TYPE_REG && isR15(p.GetFrom3().Reg) {
1293 return true
1294 }
1295 return false
1296 }
1297
1298 func errorCheck(ctxt *obj.Link, s *obj.LSym) {
1299
1300
1301 if !ctxt.Flag_dynlink {
1302 return
1303 }
1304
1305
1306
1307 var work []*obj.Prog
1308 var mentionsR15 bool
1309 for p := s.Func().Text; p != nil; p = p.Link {
1310 if progUsesGlobal(p) {
1311 work = append(work, p)
1312 p.Mark |= markBit
1313 }
1314 if progMentionsR15(p) {
1315 mentionsR15 = true
1316 }
1317 }
1318 if mentionsR15 {
1319 for len(work) > 0 {
1320 p := work[len(work)-1]
1321 work = work[:len(work)-1]
1322 if progReadsR15(p) {
1323 pos := ctxt.PosTable.Pos(p.Pos)
1324 ctxt.Diag("%s:%s: when dynamic linking, R15 is clobbered by a global variable access and is used here: %v", path.Base(pos.Filename()), pos.LineNumber(), p)
1325 break
1326 }
1327 if progWritesR15(p) {
1328
1329 continue
1330 }
1331 if q := p.To.Target(); q != nil && q.Mark&markBit == 0 {
1332 q.Mark |= markBit
1333 work = append(work, q)
1334 }
1335 if p.As == obj.AJMP || p.As == obj.ARET {
1336 continue
1337 }
1338 if q := p.Link; q != nil && q.Mark&markBit == 0 {
1339 q.Mark |= markBit
1340 work = append(work, q)
1341 }
1342 }
1343 }
1344
1345
1346 for p := s.Func().Text; p != nil; p = p.Link {
1347 p.Mark &^= markBit
1348 }
1349 }
1350
1351 var unaryDst = map[obj.As]bool{
1352 ABSWAPL: true,
1353 ABSWAPQ: true,
1354 ACLDEMOTE: true,
1355 ACLFLUSH: true,
1356 ACLFLUSHOPT: true,
1357 ACLWB: true,
1358 ACMPXCHG16B: true,
1359 ACMPXCHG8B: true,
1360 ADECB: true,
1361 ADECL: true,
1362 ADECQ: true,
1363 ADECW: true,
1364 AFBSTP: true,
1365 AFFREE: true,
1366 AFLDENV: true,
1367 AFSAVE: true,
1368 AFSTCW: true,
1369 AFSTENV: true,
1370 AFSTSW: true,
1371 AFXSAVE64: true,
1372 AFXSAVE: true,
1373 AINCB: true,
1374 AINCL: true,
1375 AINCQ: true,
1376 AINCW: true,
1377 ANEGB: true,
1378 ANEGL: true,
1379 ANEGQ: true,
1380 ANEGW: true,
1381 ANOTB: true,
1382 ANOTL: true,
1383 ANOTQ: true,
1384 ANOTW: true,
1385 APOPL: true,
1386 APOPQ: true,
1387 APOPW: true,
1388 ARDFSBASEL: true,
1389 ARDFSBASEQ: true,
1390 ARDGSBASEL: true,
1391 ARDGSBASEQ: true,
1392 ARDPID: true,
1393 ARDRANDL: true,
1394 ARDRANDQ: true,
1395 ARDRANDW: true,
1396 ARDSEEDL: true,
1397 ARDSEEDQ: true,
1398 ARDSEEDW: true,
1399 ASETCC: true,
1400 ASETCS: true,
1401 ASETEQ: true,
1402 ASETGE: true,
1403 ASETGT: true,
1404 ASETHI: true,
1405 ASETLE: true,
1406 ASETLS: true,
1407 ASETLT: true,
1408 ASETMI: true,
1409 ASETNE: true,
1410 ASETOC: true,
1411 ASETOS: true,
1412 ASETPC: true,
1413 ASETPL: true,
1414 ASETPS: true,
1415 ASGDT: true,
1416 ASIDT: true,
1417 ASLDTL: true,
1418 ASLDTQ: true,
1419 ASLDTW: true,
1420 ASMSWL: true,
1421 ASMSWQ: true,
1422 ASMSWW: true,
1423 ASTMXCSR: true,
1424 ASTRL: true,
1425 ASTRQ: true,
1426 ASTRW: true,
1427 AXSAVE64: true,
1428 AXSAVE: true,
1429 AXSAVEC64: true,
1430 AXSAVEC: true,
1431 AXSAVEOPT64: true,
1432 AXSAVEOPT: true,
1433 AXSAVES64: true,
1434 AXSAVES: true,
1435 }
1436
1437 var Linkamd64 = obj.LinkArch{
1438 Arch: sys.ArchAMD64,
1439 Init: instinit,
1440 ErrorCheck: errorCheck,
1441 Preprocess: preprocess,
1442 Assemble: span6,
1443 Progedit: progedit,
1444 SEH: populateSeh,
1445 UnaryDst: unaryDst,
1446 DWARFRegisters: AMD64DWARFRegisters,
1447 }
1448
1449 var Link386 = obj.LinkArch{
1450 Arch: sys.Arch386,
1451 Init: instinit,
1452 Preprocess: preprocess,
1453 Assemble: span6,
1454 Progedit: progedit,
1455 UnaryDst: unaryDst,
1456 DWARFRegisters: X86DWARFRegisters,
1457 }
1458
View as plain text