1 // cmd/7l/noop.c, cmd/7l/obj.c, cmd/ld/pass.c from Vita Nuova.
2 // https://code.google.com/p/ken-cc/source/browse/
4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
6 // Portions Copyright © 1997-1999 Vita Nuova Limited
7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8 // Portions Copyright © 2004,2006 Bruce Ellis
9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11 // Portions Copyright © 2009 The Go Authors. All rights reserved.
13 // Permission is hereby granted, free of charge, to any person obtaining a copy
14 // of this software and associated documentation files (the "Software"), to deal
15 // in the Software without restriction, including without limitation the rights
16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 // copies of the Software, and to permit persons to whom the Software is
18 // furnished to do so, subject to the following conditions:
20 // The above copyright notice and this permission notice shall be included in
21 // all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 var complements = []obj.As{
51 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
52 // MOV g_stackguard(g), R1
53 p = obj.Appendp(p, c.newprog)
56 p.From.Type = obj.TYPE_MEM
58 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
60 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
62 p.To.Type = obj.TYPE_REG
66 if framesize <= objabi.StackSmall {
67 // small stack: SP < stackguard
70 p = obj.Appendp(p, c.newprog)
73 p.From.Type = obj.TYPE_REG
75 p.To.Type = obj.TYPE_REG
78 p = obj.Appendp(p, c.newprog)
80 p.From.Type = obj.TYPE_REG
83 } else if framesize <= objabi.StackBig {
84 // large stack: SP-framesize < stackguard-StackSmall
85 // SUB $(framesize-StackSmall), SP, R2
87 p = obj.Appendp(p, c.newprog)
90 p.From.Type = obj.TYPE_CONST
91 p.From.Offset = int64(framesize) - objabi.StackSmall
93 p.To.Type = obj.TYPE_REG
96 p = obj.Appendp(p, c.newprog)
98 p.From.Type = obj.TYPE_REG
102 // Such a large stack we need to protect against wraparound
103 // if SP is close to zero.
104 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
105 // The +StackGuard on both sides is required to keep the left side positive:
106 // SP is allowed to be slightly below stackguard. See stack.h.
107 // CMP $StackPreempt, R1
108 // BEQ label_of_call_to_morestack
109 // ADD $StackGuard, SP, R2
111 // MOV $(framesize+(StackGuard-StackSmall)), R3
113 p = obj.Appendp(p, c.newprog)
116 p.From.Type = obj.TYPE_CONST
117 p.From.Offset = objabi.StackPreempt
120 p = obj.Appendp(p, c.newprog)
123 p.To.Type = obj.TYPE_BRANCH
125 p = obj.Appendp(p, c.newprog)
127 p.From.Type = obj.TYPE_CONST
128 p.From.Offset = objabi.StackGuard
130 p.To.Type = obj.TYPE_REG
133 p = obj.Appendp(p, c.newprog)
135 p.From.Type = obj.TYPE_REG
137 p.To.Type = obj.TYPE_REG
140 p = obj.Appendp(p, c.newprog)
142 p.From.Type = obj.TYPE_CONST
143 p.From.Offset = int64(framesize) + (objabi.StackGuard - objabi.StackSmall)
144 p.To.Type = obj.TYPE_REG
147 p = obj.Appendp(p, c.newprog)
149 p.From.Type = obj.TYPE_REG
155 bls := obj.Appendp(p, c.newprog)
157 bls.To.Type = obj.TYPE_BRANCH
160 for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
163 // Now we are at the end of the function, but logically
164 // we are still in function prologue. We need to fix the
165 // SP data and PCDATA.
166 spfix := obj.Appendp(last, c.newprog)
168 spfix.Spadj = -framesize
170 pcdata := c.ctxt.EmitEntryLiveness(c.cursym, spfix, c.newprog)
173 movlr := obj.Appendp(pcdata, c.newprog)
175 movlr.From.Type = obj.TYPE_REG
176 movlr.From.Reg = REGLINK
177 movlr.To.Type = obj.TYPE_REG
178 movlr.To.Reg = REG_R3
186 debug = obj.Appendp(debug, c.newprog)
188 debug.From.Type = obj.TYPE_CONST
189 debug.From.Offset = int64(framesize)
190 debug.To.Type = obj.TYPE_REG
191 debug.To.Reg = REGTMP
194 // BL runtime.morestack(SB)
195 call := obj.Appendp(debug, c.newprog)
197 call.To.Type = obj.TYPE_BRANCH
198 morestack := "runtime.morestack"
200 case c.cursym.CFunc():
201 morestack = "runtime.morestackc"
202 case !c.cursym.Func.Text.From.Sym.NeedCtxt():
203 morestack = "runtime.morestack_noctxt"
205 call.To.Sym = c.ctxt.Lookup(morestack)
208 jmp := obj.Appendp(call, c.newprog)
210 jmp.To.Type = obj.TYPE_BRANCH
211 jmp.Pcond = c.cursym.Func.Text.Link
212 jmp.Spadj = +framesize
214 // placeholder for bls's jump target
215 // p = obj.Appendp(ctxt, p)
221 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
222 c := ctxt7{ctxt: ctxt, newprog: newprog}
227 // $0 results in C_ZCON, which matches both C_REG and various
228 // C_xCON, however the C_REG cases in asmout don't expect a
229 // constant, so they will use the register fields and assemble
230 // a R0. To prevent that, rewrite $0 as ZR.
231 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
232 p.From.Type = obj.TYPE_REG
235 if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
236 p.To.Type = obj.TYPE_REG
240 // Rewrite BR/BL to symbol as TYPE_BRANCH.
248 p.To.Type = obj.TYPE_BRANCH
253 // Rewrite float constants to values stored in memory.
256 if p.From.Type == obj.TYPE_FCONST {
257 f64 := p.From.Val.(float64)
259 if c.chipfloat7(f64) > 0 {
262 if math.Float32bits(f32) == 0 {
263 p.From.Type = obj.TYPE_REG
267 p.From.Type = obj.TYPE_MEM
268 p.From.Sym = c.ctxt.Float32Sym(f32)
269 p.From.Name = obj.NAME_EXTERN
274 if p.From.Type == obj.TYPE_FCONST {
275 f64 := p.From.Val.(float64)
276 if c.chipfloat7(f64) > 0 {
279 if math.Float64bits(f64) == 0 {
280 p.From.Type = obj.TYPE_REG
284 p.From.Type = obj.TYPE_MEM
285 p.From.Sym = c.ctxt.Float64Sym(f64)
286 p.From.Name = obj.NAME_EXTERN
293 // Rewrite negative immediates as positive immediates with
294 // complementary instruction.
296 case AADD, ASUB, ACMP, ACMN:
297 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
298 p.From.Offset = -p.From.Offset
299 p.As = complements[p.As]
301 case AADDW, ASUBW, ACMPW, ACMNW:
302 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
303 p.From.Offset = -p.From.Offset
304 p.As = complements[p.As]
308 // For 32-bit logical instruction with constant,
309 // rewrite the high 32-bit to be a repetition of
310 // the low 32-bit, so that the BITCON test can be
311 // shared for both 32-bit and 64-bit. 32-bit ops
312 // will zero the high 32-bit of the destination
314 if isANDWop(p.As) && p.From.Type == obj.TYPE_CONST {
315 v := p.From.Offset & 0xffffffff
316 p.From.Offset = v | v<<32
319 if c.ctxt.Flag_dynlink {
324 // Rewrite p, if necessary, to access global data via the global offset table.
325 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
326 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
329 // MOVD runtime.duffxxx@GOT, REGTMP
330 // ADD $offset, REGTMP
333 if p.As == obj.ADUFFZERO {
334 sym = c.ctxt.Lookup("runtime.duffzero")
336 sym = c.ctxt.Lookup("runtime.duffcopy")
338 offset := p.To.Offset
340 p.From.Type = obj.TYPE_MEM
341 p.From.Name = obj.NAME_GOTREF
343 p.To.Type = obj.TYPE_REG
345 p.To.Name = obj.NAME_NONE
348 p1 := obj.Appendp(p, c.newprog)
350 p1.From.Type = obj.TYPE_CONST
351 p1.From.Offset = offset
352 p1.To.Type = obj.TYPE_REG
354 p2 := obj.Appendp(p1, c.newprog)
356 p2.To.Type = obj.TYPE_REG
360 // We only care about global data: NAME_EXTERN means a global
361 // symbol in the Go sense, and p.Sym.Local is true for a few
362 // internally defined symbols.
363 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
364 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
365 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
367 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
369 if p.To.Type != obj.TYPE_REG {
370 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
372 p.From.Type = obj.TYPE_MEM
373 p.From.Name = obj.NAME_GOTREF
374 if p.From.Offset != 0 {
375 q := obj.Appendp(p, c.newprog)
377 q.From.Type = obj.TYPE_CONST
378 q.From.Offset = p.From.Offset
383 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
384 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
387 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
388 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
389 // An addition may be inserted between the two MOVs if there is an offset.
390 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
391 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
392 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
395 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
400 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
403 if source.Sym.Type == objabi.STLSBSS {
406 if source.Type != obj.TYPE_MEM {
407 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
409 p1 := obj.Appendp(p, c.newprog)
410 p2 := obj.Appendp(p1, c.newprog)
412 p1.From.Type = obj.TYPE_MEM
413 p1.From.Sym = source.Sym
414 p1.From.Name = obj.NAME_GOTREF
415 p1.To.Type = obj.TYPE_REG
421 if p.From.Name == obj.NAME_EXTERN {
423 p2.From.Name = obj.NAME_NONE
425 } else if p.To.Name == obj.NAME_EXTERN {
427 p2.To.Name = obj.NAME_NONE
435 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
436 if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
440 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
442 p := c.cursym.Func.Text
443 textstksiz := p.To.Offset
444 if textstksiz == -8 {
445 // Historical way to mark NOFRAME.
446 p.From.Sym.Set(obj.AttrNoFrame, true)
450 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
452 if p.From.Sym.NoFrame() {
454 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
458 c.cursym.Func.Args = p.To.Val.(int32)
459 c.cursym.Func.Locals = int32(textstksiz)
462 * find leaf subroutines
466 q := (*obj.Prog)(nil)
468 for p := c.cursym.Func.Text; p != nil; p = p.Link {
479 q.Link = q1 /* q is non-nop */
487 c.cursym.Func.Text.Mark &^= LEAF
518 for q1.As == obj.ANOP {
531 for p := c.cursym.Func.Text; p != nil; p = p.Link {
535 c.cursym.Func.Text = p
536 c.autosize = int32(textstksiz)
538 if p.Mark&LEAF != 0 && c.autosize == 0 {
539 // A leaf function with no locals has no frame.
540 p.From.Sym.Set(obj.AttrNoFrame, true)
543 if !p.From.Sym.NoFrame() {
544 // If there is a stack frame at all, it includes
545 // space to save the LR.
550 extrasize := int32(0)
551 if c.autosize%16 == 8 {
552 // Allocate extra 8 bytes on the frame top to save FP
554 } else if c.autosize&(16-1) == 0 {
555 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
558 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
560 c.autosize += extrasize
561 c.cursym.Func.Locals += extrasize
563 // low 32 bits for autosize
564 // high 32 bits for extrasize
565 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
571 if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
572 if c.ctxt.Debugvlog {
573 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
575 c.cursym.Func.Text.Mark |= LEAF
578 if cursym.Func.Text.Mark&LEAF != 0 {
579 cursym.Set(obj.AttrLeaf, true)
580 if p.From.Sym.NoFrame() {
585 if !p.From.Sym.NoSplit() {
586 p = c.stacksplit(p, c.autosize) // emit split check
589 aoffset := c.autosize
594 // Frame is non-empty. Make sure to save link register, even if
595 // it is a leaf function, so that traceback works.
597 if c.autosize > aoffset {
598 // Frame size is too large for a MOVD.W instruction.
599 // Store link register before decrementing SP, so if a signal comes
600 // during the execution of the function prologue, the traceback
601 // code will not see a half-updated stack frame.
602 q = obj.Appendp(q, c.newprog)
605 q.From.Type = obj.TYPE_CONST
606 q.From.Offset = int64(c.autosize)
608 q.To.Type = obj.TYPE_REG
611 q = obj.Appendp(q, c.newprog)
614 q.From.Type = obj.TYPE_REG
616 q.To.Type = obj.TYPE_MEM
619 q1 = obj.Appendp(q, c.newprog)
622 q1.From.Type = obj.TYPE_REG
624 q1.To.Type = obj.TYPE_REG
626 q1.Spadj = c.autosize
628 // small frame, update SP and save LR in a single MOVD.W instruction
629 q1 = obj.Appendp(q, c.newprog)
632 q1.From.Type = obj.TYPE_REG
633 q1.From.Reg = REGLINK
634 q1.To.Type = obj.TYPE_MEM
636 q1.To.Offset = int64(-aoffset)
641 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
642 q1 = obj.Appendp(q1, c.newprog)
645 q1.From.Type = obj.TYPE_REG
647 q1.To.Type = obj.TYPE_MEM
651 q1 = obj.Appendp(q1, c.newprog)
654 q1.From.Type = obj.TYPE_CONST
657 q1.To.Type = obj.TYPE_REG
661 if c.cursym.Func.Text.From.Sym.Wrapper() {
662 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
664 // MOV g_panic(g), R1
668 // ... function body ...
670 // MOV panic_argp(R1), R2
671 // ADD $(autosize+8), RSP, R3
675 // MOVD R4, panic_argp(R1)
678 // The NOP is needed to give the jumps somewhere to land.
679 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
682 // MOV g_panic(g), R1
683 q = obj.Appendp(q, c.newprog)
685 q.From.Type = obj.TYPE_MEM
687 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
688 q.To.Type = obj.TYPE_REG
691 // CBNZ R1, checkargp
692 cbnz := obj.Appendp(q, c.newprog)
694 cbnz.From.Type = obj.TYPE_REG
695 cbnz.From.Reg = REG_R1
696 cbnz.To.Type = obj.TYPE_BRANCH
698 // Empty branch target at the top of the function body
699 end := obj.Appendp(cbnz, c.newprog)
702 // find the end of the function
704 for last = end; last.Link != nil; last = last.Link {
707 // MOV panic_argp(R1), R2
708 mov := obj.Appendp(last, c.newprog)
710 mov.From.Type = obj.TYPE_MEM
711 mov.From.Reg = REG_R1
712 mov.From.Offset = 0 // Panic.argp
713 mov.To.Type = obj.TYPE_REG
716 // CBNZ branches to the MOV above
719 // ADD $(autosize+8), SP, R3
720 q = obj.Appendp(mov, c.newprog)
722 q.From.Type = obj.TYPE_CONST
723 q.From.Offset = int64(c.autosize) + 8
725 q.To.Type = obj.TYPE_REG
729 q = obj.Appendp(q, c.newprog)
731 q.From.Type = obj.TYPE_REG
736 q = obj.Appendp(q, c.newprog)
738 q.To.Type = obj.TYPE_BRANCH
742 q = obj.Appendp(q, c.newprog)
744 q.From.Type = obj.TYPE_CONST
747 q.To.Type = obj.TYPE_REG
750 // MOV R4, panic_argp(R1)
751 q = obj.Appendp(q, c.newprog)
753 q.From.Type = obj.TYPE_REG
755 q.To.Type = obj.TYPE_MEM
757 q.To.Offset = 0 // Panic.argp
760 q = obj.Appendp(q, c.newprog)
762 q.To.Type = obj.TYPE_BRANCH
768 if p.From.Type == obj.TYPE_CONST {
769 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
775 if c.cursym.Func.Text.Mark&LEAF != 0 {
778 p.From.Type = obj.TYPE_CONST
779 p.From.Offset = int64(c.autosize)
780 p.To.Type = obj.TYPE_REG
782 p.Spadj = -c.autosize
784 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
785 p = obj.Appendp(p, c.newprog)
787 p.From.Type = obj.TYPE_CONST
790 p.To.Type = obj.TYPE_REG
795 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
797 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
799 p.From.Type = obj.TYPE_MEM
802 p.To.Type = obj.TYPE_REG
804 p = obj.Appendp(p, c.newprog)
807 aoffset := c.autosize
813 p.From.Type = obj.TYPE_MEM
815 p.From.Offset = int64(aoffset)
817 p.To.Type = obj.TYPE_REG
820 if c.autosize > aoffset {
823 q.From.Type = obj.TYPE_CONST
824 q.From.Offset = int64(c.autosize) - int64(aoffset)
825 q.To.Type = obj.TYPE_REG
828 q.Spadj = int32(-q.From.Offset)
835 if p.As != obj.ARET {
843 if retjmp != nil { // retjmp
845 p.To.Type = obj.TYPE_BRANCH
847 p.Spadj = +c.autosize
852 p.To.Type = obj.TYPE_MEM
855 p.Spadj = +c.autosize
858 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
860 p.Spadj = int32(-p.From.Offset)
862 p.Spadj = int32(+p.From.Offset)
866 case obj.AGETCALLERPC:
870 p.From.Type = obj.TYPE_REG
875 p.From.Type = obj.TYPE_MEM
880 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
882 // STP (FP, R27), -24(SP)
889 // copy DUFFCOPY from q1 to q4
890 q4 := obj.Appendp(p, c.newprog)
892 q4.As = obj.ADUFFCOPY
896 q1.From.Type = obj.TYPE_BRANCH
897 q1.To.Type = obj.TYPE_REG
900 q2 := obj.Appendp(q1, c.newprog)
903 q2.From.Type = obj.TYPE_REGREG
905 q2.From.Offset = int64(REG_R27)
906 q2.To.Type = obj.TYPE_MEM
910 // maintaine FP for DUFFCOPY
911 q3 := obj.Appendp(q2, c.newprog)
914 q3.From.Type = obj.TYPE_CONST
917 q3.To.Type = obj.TYPE_REG
920 q5 := obj.Appendp(q4, c.newprog)
923 q5.From.Type = obj.TYPE_CONST
926 q5.To.Type = obj.TYPE_REG
933 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
935 // STP (FP, R27), -24(SP)
942 // copy DUFFZERO from q1 to q4
943 q4 := obj.Appendp(p, c.newprog)
945 q4.As = obj.ADUFFZERO
949 q1.From.Type = obj.TYPE_BRANCH
950 q1.To.Type = obj.TYPE_REG
953 q2 := obj.Appendp(q1, c.newprog)
956 q2.From.Type = obj.TYPE_REGREG
958 q2.From.Offset = int64(REG_R27)
959 q2.To.Type = obj.TYPE_MEM
963 // maintaine FP for DUFFZERO
964 q3 := obj.Appendp(q2, c.newprog)
967 q3.From.Type = obj.TYPE_CONST
970 q3.To.Type = obj.TYPE_REG
973 q5 := obj.Appendp(q4, c.newprog)
976 q5.From.Type = obj.TYPE_CONST
979 q5.To.Type = obj.TYPE_REG
988 func nocache(p *obj.Prog) {
994 var unaryDst = map[obj.As]bool{
1002 var Linkarm64 = obj.LinkArch{
1003 Arch: sys.ArchARM64,
1005 Preprocess: preprocess,
1009 DWARFRegisters: ARM64DWARFRegisters,