1 // Inferno utils/6l/pass.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/6l/pass.c
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
41 func CanUse1InsnTLS(ctxt *obj.Link) bool {
43 // For android, we use a disgusting hack that assumes
44 // the thread-local storage slot for g is allocated
45 // using pthread_key_create with a fixed offset
46 // (see src/runtime/cgo/gcc_android_amd64.c).
47 // This makes access to the TLS storage (for g) doable
48 // with 1 instruction.
52 if ctxt.Arch.RegSize == 4 {
53 switch ctxt.Headtype {
64 switch ctxt.Headtype {
69 return !ctxt.Flag_shared
75 func progedit(ctxt *obj.Link, p *obj.Prog) {
76 // Maintain information about code generation mode.
78 ctxt.Mode = ctxt.Arch.RegSize * 8
80 p.Mode = int8(ctxt.Mode)
84 if p.From.Type == obj.TYPE_CONST || (p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_NONE) {
85 switch int(p.From.Offset) {
87 ctxt.Mode = int(p.From.Offset)
93 // Thread-local storage references use the TLS pseudo-register.
94 // As a register, TLS refers to the thread-local storage base, and it
95 // can only be loaded into another register:
99 // An offset from the thread-local storage base is written off(reg)(TLS*1).
100 // Semantically it is off(reg), but the (TLS*1) annotation marks this as
101 // indexing from the loaded TLS base. This emits a relocation so that
102 // if the linker needs to adjust the offset, it can. For example:
105 // MOVQ 0(AX)(TLS*1), CX // load g into CX
107 // On systems that support direct access to the TLS memory, this
108 // pair of instructions can be reduced to a direct TLS memory reference:
110 // MOVQ 0(TLS), CX // load g into CX
112 // The 2-instruction and 1-instruction forms correspond to the two code
113 // sequences for loading a TLS variable in the local exec model given in "ELF
114 // Handling For Thread-Local Storage".
116 // We apply this rewrite on systems that support the 1-instruction form.
117 // The decision is made using only the operating system and the -shared flag,
118 // not the link mode. If some link modes on a particular operating system
119 // require the 2-instruction form, then all builds for that operating system
120 // will use the 2-instruction form, so that the link mode decision can be
121 // delayed to link time.
123 // In this way, all supported systems use identical instructions to
124 // access TLS, and they are rewritten appropriately first here in
125 // liblink and then finally using relocations in the linker.
127 // When -shared is passed, we leave the code in the 2-instruction form but
128 // assemble (and relocate) them in different ways to generate the initial
129 // exec code sequence. It's a bit of a fluke that this is possible without
130 // rewriting the instructions more comprehensively, and it only does because
131 // we only support a single TLS variable (g).
133 if CanUse1InsnTLS(ctxt) {
134 // Reduce 2-instruction sequence to 1-instruction sequence.
137 // ... off(BX)(TLS*1) ...
142 // TODO(rsc): Remove the Hsolaris special case. It exists only to
143 // guarantee we are producing byte-identical binaries as before this code.
144 // But it should be unnecessary.
145 if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 && ctxt.Headtype != obj.Hsolaris {
148 if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 {
151 p.From.Index = REG_NONE
154 if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
157 p.To.Index = REG_NONE
160 // load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it
161 // as the 2-instruction sequence if necessary.
165 // MOVQ 0(BX)(TLS*1), BX
166 if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
167 q := obj.Appendp(ctxt, p)
170 q.From.Type = obj.TYPE_MEM
171 q.From.Reg = p.To.Reg
172 q.From.Index = REG_TLS
173 q.From.Scale = 2 // TODO: use 1
175 p.From.Type = obj.TYPE_REG
177 p.From.Index = REG_NONE
183 if ctxt.Headtype == obj.Hwindows && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 {
184 if p.From.Scale == 1 && p.From.Index == REG_TLS {
187 if p.To.Scale == 1 && p.To.Index == REG_TLS {
192 // Rewrite 0 to $0 in 3rd argument to CMPPS etc.
193 // That's what the tables expect.
195 case ACMPPD, ACMPPS, ACMPSD, ACMPSS:
196 if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE && p.To.Reg == REG_NONE && p.To.Index == REG_NONE && p.To.Sym == nil {
197 p.To.Type = obj.TYPE_CONST
201 // Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH.
203 case obj.ACALL, obj.AJMP, obj.ARET:
204 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
205 p.To.Type = obj.TYPE_BRANCH
209 // Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ.
210 if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Family == sys.AMD64 || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) {
214 p.From.Type = obj.TYPE_MEM
217 p.From.Type = obj.TYPE_MEM
221 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
223 nacladdr(ctxt, p, p.From3)
225 nacladdr(ctxt, p, &p.From)
226 nacladdr(ctxt, p, &p.To)
229 // Rewrite float constants to values stored in memory.
231 // Convert AMOVSS $(0), Xx to AXORPS Xx, Xx
233 if p.From.Type == obj.TYPE_FCONST {
234 // f == 0 can't be used here due to -0, so use Float64bits
235 if f := p.From.Val.(float64); math.Float64bits(f) == 0 {
236 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
260 if p.From.Type == obj.TYPE_FCONST {
261 f32 := float32(p.From.Val.(float64))
262 i32 := math.Float32bits(f32)
263 literal := fmt.Sprintf("$f32.%08x", i32)
264 s := obj.Linklookup(ctxt, literal, 0)
265 p.From.Type = obj.TYPE_MEM
266 p.From.Name = obj.NAME_EXTERN
268 p.From.Sym.Local = true
273 // Convert AMOVSD $(0), Xx to AXORPS Xx, Xx
274 if p.From.Type == obj.TYPE_FCONST {
275 // f == 0 can't be used here due to -0, so use Float64bits
276 if f := p.From.Val.(float64); math.Float64bits(f) == 0 {
277 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
301 if p.From.Type == obj.TYPE_FCONST {
302 i64 := math.Float64bits(p.From.Val.(float64))
303 literal := fmt.Sprintf("$f64.%016x", i64)
304 s := obj.Linklookup(ctxt, literal, 0)
305 p.From.Type = obj.TYPE_MEM
306 p.From.Name = obj.NAME_EXTERN
308 p.From.Sym.Local = true
313 if ctxt.Flag_dynlink {
314 rewriteToUseGot(ctxt, p)
317 if ctxt.Flag_shared && p.Mode == 32 {
318 rewriteToPcrel(ctxt, p)
322 // Rewrite p, if necessary, to access global data via the global offset table.
323 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
324 var add, lea, mov obj.As
338 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
341 // $MOV runtime.duffxxx@GOT, $reg
342 // $ADD $offset, $reg
345 if p.As == obj.ADUFFZERO {
346 sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
348 sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
350 offset := p.To.Offset
352 p.From.Type = obj.TYPE_MEM
353 p.From.Name = obj.NAME_GOTREF
355 p.To.Type = obj.TYPE_REG
359 p1 := obj.Appendp(ctxt, p)
361 p1.From.Type = obj.TYPE_CONST
362 p1.From.Offset = offset
363 p1.To.Type = obj.TYPE_REG
365 p2 := obj.Appendp(ctxt, p1)
367 p2.To.Type = obj.TYPE_REG
371 // We only care about global data: NAME_EXTERN means a global
372 // symbol in the Go sense, and p.Sym.Local is true for a few
373 // internally defined symbols.
374 if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
375 // $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below
377 p.From.Type = obj.TYPE_ADDR
379 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
380 // $MOV $sym, Rx becomes $MOV sym@GOT, Rx
381 // $MOV $sym+<off>, Rx becomes $MOV sym@GOT, Rx; $LEA <off>(Rx), Rx
382 // On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX
386 if p.To.Type != obj.TYPE_REG || pAs != mov {
388 ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
393 p.To.Type = obj.TYPE_REG
396 p.To.Name = obj.NAME_NONE
398 p.From.Type = obj.TYPE_MEM
399 p.From.Name = obj.NAME_GOTREF
401 if p.From.Offset != 0 {
402 q = obj.Appendp(ctxt, p)
404 q.From.Type = obj.TYPE_MEM
405 q.From.Reg = p.To.Reg
406 q.From.Offset = p.From.Offset
411 q = obj.Appendp(ctxt, q)
414 q.From.Type = obj.TYPE_REG
418 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
419 ctxt.Diag("don't know how to handle %v with -dynlink", p)
422 // MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry
423 // MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15)
424 // An addition may be inserted between the two MOVs if there is an offset.
425 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
426 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
427 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
430 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
435 if p.As == obj.ACALL {
436 // When dynlinking on 386, almost any call might end up being a call
437 // to a PLT, so make sure the GOT pointer is loaded into BX.
438 // RegTo2 is set on the replacement call insn to stop it being
439 // processed when it is in turn passed to progedit.
440 if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local) || p.RegTo2 != 0 {
443 p1 := obj.Appendp(ctxt, p)
444 p2 := obj.Appendp(ctxt, p1)
447 p1.From.Type = obj.TYPE_MEM
448 p1.From.Name = obj.NAME_STATIC
449 p1.From.Sym = obj.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
450 p1.To.Type = obj.TYPE_REG
459 // p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr
460 // in ../pass.go complain, so set it back to TYPE_MEM here, until p2
461 // itself gets passed to progedit.
462 p2.To.Type = obj.TYPE_MEM
469 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP {
472 if source.Type != obj.TYPE_MEM {
473 ctxt.Diag("don't know how to handle %v with -dynlink", p)
475 p1 := obj.Appendp(ctxt, p)
476 p2 := obj.Appendp(ctxt, p1)
479 p1.From.Type = obj.TYPE_MEM
480 p1.From.Sym = source.Sym
481 p1.From.Name = obj.NAME_GOTREF
482 p1.To.Type = obj.TYPE_REG
488 if p.From.Name == obj.NAME_EXTERN {
490 p2.From.Name = obj.NAME_NONE
492 } else if p.To.Name == obj.NAME_EXTERN {
494 p2.To.Name = obj.NAME_NONE
502 func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
503 // RegTo2 is set on the instructions we insert here so they don't get
508 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
511 // Any Prog (aside from the above special cases) with an Addr with Name ==
512 // NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.cx
513 // inserted before it.
514 isName := func(a *obj.Addr) bool {
515 if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 {
518 if a.Sym.Type == obj.STLSBSS {
521 return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF
524 if isName(&p.From) && p.From.Type == obj.TYPE_ADDR {
525 // Handle things like "MOVL $sym, (SP)" or "PUSHL $sym" by rewriting
526 // to "MOVL $sym, CX; MOVL CX, (SP)" or "MOVL $sym, CX; PUSHL CX"
528 if p.To.Type != obj.TYPE_REG {
529 q := obj.Appendp(ctxt, p)
531 q.From.Type = obj.TYPE_REG
535 p.To.Type = obj.TYPE_REG
538 p.To.Name = obj.NAME_NONE
542 if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) {
545 q := obj.Appendp(ctxt, p)
547 r := obj.Appendp(ctxt, q)
550 q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0)
551 q.To.Type = obj.TYPE_MEM
552 q.To.Name = obj.NAME_EXTERN
553 q.To.Sym.Local = true
563 func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
564 if p.As == ALEAL || p.As == ALEAQ {
569 ctxt.Diag("invalid address: %v", p)
573 if a.Reg == REG_TLS {
576 if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE {
579 case REG_BP, REG_SP, REG_R15:
583 if a.Index != REG_NONE {
584 ctxt.Diag("invalid address %v", p)
587 if a.Index != REG_NONE {
595 func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
596 if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil {
597 ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0)
602 if cursym.Text == nil || cursym.Text.Link == nil {
607 autoffset := int32(p.To.Offset)
613 if p.Mode == 64 && obj.Framepointer_enabled != 0 && autoffset > 0 {
614 // Make room for to save a base pointer. If autoffset == 0,
615 // this might do something special like a tail jump to
616 // another function, so in that case we omit this.
617 bpsize = ctxt.Arch.PtrSize
619 autoffset += int32(bpsize)
620 p.To.Offset += int64(bpsize)
625 textarg := int64(p.To.Val.(int32))
626 cursym.Args = int32(textarg)
627 cursym.Locals = int32(p.To.Offset)
629 // TODO(rsc): Remove.
630 if p.Mode == 32 && cursym.Locals < 0 {
634 // TODO(rsc): Remove 'p.Mode == 64 &&'.
635 if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 {
636 for q := p; q != nil; q = q.Link {
637 if q.As == obj.ACALL {
640 if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 {
645 p.From3.Offset |= obj.NOSPLIT
649 if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 {
650 p = obj.Appendp(ctxt, p)
651 p = load_g_cx(ctxt, p) // load g into CX
654 if cursym.Text.From3Offset()&obj.NOSPLIT == 0 {
655 p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check
659 if autoffset%int32(ctxt.Arch.RegSize) != 0 {
660 ctxt.Diag("unaligned stack size %d", autoffset)
662 p = obj.Appendp(ctxt, p)
664 p.From.Type = obj.TYPE_CONST
665 p.From.Offset = int64(autoffset)
668 // zero-byte stack adjustment.
669 // Insert a fake non-zero adjustment so that stkcheck can
670 // recognize the end of the stack-splitting prolog.
671 p = obj.Appendp(ctxt, p)
674 p.Spadj = int32(-ctxt.Arch.PtrSize)
675 p = obj.Appendp(ctxt, p)
677 p.Spadj = int32(ctxt.Arch.PtrSize)
684 p = obj.Appendp(ctxt, p)
687 p.From.Type = obj.TYPE_REG
689 p.To.Type = obj.TYPE_MEM
692 p.To.Offset = int64(autoffset) - int64(bpsize)
694 // Move current frame to BP
695 p = obj.Appendp(ctxt, p)
698 p.From.Type = obj.TYPE_MEM
701 p.From.Offset = int64(autoffset) - int64(bpsize)
702 p.To.Type = obj.TYPE_REG
706 if cursym.Text.From3Offset()&obj.WRAPPER != 0 {
707 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
709 // MOVQ g_panic(CX), BX
712 // LEAQ (autoffset+8)(SP), DI
713 // CMPQ panic_argp(BX), DI
715 // MOVQ SP, panic_argp(BX)
719 // The NOP is needed to give the jumps somewhere to land.
720 // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
722 p = obj.Appendp(ctxt, p)
725 p.From.Type = obj.TYPE_MEM
727 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
728 p.To.Type = obj.TYPE_REG
730 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
732 p.From.Type = obj.TYPE_MEM
735 p.From.Index = REG_CX
741 p = obj.Appendp(ctxt, p)
743 p.From.Type = obj.TYPE_REG
745 p.To.Type = obj.TYPE_REG
747 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
751 p = obj.Appendp(ctxt, p)
753 p.To.Type = obj.TYPE_BRANCH
756 p = obj.Appendp(ctxt, p)
758 p.From.Type = obj.TYPE_MEM
760 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize)
761 p.To.Type = obj.TYPE_REG
763 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
767 p = obj.Appendp(ctxt, p)
769 p.From.Type = obj.TYPE_MEM
771 p.From.Offset = 0 // Panic.argp
772 p.To.Type = obj.TYPE_REG
774 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
776 p.From.Type = obj.TYPE_MEM
779 p.From.Index = REG_BX
785 p = obj.Appendp(ctxt, p)
787 p.To.Type = obj.TYPE_BRANCH
790 p = obj.Appendp(ctxt, p)
792 p.From.Type = obj.TYPE_REG
794 p.To.Type = obj.TYPE_MEM
796 p.To.Offset = 0 // Panic.argp
797 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
799 p.To.Type = obj.TYPE_MEM
808 p = obj.Appendp(ctxt, p)
816 for ; p != nil; p = p.Link {
817 pcsize = int(p.Mode) / 8
819 if a == obj.NAME_AUTO {
820 p.From.Offset += int64(deltasp) - int64(bpsize)
822 if a == obj.NAME_PARAM {
823 p.From.Offset += int64(deltasp) + int64(pcsize)
826 a = int(p.From3.Name)
827 if a == obj.NAME_AUTO {
828 p.From3.Offset += int64(deltasp) - int64(bpsize)
830 if a == obj.NAME_PARAM {
831 p.From3.Offset += int64(deltasp) + int64(pcsize)
835 if a == obj.NAME_AUTO {
836 p.To.Offset += int64(deltasp) - int64(bpsize)
838 if a == obj.NAME_PARAM {
839 p.To.Offset += int64(deltasp) + int64(pcsize)
846 case APUSHL, APUSHFL:
851 case APUSHQ, APUSHFQ:
856 case APUSHW, APUSHFW:
880 if autoffset != deltasp {
881 ctxt.Diag("unbalanced PUSH/POP")
886 // Restore caller's BP
889 p.From.Type = obj.TYPE_MEM
892 p.From.Offset = int64(autoffset) - int64(bpsize)
893 p.To.Type = obj.TYPE_REG
895 p = obj.Appendp(ctxt, p)
899 p.From.Type = obj.TYPE_CONST
900 p.From.Offset = int64(-autoffset)
902 p = obj.Appendp(ctxt, p)
905 // If there are instructions following
906 // this ARET, they come from a branch
907 // with the same stackframe, so undo
912 if p.To.Sym != nil { // retjmp
918 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
919 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
920 a.Type = obj.TYPE_MEM
927 a.Type = obj.TYPE_MEM
931 // Append code to p to load g into cx.
932 // Overwrites p with the first instruction (no first appendp).
933 // Overwriting p is unusual but it lets use this in both the
934 // prologue (caller must call appendp first) and in the epilogue.
935 // Returns last new instruction.
936 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
938 if ctxt.Arch.PtrSize == 4 {
941 p.From.Type = obj.TYPE_MEM
944 p.To.Type = obj.TYPE_REG
953 if p.From.Index == REG_TLS {
960 // Append code to p to check for stack split.
961 // Appends to (does not overwrite) p.
962 // Assumes g is in CX.
963 // Returns last new instruction.
964 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog {
970 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
978 if framesize <= obj.StackSmall {
979 // small stack: SP <= stackguard
980 // CMPQ SP, stackguard
981 p = obj.Appendp(ctxt, p)
984 p.From.Type = obj.TYPE_REG
986 indir_cx(ctxt, p, &p.To)
987 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
988 if ctxt.Cursym.Cfunc {
989 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
991 } else if framesize <= obj.StackBig {
992 // large stack: SP-framesize <= stackguard-StackSmall
994 // CMPQ AX, stackguard
995 p = obj.Appendp(ctxt, p)
998 p.From.Type = obj.TYPE_MEM
1000 p.From.Offset = -(int64(framesize) - obj.StackSmall)
1001 p.To.Type = obj.TYPE_REG
1004 p = obj.Appendp(ctxt, p)
1006 p.From.Type = obj.TYPE_REG
1008 indir_cx(ctxt, p, &p.To)
1009 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
1010 if ctxt.Cursym.Cfunc {
1011 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
1014 // Such a large stack we need to protect against wraparound.
1015 // If SP is close to zero:
1016 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
1017 // The +StackGuard on both sides is required to keep the left side positive:
1018 // SP is allowed to be slightly below stackguard. See stack.h.
1020 // Preemption sets stackguard to StackPreempt, a very large value.
1021 // That breaks the math above, so we have to check for that explicitly.
1022 // MOVQ stackguard, CX
1023 // CMPQ CX, $StackPreempt
1024 // JEQ label-of-call-to-morestack
1025 // LEAQ StackGuard(SP), AX
1027 // CMPQ AX, $(framesize+(StackGuard-StackSmall))
1029 p = obj.Appendp(ctxt, p)
1032 indir_cx(ctxt, p, &p.From)
1033 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
1034 if ctxt.Cursym.Cfunc {
1035 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
1037 p.To.Type = obj.TYPE_REG
1040 p = obj.Appendp(ctxt, p)
1042 p.From.Type = obj.TYPE_REG
1044 p.To.Type = obj.TYPE_CONST
1045 p.To.Offset = obj.StackPreempt
1047 p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
1050 p = obj.Appendp(ctxt, p)
1052 p.To.Type = obj.TYPE_BRANCH
1055 p = obj.Appendp(ctxt, p)
1057 p.From.Type = obj.TYPE_MEM
1059 p.From.Offset = obj.StackGuard
1060 p.To.Type = obj.TYPE_REG
1063 p = obj.Appendp(ctxt, p)
1065 p.From.Type = obj.TYPE_REG
1067 p.To.Type = obj.TYPE_REG
1070 p = obj.Appendp(ctxt, p)
1072 p.From.Type = obj.TYPE_REG
1074 p.To.Type = obj.TYPE_CONST
1075 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
1079 jls := obj.Appendp(ctxt, p)
1081 jls.To.Type = obj.TYPE_BRANCH
1084 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
1087 spfix := obj.Appendp(ctxt, last)
1089 spfix.Spadj = -framesize
1091 call := obj.Appendp(ctxt, spfix)
1092 call.Lineno = ctxt.Cursym.Text.Lineno
1093 call.Mode = ctxt.Cursym.Text.Mode
1095 call.To.Type = obj.TYPE_BRANCH
1096 morestack := "runtime.morestack"
1098 case ctxt.Cursym.Cfunc:
1099 morestack = "runtime.morestackc"
1100 case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
1101 morestack = "runtime.morestack_noctxt"
1103 call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
1105 jmp := obj.Appendp(ctxt, call)
1107 jmp.To.Type = obj.TYPE_BRANCH
1108 jmp.Pcond = ctxt.Cursym.Text.Link
1109 jmp.Spadj = +framesize
1119 func follow(ctxt *obj.Link, s *obj.LSym) {
1122 firstp := ctxt.NewProg()
1124 xfol(ctxt, s.Text, &lastp)
1126 s.Text = firstp.Link
1129 func nofollow(a obj.As) bool {
1146 func pushpop(a obj.As) bool {
1166 func relinv(a obj.As) obj.As {
1202 log.Fatalf("unknown relation: %s", obj.Aconv(a))
1206 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
1215 if p.As == obj.AJMP {
1217 if q != nil && q.As != obj.ATEXT {
1218 /* mark instruction as done and continue layout at target of jump */
1222 if p.Mark&DONE == 0 {
1228 if p.Mark&DONE != 0 {
1230 * p goes here, but already used it elsewhere.
1231 * copy up to 4 instructions or else branch to other copy.
1235 for ; i < 4; i, q = i+1, q.Link {
1248 if nofollow(a) || pushpop(a) {
1249 break // NOTE(rsc): arm does goto copy
1251 if q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
1254 if a == obj.ACALL || a == ALOOP {
1258 if p.As == obj.ANOP {
1263 q = obj.Copyp(ctxt, p)
1268 if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
1276 xfol(ctxt, q.Link, last)
1278 if p.Mark&DONE != 0 {
1288 q.To.Type = obj.TYPE_BRANCH
1301 /* continue loop with what comes after p */
1305 if p.Pcond != nil && a != obj.ACALL {
1307 * some kind of conditional branch.
1308 * recurse to follow one path.
1309 * continue loop on the other.
1311 q = obj.Brchain(ctxt, p.Pcond)
1315 q = obj.Brchain(ctxt, p.Link)
1319 if p.From.Type == obj.TYPE_CONST {
1320 if p.From.Offset == 1 {
1322 * expect conditional jump to be taken.
1323 * rewrite so that's the fall-through case.
1333 if q.Mark&DONE != 0 {
1342 xfol(ctxt, p.Link, last)
1343 if p.Pcond.Mark&DONE != 0 {
1354 var unaryDst = map[obj.As]bool{
1404 var Linkamd64 = obj.LinkArch{
1405 Arch: sys.ArchAMD64,
1406 Preprocess: preprocess,
1413 var Linkamd64p32 = obj.LinkArch{
1414 Arch: sys.ArchAMD64P32,
1415 Preprocess: preprocess,
1422 var Link386 = obj.LinkArch{
1424 Preprocess: preprocess,