]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/internal/obj/x86/obj6.go
all: make copyright headers consistent with one space after period
[gostls13.git] / src / cmd / internal / obj / x86 / obj6.go
1 // Inferno utils/6l/pass.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/6l/pass.c
3 //
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.
12 //
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:
19 //
20 // The above copyright notice and this permission notice shall be included in
21 // all copies or substantial portions of the Software.
22 //
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
29 // THE SOFTWARE.
30
31 package x86
32
33 import (
34         "cmd/internal/obj"
35         "cmd/internal/sys"
36         "fmt"
37         "log"
38         "math"
39 )
40
41 func CanUse1InsnTLS(ctxt *obj.Link) bool {
42         if isAndroid {
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.
49                 return true
50         }
51
52         if ctxt.Arch.RegSize == 4 {
53                 switch ctxt.Headtype {
54                 case obj.Hlinux,
55                         obj.Hnacl,
56                         obj.Hplan9,
57                         obj.Hwindows:
58                         return false
59                 }
60
61                 return true
62         }
63
64         switch ctxt.Headtype {
65         case obj.Hplan9,
66                 obj.Hwindows:
67                 return false
68         case obj.Hlinux:
69                 return !ctxt.Flag_shared
70         }
71
72         return true
73 }
74
75 func progedit(ctxt *obj.Link, p *obj.Prog) {
76         // Maintain information about code generation mode.
77         if ctxt.Mode == 0 {
78                 ctxt.Mode = ctxt.Arch.RegSize * 8
79         }
80         p.Mode = int8(ctxt.Mode)
81
82         switch p.As {
83         case AMODE:
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) {
86                         case 16, 32, 64:
87                                 ctxt.Mode = int(p.From.Offset)
88                         }
89                 }
90                 obj.Nopout(p)
91         }
92
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:
96         //
97         //         MOVQ TLS, AX
98         //
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:
103         //
104         //         MOVQ TLS, AX
105         //         MOVQ 0(AX)(TLS*1), CX // load g into CX
106         //
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:
109         //
110         //         MOVQ 0(TLS), CX // load g into CX
111         //
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".
115         //
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.
122         //
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.
126         //
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).
132
133         if CanUse1InsnTLS(ctxt) {
134                 // Reduce 2-instruction sequence to 1-instruction sequence.
135                 // Sequences like
136                 //      MOVQ TLS, BX
137                 //      ... off(BX)(TLS*1) ...
138                 // become
139                 //      NOP
140                 //      ... off(TLS) ...
141                 //
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 {
146                         obj.Nopout(p)
147                 }
148                 if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 {
149                         p.From.Reg = REG_TLS
150                         p.From.Scale = 0
151                         p.From.Index = REG_NONE
152                 }
153
154                 if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
155                         p.To.Reg = REG_TLS
156                         p.To.Scale = 0
157                         p.To.Index = REG_NONE
158                 }
159         } else {
160                 // load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it
161                 // as the 2-instruction sequence if necessary.
162                 //      MOVQ 0(TLS), BX
163                 // becomes
164                 //      MOVQ TLS, BX
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)
168                         q.As = p.As
169                         q.From = p.From
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
174                         q.To = p.To
175                         p.From.Type = obj.TYPE_REG
176                         p.From.Reg = REG_TLS
177                         p.From.Index = REG_NONE
178                         p.From.Offset = 0
179                 }
180         }
181
182         // TODO: Remove.
183         if ctxt.Headtype == obj.Hwindows && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 {
184                 if p.From.Scale == 1 && p.From.Index == REG_TLS {
185                         p.From.Scale = 2
186                 }
187                 if p.To.Scale == 1 && p.To.Index == REG_TLS {
188                         p.To.Scale = 2
189                 }
190         }
191
192         // Rewrite 0 to $0 in 3rd argument to CMPPS etc.
193         // That's what the tables expect.
194         switch p.As {
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
198                 }
199         }
200
201         // Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH.
202         switch p.As {
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
206                 }
207         }
208
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) {
211                 switch p.As {
212                 case AMOVL:
213                         p.As = ALEAL
214                         p.From.Type = obj.TYPE_MEM
215                 case AMOVQ:
216                         p.As = ALEAQ
217                         p.From.Type = obj.TYPE_MEM
218                 }
219         }
220
221         if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
222                 if p.From3 != nil {
223                         nacladdr(ctxt, p, p.From3)
224                 }
225                 nacladdr(ctxt, p, &p.From)
226                 nacladdr(ctxt, p, &p.To)
227         }
228
229         // Rewrite float constants to values stored in memory.
230         switch p.As {
231         // Convert AMOVSS $(0), Xx to AXORPS Xx, Xx
232         case AMOVSS:
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 {
237                                         p.As = AXORPS
238                                         p.From = p.To
239                                         break
240                                 }
241                         }
242                 }
243                 fallthrough
244
245         case AFMOVF,
246                 AFADDF,
247                 AFSUBF,
248                 AFSUBRF,
249                 AFMULF,
250                 AFDIVF,
251                 AFDIVRF,
252                 AFCOMF,
253                 AFCOMFP,
254                 AADDSS,
255                 ASUBSS,
256                 AMULSS,
257                 ADIVSS,
258                 ACOMISS,
259                 AUCOMISS:
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
267                         p.From.Sym = s
268                         p.From.Sym.Local = true
269                         p.From.Offset = 0
270                 }
271
272         case AMOVSD:
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 {
278                                         p.As = AXORPS
279                                         p.From = p.To
280                                         break
281                                 }
282                         }
283                 }
284                 fallthrough
285
286         case AFMOVD,
287                 AFADDD,
288                 AFSUBD,
289                 AFSUBRD,
290                 AFMULD,
291                 AFDIVD,
292                 AFDIVRD,
293                 AFCOMD,
294                 AFCOMDP,
295                 AADDSD,
296                 ASUBSD,
297                 AMULSD,
298                 ADIVSD,
299                 ACOMISD,
300                 AUCOMISD:
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
307                         p.From.Sym = s
308                         p.From.Sym.Local = true
309                         p.From.Offset = 0
310                 }
311         }
312
313         if ctxt.Flag_dynlink {
314                 rewriteToUseGot(ctxt, p)
315         }
316
317         if ctxt.Flag_shared && p.Mode == 32 {
318                 rewriteToPcrel(ctxt, p)
319         }
320 }
321
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
325         var reg int16
326         if p.Mode == 64 {
327                 add = AADDQ
328                 lea = ALEAQ
329                 mov = AMOVQ
330                 reg = REG_R15
331         } else {
332                 add = AADDL
333                 lea = ALEAL
334                 mov = AMOVL
335                 reg = REG_CX
336         }
337
338         if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
339                 //     ADUFFxxx $offset
340                 // becomes
341                 //     $MOV runtime.duffxxx@GOT, $reg
342                 //     $ADD $offset, $reg
343                 //     CALL $reg
344                 var sym *obj.LSym
345                 if p.As == obj.ADUFFZERO {
346                         sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
347                 } else {
348                         sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
349                 }
350                 offset := p.To.Offset
351                 p.As = mov
352                 p.From.Type = obj.TYPE_MEM
353                 p.From.Name = obj.NAME_GOTREF
354                 p.From.Sym = sym
355                 p.To.Type = obj.TYPE_REG
356                 p.To.Reg = reg
357                 p.To.Offset = 0
358                 p.To.Sym = nil
359                 p1 := obj.Appendp(ctxt, p)
360                 p1.As = add
361                 p1.From.Type = obj.TYPE_CONST
362                 p1.From.Offset = offset
363                 p1.To.Type = obj.TYPE_REG
364                 p1.To.Reg = reg
365                 p2 := obj.Appendp(ctxt, p1)
366                 p2.As = obj.ACALL
367                 p2.To.Type = obj.TYPE_REG
368                 p2.To.Reg = reg
369         }
370
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
376                 p.As = mov
377                 p.From.Type = obj.TYPE_ADDR
378         }
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
383                 cmplxdest := false
384                 pAs := p.As
385                 var dest obj.Addr
386                 if p.To.Type != obj.TYPE_REG || pAs != mov {
387                         if p.Mode == 64 {
388                                 ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
389                         }
390                         cmplxdest = true
391                         dest = p.To
392                         p.As = mov
393                         p.To.Type = obj.TYPE_REG
394                         p.To.Reg = REG_CX
395                         p.To.Sym = nil
396                         p.To.Name = obj.NAME_NONE
397                 }
398                 p.From.Type = obj.TYPE_MEM
399                 p.From.Name = obj.NAME_GOTREF
400                 q := p
401                 if p.From.Offset != 0 {
402                         q = obj.Appendp(ctxt, p)
403                         q.As = lea
404                         q.From.Type = obj.TYPE_MEM
405                         q.From.Reg = p.To.Reg
406                         q.From.Offset = p.From.Offset
407                         q.To = p.To
408                         p.From.Offset = 0
409                 }
410                 if cmplxdest {
411                         q = obj.Appendp(ctxt, q)
412                         q.As = pAs
413                         q.To = dest
414                         q.From.Type = obj.TYPE_REG
415                         q.From.Reg = REG_CX
416                 }
417         }
418         if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
419                 ctxt.Diag("don't know how to handle %v with -dynlink", p)
420         }
421         var source *obj.Addr
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)
428                 }
429                 source = &p.From
430         } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
431                 source = &p.To
432         } else {
433                 return
434         }
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 {
441                         return
442                 }
443                 p1 := obj.Appendp(ctxt, p)
444                 p2 := obj.Appendp(ctxt, p1)
445
446                 p1.As = ALEAL
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
451                 p1.To.Reg = REG_BX
452
453                 p2.As = p.As
454                 p2.Scond = p.Scond
455                 p2.From = p.From
456                 p2.From3 = p.From3
457                 p2.Reg = p.Reg
458                 p2.To = p.To
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
463                 p2.RegTo2 = 1
464
465                 obj.Nopout(p)
466                 return
467
468         }
469         if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP {
470                 return
471         }
472         if source.Type != obj.TYPE_MEM {
473                 ctxt.Diag("don't know how to handle %v with -dynlink", p)
474         }
475         p1 := obj.Appendp(ctxt, p)
476         p2 := obj.Appendp(ctxt, p1)
477
478         p1.As = mov
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
483         p1.To.Reg = reg
484
485         p2.As = p.As
486         p2.From = p.From
487         p2.To = p.To
488         if p.From.Name == obj.NAME_EXTERN {
489                 p2.From.Reg = reg
490                 p2.From.Name = obj.NAME_NONE
491                 p2.From.Sym = nil
492         } else if p.To.Name == obj.NAME_EXTERN {
493                 p2.To.Reg = reg
494                 p2.To.Name = obj.NAME_NONE
495                 p2.To.Sym = nil
496         } else {
497                 return
498         }
499         obj.Nopout(p)
500 }
501
502 func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
503         // RegTo2 is set on the instructions we insert here so they don't get
504         // processed twice.
505         if p.RegTo2 != 0 {
506                 return
507         }
508         if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
509                 return
510         }
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 {
516                         return false
517                 }
518                 if a.Sym.Type == obj.STLSBSS {
519                         return false
520                 }
521                 return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF
522         }
523
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"
527                 // respectively.
528                 if p.To.Type != obj.TYPE_REG {
529                         q := obj.Appendp(ctxt, p)
530                         q.As = p.As
531                         q.From.Type = obj.TYPE_REG
532                         q.From.Reg = REG_CX
533                         q.To = p.To
534                         p.As = AMOVL
535                         p.To.Type = obj.TYPE_REG
536                         p.To.Reg = REG_CX
537                         p.To.Sym = nil
538                         p.To.Name = obj.NAME_NONE
539                 }
540         }
541
542         if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) {
543                 return
544         }
545         q := obj.Appendp(ctxt, p)
546         q.RegTo2 = 1
547         r := obj.Appendp(ctxt, q)
548         r.RegTo2 = 1
549         q.As = obj.ACALL
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
554         r.As = p.As
555         r.Scond = p.Scond
556         r.From = p.From
557         r.From3 = p.From3
558         r.Reg = p.Reg
559         r.To = p.To
560         obj.Nopout(p)
561 }
562
563 func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
564         if p.As == ALEAL || p.As == ALEAQ {
565                 return
566         }
567
568         if a.Reg == REG_BP {
569                 ctxt.Diag("invalid address: %v", p)
570                 return
571         }
572
573         if a.Reg == REG_TLS {
574                 a.Reg = REG_BP
575         }
576         if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE {
577                 switch a.Reg {
578                 // all ok
579                 case REG_BP, REG_SP, REG_R15:
580                         break
581
582                 default:
583                         if a.Index != REG_NONE {
584                                 ctxt.Diag("invalid address %v", p)
585                         }
586                         a.Index = a.Reg
587                         if a.Index != REG_NONE {
588                                 a.Scale = 1
589                         }
590                         a.Reg = REG_R15
591                 }
592         }
593 }
594
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)
598         }
599
600         ctxt.Cursym = cursym
601
602         if cursym.Text == nil || cursym.Text.Link == nil {
603                 return
604         }
605
606         p := cursym.Text
607         autoffset := int32(p.To.Offset)
608         if autoffset < 0 {
609                 autoffset = 0
610         }
611
612         var bpsize int
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
618
619                 autoffset += int32(bpsize)
620                 p.To.Offset += int64(bpsize)
621         } else {
622                 bpsize = 0
623         }
624
625         textarg := int64(p.To.Val.(int32))
626         cursym.Args = int32(textarg)
627         cursym.Locals = int32(p.To.Offset)
628
629         // TODO(rsc): Remove.
630         if p.Mode == 32 && cursym.Locals < 0 {
631                 cursym.Locals = 0
632         }
633
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 {
638                                 goto noleaf
639                         }
640                         if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 {
641                                 goto noleaf
642                         }
643                 }
644
645                 p.From3.Offset |= obj.NOSPLIT
646         noleaf:
647         }
648
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
652         }
653
654         if cursym.Text.From3Offset()&obj.NOSPLIT == 0 {
655                 p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check
656         }
657
658         if autoffset != 0 {
659                 if autoffset%int32(ctxt.Arch.RegSize) != 0 {
660                         ctxt.Diag("unaligned stack size %d", autoffset)
661                 }
662                 p = obj.Appendp(ctxt, p)
663                 p.As = AADJSP
664                 p.From.Type = obj.TYPE_CONST
665                 p.From.Offset = int64(autoffset)
666                 p.Spadj = autoffset
667         } else {
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)
672
673                 p.As = obj.ANOP
674                 p.Spadj = int32(-ctxt.Arch.PtrSize)
675                 p = obj.Appendp(ctxt, p)
676                 p.As = obj.ANOP
677                 p.Spadj = int32(ctxt.Arch.PtrSize)
678         }
679
680         deltasp := autoffset
681
682         if bpsize > 0 {
683                 // Save caller's BP
684                 p = obj.Appendp(ctxt, p)
685
686                 p.As = AMOVQ
687                 p.From.Type = obj.TYPE_REG
688                 p.From.Reg = REG_BP
689                 p.To.Type = obj.TYPE_MEM
690                 p.To.Reg = REG_SP
691                 p.To.Scale = 1
692                 p.To.Offset = int64(autoffset) - int64(bpsize)
693
694                 // Move current frame to BP
695                 p = obj.Appendp(ctxt, p)
696
697                 p.As = ALEAQ
698                 p.From.Type = obj.TYPE_MEM
699                 p.From.Reg = REG_SP
700                 p.From.Scale = 1
701                 p.From.Offset = int64(autoffset) - int64(bpsize)
702                 p.To.Type = obj.TYPE_REG
703                 p.To.Reg = REG_BP
704         }
705
706         if cursym.Text.From3Offset()&obj.WRAPPER != 0 {
707                 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
708                 //
709                 //      MOVQ g_panic(CX), BX
710                 //      TESTQ BX, BX
711                 //      JEQ end
712                 //      LEAQ (autoffset+8)(SP), DI
713                 //      CMPQ panic_argp(BX), DI
714                 //      JNE end
715                 //      MOVQ SP, panic_argp(BX)
716                 // end:
717                 //      NOP
718                 //
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.
721
722                 p = obj.Appendp(ctxt, p)
723
724                 p.As = AMOVQ
725                 p.From.Type = obj.TYPE_MEM
726                 p.From.Reg = REG_CX
727                 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
728                 p.To.Type = obj.TYPE_REG
729                 p.To.Reg = REG_BX
730                 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
731                         p.As = AMOVL
732                         p.From.Type = obj.TYPE_MEM
733                         p.From.Reg = REG_R15
734                         p.From.Scale = 1
735                         p.From.Index = REG_CX
736                 }
737                 if p.Mode == 32 {
738                         p.As = AMOVL
739                 }
740
741                 p = obj.Appendp(ctxt, p)
742                 p.As = ATESTQ
743                 p.From.Type = obj.TYPE_REG
744                 p.From.Reg = REG_BX
745                 p.To.Type = obj.TYPE_REG
746                 p.To.Reg = REG_BX
747                 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
748                         p.As = ATESTL
749                 }
750
751                 p = obj.Appendp(ctxt, p)
752                 p.As = AJEQ
753                 p.To.Type = obj.TYPE_BRANCH
754                 p1 := p
755
756                 p = obj.Appendp(ctxt, p)
757                 p.As = ALEAQ
758                 p.From.Type = obj.TYPE_MEM
759                 p.From.Reg = REG_SP
760                 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize)
761                 p.To.Type = obj.TYPE_REG
762                 p.To.Reg = REG_DI
763                 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
764                         p.As = ALEAL
765                 }
766
767                 p = obj.Appendp(ctxt, p)
768                 p.As = ACMPQ
769                 p.From.Type = obj.TYPE_MEM
770                 p.From.Reg = REG_BX
771                 p.From.Offset = 0 // Panic.argp
772                 p.To.Type = obj.TYPE_REG
773                 p.To.Reg = REG_DI
774                 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
775                         p.As = ACMPL
776                         p.From.Type = obj.TYPE_MEM
777                         p.From.Reg = REG_R15
778                         p.From.Scale = 1
779                         p.From.Index = REG_BX
780                 }
781                 if p.Mode == 32 {
782                         p.As = ACMPL
783                 }
784
785                 p = obj.Appendp(ctxt, p)
786                 p.As = AJNE
787                 p.To.Type = obj.TYPE_BRANCH
788                 p2 := p
789
790                 p = obj.Appendp(ctxt, p)
791                 p.As = AMOVQ
792                 p.From.Type = obj.TYPE_REG
793                 p.From.Reg = REG_SP
794                 p.To.Type = obj.TYPE_MEM
795                 p.To.Reg = REG_BX
796                 p.To.Offset = 0 // Panic.argp
797                 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
798                         p.As = AMOVL
799                         p.To.Type = obj.TYPE_MEM
800                         p.To.Reg = REG_R15
801                         p.To.Scale = 1
802                         p.To.Index = REG_BX
803                 }
804                 if p.Mode == 32 {
805                         p.As = AMOVL
806                 }
807
808                 p = obj.Appendp(ctxt, p)
809                 p.As = obj.ANOP
810                 p1.Pcond = p
811                 p2.Pcond = p
812         }
813
814         var a int
815         var pcsize int
816         for ; p != nil; p = p.Link {
817                 pcsize = int(p.Mode) / 8
818                 a = int(p.From.Name)
819                 if a == obj.NAME_AUTO {
820                         p.From.Offset += int64(deltasp) - int64(bpsize)
821                 }
822                 if a == obj.NAME_PARAM {
823                         p.From.Offset += int64(deltasp) + int64(pcsize)
824                 }
825                 if p.From3 != nil {
826                         a = int(p.From3.Name)
827                         if a == obj.NAME_AUTO {
828                                 p.From3.Offset += int64(deltasp) - int64(bpsize)
829                         }
830                         if a == obj.NAME_PARAM {
831                                 p.From3.Offset += int64(deltasp) + int64(pcsize)
832                         }
833                 }
834                 a = int(p.To.Name)
835                 if a == obj.NAME_AUTO {
836                         p.To.Offset += int64(deltasp) - int64(bpsize)
837                 }
838                 if a == obj.NAME_PARAM {
839                         p.To.Offset += int64(deltasp) + int64(pcsize)
840                 }
841
842                 switch p.As {
843                 default:
844                         continue
845
846                 case APUSHL, APUSHFL:
847                         deltasp += 4
848                         p.Spadj = 4
849                         continue
850
851                 case APUSHQ, APUSHFQ:
852                         deltasp += 8
853                         p.Spadj = 8
854                         continue
855
856                 case APUSHW, APUSHFW:
857                         deltasp += 2
858                         p.Spadj = 2
859                         continue
860
861                 case APOPL, APOPFL:
862                         deltasp -= 4
863                         p.Spadj = -4
864                         continue
865
866                 case APOPQ, APOPFQ:
867                         deltasp -= 8
868                         p.Spadj = -8
869                         continue
870
871                 case APOPW, APOPFW:
872                         deltasp -= 2
873                         p.Spadj = -2
874                         continue
875
876                 case obj.ARET:
877                         break
878                 }
879
880                 if autoffset != deltasp {
881                         ctxt.Diag("unbalanced PUSH/POP")
882                 }
883
884                 if autoffset != 0 {
885                         if bpsize > 0 {
886                                 // Restore caller's BP
887                                 p.As = AMOVQ
888
889                                 p.From.Type = obj.TYPE_MEM
890                                 p.From.Reg = REG_SP
891                                 p.From.Scale = 1
892                                 p.From.Offset = int64(autoffset) - int64(bpsize)
893                                 p.To.Type = obj.TYPE_REG
894                                 p.To.Reg = REG_BP
895                                 p = obj.Appendp(ctxt, p)
896                         }
897
898                         p.As = AADJSP
899                         p.From.Type = obj.TYPE_CONST
900                         p.From.Offset = int64(-autoffset)
901                         p.Spadj = -autoffset
902                         p = obj.Appendp(ctxt, p)
903                         p.As = obj.ARET
904
905                         // If there are instructions following
906                         // this ARET, they come from a branch
907                         // with the same stackframe, so undo
908                         // the cleanup.
909                         p.Spadj = +autoffset
910                 }
911
912                 if p.To.Sym != nil { // retjmp
913                         p.As = obj.AJMP
914                 }
915         }
916 }
917
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
921                 a.Reg = REG_R15
922                 a.Index = REG_CX
923                 a.Scale = 1
924                 return
925         }
926
927         a.Type = obj.TYPE_MEM
928         a.Reg = REG_CX
929 }
930
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 {
937         p.As = AMOVQ
938         if ctxt.Arch.PtrSize == 4 {
939                 p.As = AMOVL
940         }
941         p.From.Type = obj.TYPE_MEM
942         p.From.Reg = REG_TLS
943         p.From.Offset = 0
944         p.To.Type = obj.TYPE_REG
945         p.To.Reg = REG_CX
946
947         next := p.Link
948         progedit(ctxt, p)
949         for p.Link != next {
950                 p = p.Link
951         }
952
953         if p.From.Index == REG_TLS {
954                 p.From.Scale = 2
955         }
956
957         return p
958 }
959
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 {
965         cmp := ACMPQ
966         lea := ALEAQ
967         mov := AMOVQ
968         sub := ASUBQ
969
970         if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
971                 cmp = ACMPL
972                 lea = ALEAL
973                 mov = AMOVL
974                 sub = ASUBL
975         }
976
977         var q1 *obj.Prog
978         if framesize <= obj.StackSmall {
979                 // small stack: SP <= stackguard
980                 //      CMPQ SP, stackguard
981                 p = obj.Appendp(ctxt, p)
982
983                 p.As = cmp
984                 p.From.Type = obj.TYPE_REG
985                 p.From.Reg = REG_SP
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
990                 }
991         } else if framesize <= obj.StackBig {
992                 // large stack: SP-framesize <= stackguard-StackSmall
993                 //      LEAQ -xxx(SP), AX
994                 //      CMPQ AX, stackguard
995                 p = obj.Appendp(ctxt, p)
996
997                 p.As = lea
998                 p.From.Type = obj.TYPE_MEM
999                 p.From.Reg = REG_SP
1000                 p.From.Offset = -(int64(framesize) - obj.StackSmall)
1001                 p.To.Type = obj.TYPE_REG
1002                 p.To.Reg = REG_AX
1003
1004                 p = obj.Appendp(ctxt, p)
1005                 p.As = cmp
1006                 p.From.Type = obj.TYPE_REG
1007                 p.From.Reg = REG_AX
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
1012                 }
1013         } else {
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.
1019                 //
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
1026                 //      SUBQ    CX, AX
1027                 //      CMPQ    AX, $(framesize+(StackGuard-StackSmall))
1028
1029                 p = obj.Appendp(ctxt, p)
1030
1031                 p.As = mov
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
1036                 }
1037                 p.To.Type = obj.TYPE_REG
1038                 p.To.Reg = REG_SI
1039
1040                 p = obj.Appendp(ctxt, p)
1041                 p.As = cmp
1042                 p.From.Type = obj.TYPE_REG
1043                 p.From.Reg = REG_SI
1044                 p.To.Type = obj.TYPE_CONST
1045                 p.To.Offset = obj.StackPreempt
1046                 if p.Mode == 32 {
1047                         p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
1048                 }
1049
1050                 p = obj.Appendp(ctxt, p)
1051                 p.As = AJEQ
1052                 p.To.Type = obj.TYPE_BRANCH
1053                 q1 = p
1054
1055                 p = obj.Appendp(ctxt, p)
1056                 p.As = lea
1057                 p.From.Type = obj.TYPE_MEM
1058                 p.From.Reg = REG_SP
1059                 p.From.Offset = obj.StackGuard
1060                 p.To.Type = obj.TYPE_REG
1061                 p.To.Reg = REG_AX
1062
1063                 p = obj.Appendp(ctxt, p)
1064                 p.As = sub
1065                 p.From.Type = obj.TYPE_REG
1066                 p.From.Reg = REG_SI
1067                 p.To.Type = obj.TYPE_REG
1068                 p.To.Reg = REG_AX
1069
1070                 p = obj.Appendp(ctxt, p)
1071                 p.As = cmp
1072                 p.From.Type = obj.TYPE_REG
1073                 p.From.Reg = REG_AX
1074                 p.To.Type = obj.TYPE_CONST
1075                 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
1076         }
1077
1078         // common
1079         jls := obj.Appendp(ctxt, p)
1080         jls.As = AJLS
1081         jls.To.Type = obj.TYPE_BRANCH
1082
1083         var last *obj.Prog
1084         for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
1085         }
1086
1087         spfix := obj.Appendp(ctxt, last)
1088         spfix.As = obj.ANOP
1089         spfix.Spadj = -framesize
1090
1091         call := obj.Appendp(ctxt, spfix)
1092         call.Lineno = ctxt.Cursym.Text.Lineno
1093         call.Mode = ctxt.Cursym.Text.Mode
1094         call.As = obj.ACALL
1095         call.To.Type = obj.TYPE_BRANCH
1096         morestack := "runtime.morestack"
1097         switch {
1098         case ctxt.Cursym.Cfunc:
1099                 morestack = "runtime.morestackc"
1100         case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
1101                 morestack = "runtime.morestack_noctxt"
1102         }
1103         call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
1104
1105         jmp := obj.Appendp(ctxt, call)
1106         jmp.As = obj.AJMP
1107         jmp.To.Type = obj.TYPE_BRANCH
1108         jmp.Pcond = ctxt.Cursym.Text.Link
1109         jmp.Spadj = +framesize
1110
1111         jls.Pcond = call
1112         if q1 != nil {
1113                 q1.Pcond = call
1114         }
1115
1116         return jls
1117 }
1118
1119 func follow(ctxt *obj.Link, s *obj.LSym) {
1120         ctxt.Cursym = s
1121
1122         firstp := ctxt.NewProg()
1123         lastp := firstp
1124         xfol(ctxt, s.Text, &lastp)
1125         lastp.Link = nil
1126         s.Text = firstp.Link
1127 }
1128
1129 func nofollow(a obj.As) bool {
1130         switch a {
1131         case obj.AJMP,
1132                 obj.ARET,
1133                 AIRETL,
1134                 AIRETQ,
1135                 AIRETW,
1136                 ARETFL,
1137                 ARETFQ,
1138                 ARETFW,
1139                 obj.AUNDEF:
1140                 return true
1141         }
1142
1143         return false
1144 }
1145
1146 func pushpop(a obj.As) bool {
1147         switch a {
1148         case APUSHL,
1149                 APUSHFL,
1150                 APUSHQ,
1151                 APUSHFQ,
1152                 APUSHW,
1153                 APUSHFW,
1154                 APOPL,
1155                 APOPFL,
1156                 APOPQ,
1157                 APOPFQ,
1158                 APOPW,
1159                 APOPFW:
1160                 return true
1161         }
1162
1163         return false
1164 }
1165
1166 func relinv(a obj.As) obj.As {
1167         switch a {
1168         case AJEQ:
1169                 return AJNE
1170         case AJNE:
1171                 return AJEQ
1172         case AJLE:
1173                 return AJGT
1174         case AJLS:
1175                 return AJHI
1176         case AJLT:
1177                 return AJGE
1178         case AJMI:
1179                 return AJPL
1180         case AJGE:
1181                 return AJLT
1182         case AJPL:
1183                 return AJMI
1184         case AJGT:
1185                 return AJLE
1186         case AJHI:
1187                 return AJLS
1188         case AJCS:
1189                 return AJCC
1190         case AJCC:
1191                 return AJCS
1192         case AJPS:
1193                 return AJPC
1194         case AJPC:
1195                 return AJPS
1196         case AJOS:
1197                 return AJOC
1198         case AJOC:
1199                 return AJOS
1200         }
1201
1202         log.Fatalf("unknown relation: %s", obj.Aconv(a))
1203         return 0
1204 }
1205
1206 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
1207         var q *obj.Prog
1208         var i int
1209         var a obj.As
1210
1211 loop:
1212         if p == nil {
1213                 return
1214         }
1215         if p.As == obj.AJMP {
1216                 q = p.Pcond
1217                 if q != nil && q.As != obj.ATEXT {
1218                         /* mark instruction as done and continue layout at target of jump */
1219                         p.Mark |= DONE
1220
1221                         p = q
1222                         if p.Mark&DONE == 0 {
1223                                 goto loop
1224                         }
1225                 }
1226         }
1227
1228         if p.Mark&DONE != 0 {
1229                 /*
1230                  * p goes here, but already used it elsewhere.
1231                  * copy up to 4 instructions or else branch to other copy.
1232                  */
1233                 i = 0
1234                 q = p
1235                 for ; i < 4; i, q = i+1, q.Link {
1236                         if q == nil {
1237                                 break
1238                         }
1239                         if q == *last {
1240                                 break
1241                         }
1242                         a = q.As
1243                         if a == obj.ANOP {
1244                                 i--
1245                                 continue
1246                         }
1247
1248                         if nofollow(a) || pushpop(a) {
1249                                 break // NOTE(rsc): arm does goto copy
1250                         }
1251                         if q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
1252                                 continue
1253                         }
1254                         if a == obj.ACALL || a == ALOOP {
1255                                 continue
1256                         }
1257                         for {
1258                                 if p.As == obj.ANOP {
1259                                         p = p.Link
1260                                         continue
1261                                 }
1262
1263                                 q = obj.Copyp(ctxt, p)
1264                                 p = p.Link
1265                                 q.Mark |= DONE
1266                                 (*last).Link = q
1267                                 *last = q
1268                                 if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
1269                                         continue
1270                                 }
1271
1272                                 q.As = relinv(q.As)
1273                                 p = q.Pcond
1274                                 q.Pcond = q.Link
1275                                 q.Link = p
1276                                 xfol(ctxt, q.Link, last)
1277                                 p = q.Link
1278                                 if p.Mark&DONE != 0 {
1279                                         return
1280                                 }
1281                                 goto loop
1282                                 /* */
1283                         }
1284                 }
1285                 q = ctxt.NewProg()
1286                 q.As = obj.AJMP
1287                 q.Lineno = p.Lineno
1288                 q.To.Type = obj.TYPE_BRANCH
1289                 q.To.Offset = p.Pc
1290                 q.Pcond = p
1291                 p = q
1292         }
1293
1294         /* emit p */
1295         p.Mark |= DONE
1296
1297         (*last).Link = p
1298         *last = p
1299         a = p.As
1300
1301         /* continue loop with what comes after p */
1302         if nofollow(a) {
1303                 return
1304         }
1305         if p.Pcond != nil && a != obj.ACALL {
1306                 /*
1307                  * some kind of conditional branch.
1308                  * recurse to follow one path.
1309                  * continue loop on the other.
1310                  */
1311                 q = obj.Brchain(ctxt, p.Pcond)
1312                 if q != nil {
1313                         p.Pcond = q
1314                 }
1315                 q = obj.Brchain(ctxt, p.Link)
1316                 if q != nil {
1317                         p.Link = q
1318                 }
1319                 if p.From.Type == obj.TYPE_CONST {
1320                         if p.From.Offset == 1 {
1321                                 /*
1322                                  * expect conditional jump to be taken.
1323                                  * rewrite so that's the fall-through case.
1324                                  */
1325                                 p.As = relinv(a)
1326
1327                                 q = p.Link
1328                                 p.Link = p.Pcond
1329                                 p.Pcond = q
1330                         }
1331                 } else {
1332                         q = p.Link
1333                         if q.Mark&DONE != 0 {
1334                                 if a != ALOOP {
1335                                         p.As = relinv(a)
1336                                         p.Link = p.Pcond
1337                                         p.Pcond = q
1338                                 }
1339                         }
1340                 }
1341
1342                 xfol(ctxt, p.Link, last)
1343                 if p.Pcond.Mark&DONE != 0 {
1344                         return
1345                 }
1346                 p = p.Pcond
1347                 goto loop
1348         }
1349
1350         p = p.Link
1351         goto loop
1352 }
1353
1354 var unaryDst = map[obj.As]bool{
1355         ABSWAPL:    true,
1356         ABSWAPQ:    true,
1357         ACMPXCHG8B: true,
1358         ADECB:      true,
1359         ADECL:      true,
1360         ADECQ:      true,
1361         ADECW:      true,
1362         AINCB:      true,
1363         AINCL:      true,
1364         AINCQ:      true,
1365         AINCW:      true,
1366         ANEGB:      true,
1367         ANEGL:      true,
1368         ANEGQ:      true,
1369         ANEGW:      true,
1370         ANOTB:      true,
1371         ANOTL:      true,
1372         ANOTQ:      true,
1373         ANOTW:      true,
1374         APOPL:      true,
1375         APOPQ:      true,
1376         APOPW:      true,
1377         ASETCC:     true,
1378         ASETCS:     true,
1379         ASETEQ:     true,
1380         ASETGE:     true,
1381         ASETGT:     true,
1382         ASETHI:     true,
1383         ASETLE:     true,
1384         ASETLS:     true,
1385         ASETLT:     true,
1386         ASETMI:     true,
1387         ASETNE:     true,
1388         ASETOC:     true,
1389         ASETOS:     true,
1390         ASETPC:     true,
1391         ASETPL:     true,
1392         ASETPS:     true,
1393         AFFREE:     true,
1394         AFLDENV:    true,
1395         AFSAVE:     true,
1396         AFSTCW:     true,
1397         AFSTENV:    true,
1398         AFSTSW:     true,
1399         AFXSAVE:    true,
1400         AFXSAVE64:  true,
1401         ASTMXCSR:   true,
1402 }
1403
1404 var Linkamd64 = obj.LinkArch{
1405         Arch:       sys.ArchAMD64,
1406         Preprocess: preprocess,
1407         Assemble:   span6,
1408         Follow:     follow,
1409         Progedit:   progedit,
1410         UnaryDst:   unaryDst,
1411 }
1412
1413 var Linkamd64p32 = obj.LinkArch{
1414         Arch:       sys.ArchAMD64P32,
1415         Preprocess: preprocess,
1416         Assemble:   span6,
1417         Follow:     follow,
1418         Progedit:   progedit,
1419         UnaryDst:   unaryDst,
1420 }
1421
1422 var Link386 = obj.LinkArch{
1423         Arch:       sys.Arch386,
1424         Preprocess: preprocess,
1425         Assemble:   span6,
1426         Follow:     follow,
1427         Progedit:   progedit,
1428         UnaryDst:   unaryDst,
1429 }