rsc.io/go@v0.0.0-20150416155037-e040fd465409/src/cmd/internal/obj/x86/obj6.go (about)

     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  	"encoding/binary"
    36  	"fmt"
    37  	"log"
    38  	"math"
    39  )
    40  
    41  func canuselocaltls(ctxt *obj.Link) bool {
    42  	if ctxt.Arch.Regsize == 4 {
    43  		switch ctxt.Headtype {
    44  		case obj.Hlinux,
    45  			obj.Hnacl,
    46  			obj.Hplan9,
    47  			obj.Hwindows:
    48  			return false
    49  		}
    50  
    51  		return true
    52  	}
    53  
    54  	switch ctxt.Headtype {
    55  	case obj.Hplan9,
    56  		obj.Hwindows:
    57  		return false
    58  	case obj.Hlinux:
    59  		return ctxt.Flag_shared == 0
    60  	}
    61  
    62  	return true
    63  }
    64  
    65  func progedit(ctxt *obj.Link, p *obj.Prog) {
    66  	// Maintain information about code generation mode.
    67  	if ctxt.Mode == 0 {
    68  		ctxt.Mode = ctxt.Arch.Regsize * 8
    69  	}
    70  	p.Mode = int8(ctxt.Mode)
    71  
    72  	switch p.As {
    73  	case AMODE:
    74  		if p.From.Type == obj.TYPE_CONST || (p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_NONE) {
    75  			switch int(p.From.Offset) {
    76  			case 16, 32, 64:
    77  				ctxt.Mode = int(p.From.Offset)
    78  			}
    79  		}
    80  		obj.Nopout(p)
    81  	}
    82  
    83  	// Thread-local storage references use the TLS pseudo-register.
    84  	// As a register, TLS refers to the thread-local storage base, and it
    85  	// can only be loaded into another register:
    86  	//
    87  	//         MOVQ TLS, AX
    88  	//
    89  	// An offset from the thread-local storage base is written off(reg)(TLS*1).
    90  	// Semantically it is off(reg), but the (TLS*1) annotation marks this as
    91  	// indexing from the loaded TLS base. This emits a relocation so that
    92  	// if the linker needs to adjust the offset, it can. For example:
    93  	//
    94  	//         MOVQ TLS, AX
    95  	//         MOVQ 8(AX)(TLS*1), CX // load m into CX
    96  	//
    97  	// On systems that support direct access to the TLS memory, this
    98  	// pair of instructions can be reduced to a direct TLS memory reference:
    99  	//
   100  	//         MOVQ 8(TLS), CX // load m into CX
   101  	//
   102  	// The 2-instruction and 1-instruction forms correspond roughly to
   103  	// ELF TLS initial exec mode and ELF TLS local exec mode, respectively.
   104  	//
   105  	// We applies this rewrite on systems that support the 1-instruction form.
   106  	// The decision is made using only the operating system (and probably
   107  	// the -shared flag, eventually), not the link mode. If some link modes
   108  	// on a particular operating system require the 2-instruction form,
   109  	// then all builds for that operating system will use the 2-instruction
   110  	// form, so that the link mode decision can be delayed to link time.
   111  	//
   112  	// In this way, all supported systems use identical instructions to
   113  	// access TLS, and they are rewritten appropriately first here in
   114  	// liblink and then finally using relocations in the linker.
   115  
   116  	if canuselocaltls(ctxt) {
   117  		// Reduce TLS initial exec model to TLS local exec model.
   118  		// Sequences like
   119  		//	MOVQ TLS, BX
   120  		//	... off(BX)(TLS*1) ...
   121  		// become
   122  		//	NOP
   123  		//	... off(TLS) ...
   124  		//
   125  		// TODO(rsc): Remove the Hsolaris special case. It exists only to
   126  		// guarantee we are producing byte-identical binaries as before this code.
   127  		// But it should be unnecessary.
   128  		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 {
   129  			obj.Nopout(p)
   130  		}
   131  		if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 {
   132  			p.From.Reg = REG_TLS
   133  			p.From.Scale = 0
   134  			p.From.Index = REG_NONE
   135  		}
   136  
   137  		if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
   138  			p.To.Reg = REG_TLS
   139  			p.To.Scale = 0
   140  			p.To.Index = REG_NONE
   141  		}
   142  	} else {
   143  		// As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load.
   144  		// The instruction
   145  		//	MOVQ off(TLS), BX
   146  		// becomes the sequence
   147  		//	MOVQ TLS, BX
   148  		//	MOVQ off(BX)(TLS*1), BX
   149  		// This allows the C compilers to emit references to m and g using the direct off(TLS) form.
   150  		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 {
   151  			q := obj.Appendp(ctxt, p)
   152  			q.As = p.As
   153  			q.From = p.From
   154  			q.From.Type = obj.TYPE_MEM
   155  			q.From.Reg = p.To.Reg
   156  			q.From.Index = REG_TLS
   157  			q.From.Scale = 2 // TODO: use 1
   158  			q.To = p.To
   159  			p.From.Type = obj.TYPE_REG
   160  			p.From.Reg = REG_TLS
   161  			p.From.Index = REG_NONE
   162  			p.From.Offset = 0
   163  		}
   164  	}
   165  
   166  	// TODO: Remove.
   167  	if ctxt.Headtype == obj.Hwindows && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 {
   168  		if p.From.Scale == 1 && p.From.Index == REG_TLS {
   169  			p.From.Scale = 2
   170  		}
   171  		if p.To.Scale == 1 && p.To.Index == REG_TLS {
   172  			p.To.Scale = 2
   173  		}
   174  	}
   175  
   176  	// Rewrite 0 to $0 in 3rd argment to CMPPS etc.
   177  	// That's what the tables expect.
   178  	switch p.As {
   179  	case ACMPPD, ACMPPS, ACMPSD, ACMPSS:
   180  		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 {
   181  			p.To.Type = obj.TYPE_CONST
   182  		}
   183  	}
   184  
   185  	// Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH.
   186  	switch p.As {
   187  	case obj.ACALL, obj.AJMP, obj.ARET:
   188  		if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
   189  			p.To.Type = obj.TYPE_BRANCH
   190  		}
   191  	}
   192  
   193  	// Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ.
   194  	if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Thechar == '6' || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) {
   195  		switch p.As {
   196  		case AMOVL:
   197  			p.As = ALEAL
   198  			p.From.Type = obj.TYPE_MEM
   199  		case AMOVQ:
   200  			p.As = ALEAQ
   201  			p.From.Type = obj.TYPE_MEM
   202  		}
   203  	}
   204  
   205  	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   206  		nacladdr(ctxt, p, &p.From3)
   207  		nacladdr(ctxt, p, &p.From)
   208  		nacladdr(ctxt, p, &p.To)
   209  	}
   210  
   211  	// Rewrite float constants to values stored in memory.
   212  	switch p.As {
   213  	// Convert AMOVSS $(0), Xx to AXORPS Xx, Xx
   214  	case AMOVSS:
   215  		if p.From.Type == obj.TYPE_FCONST {
   216  			if p.From.Val.(float64) == 0 {
   217  				if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
   218  					p.As = AXORPS
   219  					p.From = p.To
   220  					break
   221  				}
   222  			}
   223  		}
   224  		fallthrough
   225  
   226  	case AFMOVF,
   227  		AFADDF,
   228  		AFSUBF,
   229  		AFSUBRF,
   230  		AFMULF,
   231  		AFDIVF,
   232  		AFDIVRF,
   233  		AFCOMF,
   234  		AFCOMFP,
   235  		AADDSS,
   236  		ASUBSS,
   237  		AMULSS,
   238  		ADIVSS,
   239  		ACOMISS,
   240  		AUCOMISS:
   241  		if p.From.Type == obj.TYPE_FCONST {
   242  			f32 := float32(p.From.Val.(float64))
   243  			i32 := math.Float32bits(f32)
   244  			literal := fmt.Sprintf("$f32.%08x", i32)
   245  			s := obj.Linklookup(ctxt, literal, 0)
   246  			if s.Type == 0 {
   247  				s.Type = obj.SRODATA
   248  				obj.Adduint32(ctxt, s, i32)
   249  			}
   250  
   251  			p.From.Type = obj.TYPE_MEM
   252  			p.From.Name = obj.NAME_EXTERN
   253  			p.From.Sym = s
   254  			p.From.Offset = 0
   255  		}
   256  
   257  	case AMOVSD:
   258  		// Convert AMOVSD $(0), Xx to AXORPS Xx, Xx
   259  		if p.From.Type == obj.TYPE_FCONST {
   260  			if p.From.Val.(float64) == 0 {
   261  				if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
   262  					p.As = AXORPS
   263  					p.From = p.To
   264  					break
   265  				}
   266  			}
   267  		}
   268  		fallthrough
   269  
   270  	case AFMOVD,
   271  		AFADDD,
   272  		AFSUBD,
   273  		AFSUBRD,
   274  		AFMULD,
   275  		AFDIVD,
   276  		AFDIVRD,
   277  		AFCOMD,
   278  		AFCOMDP,
   279  		AADDSD,
   280  		ASUBSD,
   281  		AMULSD,
   282  		ADIVSD,
   283  		ACOMISD,
   284  		AUCOMISD:
   285  		if p.From.Type == obj.TYPE_FCONST {
   286  			i64 := math.Float64bits(p.From.Val.(float64))
   287  			literal := fmt.Sprintf("$f64.%016x", i64)
   288  			s := obj.Linklookup(ctxt, literal, 0)
   289  			if s.Type == 0 {
   290  				s.Type = obj.SRODATA
   291  				obj.Adduint64(ctxt, s, i64)
   292  			}
   293  
   294  			p.From.Type = obj.TYPE_MEM
   295  			p.From.Name = obj.NAME_EXTERN
   296  			p.From.Sym = s
   297  			p.From.Offset = 0
   298  		}
   299  	}
   300  
   301  	if ctxt.Flag_dynlink && (p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO) {
   302  		var sym *obj.LSym
   303  		if p.As == obj.ADUFFZERO {
   304  			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
   305  		} else {
   306  			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
   307  		}
   308  		offset := p.To.Offset
   309  		p.As = AMOVQ
   310  		p.From.Type = obj.TYPE_MEM
   311  		p.From.Name = obj.NAME_GOTREF
   312  		p.From.Sym = sym
   313  		p.To.Type = obj.TYPE_REG
   314  		p.To.Reg = REG_R15
   315  		p.To.Offset = 0
   316  		p.To.Sym = nil
   317  		p1 := obj.Appendp(ctxt, p)
   318  		p1.As = AADDQ
   319  		p1.From.Type = obj.TYPE_CONST
   320  		p1.From.Offset = offset
   321  		p1.To.Type = obj.TYPE_REG
   322  		p1.To.Reg = REG_R15
   323  		p2 := obj.Appendp(ctxt, p1)
   324  		p2.As = obj.ACALL
   325  		p2.To.Type = obj.TYPE_REG
   326  		p2.To.Reg = REG_R15
   327  	}
   328  
   329  	if ctxt.Flag_dynlink {
   330  		if p.As == ALEAQ && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN {
   331  			p.As = AMOVQ
   332  			p.From.Type = obj.TYPE_ADDR
   333  		}
   334  		if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN {
   335  			if p.As != AMOVQ {
   336  				ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   337  			}
   338  			if p.To.Type != obj.TYPE_REG {
   339  				ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   340  			}
   341  			p.From.Type = obj.TYPE_MEM
   342  			p.From.Name = obj.NAME_GOTREF
   343  			if p.From.Offset != 0 {
   344  				q := obj.Appendp(ctxt, p)
   345  				q.As = AADDQ
   346  				q.From.Type = obj.TYPE_CONST
   347  				q.From.Offset = p.From.Offset
   348  				q.To = p.To
   349  				p.From.Offset = 0
   350  			}
   351  		}
   352  		if p.From3.Name == obj.NAME_EXTERN {
   353  			ctxt.Diag("don't know how to handle %v with -dynlink", p)
   354  		}
   355  		if p.To2.Name == obj.NAME_EXTERN {
   356  			ctxt.Diag("don't know how to handle %v with -dynlink", p)
   357  		}
   358  		var source *obj.Addr
   359  		if p.From.Name == obj.NAME_EXTERN {
   360  			if p.To.Name == obj.NAME_EXTERN {
   361  				ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   362  			}
   363  			source = &p.From
   364  		} else if p.To.Name == obj.NAME_EXTERN {
   365  			source = &p.To
   366  		} else {
   367  			return
   368  		}
   369  		if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   370  			return
   371  		}
   372  		if source.Type != obj.TYPE_MEM {
   373  			ctxt.Diag("don't know how to handle %v with -dynlink", p)
   374  		}
   375  		p1 := obj.Appendp(ctxt, p)
   376  		p2 := obj.Appendp(ctxt, p1)
   377  
   378  		p1.As = AMOVQ
   379  		p1.From.Type = obj.TYPE_MEM
   380  		p1.From.Sym = source.Sym
   381  		p1.From.Name = obj.NAME_GOTREF
   382  		p1.To.Type = obj.TYPE_REG
   383  		p1.To.Reg = REG_R15
   384  
   385  		p2.As = p.As
   386  		p2.From = p.From
   387  		p2.To = p.To
   388  		if p.From.Name == obj.NAME_EXTERN {
   389  			p2.From.Reg = REG_R15
   390  			p2.From.Name = obj.NAME_NONE
   391  			p2.From.Sym = nil
   392  		} else if p.To.Name == obj.NAME_EXTERN {
   393  			p2.To.Reg = REG_R15
   394  			p2.To.Name = obj.NAME_NONE
   395  			p2.To.Sym = nil
   396  		} else {
   397  			return
   398  		}
   399  		l := p.Link
   400  		l2 := p2.Link
   401  		*p = *p1
   402  		*p1 = *p2
   403  		p.Link = l
   404  		p1.Link = l2
   405  	}
   406  }
   407  
   408  func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
   409  	if p.As == ALEAL || p.As == ALEAQ {
   410  		return
   411  	}
   412  
   413  	if a.Reg == REG_BP {
   414  		ctxt.Diag("invalid address: %v", p)
   415  		return
   416  	}
   417  
   418  	if a.Reg == REG_TLS {
   419  		a.Reg = REG_BP
   420  	}
   421  	if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE {
   422  		switch a.Reg {
   423  		// all ok
   424  		case REG_BP, REG_SP, REG_R15:
   425  			break
   426  
   427  		default:
   428  			if a.Index != REG_NONE {
   429  				ctxt.Diag("invalid address %v", p)
   430  			}
   431  			a.Index = a.Reg
   432  			if a.Index != REG_NONE {
   433  				a.Scale = 1
   434  			}
   435  			a.Reg = REG_R15
   436  		}
   437  	}
   438  }
   439  
   440  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   441  	if ctxt.Tlsg == nil {
   442  		ctxt.Tlsg = obj.Linklookup(ctxt, "runtime.tlsg", 0)
   443  	}
   444  	if ctxt.Symmorestack[0] == nil {
   445  		ctxt.Symmorestack[0] = obj.Linklookup(ctxt, "runtime.morestack", 0)
   446  		ctxt.Symmorestack[1] = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
   447  	}
   448  
   449  	if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil {
   450  		ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0)
   451  	}
   452  
   453  	ctxt.Cursym = cursym
   454  
   455  	if cursym.Text == nil || cursym.Text.Link == nil {
   456  		return
   457  	}
   458  
   459  	p := cursym.Text
   460  	autoffset := int32(p.To.Offset)
   461  	if autoffset < 0 {
   462  		autoffset = 0
   463  	}
   464  
   465  	var bpsize int
   466  	if p.Mode == 64 && obj.Framepointer_enabled != 0 && autoffset > 0 {
   467  		// Make room for to save a base pointer.  If autoffset == 0,
   468  		// this might do something special like a tail jump to
   469  		// another function, so in that case we omit this.
   470  		bpsize = ctxt.Arch.Ptrsize
   471  
   472  		autoffset += int32(bpsize)
   473  		p.To.Offset += int64(bpsize)
   474  	} else {
   475  		bpsize = 0
   476  	}
   477  
   478  	textarg := int64(p.To.Val.(int32))
   479  	cursym.Args = int32(textarg)
   480  	cursym.Locals = int32(p.To.Offset)
   481  
   482  	// TODO(rsc): Remove.
   483  	if p.Mode == 32 && cursym.Locals < 0 {
   484  		cursym.Locals = 0
   485  	}
   486  
   487  	// TODO(rsc): Remove 'p.Mode == 64 &&'.
   488  	if p.Mode == 64 && autoffset < obj.StackSmall && p.From3.Offset&obj.NOSPLIT == 0 {
   489  		for q := p; q != nil; q = q.Link {
   490  			if q.As == obj.ACALL {
   491  				goto noleaf
   492  			}
   493  			if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 {
   494  				goto noleaf
   495  			}
   496  		}
   497  
   498  		p.From3.Offset |= obj.NOSPLIT
   499  	noleaf:
   500  	}
   501  
   502  	if p.From3.Offset&obj.NOSPLIT == 0 || (p.From3.Offset&obj.WRAPPER != 0) {
   503  		p = obj.Appendp(ctxt, p)
   504  		p = load_g_cx(ctxt, p) // load g into CX
   505  	}
   506  
   507  	var q *obj.Prog
   508  	if cursym.Text.From3.Offset&obj.NOSPLIT == 0 {
   509  		p = stacksplit(ctxt, p, autoffset, int32(textarg), cursym.Text.From3.Offset&obj.NEEDCTXT == 0, &q) // emit split check
   510  	}
   511  
   512  	if autoffset != 0 {
   513  		if autoffset%int32(ctxt.Arch.Regsize) != 0 {
   514  			ctxt.Diag("unaligned stack size %d", autoffset)
   515  		}
   516  		p = obj.Appendp(ctxt, p)
   517  		p.As = AADJSP
   518  		p.From.Type = obj.TYPE_CONST
   519  		p.From.Offset = int64(autoffset)
   520  		p.Spadj = autoffset
   521  	} else {
   522  		// zero-byte stack adjustment.
   523  		// Insert a fake non-zero adjustment so that stkcheck can
   524  		// recognize the end of the stack-splitting prolog.
   525  		p = obj.Appendp(ctxt, p)
   526  
   527  		p.As = obj.ANOP
   528  		p.Spadj = int32(-ctxt.Arch.Ptrsize)
   529  		p = obj.Appendp(ctxt, p)
   530  		p.As = obj.ANOP
   531  		p.Spadj = int32(ctxt.Arch.Ptrsize)
   532  	}
   533  
   534  	if q != nil {
   535  		q.Pcond = p
   536  	}
   537  	deltasp := autoffset
   538  
   539  	if bpsize > 0 {
   540  		// Save caller's BP
   541  		p = obj.Appendp(ctxt, p)
   542  
   543  		p.As = AMOVQ
   544  		p.From.Type = obj.TYPE_REG
   545  		p.From.Reg = REG_BP
   546  		p.To.Type = obj.TYPE_MEM
   547  		p.To.Reg = REG_SP
   548  		p.To.Scale = 1
   549  		p.To.Offset = int64(autoffset) - int64(bpsize)
   550  
   551  		// Move current frame to BP
   552  		p = obj.Appendp(ctxt, p)
   553  
   554  		p.As = ALEAQ
   555  		p.From.Type = obj.TYPE_MEM
   556  		p.From.Reg = REG_SP
   557  		p.From.Scale = 1
   558  		p.From.Offset = int64(autoffset) - int64(bpsize)
   559  		p.To.Type = obj.TYPE_REG
   560  		p.To.Reg = REG_BP
   561  	}
   562  
   563  	if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   564  		// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   565  		//
   566  		//	MOVQ g_panic(CX), BX
   567  		//	TESTQ BX, BX
   568  		//	JEQ end
   569  		//	LEAQ (autoffset+8)(SP), DI
   570  		//	CMPQ panic_argp(BX), DI
   571  		//	JNE end
   572  		//	MOVQ SP, panic_argp(BX)
   573  		// end:
   574  		//	NOP
   575  		//
   576  		// The NOP is needed to give the jumps somewhere to land.
   577  		// It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
   578  
   579  		p = obj.Appendp(ctxt, p)
   580  
   581  		p.As = AMOVQ
   582  		p.From.Type = obj.TYPE_MEM
   583  		p.From.Reg = REG_CX
   584  		p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   585  		p.To.Type = obj.TYPE_REG
   586  		p.To.Reg = REG_BX
   587  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   588  			p.As = AMOVL
   589  			p.From.Type = obj.TYPE_MEM
   590  			p.From.Reg = REG_R15
   591  			p.From.Scale = 1
   592  			p.From.Index = REG_CX
   593  		}
   594  		if p.Mode == 32 {
   595  			p.As = AMOVL
   596  		}
   597  
   598  		p = obj.Appendp(ctxt, p)
   599  		p.As = ATESTQ
   600  		p.From.Type = obj.TYPE_REG
   601  		p.From.Reg = REG_BX
   602  		p.To.Type = obj.TYPE_REG
   603  		p.To.Reg = REG_BX
   604  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   605  			p.As = ATESTL
   606  		}
   607  
   608  		p = obj.Appendp(ctxt, p)
   609  		p.As = AJEQ
   610  		p.To.Type = obj.TYPE_BRANCH
   611  		p1 := p
   612  
   613  		p = obj.Appendp(ctxt, p)
   614  		p.As = ALEAQ
   615  		p.From.Type = obj.TYPE_MEM
   616  		p.From.Reg = REG_SP
   617  		p.From.Offset = int64(autoffset) + int64(ctxt.Arch.Regsize)
   618  		p.To.Type = obj.TYPE_REG
   619  		p.To.Reg = REG_DI
   620  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   621  			p.As = ALEAL
   622  		}
   623  
   624  		p = obj.Appendp(ctxt, p)
   625  		p.As = ACMPQ
   626  		p.From.Type = obj.TYPE_MEM
   627  		p.From.Reg = REG_BX
   628  		p.From.Offset = 0 // Panic.argp
   629  		p.To.Type = obj.TYPE_REG
   630  		p.To.Reg = REG_DI
   631  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   632  			p.As = ACMPL
   633  			p.From.Type = obj.TYPE_MEM
   634  			p.From.Reg = REG_R15
   635  			p.From.Scale = 1
   636  			p.From.Index = REG_BX
   637  		}
   638  		if p.Mode == 32 {
   639  			p.As = ACMPL
   640  		}
   641  
   642  		p = obj.Appendp(ctxt, p)
   643  		p.As = AJNE
   644  		p.To.Type = obj.TYPE_BRANCH
   645  		p2 := p
   646  
   647  		p = obj.Appendp(ctxt, p)
   648  		p.As = AMOVQ
   649  		p.From.Type = obj.TYPE_REG
   650  		p.From.Reg = REG_SP
   651  		p.To.Type = obj.TYPE_MEM
   652  		p.To.Reg = REG_BX
   653  		p.To.Offset = 0 // Panic.argp
   654  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   655  			p.As = AMOVL
   656  			p.To.Type = obj.TYPE_MEM
   657  			p.To.Reg = REG_R15
   658  			p.To.Scale = 1
   659  			p.To.Index = REG_BX
   660  		}
   661  		if p.Mode == 32 {
   662  			p.As = AMOVL
   663  		}
   664  
   665  		p = obj.Appendp(ctxt, p)
   666  		p.As = obj.ANOP
   667  		p1.Pcond = p
   668  		p2.Pcond = p
   669  	}
   670  
   671  	if ctxt.Debugzerostack != 0 && autoffset != 0 && cursym.Text.From3.Offset&obj.NOSPLIT == 0 {
   672  		// 6l -Z means zero the stack frame on entry.
   673  		// This slows down function calls but can help avoid
   674  		// false positives in garbage collection.
   675  		p = obj.Appendp(ctxt, p)
   676  
   677  		p.As = AMOVQ
   678  		p.From.Type = obj.TYPE_REG
   679  		p.From.Reg = REG_SP
   680  		p.To.Type = obj.TYPE_REG
   681  		p.To.Reg = REG_DI
   682  		if p.Mode == 32 {
   683  			p.As = AMOVL
   684  		}
   685  
   686  		p = obj.Appendp(ctxt, p)
   687  		p.As = AMOVQ
   688  		p.From.Type = obj.TYPE_CONST
   689  		p.From.Offset = int64(autoffset) / int64(ctxt.Arch.Regsize)
   690  		p.To.Type = obj.TYPE_REG
   691  		p.To.Reg = REG_CX
   692  		if p.Mode == 32 {
   693  			p.As = AMOVL
   694  		}
   695  
   696  		p = obj.Appendp(ctxt, p)
   697  		p.As = AMOVQ
   698  		p.From.Type = obj.TYPE_CONST
   699  		p.From.Offset = 0
   700  		p.To.Type = obj.TYPE_REG
   701  		p.To.Reg = REG_AX
   702  		if p.Mode == 32 {
   703  			p.As = AMOVL
   704  		}
   705  
   706  		p = obj.Appendp(ctxt, p)
   707  		p.As = AREP
   708  
   709  		p = obj.Appendp(ctxt, p)
   710  		p.As = ASTOSQ
   711  		if p.Mode == 32 {
   712  			p.As = ASTOSL
   713  		}
   714  	}
   715  
   716  	var a int
   717  	var pcsize int
   718  	for ; p != nil; p = p.Link {
   719  		pcsize = int(p.Mode) / 8
   720  		a = int(p.From.Name)
   721  		if a == obj.NAME_AUTO {
   722  			p.From.Offset += int64(deltasp) - int64(bpsize)
   723  		}
   724  		if a == obj.NAME_PARAM {
   725  			p.From.Offset += int64(deltasp) + int64(pcsize)
   726  		}
   727  		a = int(p.From3.Name)
   728  		if a == obj.NAME_AUTO {
   729  			p.From3.Offset += int64(deltasp) - int64(bpsize)
   730  		}
   731  		if a == obj.NAME_PARAM {
   732  			p.From3.Offset += int64(deltasp) + int64(pcsize)
   733  		}
   734  		a = int(p.To.Name)
   735  		if a == obj.NAME_AUTO {
   736  			p.To.Offset += int64(deltasp) - int64(bpsize)
   737  		}
   738  		if a == obj.NAME_PARAM {
   739  			p.To.Offset += int64(deltasp) + int64(pcsize)
   740  		}
   741  
   742  		switch p.As {
   743  		default:
   744  			continue
   745  
   746  		case APUSHL, APUSHFL:
   747  			deltasp += 4
   748  			p.Spadj = 4
   749  			continue
   750  
   751  		case APUSHQ, APUSHFQ:
   752  			deltasp += 8
   753  			p.Spadj = 8
   754  			continue
   755  
   756  		case APUSHW, APUSHFW:
   757  			deltasp += 2
   758  			p.Spadj = 2
   759  			continue
   760  
   761  		case APOPL, APOPFL:
   762  			deltasp -= 4
   763  			p.Spadj = -4
   764  			continue
   765  
   766  		case APOPQ, APOPFQ:
   767  			deltasp -= 8
   768  			p.Spadj = -8
   769  			continue
   770  
   771  		case APOPW, APOPFW:
   772  			deltasp -= 2
   773  			p.Spadj = -2
   774  			continue
   775  
   776  		case obj.ARET:
   777  			break
   778  		}
   779  
   780  		if autoffset != deltasp {
   781  			ctxt.Diag("unbalanced PUSH/POP")
   782  		}
   783  
   784  		if autoffset != 0 {
   785  			if bpsize > 0 {
   786  				// Restore caller's BP
   787  				p.As = AMOVQ
   788  
   789  				p.From.Type = obj.TYPE_MEM
   790  				p.From.Reg = REG_SP
   791  				p.From.Scale = 1
   792  				p.From.Offset = int64(autoffset) - int64(bpsize)
   793  				p.To.Type = obj.TYPE_REG
   794  				p.To.Reg = REG_BP
   795  				p = obj.Appendp(ctxt, p)
   796  			}
   797  
   798  			p.As = AADJSP
   799  			p.From.Type = obj.TYPE_CONST
   800  			p.From.Offset = int64(-autoffset)
   801  			p.Spadj = -autoffset
   802  			p = obj.Appendp(ctxt, p)
   803  			p.As = obj.ARET
   804  
   805  			// If there are instructions following
   806  			// this ARET, they come from a branch
   807  			// with the same stackframe, so undo
   808  			// the cleanup.
   809  			p.Spadj = +autoffset
   810  		}
   811  
   812  		if p.To.Sym != nil { // retjmp
   813  			p.As = obj.AJMP
   814  		}
   815  	}
   816  }
   817  
   818  func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
   819  	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   820  		a.Type = obj.TYPE_MEM
   821  		a.Reg = REG_R15
   822  		a.Index = REG_CX
   823  		a.Scale = 1
   824  		return
   825  	}
   826  
   827  	a.Type = obj.TYPE_MEM
   828  	a.Reg = REG_CX
   829  }
   830  
   831  // Append code to p to load g into cx.
   832  // Overwrites p with the first instruction (no first appendp).
   833  // Overwriting p is unusual but it lets use this in both the
   834  // prologue (caller must call appendp first) and in the epilogue.
   835  // Returns last new instruction.
   836  func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
   837  	p.As = AMOVQ
   838  	if ctxt.Arch.Ptrsize == 4 {
   839  		p.As = AMOVL
   840  	}
   841  	p.From.Type = obj.TYPE_MEM
   842  	p.From.Reg = REG_TLS
   843  	p.From.Offset = 0
   844  	p.To.Type = obj.TYPE_REG
   845  	p.To.Reg = REG_CX
   846  
   847  	next := p.Link
   848  	progedit(ctxt, p)
   849  	for p.Link != next {
   850  		p = p.Link
   851  	}
   852  
   853  	if p.From.Index == REG_TLS {
   854  		p.From.Scale = 2
   855  	}
   856  
   857  	return p
   858  }
   859  
   860  // Append code to p to check for stack split.
   861  // Appends to (does not overwrite) p.
   862  // Assumes g is in CX.
   863  // Returns last new instruction.
   864  // On return, *jmpok is the instruction that should jump
   865  // to the stack frame allocation if no split is needed.
   866  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32, noctxt bool, jmpok **obj.Prog) *obj.Prog {
   867  	cmp := ACMPQ
   868  	lea := ALEAQ
   869  	mov := AMOVQ
   870  	sub := ASUBQ
   871  
   872  	if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   873  		cmp = ACMPL
   874  		lea = ALEAL
   875  		mov = AMOVL
   876  		sub = ASUBL
   877  	}
   878  
   879  	var q1 *obj.Prog
   880  	if framesize <= obj.StackSmall {
   881  		// small stack: SP <= stackguard
   882  		//	CMPQ SP, stackguard
   883  		p = obj.Appendp(ctxt, p)
   884  
   885  		p.As = int16(cmp)
   886  		p.From.Type = obj.TYPE_REG
   887  		p.From.Reg = REG_SP
   888  		indir_cx(ctxt, p, &p.To)
   889  		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   890  		if ctxt.Cursym.Cfunc != 0 {
   891  			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   892  		}
   893  	} else if framesize <= obj.StackBig {
   894  		// large stack: SP-framesize <= stackguard-StackSmall
   895  		//	LEAQ -xxx(SP), AX
   896  		//	CMPQ AX, stackguard
   897  		p = obj.Appendp(ctxt, p)
   898  
   899  		p.As = int16(lea)
   900  		p.From.Type = obj.TYPE_MEM
   901  		p.From.Reg = REG_SP
   902  		p.From.Offset = -(int64(framesize) - obj.StackSmall)
   903  		p.To.Type = obj.TYPE_REG
   904  		p.To.Reg = REG_AX
   905  
   906  		p = obj.Appendp(ctxt, p)
   907  		p.As = int16(cmp)
   908  		p.From.Type = obj.TYPE_REG
   909  		p.From.Reg = REG_AX
   910  		indir_cx(ctxt, p, &p.To)
   911  		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   912  		if ctxt.Cursym.Cfunc != 0 {
   913  			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   914  		}
   915  	} else {
   916  		// Such a large stack we need to protect against wraparound.
   917  		// If SP is close to zero:
   918  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   919  		// The +StackGuard on both sides is required to keep the left side positive:
   920  		// SP is allowed to be slightly below stackguard. See stack.h.
   921  		//
   922  		// Preemption sets stackguard to StackPreempt, a very large value.
   923  		// That breaks the math above, so we have to check for that explicitly.
   924  		//	MOVQ	stackguard, CX
   925  		//	CMPQ	CX, $StackPreempt
   926  		//	JEQ	label-of-call-to-morestack
   927  		//	LEAQ	StackGuard(SP), AX
   928  		//	SUBQ	CX, AX
   929  		//	CMPQ	AX, $(framesize+(StackGuard-StackSmall))
   930  
   931  		p = obj.Appendp(ctxt, p)
   932  
   933  		p.As = int16(mov)
   934  		indir_cx(ctxt, p, &p.From)
   935  		p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   936  		if ctxt.Cursym.Cfunc != 0 {
   937  			p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   938  		}
   939  		p.To.Type = obj.TYPE_REG
   940  		p.To.Reg = REG_SI
   941  
   942  		p = obj.Appendp(ctxt, p)
   943  		p.As = int16(cmp)
   944  		p.From.Type = obj.TYPE_REG
   945  		p.From.Reg = REG_SI
   946  		p.To.Type = obj.TYPE_CONST
   947  		p.To.Offset = obj.StackPreempt
   948  		if p.Mode == 32 {
   949  			p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
   950  		}
   951  
   952  		p = obj.Appendp(ctxt, p)
   953  		p.As = AJEQ
   954  		p.To.Type = obj.TYPE_BRANCH
   955  		q1 = p
   956  
   957  		p = obj.Appendp(ctxt, p)
   958  		p.As = int16(lea)
   959  		p.From.Type = obj.TYPE_MEM
   960  		p.From.Reg = REG_SP
   961  		p.From.Offset = obj.StackGuard
   962  		p.To.Type = obj.TYPE_REG
   963  		p.To.Reg = REG_AX
   964  
   965  		p = obj.Appendp(ctxt, p)
   966  		p.As = int16(sub)
   967  		p.From.Type = obj.TYPE_REG
   968  		p.From.Reg = REG_SI
   969  		p.To.Type = obj.TYPE_REG
   970  		p.To.Reg = REG_AX
   971  
   972  		p = obj.Appendp(ctxt, p)
   973  		p.As = int16(cmp)
   974  		p.From.Type = obj.TYPE_REG
   975  		p.From.Reg = REG_AX
   976  		p.To.Type = obj.TYPE_CONST
   977  		p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
   978  	}
   979  
   980  	// common
   981  	p = obj.Appendp(ctxt, p)
   982  
   983  	p.As = AJHI
   984  	p.To.Type = obj.TYPE_BRANCH
   985  	q := p
   986  
   987  	p = obj.Appendp(ctxt, p)
   988  	p.As = obj.ACALL
   989  	p.To.Type = obj.TYPE_BRANCH
   990  	if ctxt.Cursym.Cfunc != 0 {
   991  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
   992  	} else {
   993  		p.To.Sym = ctxt.Symmorestack[bool2int(noctxt)]
   994  	}
   995  
   996  	p = obj.Appendp(ctxt, p)
   997  	p.As = obj.AJMP
   998  	p.To.Type = obj.TYPE_BRANCH
   999  	p.Pcond = ctxt.Cursym.Text.Link
  1000  
  1001  	if q != nil {
  1002  		q.Pcond = p.Link
  1003  	}
  1004  	if q1 != nil {
  1005  		q1.Pcond = q.Link
  1006  	}
  1007  
  1008  	*jmpok = q
  1009  	return p
  1010  }
  1011  
  1012  func follow(ctxt *obj.Link, s *obj.LSym) {
  1013  	ctxt.Cursym = s
  1014  
  1015  	firstp := ctxt.NewProg()
  1016  	lastp := firstp
  1017  	xfol(ctxt, s.Text, &lastp)
  1018  	lastp.Link = nil
  1019  	s.Text = firstp.Link
  1020  }
  1021  
  1022  func nofollow(a int) bool {
  1023  	switch a {
  1024  	case obj.AJMP,
  1025  		obj.ARET,
  1026  		AIRETL,
  1027  		AIRETQ,
  1028  		AIRETW,
  1029  		ARETFL,
  1030  		ARETFQ,
  1031  		ARETFW,
  1032  		obj.AUNDEF:
  1033  		return true
  1034  	}
  1035  
  1036  	return false
  1037  }
  1038  
  1039  func pushpop(a int) bool {
  1040  	switch a {
  1041  	case APUSHL,
  1042  		APUSHFL,
  1043  		APUSHQ,
  1044  		APUSHFQ,
  1045  		APUSHW,
  1046  		APUSHFW,
  1047  		APOPL,
  1048  		APOPFL,
  1049  		APOPQ,
  1050  		APOPFQ,
  1051  		APOPW,
  1052  		APOPFW:
  1053  		return true
  1054  	}
  1055  
  1056  	return false
  1057  }
  1058  
  1059  func relinv(a int16) int16 {
  1060  	switch a {
  1061  	case AJEQ:
  1062  		return AJNE
  1063  	case AJNE:
  1064  		return AJEQ
  1065  	case AJLE:
  1066  		return AJGT
  1067  	case AJLS:
  1068  		return AJHI
  1069  	case AJLT:
  1070  		return AJGE
  1071  	case AJMI:
  1072  		return AJPL
  1073  	case AJGE:
  1074  		return AJLT
  1075  	case AJPL:
  1076  		return AJMI
  1077  	case AJGT:
  1078  		return AJLE
  1079  	case AJHI:
  1080  		return AJLS
  1081  	case AJCS:
  1082  		return AJCC
  1083  	case AJCC:
  1084  		return AJCS
  1085  	case AJPS:
  1086  		return AJPC
  1087  	case AJPC:
  1088  		return AJPS
  1089  	case AJOS:
  1090  		return AJOC
  1091  	case AJOC:
  1092  		return AJOS
  1093  	}
  1094  
  1095  	log.Fatalf("unknown relation: %s", obj.Aconv(int(a)))
  1096  	return 0
  1097  }
  1098  
  1099  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1100  	var q *obj.Prog
  1101  	var i int
  1102  	var a int
  1103  
  1104  loop:
  1105  	if p == nil {
  1106  		return
  1107  	}
  1108  	if p.As == obj.AJMP {
  1109  		q = p.Pcond
  1110  		if q != nil && q.As != obj.ATEXT {
  1111  			/* mark instruction as done and continue layout at target of jump */
  1112  			p.Mark = 1
  1113  
  1114  			p = q
  1115  			if p.Mark == 0 {
  1116  				goto loop
  1117  			}
  1118  		}
  1119  	}
  1120  
  1121  	if p.Mark != 0 {
  1122  		/*
  1123  		 * p goes here, but already used it elsewhere.
  1124  		 * copy up to 4 instructions or else branch to other copy.
  1125  		 */
  1126  		i = 0
  1127  		q = p
  1128  		for ; i < 4; i, q = i+1, q.Link {
  1129  			if q == nil {
  1130  				break
  1131  			}
  1132  			if q == *last {
  1133  				break
  1134  			}
  1135  			a = int(q.As)
  1136  			if a == obj.ANOP {
  1137  				i--
  1138  				continue
  1139  			}
  1140  
  1141  			if nofollow(a) || pushpop(a) {
  1142  				break // NOTE(rsc): arm does goto copy
  1143  			}
  1144  			if q.Pcond == nil || q.Pcond.Mark != 0 {
  1145  				continue
  1146  			}
  1147  			if a == obj.ACALL || a == ALOOP {
  1148  				continue
  1149  			}
  1150  			for {
  1151  				if p.As == obj.ANOP {
  1152  					p = p.Link
  1153  					continue
  1154  				}
  1155  
  1156  				q = obj.Copyp(ctxt, p)
  1157  				p = p.Link
  1158  				q.Mark = 1
  1159  				(*last).Link = q
  1160  				*last = q
  1161  				if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark != 0 {
  1162  					continue
  1163  				}
  1164  
  1165  				q.As = relinv(q.As)
  1166  				p = q.Pcond
  1167  				q.Pcond = q.Link
  1168  				q.Link = p
  1169  				xfol(ctxt, q.Link, last)
  1170  				p = q.Link
  1171  				if p.Mark != 0 {
  1172  					return
  1173  				}
  1174  				goto loop
  1175  				/* */
  1176  			}
  1177  		}
  1178  		q = ctxt.NewProg()
  1179  		q.As = obj.AJMP
  1180  		q.Lineno = p.Lineno
  1181  		q.To.Type = obj.TYPE_BRANCH
  1182  		q.To.Offset = p.Pc
  1183  		q.Pcond = p
  1184  		p = q
  1185  	}
  1186  
  1187  	/* emit p */
  1188  	p.Mark = 1
  1189  
  1190  	(*last).Link = p
  1191  	*last = p
  1192  	a = int(p.As)
  1193  
  1194  	/* continue loop with what comes after p */
  1195  	if nofollow(a) {
  1196  		return
  1197  	}
  1198  	if p.Pcond != nil && a != obj.ACALL {
  1199  		/*
  1200  		 * some kind of conditional branch.
  1201  		 * recurse to follow one path.
  1202  		 * continue loop on the other.
  1203  		 */
  1204  		q = obj.Brchain(ctxt, p.Pcond)
  1205  		if q != nil {
  1206  			p.Pcond = q
  1207  		}
  1208  		q = obj.Brchain(ctxt, p.Link)
  1209  		if q != nil {
  1210  			p.Link = q
  1211  		}
  1212  		if p.From.Type == obj.TYPE_CONST {
  1213  			if p.From.Offset == 1 {
  1214  				/*
  1215  				 * expect conditional jump to be taken.
  1216  				 * rewrite so that's the fall-through case.
  1217  				 */
  1218  				p.As = relinv(int16(a))
  1219  
  1220  				q = p.Link
  1221  				p.Link = p.Pcond
  1222  				p.Pcond = q
  1223  			}
  1224  		} else {
  1225  			q = p.Link
  1226  			if q.Mark != 0 {
  1227  				if a != ALOOP {
  1228  					p.As = relinv(int16(a))
  1229  					p.Link = p.Pcond
  1230  					p.Pcond = q
  1231  				}
  1232  			}
  1233  		}
  1234  
  1235  		xfol(ctxt, p.Link, last)
  1236  		if p.Pcond.Mark != 0 {
  1237  			return
  1238  		}
  1239  		p = p.Pcond
  1240  		goto loop
  1241  	}
  1242  
  1243  	p = p.Link
  1244  	goto loop
  1245  }
  1246  
  1247  var unaryDst = map[int]bool{
  1248  	ABSWAPL:    true,
  1249  	ABSWAPQ:    true,
  1250  	ACMPXCHG8B: true,
  1251  	ADECB:      true,
  1252  	ADECL:      true,
  1253  	ADECQ:      true,
  1254  	ADECW:      true,
  1255  	AINCB:      true,
  1256  	AINCL:      true,
  1257  	AINCQ:      true,
  1258  	AINCW:      true,
  1259  	ANEGB:      true,
  1260  	ANEGL:      true,
  1261  	ANEGQ:      true,
  1262  	ANEGW:      true,
  1263  	ANOTB:      true,
  1264  	ANOTL:      true,
  1265  	ANOTQ:      true,
  1266  	ANOTW:      true,
  1267  	APOPL:      true,
  1268  	APOPQ:      true,
  1269  	APOPW:      true,
  1270  	ASETCC:     true,
  1271  	ASETCS:     true,
  1272  	ASETEQ:     true,
  1273  	ASETGE:     true,
  1274  	ASETGT:     true,
  1275  	ASETHI:     true,
  1276  	ASETLE:     true,
  1277  	ASETLS:     true,
  1278  	ASETLT:     true,
  1279  	ASETMI:     true,
  1280  	ASETNE:     true,
  1281  	ASETOC:     true,
  1282  	ASETOS:     true,
  1283  	ASETPC:     true,
  1284  	ASETPL:     true,
  1285  	ASETPS:     true,
  1286  	AFFREE:     true,
  1287  	AFLDENV:    true,
  1288  	AFSAVE:     true,
  1289  	AFSTCW:     true,
  1290  	AFSTENV:    true,
  1291  	AFSTSW:     true,
  1292  	AFXSAVE:    true,
  1293  	AFXSAVE64:  true,
  1294  	ASTMXCSR:   true,
  1295  }
  1296  
  1297  var Linkamd64 = obj.LinkArch{
  1298  	ByteOrder:  binary.LittleEndian,
  1299  	Name:       "amd64",
  1300  	Thechar:    '6',
  1301  	Preprocess: preprocess,
  1302  	Assemble:   span6,
  1303  	Follow:     follow,
  1304  	Progedit:   progedit,
  1305  	UnaryDst:   unaryDst,
  1306  	Minlc:      1,
  1307  	Ptrsize:    8,
  1308  	Regsize:    8,
  1309  }
  1310  
  1311  var Linkamd64p32 = obj.LinkArch{
  1312  	ByteOrder:  binary.LittleEndian,
  1313  	Name:       "amd64p32",
  1314  	Thechar:    '6',
  1315  	Preprocess: preprocess,
  1316  	Assemble:   span6,
  1317  	Follow:     follow,
  1318  	Progedit:   progedit,
  1319  	UnaryDst:   unaryDst,
  1320  	Minlc:      1,
  1321  	Ptrsize:    4,
  1322  	Regsize:    8,
  1323  }
  1324  
  1325  var Link386 = obj.LinkArch{
  1326  	ByteOrder:  binary.LittleEndian,
  1327  	Name:       "386",
  1328  	Thechar:    '8',
  1329  	Preprocess: preprocess,
  1330  	Assemble:   span6,
  1331  	Follow:     follow,
  1332  	Progedit:   progedit,
  1333  	UnaryDst:   unaryDst,
  1334  	Minlc:      1,
  1335  	Ptrsize:    4,
  1336  	Regsize:    4,
  1337  }