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