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 arm
32
33 import (
34 "github.com/twitchyliquid64/golang-asm/obj"
35 "github.com/twitchyliquid64/golang-asm/objabi"
36 "github.com/twitchyliquid64/golang-asm/sys"
37 )
38
39 var progedit_tlsfallback *obj.LSym
40
41 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
42 p.From.Class = 0
43 p.To.Class = 0
44
45 c := ctxt5{ctxt: ctxt, newprog: newprog}
46
47
48 switch p.As {
49 case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY:
50 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
51 p.To.Type = obj.TYPE_BRANCH
52 }
53 }
54
55
56 switch p.As {
57
58 case AMRC:
59 if p.To.Offset&0xffff0fff == 0xee1d0f70 {
60
61
62 if p.To.Offset&0xf000 != 0 {
63 ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
64 }
65
66 if objabi.GOARM < 7 {
67
68 if progedit_tlsfallback == nil {
69 progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback")
70 }
71
72
73 p.As = AMOVW
74
75 p.From.Type = obj.TYPE_REG
76 p.From.Reg = REGLINK
77 p.To.Type = obj.TYPE_REG
78 p.To.Reg = REGTMP
79
80
81 p = obj.Appendp(p, newprog)
82
83 p.As = ABL
84 p.To.Type = obj.TYPE_BRANCH
85 p.To.Sym = progedit_tlsfallback
86 p.To.Offset = 0
87
88
89 p = obj.Appendp(p, newprog)
90
91 p.As = AMOVW
92 p.From.Type = obj.TYPE_REG
93 p.From.Reg = REGTMP
94 p.To.Type = obj.TYPE_REG
95 p.To.Reg = REGLINK
96 break
97 }
98 }
99
100
101 p.As = AWORD
102 }
103
104
105 switch p.As {
106 case AMOVF:
107 if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
108 f32 := float32(p.From.Val.(float64))
109 p.From.Type = obj.TYPE_MEM
110 p.From.Sym = ctxt.Float32Sym(f32)
111 p.From.Name = obj.NAME_EXTERN
112 p.From.Offset = 0
113 }
114
115 case AMOVD:
116 if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
117 p.From.Type = obj.TYPE_MEM
118 p.From.Sym = ctxt.Float64Sym(p.From.Val.(float64))
119 p.From.Name = obj.NAME_EXTERN
120 p.From.Offset = 0
121 }
122 }
123
124 if ctxt.Flag_dynlink {
125 c.rewriteToUseGot(p)
126 }
127 }
128
129
130 func (c *ctxt5) rewriteToUseGot(p *obj.Prog) {
131 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
132
133
134
135
136
137 var sym *obj.LSym
138 if p.As == obj.ADUFFZERO {
139 sym = c.ctxt.Lookup("runtime.duffzero")
140 } else {
141 sym = c.ctxt.Lookup("runtime.duffcopy")
142 }
143 offset := p.To.Offset
144 p.As = AMOVW
145 p.From.Type = obj.TYPE_MEM
146 p.From.Name = obj.NAME_GOTREF
147 p.From.Sym = sym
148 p.To.Type = obj.TYPE_REG
149 p.To.Reg = REG_R9
150 p.To.Name = obj.NAME_NONE
151 p.To.Offset = 0
152 p.To.Sym = nil
153 p1 := obj.Appendp(p, c.newprog)
154 p1.As = AADD
155 p1.From.Type = obj.TYPE_CONST
156 p1.From.Offset = offset
157 p1.To.Type = obj.TYPE_REG
158 p1.To.Reg = REG_R9
159 p2 := obj.Appendp(p1, c.newprog)
160 p2.As = obj.ACALL
161 p2.To.Type = obj.TYPE_MEM
162 p2.To.Reg = REG_R9
163 return
164 }
165
166
167
168
169 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
170
171
172 if p.As != AMOVW {
173 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
174 }
175 if p.To.Type != obj.TYPE_REG {
176 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
177 }
178 p.From.Type = obj.TYPE_MEM
179 p.From.Name = obj.NAME_GOTREF
180 if p.From.Offset != 0 {
181 q := obj.Appendp(p, c.newprog)
182 q.As = AADD
183 q.From.Type = obj.TYPE_CONST
184 q.From.Offset = p.From.Offset
185 q.To = p.To
186 p.From.Offset = 0
187 }
188 }
189 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
190 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
191 }
192 var source *obj.Addr
193
194
195
196 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
197 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
198 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
199 }
200 source = &p.From
201 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
202 source = &p.To
203 } else {
204 return
205 }
206 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
207 return
208 }
209 if source.Sym.Type == objabi.STLSBSS {
210 return
211 }
212 if source.Type != obj.TYPE_MEM {
213 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
214 }
215 p1 := obj.Appendp(p, c.newprog)
216 p2 := obj.Appendp(p1, c.newprog)
217
218 p1.As = AMOVW
219 p1.From.Type = obj.TYPE_MEM
220 p1.From.Sym = source.Sym
221 p1.From.Name = obj.NAME_GOTREF
222 p1.To.Type = obj.TYPE_REG
223 p1.To.Reg = REG_R9
224
225 p2.As = p.As
226 p2.From = p.From
227 p2.To = p.To
228 if p.From.Name == obj.NAME_EXTERN {
229 p2.From.Reg = REG_R9
230 p2.From.Name = obj.NAME_NONE
231 p2.From.Sym = nil
232 } else if p.To.Name == obj.NAME_EXTERN {
233 p2.To.Reg = REG_R9
234 p2.To.Name = obj.NAME_NONE
235 p2.To.Sym = nil
236 } else {
237 return
238 }
239 obj.Nopout(p)
240 }
241
242
243 const (
244 FOLL = 1 << 0
245 LABEL = 1 << 1
246 LEAF = 1 << 2
247 )
248
249 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
250 autosize := int32(0)
251
252 if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
253 return
254 }
255
256 c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog}
257
258 p := c.cursym.Func.Text
259 autoffset := int32(p.To.Offset)
260 if autoffset == -4 {
261
262 p.From.Sym.Set(obj.AttrNoFrame, true)
263 autoffset = 0
264 }
265 if autoffset < 0 || autoffset%4 != 0 {
266 c.ctxt.Diag("frame size %d not 0 or a positive multiple of 4", autoffset)
267 }
268 if p.From.Sym.NoFrame() {
269 if autoffset != 0 {
270 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", autoffset)
271 }
272 }
273
274 cursym.Func.Locals = autoffset
275 cursym.Func.Args = p.To.Val.(int32)
276
277
280 for p := cursym.Func.Text; p != nil; p = p.Link {
281 switch p.As {
282 case obj.ATEXT:
283 p.Mark |= LEAF
284
285 case ADIV, ADIVU, AMOD, AMODU:
286 cursym.Func.Text.Mark &^= LEAF
287
288 case ABL,
289 ABX,
290 obj.ADUFFZERO,
291 obj.ADUFFCOPY:
292 cursym.Func.Text.Mark &^= LEAF
293 }
294 }
295
296 var q2 *obj.Prog
297 for p := cursym.Func.Text; p != nil; p = p.Link {
298 o := p.As
299 switch o {
300 case obj.ATEXT:
301 autosize = autoffset
302
303 if p.Mark&LEAF != 0 && autosize == 0 {
304
305 p.From.Sym.Set(obj.AttrNoFrame, true)
306 }
307
308 if !p.From.Sym.NoFrame() {
309
310
311 autosize += 4
312 }
313
314 if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 {
315
316
317 if ctxt.Debugvlog {
318 ctxt.Logf("save suppressed in: %s\n", cursym.Name)
319 }
320
321 cursym.Func.Text.Mark |= LEAF
322 }
323
324
325 p.To.Offset = int64(autosize) - 4
326
327 if cursym.Func.Text.Mark&LEAF != 0 {
328 cursym.Set(obj.AttrLeaf, true)
329 if p.From.Sym.NoFrame() {
330 break
331 }
332 }
333
334 if !p.From.Sym.NoSplit() {
335 p = c.stacksplit(p, autosize)
336 }
337
338
339 p = obj.Appendp(p, c.newprog)
340
341 p.As = AMOVW
342 p.Scond |= C_WBIT
343 p.From.Type = obj.TYPE_REG
344 p.From.Reg = REGLINK
345 p.To.Type = obj.TYPE_MEM
346 p.To.Offset = int64(-autosize)
347 p.To.Reg = REGSP
348 p.Spadj = autosize
349
350 if cursym.Func.Text.From.Sym.Wrapper() {
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371 p = obj.Appendp(p, newprog)
372 p.As = AMOVW
373 p.From.Type = obj.TYPE_MEM
374 p.From.Reg = REGG
375 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize)
376 p.To.Type = obj.TYPE_REG
377 p.To.Reg = REG_R1
378
379 p = obj.Appendp(p, newprog)
380 p.As = ACMP
381 p.From.Type = obj.TYPE_CONST
382 p.From.Offset = 0
383 p.Reg = REG_R1
384
385
386 bne := obj.Appendp(p, newprog)
387 bne.As = ABNE
388 bne.To.Type = obj.TYPE_BRANCH
389
390
391 end := obj.Appendp(bne, newprog)
392 end.As = obj.ANOP
393
394
395 var last *obj.Prog
396 for last = end; last.Link != nil; last = last.Link {
397 }
398
399
400 mov := obj.Appendp(last, newprog)
401 mov.As = AMOVW
402 mov.From.Type = obj.TYPE_MEM
403 mov.From.Reg = REG_R1
404 mov.From.Offset = 0
405 mov.To.Type = obj.TYPE_REG
406 mov.To.Reg = REG_R2
407
408
409 bne.To.SetTarget(mov)
410
411
412 p = obj.Appendp(mov, newprog)
413 p.As = AADD
414 p.From.Type = obj.TYPE_CONST
415 p.From.Offset = int64(autosize) + 4
416 p.Reg = REG_R13
417 p.To.Type = obj.TYPE_REG
418 p.To.Reg = REG_R3
419
420
421 p = obj.Appendp(p, newprog)
422 p.As = ACMP
423 p.From.Type = obj.TYPE_REG
424 p.From.Reg = REG_R2
425 p.Reg = REG_R3
426
427
428 p = obj.Appendp(p, newprog)
429 p.As = ABNE
430 p.To.Type = obj.TYPE_BRANCH
431 p.To.SetTarget(end)
432
433
434 p = obj.Appendp(p, newprog)
435 p.As = AADD
436 p.From.Type = obj.TYPE_CONST
437 p.From.Offset = 4
438 p.Reg = REG_R13
439 p.To.Type = obj.TYPE_REG
440 p.To.Reg = REG_R4
441
442
443 p = obj.Appendp(p, newprog)
444 p.As = AMOVW
445 p.From.Type = obj.TYPE_REG
446 p.From.Reg = REG_R4
447 p.To.Type = obj.TYPE_MEM
448 p.To.Reg = REG_R1
449 p.To.Offset = 0
450
451
452 p = obj.Appendp(p, newprog)
453 p.As = AB
454 p.To.Type = obj.TYPE_BRANCH
455 p.To.SetTarget(end)
456
457
458 p = end
459 }
460
461 case obj.ARET:
462 nocache(p)
463 if cursym.Func.Text.Mark&LEAF != 0 {
464 if autosize == 0 {
465 p.As = AB
466 p.From = obj.Addr{}
467 if p.To.Sym != nil {
468 p.To.Type = obj.TYPE_BRANCH
469 } else {
470 p.To.Type = obj.TYPE_MEM
471 p.To.Offset = 0
472 p.To.Reg = REGLINK
473 }
474
475 break
476 }
477 }
478
479 p.As = AMOVW
480 p.Scond |= C_PBIT
481 p.From.Type = obj.TYPE_MEM
482 p.From.Offset = int64(autosize)
483 p.From.Reg = REGSP
484 p.To.Type = obj.TYPE_REG
485 p.To.Reg = REGPC
486
487
488
489
490 if p.To.Sym != nil {
491 p.To.Reg = REGLINK
492 q2 = obj.Appendp(p, newprog)
493 q2.As = AB
494 q2.To.Type = obj.TYPE_BRANCH
495 q2.To.Sym = p.To.Sym
496 p.To.Sym = nil
497 p = q2
498 }
499
500 case AADD:
501 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
502 p.Spadj = int32(-p.From.Offset)
503 }
504
505 case ASUB:
506 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
507 p.Spadj = int32(p.From.Offset)
508 }
509
510 case ADIV, ADIVU, AMOD, AMODU:
511 if cursym.Func.Text.From.Sym.NoSplit() {
512 ctxt.Diag("cannot divide in NOSPLIT function")
513 }
514 const debugdivmod = false
515 if debugdivmod {
516 break
517 }
518 if p.From.Type != obj.TYPE_REG {
519 break
520 }
521 if p.To.Type != obj.TYPE_REG {
522 break
523 }
524
525
526 q1 := *p
527 if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
528 ctxt.Diag("div already using REGTMP: %v", p)
529 }
530
531
532 p.As = AMOVW
533 p.Pos = q1.Pos
534 p.From.Type = obj.TYPE_MEM
535 p.From.Reg = REGG
536 p.From.Offset = 6 * 4
537 p.Reg = 0
538 p.To.Type = obj.TYPE_REG
539 p.To.Reg = REGTMP
540
541
542 p = obj.Appendp(p, newprog)
543 p.As = AMOVW
544 p.Pos = q1.Pos
545 p.From.Type = obj.TYPE_REG
546 p.From.Reg = q1.From.Reg
547 p.To.Type = obj.TYPE_MEM
548 p.To.Reg = REGTMP
549 p.To.Offset = 8 * 4
550
551
552 p = obj.Appendp(p, newprog)
553 p.As = AMOVW
554 p.Pos = q1.Pos
555 p.From.Type = obj.TYPE_REG
556 p.From.Reg = q1.Reg
557 if q1.Reg == 0 {
558 p.From.Reg = q1.To.Reg
559 }
560 p.To.Type = obj.TYPE_REG
561 p.To.Reg = REG_R8
562 p.To.Offset = 0
563
564
565 p = obj.Appendp(p, newprog)
566 p.As = ABL
567 p.Pos = q1.Pos
568 p.To.Type = obj.TYPE_BRANCH
569 switch o {
570 case ADIV:
571 p.To.Sym = symdiv
572 case ADIVU:
573 p.To.Sym = symdivu
574 case AMOD:
575 p.To.Sym = symmod
576 case AMODU:
577 p.To.Sym = symmodu
578 }
579
580
581 p = obj.Appendp(p, newprog)
582 p.As = AMOVW
583 p.Pos = q1.Pos
584 p.From.Type = obj.TYPE_REG
585 p.From.Reg = REGTMP
586 p.From.Offset = 0
587 p.To.Type = obj.TYPE_REG
588 p.To.Reg = q1.To.Reg
589
590 case AMOVW:
591 if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
592 p.Spadj = int32(-p.To.Offset)
593 }
594 if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
595 p.Spadj = int32(-p.From.Offset)
596 }
597 if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
598 p.Spadj = int32(-p.From.Offset)
599 }
600
601 case obj.AGETCALLERPC:
602 if cursym.Leaf() {
603
604 p.As = AMOVW
605 p.From.Type = obj.TYPE_REG
606 p.From.Reg = REGLINK
607 } else {
608
609 p.As = AMOVW
610 p.From.Type = obj.TYPE_MEM
611 p.From.Reg = REGSP
612 }
613 }
614 }
615 }
616
617 func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
618
619 p = obj.Appendp(p, c.newprog)
620
621 p.As = AMOVW
622 p.From.Type = obj.TYPE_MEM
623 p.From.Reg = REGG
624 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize)
625 if c.cursym.CFunc() {
626 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize)
627 }
628 p.To.Type = obj.TYPE_REG
629 p.To.Reg = REG_R1
630
631
632
633
634
635 p = c.ctxt.StartUnsafePoint(p, c.newprog)
636
637 if framesize <= objabi.StackSmall {
638
639
640 p = obj.Appendp(p, c.newprog)
641
642 p.As = ACMP
643 p.From.Type = obj.TYPE_REG
644 p.From.Reg = REG_R1
645 p.Reg = REGSP
646 } else if framesize <= objabi.StackBig {
647
648
649
650 p = obj.Appendp(p, c.newprog)
651
652 p.As = AMOVW
653 p.From.Type = obj.TYPE_ADDR
654 p.From.Reg = REGSP
655 p.From.Offset = -(int64(framesize) - objabi.StackSmall)
656 p.To.Type = obj.TYPE_REG
657 p.To.Reg = REG_R2
658
659 p = obj.Appendp(p, c.newprog)
660 p.As = ACMP
661 p.From.Type = obj.TYPE_REG
662 p.From.Reg = REG_R1
663 p.Reg = REG_R2
664 } else {
665
666
667
668
669
670
671
672
673
674
675 p = obj.Appendp(p, c.newprog)
676
677 p.As = ACMP
678 p.From.Type = obj.TYPE_CONST
679 p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))
680 p.Reg = REG_R1
681
682 p = obj.Appendp(p, c.newprog)
683 p.As = AMOVW
684 p.From.Type = obj.TYPE_ADDR
685 p.From.Reg = REGSP
686 p.From.Offset = int64(objabi.StackGuard)
687 p.To.Type = obj.TYPE_REG
688 p.To.Reg = REG_R2
689 p.Scond = C_SCOND_NE
690
691 p = obj.Appendp(p, c.newprog)
692 p.As = ASUB
693 p.From.Type = obj.TYPE_REG
694 p.From.Reg = REG_R1
695 p.To.Type = obj.TYPE_REG
696 p.To.Reg = REG_R2
697 p.Scond = C_SCOND_NE
698
699 p = obj.Appendp(p, c.newprog)
700 p.As = AMOVW
701 p.From.Type = obj.TYPE_ADDR
702 p.From.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall)
703 p.To.Type = obj.TYPE_REG
704 p.To.Reg = REG_R3
705 p.Scond = C_SCOND_NE
706
707 p = obj.Appendp(p, c.newprog)
708 p.As = ACMP
709 p.From.Type = obj.TYPE_REG
710 p.From.Reg = REG_R3
711 p.Reg = REG_R2
712 p.Scond = C_SCOND_NE
713 }
714
715
716 bls := obj.Appendp(p, c.newprog)
717 bls.As = ABLS
718 bls.To.Type = obj.TYPE_BRANCH
719
720 end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
721
722 var last *obj.Prog
723 for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
724 }
725
726
727
728
729 spfix := obj.Appendp(last, c.newprog)
730 spfix.As = obj.ANOP
731 spfix.Spadj = -framesize
732
733 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
734 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
735
736
737 movw := obj.Appendp(pcdata, c.newprog)
738 movw.As = AMOVW
739 movw.From.Type = obj.TYPE_REG
740 movw.From.Reg = REGLINK
741 movw.To.Type = obj.TYPE_REG
742 movw.To.Reg = REG_R3
743
744 bls.To.SetTarget(movw)
745
746
747 call := obj.Appendp(movw, c.newprog)
748 call.As = obj.ACALL
749 call.To.Type = obj.TYPE_BRANCH
750 morestack := "runtime.morestack"
751 switch {
752 case c.cursym.CFunc():
753 morestack = "runtime.morestackc"
754 case !c.cursym.Func.Text.From.Sym.NeedCtxt():
755 morestack = "runtime.morestack_noctxt"
756 }
757 call.To.Sym = c.ctxt.Lookup(morestack)
758
759 pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
760
761
762 b := obj.Appendp(pcdata, c.newprog)
763 b.As = obj.AJMP
764 b.To.Type = obj.TYPE_BRANCH
765 b.To.SetTarget(c.cursym.Func.Text.Link)
766 b.Spadj = +framesize
767
768 return end
769 }
770
771 var unaryDst = map[obj.As]bool{
772 ASWI: true,
773 AWORD: true,
774 }
775
776 var Linkarm = obj.LinkArch{
777 Arch: sys.ArchARM,
778 Init: buildop,
779 Preprocess: preprocess,
780 Assemble: span5,
781 Progedit: progedit,
782 UnaryDst: unaryDst,
783 DWARFRegisters: ARMDWARFRegisters,
784 }
785
View as plain text