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