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 f32 := float32(p.From.Val.(float64))
258 if math.Float32bits(f32) == 0 {
259 p.From.Type = obj.TYPE_REG
263 p.From.Type = obj.TYPE_MEM
264 p.From.Sym = c.ctxt.Float32Sym(f32)
265 p.From.Name = obj.NAME_EXTERN
270 if p.From.Type == obj.TYPE_FCONST {
271 f64 := p.From.Val.(float64)
272 if math.Float64bits(f64) == 0 {
273 p.From.Type = obj.TYPE_REG
277 p.From.Type = obj.TYPE_MEM
278 p.From.Sym = c.ctxt.Float64Sym(f64)
279 p.From.Name = obj.NAME_EXTERN
286 // Rewrite negative immediates as positive immediates with
287 // complementary instruction.
289 case AADD, ASUB, ACMP, ACMN:
290 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
291 p.From.Offset = -p.From.Offset
292 p.As = complements[p.As]
294 case AADDW, ASUBW, ACMPW, ACMNW:
295 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
296 p.From.Offset = -p.From.Offset
297 p.As = complements[p.As]
301 // For 32-bit logical instruction with constant,
302 // rewrite the high 32-bit to be a repetition of
303 // the low 32-bit, so that the BITCON test can be
304 // shared for both 32-bit and 64-bit. 32-bit ops
305 // will zero the high 32-bit of the destination
308 case AANDW, AORRW, AEORW, AANDSW, ATSTW:
309 if p.From.Type == obj.TYPE_CONST {
310 v := p.From.Offset & 0xffffffff
311 p.From.Offset = v | v<<32
315 if c.ctxt.Flag_dynlink {
320 // Rewrite p, if necessary, to access global data via the global offset table.
321 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
322 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
325 // MOVD runtime.duffxxx@GOT, REGTMP
326 // ADD $offset, REGTMP
329 if p.As == obj.ADUFFZERO {
330 sym = c.ctxt.Lookup("runtime.duffzero")
332 sym = c.ctxt.Lookup("runtime.duffcopy")
334 offset := p.To.Offset
336 p.From.Type = obj.TYPE_MEM
337 p.From.Name = obj.NAME_GOTREF
339 p.To.Type = obj.TYPE_REG
341 p.To.Name = obj.NAME_NONE
344 p1 := obj.Appendp(p, c.newprog)
346 p1.From.Type = obj.TYPE_CONST
347 p1.From.Offset = offset
348 p1.To.Type = obj.TYPE_REG
350 p2 := obj.Appendp(p1, c.newprog)
352 p2.To.Type = obj.TYPE_REG
356 // We only care about global data: NAME_EXTERN means a global
357 // symbol in the Go sense, and p.Sym.Local is true for a few
358 // internally defined symbols.
359 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
360 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
361 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
363 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
365 if p.To.Type != obj.TYPE_REG {
366 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
368 p.From.Type = obj.TYPE_MEM
369 p.From.Name = obj.NAME_GOTREF
370 if p.From.Offset != 0 {
371 q := obj.Appendp(p, c.newprog)
373 q.From.Type = obj.TYPE_CONST
374 q.From.Offset = p.From.Offset
379 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
380 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
383 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
384 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
385 // An addition may be inserted between the two MOVs if there is an offset.
386 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
387 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
388 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
391 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
396 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
399 if source.Sym.Type == objabi.STLSBSS {
402 if source.Type != obj.TYPE_MEM {
403 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
405 p1 := obj.Appendp(p, c.newprog)
406 p2 := obj.Appendp(p1, c.newprog)
408 p1.From.Type = obj.TYPE_MEM
409 p1.From.Sym = source.Sym
410 p1.From.Name = obj.NAME_GOTREF
411 p1.To.Type = obj.TYPE_REG
417 if p.From.Name == obj.NAME_EXTERN {
419 p2.From.Name = obj.NAME_NONE
421 } else if p.To.Name == obj.NAME_EXTERN {
423 p2.To.Name = obj.NAME_NONE
431 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
432 if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
436 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
438 p := c.cursym.Func.Text
439 textstksiz := p.To.Offset
440 if textstksiz == -8 {
441 // Historical way to mark NOFRAME.
442 p.From.Sym.Set(obj.AttrNoFrame, true)
446 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
448 if p.From.Sym.NoFrame() {
450 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
454 c.cursym.Func.Args = p.To.Val.(int32)
455 c.cursym.Func.Locals = int32(textstksiz)
458 * find leaf subroutines
462 q := (*obj.Prog)(nil)
464 for p := c.cursym.Func.Text; p != nil; p = p.Link {
475 q.Link = q1 /* q is non-nop */
483 c.cursym.Func.Text.Mark &^= LEAF
514 for q1.As == obj.ANOP {
527 for p := c.cursym.Func.Text; p != nil; p = p.Link {
531 c.cursym.Func.Text = p
532 c.autosize = int32(textstksiz)
534 if p.Mark&LEAF != 0 && c.autosize == 0 {
535 // A leaf function with no locals has no frame.
536 p.From.Sym.Set(obj.AttrNoFrame, true)
539 if !p.From.Sym.NoFrame() {
540 // If there is a stack frame at all, it includes
541 // space to save the LR.
545 if c.autosize != 0 && c.autosize&(16-1) != 0 {
546 // The frame includes an LR.
547 // If the frame size is 8, it's only an LR,
548 // so there's no potential for breaking references to
549 // local variables by growing the frame size,
550 // because there are no local variables.
551 // But otherwise, if there is a non-empty locals section,
552 // the author of the code is responsible for making sure
553 // that the frame size is 8 mod 16.
556 c.cursym.Func.Locals += 8
558 c.ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, c.autosize-8)
561 if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
562 if c.ctxt.Debugvlog {
563 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
565 c.cursym.Func.Text.Mark |= LEAF
568 // FP offsets need an updated p.To.Offset.
569 p.To.Offset = int64(c.autosize) - 8
571 if cursym.Func.Text.Mark&LEAF != 0 {
572 cursym.Set(obj.AttrLeaf, true)
573 if p.From.Sym.NoFrame() {
578 if !p.From.Sym.NoSplit() {
579 p = c.stacksplit(p, c.autosize) // emit split check
582 aoffset := c.autosize
587 // Frame is non-empty. Make sure to save link register, even if
588 // it is a leaf function, so that traceback works.
590 if c.autosize > aoffset {
591 // Frame size is too large for a MOVD.W instruction.
592 // Store link register before decrementing SP, so if a signal comes
593 // during the execution of the function prologue, the traceback
594 // code will not see a half-updated stack frame.
595 q = obj.Appendp(q, c.newprog)
598 q.From.Type = obj.TYPE_CONST
599 q.From.Offset = int64(c.autosize)
601 q.To.Type = obj.TYPE_REG
604 q = obj.Appendp(q, c.newprog)
607 q.From.Type = obj.TYPE_REG
609 q.To.Type = obj.TYPE_MEM
612 q1 = obj.Appendp(q, c.newprog)
615 q1.From.Type = obj.TYPE_REG
617 q1.To.Type = obj.TYPE_REG
619 q1.Spadj = c.autosize
621 // small frame, update SP and save LR in a single MOVD.W instruction
622 q1 = obj.Appendp(q, c.newprog)
625 q1.From.Type = obj.TYPE_REG
626 q1.From.Reg = REGLINK
627 q1.To.Type = obj.TYPE_MEM
629 q1.To.Offset = int64(-aoffset)
634 if c.cursym.Func.Text.From.Sym.Wrapper() {
635 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
637 // MOV g_panic(g), R1
641 // ... function body ...
643 // MOV panic_argp(R1), R2
644 // ADD $(autosize+8), RSP, R3
648 // MOVD R4, panic_argp(R1)
651 // The NOP is needed to give the jumps somewhere to land.
652 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
655 // MOV g_panic(g), R1
656 q = obj.Appendp(q, c.newprog)
658 q.From.Type = obj.TYPE_MEM
660 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
661 q.To.Type = obj.TYPE_REG
664 // CBNZ R1, checkargp
665 cbnz := obj.Appendp(q, c.newprog)
667 cbnz.From.Type = obj.TYPE_REG
668 cbnz.From.Reg = REG_R1
669 cbnz.To.Type = obj.TYPE_BRANCH
671 // Empty branch target at the top of the function body
672 end := obj.Appendp(cbnz, c.newprog)
675 // find the end of the function
677 for last = end; last.Link != nil; last = last.Link {
680 // MOV panic_argp(R1), R2
681 mov := obj.Appendp(last, c.newprog)
683 mov.From.Type = obj.TYPE_MEM
684 mov.From.Reg = REG_R1
685 mov.From.Offset = 0 // Panic.argp
686 mov.To.Type = obj.TYPE_REG
689 // CBNZ branches to the MOV above
692 // ADD $(autosize+8), SP, R3
693 q = obj.Appendp(mov, c.newprog)
695 q.From.Type = obj.TYPE_CONST
696 q.From.Offset = int64(c.autosize) + 8
698 q.To.Type = obj.TYPE_REG
702 q = obj.Appendp(q, c.newprog)
704 q.From.Type = obj.TYPE_REG
709 q = obj.Appendp(q, c.newprog)
711 q.To.Type = obj.TYPE_BRANCH
715 q = obj.Appendp(q, c.newprog)
717 q.From.Type = obj.TYPE_CONST
720 q.To.Type = obj.TYPE_REG
723 // MOV R4, panic_argp(R1)
724 q = obj.Appendp(q, c.newprog)
726 q.From.Type = obj.TYPE_REG
728 q.To.Type = obj.TYPE_MEM
730 q.To.Offset = 0 // Panic.argp
733 q = obj.Appendp(q, c.newprog)
735 q.To.Type = obj.TYPE_BRANCH
741 if p.From.Type == obj.TYPE_CONST {
742 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
748 if c.cursym.Func.Text.Mark&LEAF != 0 {
751 p.From.Type = obj.TYPE_CONST
752 p.From.Offset = int64(c.autosize)
753 p.To.Type = obj.TYPE_REG
755 p.Spadj = -c.autosize
758 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
759 aoffset := c.autosize
765 p.From.Type = obj.TYPE_MEM
767 p.From.Offset = int64(aoffset)
769 p.To.Type = obj.TYPE_REG
772 if c.autosize > aoffset {
775 q.From.Type = obj.TYPE_CONST
776 q.From.Offset = int64(c.autosize) - int64(aoffset)
777 q.To.Type = obj.TYPE_REG
780 q.Spadj = int32(-q.From.Offset)
787 if p.As != obj.ARET {
795 if retjmp != nil { // retjmp
797 p.To.Type = obj.TYPE_BRANCH
799 p.Spadj = +c.autosize
804 p.To.Type = obj.TYPE_MEM
807 p.Spadj = +c.autosize
810 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
812 p.Spadj = int32(-p.From.Offset)
814 p.Spadj = int32(+p.From.Offset)
819 case obj.AGETCALLERPC:
823 p.From.Type = obj.TYPE_REG
828 p.From.Type = obj.TYPE_MEM
835 func nocache(p *obj.Prog) {
841 var unaryDst = map[obj.As]bool{
849 var Linkarm64 = obj.LinkArch{
852 Preprocess: preprocess,
856 DWARFRegisters: ARM64DWARFRegisters,