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 := obj.Appendp(spfix, c.newprog)
171 pcdata.Pos = c.cursym.Func.Text.Pos
172 pcdata.As = obj.APCDATA
173 pcdata.From.Type = obj.TYPE_CONST
174 pcdata.From.Offset = objabi.PCDATA_StackMapIndex
175 pcdata.To.Type = obj.TYPE_CONST
176 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
179 movlr := obj.Appendp(pcdata, c.newprog)
181 movlr.From.Type = obj.TYPE_REG
182 movlr.From.Reg = REGLINK
183 movlr.To.Type = obj.TYPE_REG
184 movlr.To.Reg = REG_R3
192 debug = obj.Appendp(debug, c.newprog)
194 debug.From.Type = obj.TYPE_CONST
195 debug.From.Offset = int64(framesize)
196 debug.To.Type = obj.TYPE_REG
197 debug.To.Reg = REGTMP
200 // BL runtime.morestack(SB)
201 call := obj.Appendp(debug, c.newprog)
203 call.To.Type = obj.TYPE_BRANCH
204 morestack := "runtime.morestack"
206 case c.cursym.CFunc():
207 morestack = "runtime.morestackc"
208 case !c.cursym.Func.Text.From.Sym.NeedCtxt():
209 morestack = "runtime.morestack_noctxt"
211 call.To.Sym = c.ctxt.Lookup(morestack)
214 jmp := obj.Appendp(call, c.newprog)
216 jmp.To.Type = obj.TYPE_BRANCH
217 jmp.Pcond = c.cursym.Func.Text.Link
218 jmp.Spadj = +framesize
220 // placeholder for bls's jump target
221 // p = obj.Appendp(ctxt, p)
227 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
228 c := ctxt7{ctxt: ctxt, newprog: newprog}
233 // $0 results in C_ZCON, which matches both C_REG and various
234 // C_xCON, however the C_REG cases in asmout don't expect a
235 // constant, so they will use the register fields and assemble
236 // a R0. To prevent that, rewrite $0 as ZR.
237 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
238 p.From.Type = obj.TYPE_REG
241 if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
242 p.To.Type = obj.TYPE_REG
246 // Rewrite BR/BL to symbol as TYPE_BRANCH.
254 p.To.Type = obj.TYPE_BRANCH
259 // Rewrite float constants to values stored in memory.
262 if p.From.Type == obj.TYPE_FCONST {
263 f32 := float32(p.From.Val.(float64))
264 if math.Float32bits(f32) == 0 {
265 p.From.Type = obj.TYPE_REG
269 p.From.Type = obj.TYPE_MEM
270 p.From.Sym = c.ctxt.Float32Sym(f32)
271 p.From.Name = obj.NAME_EXTERN
276 if p.From.Type == obj.TYPE_FCONST {
277 f64 := p.From.Val.(float64)
278 if math.Float64bits(f64) == 0 {
279 p.From.Type = obj.TYPE_REG
283 p.From.Type = obj.TYPE_MEM
284 p.From.Sym = c.ctxt.Float64Sym(f64)
285 p.From.Name = obj.NAME_EXTERN
292 // Rewrite negative immediates as positive immediates with
293 // complementary instruction.
295 case AADD, ASUB, ACMP, ACMN:
296 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
297 p.From.Offset = -p.From.Offset
298 p.As = complements[p.As]
300 case AADDW, ASUBW, ACMPW, ACMNW:
301 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
302 p.From.Offset = -p.From.Offset
303 p.As = complements[p.As]
307 // For 32-bit logical instruction with constant,
308 // rewrite the high 32-bit to be a repetition of
309 // the low 32-bit, so that the BITCON test can be
310 // shared for both 32-bit and 64-bit. 32-bit ops
311 // will zero the high 32-bit of the destination
314 case AANDW, AORRW, AEORW, AANDSW:
315 if p.From.Type == obj.TYPE_CONST {
316 v := p.From.Offset & 0xffffffff
317 p.From.Offset = v | v<<32
321 if c.ctxt.Flag_dynlink {
326 // Rewrite p, if necessary, to access global data via the global offset table.
327 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
328 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
331 // MOVD runtime.duffxxx@GOT, REGTMP
332 // ADD $offset, REGTMP
335 if p.As == obj.ADUFFZERO {
336 sym = c.ctxt.Lookup("runtime.duffzero")
338 sym = c.ctxt.Lookup("runtime.duffcopy")
340 offset := p.To.Offset
342 p.From.Type = obj.TYPE_MEM
343 p.From.Name = obj.NAME_GOTREF
345 p.To.Type = obj.TYPE_REG
347 p.To.Name = obj.NAME_NONE
350 p1 := obj.Appendp(p, c.newprog)
352 p1.From.Type = obj.TYPE_CONST
353 p1.From.Offset = offset
354 p1.To.Type = obj.TYPE_REG
356 p2 := obj.Appendp(p1, c.newprog)
358 p2.To.Type = obj.TYPE_REG
362 // We only care about global data: NAME_EXTERN means a global
363 // symbol in the Go sense, and p.Sym.Local is true for a few
364 // internally defined symbols.
365 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
366 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
367 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
369 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
371 if p.To.Type != obj.TYPE_REG {
372 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
374 p.From.Type = obj.TYPE_MEM
375 p.From.Name = obj.NAME_GOTREF
376 if p.From.Offset != 0 {
377 q := obj.Appendp(p, c.newprog)
379 q.From.Type = obj.TYPE_CONST
380 q.From.Offset = p.From.Offset
385 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
386 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
389 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
390 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
391 // An addition may be inserted between the two MOVs if there is an offset.
392 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
393 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
394 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
397 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
402 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
405 if source.Sym.Type == objabi.STLSBSS {
408 if source.Type != obj.TYPE_MEM {
409 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
411 p1 := obj.Appendp(p, c.newprog)
412 p2 := obj.Appendp(p1, c.newprog)
414 p1.From.Type = obj.TYPE_MEM
415 p1.From.Sym = source.Sym
416 p1.From.Name = obj.NAME_GOTREF
417 p1.To.Type = obj.TYPE_REG
423 if p.From.Name == obj.NAME_EXTERN {
425 p2.From.Name = obj.NAME_NONE
427 } else if p.To.Name == obj.NAME_EXTERN {
429 p2.To.Name = obj.NAME_NONE
437 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
438 if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
442 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
444 p := c.cursym.Func.Text
445 textstksiz := p.To.Offset
446 if textstksiz == -8 {
447 // Historical way to mark NOFRAME.
448 p.From.Sym.Set(obj.AttrNoFrame, true)
452 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
454 if p.From.Sym.NoFrame() {
456 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
460 c.cursym.Func.Args = p.To.Val.(int32)
461 c.cursym.Func.Locals = int32(textstksiz)
464 * find leaf subroutines
468 q := (*obj.Prog)(nil)
470 for p := c.cursym.Func.Text; p != nil; p = p.Link {
480 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.
549 if c.autosize != 0 && c.autosize&(16-1) != 0 {
550 // The frame includes an LR.
551 // If the frame size is 8, it's only an LR,
552 // so there's no potential for breaking references to
553 // local variables by growing the frame size,
554 // because there are no local variables.
555 // But otherwise, if there is a non-empty locals section,
556 // the author of the code is responsible for making sure
557 // that the frame size is 8 mod 16.
560 c.cursym.Func.Locals += 8
562 c.ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, c.autosize-8)
565 if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
566 if c.ctxt.Debugvlog {
567 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
569 c.cursym.Func.Text.Mark |= LEAF
572 // FP offsets need an updated p.To.Offset.
573 p.To.Offset = int64(c.autosize) - 8
575 if cursym.Func.Text.Mark&LEAF != 0 {
576 cursym.Set(obj.AttrLeaf, true)
577 if p.From.Sym.NoFrame() {
582 if !p.From.Sym.NoSplit() {
583 p = c.stacksplit(p, c.autosize) // emit split check
586 aoffset := c.autosize
591 // Frame is non-empty. Make sure to save link register, even if
592 // it is a leaf function, so that traceback works.
594 if c.autosize > aoffset {
595 // Frame size is too large for a MOVD.W instruction.
596 // Store link register before decrementing SP, so if a signal comes
597 // during the execution of the function prologue, the traceback
598 // code will not see a half-updated stack frame.
599 q = obj.Appendp(q, c.newprog)
602 q.From.Type = obj.TYPE_CONST
603 q.From.Offset = int64(c.autosize)
605 q.To.Type = obj.TYPE_REG
608 q = obj.Appendp(q, c.newprog)
611 q.From.Type = obj.TYPE_REG
613 q.To.Type = obj.TYPE_MEM
616 q1 = obj.Appendp(q, c.newprog)
619 q1.From.Type = obj.TYPE_REG
621 q1.To.Type = obj.TYPE_REG
623 q1.Spadj = c.autosize
625 // small frame, update SP and save LR in a single MOVD.W instruction
626 q1 = obj.Appendp(q, c.newprog)
629 q1.From.Type = obj.TYPE_REG
630 q1.From.Reg = REGLINK
631 q1.To.Type = obj.TYPE_MEM
633 q1.To.Offset = int64(-aoffset)
638 if c.cursym.Func.Text.From.Sym.Wrapper() {
639 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
641 // MOV g_panic(g), R1
645 // ... function body ...
647 // MOV panic_argp(R1), R2
648 // ADD $(autosize+8), RSP, R3
652 // MOVD R4, panic_argp(R1)
655 // The NOP is needed to give the jumps somewhere to land.
656 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
659 // MOV g_panic(g), R1
660 q = obj.Appendp(q, c.newprog)
662 q.From.Type = obj.TYPE_MEM
664 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
665 q.To.Type = obj.TYPE_REG
668 // CBNZ R1, checkargp
669 cbnz := obj.Appendp(q, c.newprog)
671 cbnz.From.Type = obj.TYPE_REG
672 cbnz.From.Reg = REG_R1
673 cbnz.To.Type = obj.TYPE_BRANCH
675 // Empty branch target at the top of the function body
676 end := obj.Appendp(cbnz, c.newprog)
679 // find the end of the function
681 for last = end; last.Link != nil; last = last.Link {
684 // MOV panic_argp(R1), R2
685 mov := obj.Appendp(last, c.newprog)
687 mov.From.Type = obj.TYPE_MEM
688 mov.From.Reg = REG_R1
689 mov.From.Offset = 0 // Panic.argp
690 mov.To.Type = obj.TYPE_REG
693 // CBNZ branches to the MOV above
696 // ADD $(autosize+8), SP, R3
697 q = obj.Appendp(mov, c.newprog)
699 q.From.Type = obj.TYPE_CONST
700 q.From.Offset = int64(c.autosize) + 8
702 q.To.Type = obj.TYPE_REG
706 q = obj.Appendp(q, c.newprog)
708 q.From.Type = obj.TYPE_REG
713 q = obj.Appendp(q, c.newprog)
715 q.To.Type = obj.TYPE_BRANCH
719 q = obj.Appendp(q, c.newprog)
721 q.From.Type = obj.TYPE_CONST
724 q.To.Type = obj.TYPE_REG
727 // MOV R4, panic_argp(R1)
728 q = obj.Appendp(q, c.newprog)
730 q.From.Type = obj.TYPE_REG
732 q.To.Type = obj.TYPE_MEM
734 q.To.Offset = 0 // Panic.argp
737 q = obj.Appendp(q, c.newprog)
739 q.To.Type = obj.TYPE_BRANCH
745 if p.From.Type == obj.TYPE_CONST {
746 c.ctxt.Diag("using BECOME (%v) is not supported!", p)
752 if c.cursym.Func.Text.Mark&LEAF != 0 {
755 p.From.Type = obj.TYPE_CONST
756 p.From.Offset = int64(c.autosize)
757 p.To.Type = obj.TYPE_REG
759 p.Spadj = -c.autosize
762 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
763 aoffset := c.autosize
769 p.From.Type = obj.TYPE_MEM
771 p.From.Offset = int64(aoffset)
773 p.To.Type = obj.TYPE_REG
776 if c.autosize > aoffset {
779 q.From.Type = obj.TYPE_CONST
780 q.From.Offset = int64(c.autosize) - int64(aoffset)
781 q.To.Type = obj.TYPE_REG
784 q.Spadj = int32(-q.From.Offset)
791 if p.As != obj.ARET {
799 if retjmp != nil { // retjmp
801 p.To.Type = obj.TYPE_BRANCH
803 p.Spadj = +c.autosize
808 p.To.Type = obj.TYPE_MEM
811 p.Spadj = +c.autosize
814 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
816 p.Spadj = int32(-p.From.Offset)
818 p.Spadj = int32(+p.From.Offset)
826 func nocache(p *obj.Prog) {
832 var unaryDst = map[obj.As]bool{
840 var Linkarm64 = obj.LinkArch{
843 Preprocess: preprocess,