github.com/alash3al/go@v0.0.0-20150827002835-d497eeb00540/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  	var a int
   661  	var pcsize int
   662  	for ; p != nil; p = p.Link {
   663  		pcsize = int(p.Mode) / 8
   664  		a = int(p.From.Name)
   665  		if a == obj.NAME_AUTO {
   666  			p.From.Offset += int64(deltasp) - int64(bpsize)
   667  		}
   668  		if a == obj.NAME_PARAM {
   669  			p.From.Offset += int64(deltasp) + int64(pcsize)
   670  		}
   671  		if p.From3 != nil {
   672  			a = int(p.From3.Name)
   673  			if a == obj.NAME_AUTO {
   674  				p.From3.Offset += int64(deltasp) - int64(bpsize)
   675  			}
   676  			if a == obj.NAME_PARAM {
   677  				p.From3.Offset += int64(deltasp) + int64(pcsize)
   678  			}
   679  		}
   680  		a = int(p.To.Name)
   681  		if a == obj.NAME_AUTO {
   682  			p.To.Offset += int64(deltasp) - int64(bpsize)
   683  		}
   684  		if a == obj.NAME_PARAM {
   685  			p.To.Offset += int64(deltasp) + int64(pcsize)
   686  		}
   687  
   688  		switch p.As {
   689  		default:
   690  			continue
   691  
   692  		case APUSHL, APUSHFL:
   693  			deltasp += 4
   694  			p.Spadj = 4
   695  			continue
   696  
   697  		case APUSHQ, APUSHFQ:
   698  			deltasp += 8
   699  			p.Spadj = 8
   700  			continue
   701  
   702  		case APUSHW, APUSHFW:
   703  			deltasp += 2
   704  			p.Spadj = 2
   705  			continue
   706  
   707  		case APOPL, APOPFL:
   708  			deltasp -= 4
   709  			p.Spadj = -4
   710  			continue
   711  
   712  		case APOPQ, APOPFQ:
   713  			deltasp -= 8
   714  			p.Spadj = -8
   715  			continue
   716  
   717  		case APOPW, APOPFW:
   718  			deltasp -= 2
   719  			p.Spadj = -2
   720  			continue
   721  
   722  		case obj.ARET:
   723  			break
   724  		}
   725  
   726  		if autoffset != deltasp {
   727  			ctxt.Diag("unbalanced PUSH/POP")
   728  		}
   729  
   730  		if autoffset != 0 {
   731  			if bpsize > 0 {
   732  				// Restore caller's BP
   733  				p.As = AMOVQ
   734  
   735  				p.From.Type = obj.TYPE_MEM
   736  				p.From.Reg = REG_SP
   737  				p.From.Scale = 1
   738  				p.From.Offset = int64(autoffset) - int64(bpsize)
   739  				p.To.Type = obj.TYPE_REG
   740  				p.To.Reg = REG_BP
   741  				p = obj.Appendp(ctxt, p)
   742  			}
   743  
   744  			p.As = AADJSP
   745  			p.From.Type = obj.TYPE_CONST
   746  			p.From.Offset = int64(-autoffset)
   747  			p.Spadj = -autoffset
   748  			p = obj.Appendp(ctxt, p)
   749  			p.As = obj.ARET
   750  
   751  			// If there are instructions following
   752  			// this ARET, they come from a branch
   753  			// with the same stackframe, so undo
   754  			// the cleanup.
   755  			p.Spadj = +autoffset
   756  		}
   757  
   758  		if p.To.Sym != nil { // retjmp
   759  			p.As = obj.AJMP
   760  		}
   761  	}
   762  }
   763  
   764  func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
   765  	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   766  		a.Type = obj.TYPE_MEM
   767  		a.Reg = REG_R15
   768  		a.Index = REG_CX
   769  		a.Scale = 1
   770  		return
   771  	}
   772  
   773  	a.Type = obj.TYPE_MEM
   774  	a.Reg = REG_CX
   775  }
   776  
   777  // Append code to p to load g into cx.
   778  // Overwrites p with the first instruction (no first appendp).
   779  // Overwriting p is unusual but it lets use this in both the
   780  // prologue (caller must call appendp first) and in the epilogue.
   781  // Returns last new instruction.
   782  func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
   783  	p.As = AMOVQ
   784  	if ctxt.Arch.Ptrsize == 4 {
   785  		p.As = AMOVL
   786  	}
   787  	p.From.Type = obj.TYPE_MEM
   788  	p.From.Reg = REG_TLS
   789  	p.From.Offset = 0
   790  	p.To.Type = obj.TYPE_REG
   791  	p.To.Reg = REG_CX
   792  
   793  	next := p.Link
   794  	progedit(ctxt, p)
   795  	for p.Link != next {
   796  		p = p.Link
   797  	}
   798  
   799  	if p.From.Index == REG_TLS {
   800  		p.From.Scale = 2
   801  	}
   802  
   803  	return p
   804  }
   805  
   806  // Append code to p to check for stack split.
   807  // Appends to (does not overwrite) p.
   808  // Assumes g is in CX.
   809  // Returns last new instruction.
   810  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog {
   811  	cmp := ACMPQ
   812  	lea := ALEAQ
   813  	mov := AMOVQ
   814  	sub := ASUBQ
   815  
   816  	if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   817  		cmp = ACMPL
   818  		lea = ALEAL
   819  		mov = AMOVL
   820  		sub = ASUBL
   821  	}
   822  
   823  	var q1 *obj.Prog
   824  	if framesize <= obj.StackSmall {
   825  		// small stack: SP <= stackguard
   826  		//	CMPQ SP, stackguard
   827  		p = obj.Appendp(ctxt, p)
   828  
   829  		p.As = int16(cmp)
   830  		p.From.Type = obj.TYPE_REG
   831  		p.From.Reg = REG_SP
   832  		indir_cx(ctxt, p, &p.To)
   833  		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   834  		if ctxt.Cursym.Cfunc != 0 {
   835  			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   836  		}
   837  	} else if framesize <= obj.StackBig {
   838  		// large stack: SP-framesize <= stackguard-StackSmall
   839  		//	LEAQ -xxx(SP), AX
   840  		//	CMPQ AX, stackguard
   841  		p = obj.Appendp(ctxt, p)
   842  
   843  		p.As = int16(lea)
   844  		p.From.Type = obj.TYPE_MEM
   845  		p.From.Reg = REG_SP
   846  		p.From.Offset = -(int64(framesize) - obj.StackSmall)
   847  		p.To.Type = obj.TYPE_REG
   848  		p.To.Reg = REG_AX
   849  
   850  		p = obj.Appendp(ctxt, p)
   851  		p.As = int16(cmp)
   852  		p.From.Type = obj.TYPE_REG
   853  		p.From.Reg = REG_AX
   854  		indir_cx(ctxt, p, &p.To)
   855  		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   856  		if ctxt.Cursym.Cfunc != 0 {
   857  			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   858  		}
   859  	} else {
   860  		// Such a large stack we need to protect against wraparound.
   861  		// If SP is close to zero:
   862  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   863  		// The +StackGuard on both sides is required to keep the left side positive:
   864  		// SP is allowed to be slightly below stackguard. See stack.h.
   865  		//
   866  		// Preemption sets stackguard to StackPreempt, a very large value.
   867  		// That breaks the math above, so we have to check for that explicitly.
   868  		//	MOVQ	stackguard, CX
   869  		//	CMPQ	CX, $StackPreempt
   870  		//	JEQ	label-of-call-to-morestack
   871  		//	LEAQ	StackGuard(SP), AX
   872  		//	SUBQ	CX, AX
   873  		//	CMPQ	AX, $(framesize+(StackGuard-StackSmall))
   874  
   875  		p = obj.Appendp(ctxt, p)
   876  
   877  		p.As = int16(mov)
   878  		indir_cx(ctxt, p, &p.From)
   879  		p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   880  		if ctxt.Cursym.Cfunc != 0 {
   881  			p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   882  		}
   883  		p.To.Type = obj.TYPE_REG
   884  		p.To.Reg = REG_SI
   885  
   886  		p = obj.Appendp(ctxt, p)
   887  		p.As = int16(cmp)
   888  		p.From.Type = obj.TYPE_REG
   889  		p.From.Reg = REG_SI
   890  		p.To.Type = obj.TYPE_CONST
   891  		p.To.Offset = obj.StackPreempt
   892  		if p.Mode == 32 {
   893  			p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
   894  		}
   895  
   896  		p = obj.Appendp(ctxt, p)
   897  		p.As = AJEQ
   898  		p.To.Type = obj.TYPE_BRANCH
   899  		q1 = p
   900  
   901  		p = obj.Appendp(ctxt, p)
   902  		p.As = int16(lea)
   903  		p.From.Type = obj.TYPE_MEM
   904  		p.From.Reg = REG_SP
   905  		p.From.Offset = obj.StackGuard
   906  		p.To.Type = obj.TYPE_REG
   907  		p.To.Reg = REG_AX
   908  
   909  		p = obj.Appendp(ctxt, p)
   910  		p.As = int16(sub)
   911  		p.From.Type = obj.TYPE_REG
   912  		p.From.Reg = REG_SI
   913  		p.To.Type = obj.TYPE_REG
   914  		p.To.Reg = REG_AX
   915  
   916  		p = obj.Appendp(ctxt, p)
   917  		p.As = int16(cmp)
   918  		p.From.Type = obj.TYPE_REG
   919  		p.From.Reg = REG_AX
   920  		p.To.Type = obj.TYPE_CONST
   921  		p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
   922  	}
   923  
   924  	// common
   925  	jls := obj.Appendp(ctxt, p)
   926  	jls.As = AJLS
   927  	jls.To.Type = obj.TYPE_BRANCH
   928  
   929  	var last *obj.Prog
   930  	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
   931  	}
   932  
   933  	call := obj.Appendp(ctxt, last)
   934  	call.Lineno = ctxt.Cursym.Text.Lineno
   935  	call.Mode = ctxt.Cursym.Text.Mode
   936  	call.As = obj.ACALL
   937  	call.To.Type = obj.TYPE_BRANCH
   938  	morestack := "runtime.morestack"
   939  	switch {
   940  	case ctxt.Cursym.Cfunc != 0:
   941  		morestack = "runtime.morestackc"
   942  	case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
   943  		morestack = "runtime.morestack_noctxt"
   944  	}
   945  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
   946  
   947  	jmp := obj.Appendp(ctxt, call)
   948  	jmp.As = obj.AJMP
   949  	jmp.To.Type = obj.TYPE_BRANCH
   950  	jmp.Pcond = ctxt.Cursym.Text.Link
   951  
   952  	jls.Pcond = call
   953  	if q1 != nil {
   954  		q1.Pcond = call
   955  	}
   956  
   957  	return jls
   958  }
   959  
   960  func follow(ctxt *obj.Link, s *obj.LSym) {
   961  	ctxt.Cursym = s
   962  
   963  	firstp := ctxt.NewProg()
   964  	lastp := firstp
   965  	xfol(ctxt, s.Text, &lastp)
   966  	lastp.Link = nil
   967  	s.Text = firstp.Link
   968  }
   969  
   970  func nofollow(a int) bool {
   971  	switch a {
   972  	case obj.AJMP,
   973  		obj.ARET,
   974  		AIRETL,
   975  		AIRETQ,
   976  		AIRETW,
   977  		ARETFL,
   978  		ARETFQ,
   979  		ARETFW,
   980  		obj.AUNDEF:
   981  		return true
   982  	}
   983  
   984  	return false
   985  }
   986  
   987  func pushpop(a int) bool {
   988  	switch a {
   989  	case APUSHL,
   990  		APUSHFL,
   991  		APUSHQ,
   992  		APUSHFQ,
   993  		APUSHW,
   994  		APUSHFW,
   995  		APOPL,
   996  		APOPFL,
   997  		APOPQ,
   998  		APOPFQ,
   999  		APOPW,
  1000  		APOPFW:
  1001  		return true
  1002  	}
  1003  
  1004  	return false
  1005  }
  1006  
  1007  func relinv(a int16) int16 {
  1008  	switch a {
  1009  	case AJEQ:
  1010  		return AJNE
  1011  	case AJNE:
  1012  		return AJEQ
  1013  	case AJLE:
  1014  		return AJGT
  1015  	case AJLS:
  1016  		return AJHI
  1017  	case AJLT:
  1018  		return AJGE
  1019  	case AJMI:
  1020  		return AJPL
  1021  	case AJGE:
  1022  		return AJLT
  1023  	case AJPL:
  1024  		return AJMI
  1025  	case AJGT:
  1026  		return AJLE
  1027  	case AJHI:
  1028  		return AJLS
  1029  	case AJCS:
  1030  		return AJCC
  1031  	case AJCC:
  1032  		return AJCS
  1033  	case AJPS:
  1034  		return AJPC
  1035  	case AJPC:
  1036  		return AJPS
  1037  	case AJOS:
  1038  		return AJOC
  1039  	case AJOC:
  1040  		return AJOS
  1041  	}
  1042  
  1043  	log.Fatalf("unknown relation: %s", obj.Aconv(int(a)))
  1044  	return 0
  1045  }
  1046  
  1047  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1048  	var q *obj.Prog
  1049  	var i int
  1050  	var a int
  1051  
  1052  loop:
  1053  	if p == nil {
  1054  		return
  1055  	}
  1056  	if p.As == obj.AJMP {
  1057  		q = p.Pcond
  1058  		if q != nil && q.As != obj.ATEXT {
  1059  			/* mark instruction as done and continue layout at target of jump */
  1060  			p.Mark = 1
  1061  
  1062  			p = q
  1063  			if p.Mark == 0 {
  1064  				goto loop
  1065  			}
  1066  		}
  1067  	}
  1068  
  1069  	if p.Mark != 0 {
  1070  		/*
  1071  		 * p goes here, but already used it elsewhere.
  1072  		 * copy up to 4 instructions or else branch to other copy.
  1073  		 */
  1074  		i = 0
  1075  		q = p
  1076  		for ; i < 4; i, q = i+1, q.Link {
  1077  			if q == nil {
  1078  				break
  1079  			}
  1080  			if q == *last {
  1081  				break
  1082  			}
  1083  			a = int(q.As)
  1084  			if a == obj.ANOP {
  1085  				i--
  1086  				continue
  1087  			}
  1088  
  1089  			if nofollow(a) || pushpop(a) {
  1090  				break // NOTE(rsc): arm does goto copy
  1091  			}
  1092  			if q.Pcond == nil || q.Pcond.Mark != 0 {
  1093  				continue
  1094  			}
  1095  			if a == obj.ACALL || a == ALOOP {
  1096  				continue
  1097  			}
  1098  			for {
  1099  				if p.As == obj.ANOP {
  1100  					p = p.Link
  1101  					continue
  1102  				}
  1103  
  1104  				q = obj.Copyp(ctxt, p)
  1105  				p = p.Link
  1106  				q.Mark = 1
  1107  				(*last).Link = q
  1108  				*last = q
  1109  				if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark != 0 {
  1110  					continue
  1111  				}
  1112  
  1113  				q.As = relinv(q.As)
  1114  				p = q.Pcond
  1115  				q.Pcond = q.Link
  1116  				q.Link = p
  1117  				xfol(ctxt, q.Link, last)
  1118  				p = q.Link
  1119  				if p.Mark != 0 {
  1120  					return
  1121  				}
  1122  				goto loop
  1123  				/* */
  1124  			}
  1125  		}
  1126  		q = ctxt.NewProg()
  1127  		q.As = obj.AJMP
  1128  		q.Lineno = p.Lineno
  1129  		q.To.Type = obj.TYPE_BRANCH
  1130  		q.To.Offset = p.Pc
  1131  		q.Pcond = p
  1132  		p = q
  1133  	}
  1134  
  1135  	/* emit p */
  1136  	p.Mark = 1
  1137  
  1138  	(*last).Link = p
  1139  	*last = p
  1140  	a = int(p.As)
  1141  
  1142  	/* continue loop with what comes after p */
  1143  	if nofollow(a) {
  1144  		return
  1145  	}
  1146  	if p.Pcond != nil && a != obj.ACALL {
  1147  		/*
  1148  		 * some kind of conditional branch.
  1149  		 * recurse to follow one path.
  1150  		 * continue loop on the other.
  1151  		 */
  1152  		q = obj.Brchain(ctxt, p.Pcond)
  1153  		if q != nil {
  1154  			p.Pcond = q
  1155  		}
  1156  		q = obj.Brchain(ctxt, p.Link)
  1157  		if q != nil {
  1158  			p.Link = q
  1159  		}
  1160  		if p.From.Type == obj.TYPE_CONST {
  1161  			if p.From.Offset == 1 {
  1162  				/*
  1163  				 * expect conditional jump to be taken.
  1164  				 * rewrite so that's the fall-through case.
  1165  				 */
  1166  				p.As = relinv(int16(a))
  1167  
  1168  				q = p.Link
  1169  				p.Link = p.Pcond
  1170  				p.Pcond = q
  1171  			}
  1172  		} else {
  1173  			q = p.Link
  1174  			if q.Mark != 0 {
  1175  				if a != ALOOP {
  1176  					p.As = relinv(int16(a))
  1177  					p.Link = p.Pcond
  1178  					p.Pcond = q
  1179  				}
  1180  			}
  1181  		}
  1182  
  1183  		xfol(ctxt, p.Link, last)
  1184  		if p.Pcond.Mark != 0 {
  1185  			return
  1186  		}
  1187  		p = p.Pcond
  1188  		goto loop
  1189  	}
  1190  
  1191  	p = p.Link
  1192  	goto loop
  1193  }
  1194  
  1195  var unaryDst = map[int]bool{
  1196  	ABSWAPL:    true,
  1197  	ABSWAPQ:    true,
  1198  	ACMPXCHG8B: true,
  1199  	ADECB:      true,
  1200  	ADECL:      true,
  1201  	ADECQ:      true,
  1202  	ADECW:      true,
  1203  	AINCB:      true,
  1204  	AINCL:      true,
  1205  	AINCQ:      true,
  1206  	AINCW:      true,
  1207  	ANEGB:      true,
  1208  	ANEGL:      true,
  1209  	ANEGQ:      true,
  1210  	ANEGW:      true,
  1211  	ANOTB:      true,
  1212  	ANOTL:      true,
  1213  	ANOTQ:      true,
  1214  	ANOTW:      true,
  1215  	APOPL:      true,
  1216  	APOPQ:      true,
  1217  	APOPW:      true,
  1218  	ASETCC:     true,
  1219  	ASETCS:     true,
  1220  	ASETEQ:     true,
  1221  	ASETGE:     true,
  1222  	ASETGT:     true,
  1223  	ASETHI:     true,
  1224  	ASETLE:     true,
  1225  	ASETLS:     true,
  1226  	ASETLT:     true,
  1227  	ASETMI:     true,
  1228  	ASETNE:     true,
  1229  	ASETOC:     true,
  1230  	ASETOS:     true,
  1231  	ASETPC:     true,
  1232  	ASETPL:     true,
  1233  	ASETPS:     true,
  1234  	AFFREE:     true,
  1235  	AFLDENV:    true,
  1236  	AFSAVE:     true,
  1237  	AFSTCW:     true,
  1238  	AFSTENV:    true,
  1239  	AFSTSW:     true,
  1240  	AFXSAVE:    true,
  1241  	AFXSAVE64:  true,
  1242  	ASTMXCSR:   true,
  1243  }
  1244  
  1245  var Linkamd64 = obj.LinkArch{
  1246  	ByteOrder:  binary.LittleEndian,
  1247  	Name:       "amd64",
  1248  	Thechar:    '6',
  1249  	Preprocess: preprocess,
  1250  	Assemble:   span6,
  1251  	Follow:     follow,
  1252  	Progedit:   progedit,
  1253  	UnaryDst:   unaryDst,
  1254  	Minlc:      1,
  1255  	Ptrsize:    8,
  1256  	Regsize:    8,
  1257  }
  1258  
  1259  var Linkamd64p32 = obj.LinkArch{
  1260  	ByteOrder:  binary.LittleEndian,
  1261  	Name:       "amd64p32",
  1262  	Thechar:    '6',
  1263  	Preprocess: preprocess,
  1264  	Assemble:   span6,
  1265  	Follow:     follow,
  1266  	Progedit:   progedit,
  1267  	UnaryDst:   unaryDst,
  1268  	Minlc:      1,
  1269  	Ptrsize:    4,
  1270  	Regsize:    8,
  1271  }
  1272  
  1273  var Link386 = obj.LinkArch{
  1274  	ByteOrder:  binary.LittleEndian,
  1275  	Name:       "386",
  1276  	Thechar:    '8',
  1277  	Preprocess: preprocess,
  1278  	Assemble:   span6,
  1279  	Follow:     follow,
  1280  	Progedit:   progedit,
  1281  	UnaryDst:   unaryDst,
  1282  	Minlc:      1,
  1283  	Ptrsize:    4,
  1284  	Regsize:    4,
  1285  }