obj0.go raw
1 // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
2 //
3 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
4 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
5 // Portions Copyright © 1997-1999 Vita Nuova Limited
6 // Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
7 // Portions Copyright © 2004,2006 Bruce Ellis
8 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
9 // Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
10 // Portions Copyright © 2009 The Go Authors. All rights reserved.
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining a copy
13 // of this software and associated documentation files (the "Software"), to deal
14 // in the Software without restriction, including without limitation the rights
15 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 // copies of the Software, and to permit persons to whom the Software is
17 // furnished to do so, subject to the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be included in
20 // all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 // THE SOFTWARE.
29
30 package mips
31
32 import (
33 "github.com/twitchyliquid64/golang-asm/obj"
34 "github.com/twitchyliquid64/golang-asm/objabi"
35 "github.com/twitchyliquid64/golang-asm/sys"
36 "encoding/binary"
37 "fmt"
38 "math"
39 )
40
41 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
42 c := ctxt0{ctxt: ctxt, newprog: newprog}
43
44 p.From.Class = 0
45 p.To.Class = 0
46
47 // Rewrite JMP/JAL to symbol as TYPE_BRANCH.
48 switch p.As {
49 case AJMP,
50 AJAL,
51 ARET,
52 obj.ADUFFZERO,
53 obj.ADUFFCOPY:
54 if p.To.Sym != nil {
55 p.To.Type = obj.TYPE_BRANCH
56 }
57 }
58
59 // Rewrite float constants to values stored in memory.
60 switch p.As {
61 case AMOVF:
62 if p.From.Type == obj.TYPE_FCONST {
63 f32 := float32(p.From.Val.(float64))
64 if math.Float32bits(f32) == 0 {
65 p.As = AMOVW
66 p.From.Type = obj.TYPE_REG
67 p.From.Reg = REGZERO
68 break
69 }
70 p.From.Type = obj.TYPE_MEM
71 p.From.Sym = ctxt.Float32Sym(f32)
72 p.From.Name = obj.NAME_EXTERN
73 p.From.Offset = 0
74 }
75
76 case AMOVD:
77 if p.From.Type == obj.TYPE_FCONST {
78 f64 := p.From.Val.(float64)
79 if math.Float64bits(f64) == 0 && c.ctxt.Arch.Family == sys.MIPS64 {
80 p.As = AMOVV
81 p.From.Type = obj.TYPE_REG
82 p.From.Reg = REGZERO
83 break
84 }
85 p.From.Type = obj.TYPE_MEM
86 p.From.Sym = ctxt.Float64Sym(f64)
87 p.From.Name = obj.NAME_EXTERN
88 p.From.Offset = 0
89 }
90
91 // Put >32-bit constants in memory and load them
92 case AMOVV:
93 if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset {
94 p.From.Type = obj.TYPE_MEM
95 p.From.Sym = ctxt.Int64Sym(p.From.Offset)
96 p.From.Name = obj.NAME_EXTERN
97 p.From.Offset = 0
98 }
99 }
100
101 // Rewrite SUB constants into ADD.
102 switch p.As {
103 case ASUB:
104 if p.From.Type == obj.TYPE_CONST {
105 p.From.Offset = -p.From.Offset
106 p.As = AADD
107 }
108
109 case ASUBU:
110 if p.From.Type == obj.TYPE_CONST {
111 p.From.Offset = -p.From.Offset
112 p.As = AADDU
113 }
114
115 case ASUBV:
116 if p.From.Type == obj.TYPE_CONST {
117 p.From.Offset = -p.From.Offset
118 p.As = AADDV
119 }
120
121 case ASUBVU:
122 if p.From.Type == obj.TYPE_CONST {
123 p.From.Offset = -p.From.Offset
124 p.As = AADDVU
125 }
126 }
127 }
128
129 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
130 // TODO(minux): add morestack short-cuts with small fixed frame-size.
131 c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym}
132
133 // a switch for enabling/disabling instruction scheduling
134 nosched := true
135
136 if c.cursym.Func.Text == nil || c.cursym.Func.Text.Link == nil {
137 return
138 }
139
140 p := c.cursym.Func.Text
141 textstksiz := p.To.Offset
142 if textstksiz == -ctxt.FixedFrameSize() {
143 // Historical way to mark NOFRAME.
144 p.From.Sym.Set(obj.AttrNoFrame, true)
145 textstksiz = 0
146 }
147 if textstksiz < 0 {
148 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
149 }
150 if p.From.Sym.NoFrame() {
151 if textstksiz != 0 {
152 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
153 }
154 }
155
156 c.cursym.Func.Args = p.To.Val.(int32)
157 c.cursym.Func.Locals = int32(textstksiz)
158
159 /*
160 * find leaf subroutines
161 * expand RET
162 * expand BECOME pseudo
163 */
164
165 for p := c.cursym.Func.Text; p != nil; p = p.Link {
166 switch p.As {
167 /* too hard, just leave alone */
168 case obj.ATEXT:
169 p.Mark |= LABEL | LEAF | SYNC
170 if p.Link != nil {
171 p.Link.Mark |= LABEL
172 }
173
174 /* too hard, just leave alone */
175 case AMOVW,
176 AMOVV:
177 if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
178 p.Mark |= LABEL | SYNC
179 break
180 }
181 if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
182 p.Mark |= LABEL | SYNC
183 }
184
185 /* too hard, just leave alone */
186 case ASYSCALL,
187 AWORD,
188 ATLBWR,
189 ATLBWI,
190 ATLBP,
191 ATLBR:
192 p.Mark |= LABEL | SYNC
193
194 case ANOR:
195 if p.To.Type == obj.TYPE_REG {
196 if p.To.Reg == REGZERO {
197 p.Mark |= LABEL | SYNC
198 }
199 }
200
201 case ABGEZAL,
202 ABLTZAL,
203 AJAL,
204 obj.ADUFFZERO,
205 obj.ADUFFCOPY:
206 c.cursym.Func.Text.Mark &^= LEAF
207 fallthrough
208
209 case AJMP,
210 ABEQ,
211 ABGEZ,
212 ABGTZ,
213 ABLEZ,
214 ABLTZ,
215 ABNE,
216 ABFPT, ABFPF:
217 if p.As == ABFPT || p.As == ABFPF {
218 // We don't treat ABFPT and ABFPF as branches here,
219 // so that we will always fill nop (0x0) in their
220 // delay slot during assembly.
221 // This is to workaround a kernel FPU emulator bug
222 // where it uses the user stack to simulate the
223 // instruction in the delay slot if it's not 0x0,
224 // and somehow that leads to SIGSEGV when the kernel
225 // jump to the stack.
226 p.Mark |= SYNC
227 } else {
228 p.Mark |= BRANCH
229 }
230 q1 := p.To.Target()
231 if q1 != nil {
232 for q1.As == obj.ANOP {
233 q1 = q1.Link
234 p.To.SetTarget(q1)
235 }
236
237 if q1.Mark&LEAF == 0 {
238 q1.Mark |= LABEL
239 }
240 }
241 //else {
242 // p.Mark |= LABEL
243 //}
244 q1 = p.Link
245 if q1 != nil {
246 q1.Mark |= LABEL
247 }
248
249 case ARET:
250 if p.Link != nil {
251 p.Link.Mark |= LABEL
252 }
253 }
254 }
255
256 var mov, add obj.As
257 if c.ctxt.Arch.Family == sys.MIPS64 {
258 add = AADDV
259 mov = AMOVV
260 } else {
261 add = AADDU
262 mov = AMOVW
263 }
264
265 var q *obj.Prog
266 var q1 *obj.Prog
267 autosize := int32(0)
268 var p1 *obj.Prog
269 var p2 *obj.Prog
270 for p := c.cursym.Func.Text; p != nil; p = p.Link {
271 o := p.As
272 switch o {
273 case obj.ATEXT:
274 autosize = int32(textstksiz)
275
276 if p.Mark&LEAF != 0 && autosize == 0 {
277 // A leaf function with no locals has no frame.
278 p.From.Sym.Set(obj.AttrNoFrame, true)
279 }
280
281 if !p.From.Sym.NoFrame() {
282 // If there is a stack frame at all, it includes
283 // space to save the LR.
284 autosize += int32(c.ctxt.FixedFrameSize())
285 }
286
287 if autosize&4 != 0 && c.ctxt.Arch.Family == sys.MIPS64 {
288 autosize += 4
289 }
290
291 if autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
292 if c.cursym.Func.Text.From.Sym.NoSplit() {
293 if ctxt.Debugvlog {
294 ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
295 }
296
297 c.cursym.Func.Text.Mark |= LEAF
298 }
299 }
300
301 p.To.Offset = int64(autosize) - ctxt.FixedFrameSize()
302
303 if c.cursym.Func.Text.Mark&LEAF != 0 {
304 c.cursym.Set(obj.AttrLeaf, true)
305 if p.From.Sym.NoFrame() {
306 break
307 }
308 }
309
310 if !p.From.Sym.NoSplit() {
311 p = c.stacksplit(p, autosize) // emit split check
312 }
313
314 q = p
315
316 if autosize != 0 {
317 // Make sure to save link register for non-empty frame, even if
318 // it is a leaf function, so that traceback works.
319 // Store link register before decrement SP, so if a signal comes
320 // during the execution of the function prologue, the traceback
321 // code will not see a half-updated stack frame.
322 // This sequence is not async preemptible, as if we open a frame
323 // at the current SP, it will clobber the saved LR.
324 q = c.ctxt.StartUnsafePoint(q, c.newprog)
325
326 q = obj.Appendp(q, newprog)
327 q.As = mov
328 q.Pos = p.Pos
329 q.From.Type = obj.TYPE_REG
330 q.From.Reg = REGLINK
331 q.To.Type = obj.TYPE_MEM
332 q.To.Offset = int64(-autosize)
333 q.To.Reg = REGSP
334
335 q = obj.Appendp(q, newprog)
336 q.As = add
337 q.Pos = p.Pos
338 q.From.Type = obj.TYPE_CONST
339 q.From.Offset = int64(-autosize)
340 q.To.Type = obj.TYPE_REG
341 q.To.Reg = REGSP
342 q.Spadj = +autosize
343
344 q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
345 }
346
347 if c.cursym.Func.Text.From.Sym.Wrapper() && c.cursym.Func.Text.Mark&LEAF == 0 {
348 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
349 //
350 // MOV g_panic(g), R1
351 // BEQ R1, end
352 // MOV panic_argp(R1), R2
353 // ADD $(autosize+FIXED_FRAME), R29, R3
354 // BNE R2, R3, end
355 // ADD $FIXED_FRAME, R29, R2
356 // MOV R2, panic_argp(R1)
357 // end:
358 // NOP
359 //
360 // The NOP is needed to give the jumps somewhere to land.
361 // It is a liblink NOP, not an mips NOP: it encodes to 0 instruction bytes.
362 //
363 // We don't generate this for leafs because that means the wrapped
364 // function was inlined into the wrapper.
365
366 q = obj.Appendp(q, newprog)
367
368 q.As = mov
369 q.From.Type = obj.TYPE_MEM
370 q.From.Reg = REGG
371 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
372 q.To.Type = obj.TYPE_REG
373 q.To.Reg = REG_R1
374
375 q = obj.Appendp(q, newprog)
376 q.As = ABEQ
377 q.From.Type = obj.TYPE_REG
378 q.From.Reg = REG_R1
379 q.To.Type = obj.TYPE_BRANCH
380 q.Mark |= BRANCH
381 p1 = q
382
383 q = obj.Appendp(q, newprog)
384 q.As = mov
385 q.From.Type = obj.TYPE_MEM
386 q.From.Reg = REG_R1
387 q.From.Offset = 0 // Panic.argp
388 q.To.Type = obj.TYPE_REG
389 q.To.Reg = REG_R2
390
391 q = obj.Appendp(q, newprog)
392 q.As = add
393 q.From.Type = obj.TYPE_CONST
394 q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
395 q.Reg = REGSP
396 q.To.Type = obj.TYPE_REG
397 q.To.Reg = REG_R3
398
399 q = obj.Appendp(q, newprog)
400 q.As = ABNE
401 q.From.Type = obj.TYPE_REG
402 q.From.Reg = REG_R2
403 q.Reg = REG_R3
404 q.To.Type = obj.TYPE_BRANCH
405 q.Mark |= BRANCH
406 p2 = q
407
408 q = obj.Appendp(q, newprog)
409 q.As = add
410 q.From.Type = obj.TYPE_CONST
411 q.From.Offset = ctxt.FixedFrameSize()
412 q.Reg = REGSP
413 q.To.Type = obj.TYPE_REG
414 q.To.Reg = REG_R2
415
416 q = obj.Appendp(q, newprog)
417 q.As = mov
418 q.From.Type = obj.TYPE_REG
419 q.From.Reg = REG_R2
420 q.To.Type = obj.TYPE_MEM
421 q.To.Reg = REG_R1
422 q.To.Offset = 0 // Panic.argp
423
424 q = obj.Appendp(q, newprog)
425
426 q.As = obj.ANOP
427 p1.To.SetTarget(q)
428 p2.To.SetTarget(q)
429 }
430
431 case ARET:
432 if p.From.Type == obj.TYPE_CONST {
433 ctxt.Diag("using BECOME (%v) is not supported!", p)
434 break
435 }
436
437 retSym := p.To.Sym
438 p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
439 p.To.Sym = nil
440
441 if c.cursym.Func.Text.Mark&LEAF != 0 {
442 if autosize == 0 {
443 p.As = AJMP
444 p.From = obj.Addr{}
445 if retSym != nil { // retjmp
446 p.To.Type = obj.TYPE_BRANCH
447 p.To.Name = obj.NAME_EXTERN
448 p.To.Sym = retSym
449 } else {
450 p.To.Type = obj.TYPE_MEM
451 p.To.Reg = REGLINK
452 p.To.Offset = 0
453 }
454 p.Mark |= BRANCH
455 break
456 }
457
458 p.As = add
459 p.From.Type = obj.TYPE_CONST
460 p.From.Offset = int64(autosize)
461 p.To.Type = obj.TYPE_REG
462 p.To.Reg = REGSP
463 p.Spadj = -autosize
464
465 q = c.newprog()
466 q.As = AJMP
467 q.Pos = p.Pos
468 q.To.Type = obj.TYPE_MEM
469 q.To.Offset = 0
470 q.To.Reg = REGLINK
471 q.Mark |= BRANCH
472 q.Spadj = +autosize
473
474 q.Link = p.Link
475 p.Link = q
476 break
477 }
478
479 p.As = mov
480 p.From.Type = obj.TYPE_MEM
481 p.From.Offset = 0
482 p.From.Reg = REGSP
483 p.To.Type = obj.TYPE_REG
484 p.To.Reg = REGLINK
485
486 if autosize != 0 {
487 q = c.newprog()
488 q.As = add
489 q.Pos = p.Pos
490 q.From.Type = obj.TYPE_CONST
491 q.From.Offset = int64(autosize)
492 q.To.Type = obj.TYPE_REG
493 q.To.Reg = REGSP
494 q.Spadj = -autosize
495
496 q.Link = p.Link
497 p.Link = q
498 }
499
500 q1 = c.newprog()
501 q1.As = AJMP
502 q1.Pos = p.Pos
503 if retSym != nil { // retjmp
504 q1.To.Type = obj.TYPE_BRANCH
505 q1.To.Name = obj.NAME_EXTERN
506 q1.To.Sym = retSym
507 } else {
508 q1.To.Type = obj.TYPE_MEM
509 q1.To.Offset = 0
510 q1.To.Reg = REGLINK
511 }
512 q1.Mark |= BRANCH
513 q1.Spadj = +autosize
514
515 q1.Link = q.Link
516 q.Link = q1
517
518 case AADD,
519 AADDU,
520 AADDV,
521 AADDVU:
522 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
523 p.Spadj = int32(-p.From.Offset)
524 }
525
526 case obj.AGETCALLERPC:
527 if cursym.Leaf() {
528 /* MOV LR, Rd */
529 p.As = mov
530 p.From.Type = obj.TYPE_REG
531 p.From.Reg = REGLINK
532 } else {
533 /* MOV (RSP), Rd */
534 p.As = mov
535 p.From.Type = obj.TYPE_MEM
536 p.From.Reg = REGSP
537 }
538 }
539 }
540
541 if c.ctxt.Arch.Family == sys.MIPS {
542 // rewrite MOVD into two MOVF in 32-bit mode to avoid unaligned memory access
543 for p = c.cursym.Func.Text; p != nil; p = p1 {
544 p1 = p.Link
545
546 if p.As != AMOVD {
547 continue
548 }
549 if p.From.Type != obj.TYPE_MEM && p.To.Type != obj.TYPE_MEM {
550 continue
551 }
552
553 p.As = AMOVF
554 q = c.newprog()
555 *q = *p
556 q.Link = p.Link
557 p.Link = q
558 p1 = q.Link
559
560 var addrOff int64
561 if c.ctxt.Arch.ByteOrder == binary.BigEndian {
562 addrOff = 4 // swap load/save order
563 }
564 if p.From.Type == obj.TYPE_MEM {
565 reg := REG_F0 + (p.To.Reg-REG_F0)&^1
566 p.To.Reg = reg
567 q.To.Reg = reg + 1
568 p.From.Offset += addrOff
569 q.From.Offset += 4 - addrOff
570 } else if p.To.Type == obj.TYPE_MEM {
571 reg := REG_F0 + (p.From.Reg-REG_F0)&^1
572 p.From.Reg = reg
573 q.From.Reg = reg + 1
574 p.To.Offset += addrOff
575 q.To.Offset += 4 - addrOff
576 }
577 }
578 }
579
580 if nosched {
581 // if we don't do instruction scheduling, simply add
582 // NOP after each branch instruction.
583 for p = c.cursym.Func.Text; p != nil; p = p.Link {
584 if p.Mark&BRANCH != 0 {
585 c.addnop(p)
586 }
587 }
588 return
589 }
590
591 // instruction scheduling
592 q = nil // p - 1
593 q1 = c.cursym.Func.Text // top of block
594 o := 0 // count of instructions
595 for p = c.cursym.Func.Text; p != nil; p = p1 {
596 p1 = p.Link
597 o++
598 if p.Mark&NOSCHED != 0 {
599 if q1 != p {
600 c.sched(q1, q)
601 }
602 for ; p != nil; p = p.Link {
603 if p.Mark&NOSCHED == 0 {
604 break
605 }
606 q = p
607 }
608 p1 = p
609 q1 = p
610 o = 0
611 continue
612 }
613 if p.Mark&(LABEL|SYNC) != 0 {
614 if q1 != p {
615 c.sched(q1, q)
616 }
617 q1 = p
618 o = 1
619 }
620 if p.Mark&(BRANCH|SYNC) != 0 {
621 c.sched(q1, p)
622 q1 = p1
623 o = 0
624 }
625 if o >= NSCHED {
626 c.sched(q1, p)
627 q1 = p1
628 o = 0
629 }
630 q = p
631 }
632 }
633
634 func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
635 var mov, add, sub obj.As
636
637 if c.ctxt.Arch.Family == sys.MIPS64 {
638 add = AADDV
639 mov = AMOVV
640 sub = ASUBVU
641 } else {
642 add = AADDU
643 mov = AMOVW
644 sub = ASUBU
645 }
646
647 // MOV g_stackguard(g), R1
648 p = obj.Appendp(p, c.newprog)
649
650 p.As = mov
651 p.From.Type = obj.TYPE_MEM
652 p.From.Reg = REGG
653 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
654 if c.cursym.CFunc() {
655 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
656 }
657 p.To.Type = obj.TYPE_REG
658 p.To.Reg = REG_R1
659
660 // Mark the stack bound check and morestack call async nonpreemptible.
661 // If we get preempted here, when resumed the preemption request is
662 // cleared, but we'll still call morestack, which will double the stack
663 // unnecessarily. See issue #35470.
664 p = c.ctxt.StartUnsafePoint(p, c.newprog)
665
666 var q *obj.Prog
667 if framesize <= objabi.StackSmall {
668 // small stack: SP < stackguard
669 // AGTU SP, stackguard, R1
670 p = obj.Appendp(p, c.newprog)
671
672 p.As = ASGTU
673 p.From.Type = obj.TYPE_REG
674 p.From.Reg = REGSP
675 p.Reg = REG_R1
676 p.To.Type = obj.TYPE_REG
677 p.To.Reg = REG_R1
678 } else if framesize <= objabi.StackBig {
679 // large stack: SP-framesize < stackguard-StackSmall
680 // ADD $-(framesize-StackSmall), SP, R2
681 // SGTU R2, stackguard, R1
682 p = obj.Appendp(p, c.newprog)
683
684 p.As = add
685 p.From.Type = obj.TYPE_CONST
686 p.From.Offset = -(int64(framesize) - objabi.StackSmall)
687 p.Reg = REGSP
688 p.To.Type = obj.TYPE_REG
689 p.To.Reg = REG_R2
690
691 p = obj.Appendp(p, c.newprog)
692 p.As = ASGTU
693 p.From.Type = obj.TYPE_REG
694 p.From.Reg = REG_R2
695 p.Reg = REG_R1
696 p.To.Type = obj.TYPE_REG
697 p.To.Reg = REG_R1
698 } else {
699 // Such a large stack we need to protect against wraparound.
700 // If SP is close to zero:
701 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
702 // The +StackGuard on both sides is required to keep the left side positive:
703 // SP is allowed to be slightly below stackguard. See stack.h.
704 //
705 // Preemption sets stackguard to StackPreempt, a very large value.
706 // That breaks the math above, so we have to check for that explicitly.
707 // // stackguard is R1
708 // MOV $StackPreempt, R2
709 // BEQ R1, R2, label-of-call-to-morestack
710 // ADD $StackGuard, SP, R2
711 // SUB R1, R2
712 // MOV $(framesize+(StackGuard-StackSmall)), R1
713 // SGTU R2, R1, R1
714 p = obj.Appendp(p, c.newprog)
715
716 p.As = mov
717 p.From.Type = obj.TYPE_CONST
718 p.From.Offset = objabi.StackPreempt
719 p.To.Type = obj.TYPE_REG
720 p.To.Reg = REG_R2
721
722 p = obj.Appendp(p, c.newprog)
723 q = p
724 p.As = ABEQ
725 p.From.Type = obj.TYPE_REG
726 p.From.Reg = REG_R1
727 p.Reg = REG_R2
728 p.To.Type = obj.TYPE_BRANCH
729 p.Mark |= BRANCH
730
731 p = obj.Appendp(p, c.newprog)
732 p.As = add
733 p.From.Type = obj.TYPE_CONST
734 p.From.Offset = int64(objabi.StackGuard)
735 p.Reg = REGSP
736 p.To.Type = obj.TYPE_REG
737 p.To.Reg = REG_R2
738
739 p = obj.Appendp(p, c.newprog)
740 p.As = sub
741 p.From.Type = obj.TYPE_REG
742 p.From.Reg = REG_R1
743 p.To.Type = obj.TYPE_REG
744 p.To.Reg = REG_R2
745
746 p = obj.Appendp(p, c.newprog)
747 p.As = mov
748 p.From.Type = obj.TYPE_CONST
749 p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
750 p.To.Type = obj.TYPE_REG
751 p.To.Reg = REG_R1
752
753 p = obj.Appendp(p, c.newprog)
754 p.As = ASGTU
755 p.From.Type = obj.TYPE_REG
756 p.From.Reg = REG_R2
757 p.Reg = REG_R1
758 p.To.Type = obj.TYPE_REG
759 p.To.Reg = REG_R1
760 }
761
762 // q1: BNE R1, done
763 p = obj.Appendp(p, c.newprog)
764 q1 := p
765
766 p.As = ABNE
767 p.From.Type = obj.TYPE_REG
768 p.From.Reg = REG_R1
769 p.To.Type = obj.TYPE_BRANCH
770 p.Mark |= BRANCH
771
772 // MOV LINK, R3
773 p = obj.Appendp(p, c.newprog)
774
775 p.As = mov
776 p.From.Type = obj.TYPE_REG
777 p.From.Reg = REGLINK
778 p.To.Type = obj.TYPE_REG
779 p.To.Reg = REG_R3
780 if q != nil {
781 q.To.SetTarget(p)
782 p.Mark |= LABEL
783 }
784
785 p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
786
787 // JAL runtime.morestack(SB)
788 p = obj.Appendp(p, c.newprog)
789
790 p.As = AJAL
791 p.To.Type = obj.TYPE_BRANCH
792 if c.cursym.CFunc() {
793 p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
794 } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
795 p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
796 } else {
797 p.To.Sym = c.ctxt.Lookup("runtime.morestack")
798 }
799 p.Mark |= BRANCH
800
801 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
802
803 // JMP start
804 p = obj.Appendp(p, c.newprog)
805
806 p.As = AJMP
807 p.To.Type = obj.TYPE_BRANCH
808 p.To.SetTarget(c.cursym.Func.Text.Link)
809 p.Mark |= BRANCH
810
811 // placeholder for q1's jump target
812 p = obj.Appendp(p, c.newprog)
813
814 p.As = obj.ANOP // zero-width place holder
815 q1.To.SetTarget(p)
816
817 return p
818 }
819
820 func (c *ctxt0) addnop(p *obj.Prog) {
821 q := c.newprog()
822 q.As = ANOOP
823 q.Pos = p.Pos
824 q.Link = p.Link
825 p.Link = q
826 }
827
828 const (
829 E_HILO = 1 << 0
830 E_FCR = 1 << 1
831 E_MCR = 1 << 2
832 E_MEM = 1 << 3
833 E_MEMSP = 1 << 4 /* uses offset and size */
834 E_MEMSB = 1 << 5 /* uses offset and size */
835 ANYMEM = E_MEM | E_MEMSP | E_MEMSB
836 //DELAY = LOAD|BRANCH|FCMP
837 DELAY = BRANCH /* only schedule branch */
838 )
839
840 type Dep struct {
841 ireg uint32
842 freg uint32
843 cc uint32
844 }
845
846 type Sch struct {
847 p obj.Prog
848 set Dep
849 used Dep
850 soffset int32
851 size uint8
852 nop uint8
853 comp bool
854 }
855
856 func (c *ctxt0) sched(p0, pe *obj.Prog) {
857 var sch [NSCHED]Sch
858
859 /*
860 * build side structure
861 */
862 s := sch[:]
863 for p := p0; ; p = p.Link {
864 s[0].p = *p
865 c.markregused(&s[0])
866 if p == pe {
867 break
868 }
869 s = s[1:]
870 }
871 se := s
872
873 for i := cap(sch) - cap(se); i >= 0; i-- {
874 s = sch[i:]
875 if s[0].p.Mark&DELAY == 0 {
876 continue
877 }
878 if -cap(s) < -cap(se) {
879 if !conflict(&s[0], &s[1]) {
880 continue
881 }
882 }
883
884 var t []Sch
885 var j int
886 for j = cap(sch) - cap(s) - 1; j >= 0; j-- {
887 t = sch[j:]
888 if t[0].comp {
889 if s[0].p.Mark&BRANCH != 0 {
890 continue
891 }
892 }
893 if t[0].p.Mark&DELAY != 0 {
894 if -cap(s) >= -cap(se) || conflict(&t[0], &s[1]) {
895 continue
896 }
897 }
898 for u := t[1:]; -cap(u) <= -cap(s); u = u[1:] {
899 if c.depend(&u[0], &t[0]) {
900 continue
901 }
902 }
903 goto out2
904 }
905
906 if s[0].p.Mark&BRANCH != 0 {
907 s[0].nop = 1
908 }
909 continue
910
911 out2:
912 // t[0] is the instruction being moved to fill the delay
913 stmp := t[0]
914 copy(t[:i-j], t[1:i-j+1])
915 s[0] = stmp
916
917 if t[i-j-1].p.Mark&BRANCH != 0 {
918 // t[i-j] is being put into a branch delay slot
919 // combine its Spadj with the branch instruction
920 t[i-j-1].p.Spadj += t[i-j].p.Spadj
921 t[i-j].p.Spadj = 0
922 }
923
924 i--
925 }
926
927 /*
928 * put it all back
929 */
930 var p *obj.Prog
931 var q *obj.Prog
932 for s, p = sch[:], p0; -cap(s) <= -cap(se); s, p = s[1:], q {
933 q = p.Link
934 if q != s[0].p.Link {
935 *p = s[0].p
936 p.Link = q
937 }
938 for s[0].nop != 0 {
939 s[0].nop--
940 c.addnop(p)
941 }
942 }
943 }
944
945 func (c *ctxt0) markregused(s *Sch) {
946 p := &s.p
947 s.comp = c.compound(p)
948 s.nop = 0
949 if s.comp {
950 s.set.ireg |= 1 << (REGTMP - REG_R0)
951 s.used.ireg |= 1 << (REGTMP - REG_R0)
952 }
953
954 ar := 0 /* dest is really reference */
955 ad := 0 /* source/dest is really address */
956 ld := 0 /* opcode is load instruction */
957 sz := 20 /* size of load/store for overlap computation */
958
959 /*
960 * flags based on opcode
961 */
962 switch p.As {
963 case obj.ATEXT:
964 c.autosize = int32(p.To.Offset + 8)
965 ad = 1
966
967 case AJAL:
968 r := p.Reg
969 if r == 0 {
970 r = REGLINK
971 }
972 s.set.ireg |= 1 << uint(r-REG_R0)
973 ar = 1
974 ad = 1
975
976 case ABGEZAL,
977 ABLTZAL:
978 s.set.ireg |= 1 << (REGLINK - REG_R0)
979 fallthrough
980 case ABEQ,
981 ABGEZ,
982 ABGTZ,
983 ABLEZ,
984 ABLTZ,
985 ABNE:
986 ar = 1
987 ad = 1
988
989 case ABFPT,
990 ABFPF:
991 ad = 1
992 s.used.cc |= E_FCR
993
994 case ACMPEQD,
995 ACMPEQF,
996 ACMPGED,
997 ACMPGEF,
998 ACMPGTD,
999 ACMPGTF:
1000 ar = 1
1001 s.set.cc |= E_FCR
1002 p.Mark |= FCMP
1003
1004 case AJMP:
1005 ar = 1
1006 ad = 1
1007
1008 case AMOVB,
1009 AMOVBU:
1010 sz = 1
1011 ld = 1
1012
1013 case AMOVH,
1014 AMOVHU:
1015 sz = 2
1016 ld = 1
1017
1018 case AMOVF,
1019 AMOVW,
1020 AMOVWL,
1021 AMOVWR:
1022 sz = 4
1023 ld = 1
1024
1025 case AMOVD,
1026 AMOVV,
1027 AMOVVL,
1028 AMOVVR:
1029 sz = 8
1030 ld = 1
1031
1032 case ADIV,
1033 ADIVU,
1034 AMUL,
1035 AMULU,
1036 AREM,
1037 AREMU,
1038 ADIVV,
1039 ADIVVU,
1040 AMULV,
1041 AMULVU,
1042 AREMV,
1043 AREMVU:
1044 s.set.cc = E_HILO
1045 fallthrough
1046 case AADD,
1047 AADDU,
1048 AADDV,
1049 AADDVU,
1050 AAND,
1051 ANOR,
1052 AOR,
1053 ASGT,
1054 ASGTU,
1055 ASLL,
1056 ASRA,
1057 ASRL,
1058 ASLLV,
1059 ASRAV,
1060 ASRLV,
1061 ASUB,
1062 ASUBU,
1063 ASUBV,
1064 ASUBVU,
1065 AXOR,
1066
1067 AADDD,
1068 AADDF,
1069 AADDW,
1070 ASUBD,
1071 ASUBF,
1072 ASUBW,
1073 AMULF,
1074 AMULD,
1075 AMULW,
1076 ADIVF,
1077 ADIVD,
1078 ADIVW:
1079 if p.Reg == 0 {
1080 if p.To.Type == obj.TYPE_REG {
1081 p.Reg = p.To.Reg
1082 }
1083 //if(p->reg == NREG)
1084 // print("botch %P\n", p);
1085 }
1086 }
1087
1088 /*
1089 * flags based on 'to' field
1090 */
1091 cls := int(p.To.Class)
1092 if cls == 0 {
1093 cls = c.aclass(&p.To) + 1
1094 p.To.Class = int8(cls)
1095 }
1096 cls--
1097 switch cls {
1098 default:
1099 fmt.Printf("unknown class %d %v\n", cls, p)
1100
1101 case C_ZCON,
1102 C_SCON,
1103 C_ADD0CON,
1104 C_AND0CON,
1105 C_ADDCON,
1106 C_ANDCON,
1107 C_UCON,
1108 C_LCON,
1109 C_NONE,
1110 C_SBRA,
1111 C_LBRA,
1112 C_ADDR,
1113 C_TEXTSIZE:
1114 break
1115
1116 case C_HI,
1117 C_LO:
1118 s.set.cc |= E_HILO
1119
1120 case C_FCREG:
1121 s.set.cc |= E_FCR
1122
1123 case C_MREG:
1124 s.set.cc |= E_MCR
1125
1126 case C_ZOREG,
1127 C_SOREG,
1128 C_LOREG:
1129 cls = int(p.To.Reg)
1130 s.used.ireg |= 1 << uint(cls-REG_R0)
1131 if ad != 0 {
1132 break
1133 }
1134 s.size = uint8(sz)
1135 s.soffset = c.regoff(&p.To)
1136
1137 m := uint32(ANYMEM)
1138 if cls == REGSB {
1139 m = E_MEMSB
1140 }
1141 if cls == REGSP {
1142 m = E_MEMSP
1143 }
1144
1145 if ar != 0 {
1146 s.used.cc |= m
1147 } else {
1148 s.set.cc |= m
1149 }
1150
1151 case C_SACON,
1152 C_LACON:
1153 s.used.ireg |= 1 << (REGSP - REG_R0)
1154
1155 case C_SECON,
1156 C_LECON:
1157 s.used.ireg |= 1 << (REGSB - REG_R0)
1158
1159 case C_REG:
1160 if ar != 0 {
1161 s.used.ireg |= 1 << uint(p.To.Reg-REG_R0)
1162 } else {
1163 s.set.ireg |= 1 << uint(p.To.Reg-REG_R0)
1164 }
1165
1166 case C_FREG:
1167 if ar != 0 {
1168 s.used.freg |= 1 << uint(p.To.Reg-REG_F0)
1169 } else {
1170 s.set.freg |= 1 << uint(p.To.Reg-REG_F0)
1171 }
1172 if ld != 0 && p.From.Type == obj.TYPE_REG {
1173 p.Mark |= LOAD
1174 }
1175
1176 case C_SAUTO,
1177 C_LAUTO:
1178 s.used.ireg |= 1 << (REGSP - REG_R0)
1179 if ad != 0 {
1180 break
1181 }
1182 s.size = uint8(sz)
1183 s.soffset = c.regoff(&p.To)
1184
1185 if ar != 0 {
1186 s.used.cc |= E_MEMSP
1187 } else {
1188 s.set.cc |= E_MEMSP
1189 }
1190
1191 case C_SEXT,
1192 C_LEXT:
1193 s.used.ireg |= 1 << (REGSB - REG_R0)
1194 if ad != 0 {
1195 break
1196 }
1197 s.size = uint8(sz)
1198 s.soffset = c.regoff(&p.To)
1199
1200 if ar != 0 {
1201 s.used.cc |= E_MEMSB
1202 } else {
1203 s.set.cc |= E_MEMSB
1204 }
1205 }
1206
1207 /*
1208 * flags based on 'from' field
1209 */
1210 cls = int(p.From.Class)
1211 if cls == 0 {
1212 cls = c.aclass(&p.From) + 1
1213 p.From.Class = int8(cls)
1214 }
1215 cls--
1216 switch cls {
1217 default:
1218 fmt.Printf("unknown class %d %v\n", cls, p)
1219
1220 case C_ZCON,
1221 C_SCON,
1222 C_ADD0CON,
1223 C_AND0CON,
1224 C_ADDCON,
1225 C_ANDCON,
1226 C_UCON,
1227 C_LCON,
1228 C_NONE,
1229 C_SBRA,
1230 C_LBRA,
1231 C_ADDR,
1232 C_TEXTSIZE:
1233 break
1234
1235 case C_HI,
1236 C_LO:
1237 s.used.cc |= E_HILO
1238
1239 case C_FCREG:
1240 s.used.cc |= E_FCR
1241
1242 case C_MREG:
1243 s.used.cc |= E_MCR
1244
1245 case C_ZOREG,
1246 C_SOREG,
1247 C_LOREG:
1248 cls = int(p.From.Reg)
1249 s.used.ireg |= 1 << uint(cls-REG_R0)
1250 if ld != 0 {
1251 p.Mark |= LOAD
1252 }
1253 s.size = uint8(sz)
1254 s.soffset = c.regoff(&p.From)
1255
1256 m := uint32(ANYMEM)
1257 if cls == REGSB {
1258 m = E_MEMSB
1259 }
1260 if cls == REGSP {
1261 m = E_MEMSP
1262 }
1263
1264 s.used.cc |= m
1265
1266 case C_SACON,
1267 C_LACON:
1268 cls = int(p.From.Reg)
1269 if cls == 0 {
1270 cls = REGSP
1271 }
1272 s.used.ireg |= 1 << uint(cls-REG_R0)
1273
1274 case C_SECON,
1275 C_LECON:
1276 s.used.ireg |= 1 << (REGSB - REG_R0)
1277
1278 case C_REG:
1279 s.used.ireg |= 1 << uint(p.From.Reg-REG_R0)
1280
1281 case C_FREG:
1282 s.used.freg |= 1 << uint(p.From.Reg-REG_F0)
1283 if ld != 0 && p.To.Type == obj.TYPE_REG {
1284 p.Mark |= LOAD
1285 }
1286
1287 case C_SAUTO,
1288 C_LAUTO:
1289 s.used.ireg |= 1 << (REGSP - REG_R0)
1290 if ld != 0 {
1291 p.Mark |= LOAD
1292 }
1293 if ad != 0 {
1294 break
1295 }
1296 s.size = uint8(sz)
1297 s.soffset = c.regoff(&p.From)
1298
1299 s.used.cc |= E_MEMSP
1300
1301 case C_SEXT:
1302 case C_LEXT:
1303 s.used.ireg |= 1 << (REGSB - REG_R0)
1304 if ld != 0 {
1305 p.Mark |= LOAD
1306 }
1307 if ad != 0 {
1308 break
1309 }
1310 s.size = uint8(sz)
1311 s.soffset = c.regoff(&p.From)
1312
1313 s.used.cc |= E_MEMSB
1314 }
1315
1316 cls = int(p.Reg)
1317 if cls != 0 {
1318 if REG_F0 <= cls && cls <= REG_F31 {
1319 s.used.freg |= 1 << uint(cls-REG_F0)
1320 } else {
1321 s.used.ireg |= 1 << uint(cls-REG_R0)
1322 }
1323 }
1324 s.set.ireg &^= (1 << (REGZERO - REG_R0)) /* R0 can't be set */
1325 }
1326
1327 /*
1328 * test to see if two instructions can be
1329 * interchanged without changing semantics
1330 */
1331 func (c *ctxt0) depend(sa, sb *Sch) bool {
1332 if sa.set.ireg&(sb.set.ireg|sb.used.ireg) != 0 {
1333 return true
1334 }
1335 if sb.set.ireg&sa.used.ireg != 0 {
1336 return true
1337 }
1338
1339 if sa.set.freg&(sb.set.freg|sb.used.freg) != 0 {
1340 return true
1341 }
1342 if sb.set.freg&sa.used.freg != 0 {
1343 return true
1344 }
1345
1346 /*
1347 * special case.
1348 * loads from same address cannot pass.
1349 * this is for hardware fifo's and the like
1350 */
1351 if sa.used.cc&sb.used.cc&E_MEM != 0 {
1352 if sa.p.Reg == sb.p.Reg {
1353 if c.regoff(&sa.p.From) == c.regoff(&sb.p.From) {
1354 return true
1355 }
1356 }
1357 }
1358
1359 x := (sa.set.cc & (sb.set.cc | sb.used.cc)) | (sb.set.cc & sa.used.cc)
1360 if x != 0 {
1361 /*
1362 * allow SB and SP to pass each other.
1363 * allow SB to pass SB iff doffsets are ok
1364 * anything else conflicts
1365 */
1366 if x != E_MEMSP && x != E_MEMSB {
1367 return true
1368 }
1369 x = sa.set.cc | sb.set.cc | sa.used.cc | sb.used.cc
1370 if x&E_MEM != 0 {
1371 return true
1372 }
1373 if offoverlap(sa, sb) {
1374 return true
1375 }
1376 }
1377
1378 return false
1379 }
1380
1381 func offoverlap(sa, sb *Sch) bool {
1382 if sa.soffset < sb.soffset {
1383 if sa.soffset+int32(sa.size) > sb.soffset {
1384 return true
1385 }
1386 return false
1387 }
1388 if sb.soffset+int32(sb.size) > sa.soffset {
1389 return true
1390 }
1391 return false
1392 }
1393
1394 /*
1395 * test 2 adjacent instructions
1396 * and find out if inserted instructions
1397 * are desired to prevent stalls.
1398 */
1399 func conflict(sa, sb *Sch) bool {
1400 if sa.set.ireg&sb.used.ireg != 0 {
1401 return true
1402 }
1403 if sa.set.freg&sb.used.freg != 0 {
1404 return true
1405 }
1406 if sa.set.cc&sb.used.cc != 0 {
1407 return true
1408 }
1409 return false
1410 }
1411
1412 func (c *ctxt0) compound(p *obj.Prog) bool {
1413 o := c.oplook(p)
1414 if o.size != 4 {
1415 return true
1416 }
1417 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSB {
1418 return true
1419 }
1420 return false
1421 }
1422
1423 var Linkmips64 = obj.LinkArch{
1424 Arch: sys.ArchMIPS64,
1425 Init: buildop,
1426 Preprocess: preprocess,
1427 Assemble: span0,
1428 Progedit: progedit,
1429 DWARFRegisters: MIPSDWARFRegisters,
1430 }
1431
1432 var Linkmips64le = obj.LinkArch{
1433 Arch: sys.ArchMIPS64LE,
1434 Init: buildop,
1435 Preprocess: preprocess,
1436 Assemble: span0,
1437 Progedit: progedit,
1438 DWARFRegisters: MIPSDWARFRegisters,
1439 }
1440
1441 var Linkmips = obj.LinkArch{
1442 Arch: sys.ArchMIPS,
1443 Init: buildop,
1444 Preprocess: preprocess,
1445 Assemble: span0,
1446 Progedit: progedit,
1447 DWARFRegisters: MIPSDWARFRegisters,
1448 }
1449
1450 var Linkmipsle = obj.LinkArch{
1451 Arch: sys.ArchMIPSLE,
1452 Init: buildop,
1453 Preprocess: preprocess,
1454 Assemble: span0,
1455 Progedit: progedit,
1456 DWARFRegisters: MIPSDWARFRegisters,
1457 }
1458