github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/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.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil {
   439  		ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0)
   440  	}
   441  
   442  	ctxt.Cursym = cursym
   443  
   444  	if cursym.Text == nil || cursym.Text.Link == nil {
   445  		return
   446  	}
   447  
   448  	p := cursym.Text
   449  	autoffset := int32(p.To.Offset)
   450  	if autoffset < 0 {
   451  		autoffset = 0
   452  	}
   453  
   454  	var bpsize int
   455  	if p.Mode == 64 && obj.Framepointer_enabled != 0 && autoffset > 0 {
   456  		// Make room for to save a base pointer.  If autoffset == 0,
   457  		// this might do something special like a tail jump to
   458  		// another function, so in that case we omit this.
   459  		bpsize = ctxt.Arch.Ptrsize
   460  
   461  		autoffset += int32(bpsize)
   462  		p.To.Offset += int64(bpsize)
   463  	} else {
   464  		bpsize = 0
   465  	}
   466  
   467  	textarg := int64(p.To.Val.(int32))
   468  	cursym.Args = int32(textarg)
   469  	cursym.Locals = int32(p.To.Offset)
   470  
   471  	// TODO(rsc): Remove.
   472  	if p.Mode == 32 && cursym.Locals < 0 {
   473  		cursym.Locals = 0
   474  	}
   475  
   476  	// TODO(rsc): Remove 'p.Mode == 64 &&'.
   477  	if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 {
   478  		for q := p; q != nil; q = q.Link {
   479  			if q.As == obj.ACALL {
   480  				goto noleaf
   481  			}
   482  			if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 {
   483  				goto noleaf
   484  			}
   485  		}
   486  
   487  		p.From3.Offset |= obj.NOSPLIT
   488  	noleaf:
   489  	}
   490  
   491  	if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 {
   492  		p = obj.Appendp(ctxt, p)
   493  		p = load_g_cx(ctxt, p) // load g into CX
   494  	}
   495  
   496  	if cursym.Text.From3Offset()&obj.NOSPLIT == 0 {
   497  		p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check
   498  	}
   499  
   500  	if autoffset != 0 {
   501  		if autoffset%int32(ctxt.Arch.Regsize) != 0 {
   502  			ctxt.Diag("unaligned stack size %d", autoffset)
   503  		}
   504  		p = obj.Appendp(ctxt, p)
   505  		p.As = AADJSP
   506  		p.From.Type = obj.TYPE_CONST
   507  		p.From.Offset = int64(autoffset)
   508  		p.Spadj = autoffset
   509  	} else {
   510  		// zero-byte stack adjustment.
   511  		// Insert a fake non-zero adjustment so that stkcheck can
   512  		// recognize the end of the stack-splitting prolog.
   513  		p = obj.Appendp(ctxt, p)
   514  
   515  		p.As = obj.ANOP
   516  		p.Spadj = int32(-ctxt.Arch.Ptrsize)
   517  		p = obj.Appendp(ctxt, p)
   518  		p.As = obj.ANOP
   519  		p.Spadj = int32(ctxt.Arch.Ptrsize)
   520  	}
   521  
   522  	deltasp := autoffset
   523  
   524  	if bpsize > 0 {
   525  		// Save caller's BP
   526  		p = obj.Appendp(ctxt, p)
   527  
   528  		p.As = AMOVQ
   529  		p.From.Type = obj.TYPE_REG
   530  		p.From.Reg = REG_BP
   531  		p.To.Type = obj.TYPE_MEM
   532  		p.To.Reg = REG_SP
   533  		p.To.Scale = 1
   534  		p.To.Offset = int64(autoffset) - int64(bpsize)
   535  
   536  		// Move current frame to BP
   537  		p = obj.Appendp(ctxt, p)
   538  
   539  		p.As = ALEAQ
   540  		p.From.Type = obj.TYPE_MEM
   541  		p.From.Reg = REG_SP
   542  		p.From.Scale = 1
   543  		p.From.Offset = int64(autoffset) - int64(bpsize)
   544  		p.To.Type = obj.TYPE_REG
   545  		p.To.Reg = REG_BP
   546  	}
   547  
   548  	if cursym.Text.From3Offset()&obj.WRAPPER != 0 {
   549  		// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   550  		//
   551  		//	MOVQ g_panic(CX), BX
   552  		//	TESTQ BX, BX
   553  		//	JEQ end
   554  		//	LEAQ (autoffset+8)(SP), DI
   555  		//	CMPQ panic_argp(BX), DI
   556  		//	JNE end
   557  		//	MOVQ SP, panic_argp(BX)
   558  		// end:
   559  		//	NOP
   560  		//
   561  		// The NOP is needed to give the jumps somewhere to land.
   562  		// It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
   563  
   564  		p = obj.Appendp(ctxt, p)
   565  
   566  		p.As = AMOVQ
   567  		p.From.Type = obj.TYPE_MEM
   568  		p.From.Reg = REG_CX
   569  		p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   570  		p.To.Type = obj.TYPE_REG
   571  		p.To.Reg = REG_BX
   572  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   573  			p.As = AMOVL
   574  			p.From.Type = obj.TYPE_MEM
   575  			p.From.Reg = REG_R15
   576  			p.From.Scale = 1
   577  			p.From.Index = REG_CX
   578  		}
   579  		if p.Mode == 32 {
   580  			p.As = AMOVL
   581  		}
   582  
   583  		p = obj.Appendp(ctxt, p)
   584  		p.As = ATESTQ
   585  		p.From.Type = obj.TYPE_REG
   586  		p.From.Reg = REG_BX
   587  		p.To.Type = obj.TYPE_REG
   588  		p.To.Reg = REG_BX
   589  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   590  			p.As = ATESTL
   591  		}
   592  
   593  		p = obj.Appendp(ctxt, p)
   594  		p.As = AJEQ
   595  		p.To.Type = obj.TYPE_BRANCH
   596  		p1 := p
   597  
   598  		p = obj.Appendp(ctxt, p)
   599  		p.As = ALEAQ
   600  		p.From.Type = obj.TYPE_MEM
   601  		p.From.Reg = REG_SP
   602  		p.From.Offset = int64(autoffset) + int64(ctxt.Arch.Regsize)
   603  		p.To.Type = obj.TYPE_REG
   604  		p.To.Reg = REG_DI
   605  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   606  			p.As = ALEAL
   607  		}
   608  
   609  		p = obj.Appendp(ctxt, p)
   610  		p.As = ACMPQ
   611  		p.From.Type = obj.TYPE_MEM
   612  		p.From.Reg = REG_BX
   613  		p.From.Offset = 0 // Panic.argp
   614  		p.To.Type = obj.TYPE_REG
   615  		p.To.Reg = REG_DI
   616  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   617  			p.As = ACMPL
   618  			p.From.Type = obj.TYPE_MEM
   619  			p.From.Reg = REG_R15
   620  			p.From.Scale = 1
   621  			p.From.Index = REG_BX
   622  		}
   623  		if p.Mode == 32 {
   624  			p.As = ACMPL
   625  		}
   626  
   627  		p = obj.Appendp(ctxt, p)
   628  		p.As = AJNE
   629  		p.To.Type = obj.TYPE_BRANCH
   630  		p2 := p
   631  
   632  		p = obj.Appendp(ctxt, p)
   633  		p.As = AMOVQ
   634  		p.From.Type = obj.TYPE_REG
   635  		p.From.Reg = REG_SP
   636  		p.To.Type = obj.TYPE_MEM
   637  		p.To.Reg = REG_BX
   638  		p.To.Offset = 0 // Panic.argp
   639  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   640  			p.As = AMOVL
   641  			p.To.Type = obj.TYPE_MEM
   642  			p.To.Reg = REG_R15
   643  			p.To.Scale = 1
   644  			p.To.Index = REG_BX
   645  		}
   646  		if p.Mode == 32 {
   647  			p.As = AMOVL
   648  		}
   649  
   650  		p = obj.Appendp(ctxt, p)
   651  		p.As = obj.ANOP
   652  		p1.Pcond = p
   653  		p2.Pcond = p
   654  	}
   655  
   656  	var a int
   657  	var pcsize int
   658  	for ; p != nil; p = p.Link {
   659  		pcsize = int(p.Mode) / 8
   660  		a = int(p.From.Name)
   661  		if a == obj.NAME_AUTO {
   662  			p.From.Offset += int64(deltasp) - int64(bpsize)
   663  		}
   664  		if a == obj.NAME_PARAM {
   665  			p.From.Offset += int64(deltasp) + int64(pcsize)
   666  		}
   667  		if p.From3 != nil {
   668  			a = int(p.From3.Name)
   669  			if a == obj.NAME_AUTO {
   670  				p.From3.Offset += int64(deltasp) - int64(bpsize)
   671  			}
   672  			if a == obj.NAME_PARAM {
   673  				p.From3.Offset += int64(deltasp) + int64(pcsize)
   674  			}
   675  		}
   676  		a = int(p.To.Name)
   677  		if a == obj.NAME_AUTO {
   678  			p.To.Offset += int64(deltasp) - int64(bpsize)
   679  		}
   680  		if a == obj.NAME_PARAM {
   681  			p.To.Offset += int64(deltasp) + int64(pcsize)
   682  		}
   683  
   684  		switch p.As {
   685  		default:
   686  			continue
   687  
   688  		case APUSHL, APUSHFL:
   689  			deltasp += 4
   690  			p.Spadj = 4
   691  			continue
   692  
   693  		case APUSHQ, APUSHFQ:
   694  			deltasp += 8
   695  			p.Spadj = 8
   696  			continue
   697  
   698  		case APUSHW, APUSHFW:
   699  			deltasp += 2
   700  			p.Spadj = 2
   701  			continue
   702  
   703  		case APOPL, APOPFL:
   704  			deltasp -= 4
   705  			p.Spadj = -4
   706  			continue
   707  
   708  		case APOPQ, APOPFQ:
   709  			deltasp -= 8
   710  			p.Spadj = -8
   711  			continue
   712  
   713  		case APOPW, APOPFW:
   714  			deltasp -= 2
   715  			p.Spadj = -2
   716  			continue
   717  
   718  		case obj.ARET:
   719  			break
   720  		}
   721  
   722  		if autoffset != deltasp {
   723  			ctxt.Diag("unbalanced PUSH/POP")
   724  		}
   725  
   726  		if autoffset != 0 {
   727  			if bpsize > 0 {
   728  				// Restore caller's BP
   729  				p.As = AMOVQ
   730  
   731  				p.From.Type = obj.TYPE_MEM
   732  				p.From.Reg = REG_SP
   733  				p.From.Scale = 1
   734  				p.From.Offset = int64(autoffset) - int64(bpsize)
   735  				p.To.Type = obj.TYPE_REG
   736  				p.To.Reg = REG_BP
   737  				p = obj.Appendp(ctxt, p)
   738  			}
   739  
   740  			p.As = AADJSP
   741  			p.From.Type = obj.TYPE_CONST
   742  			p.From.Offset = int64(-autoffset)
   743  			p.Spadj = -autoffset
   744  			p = obj.Appendp(ctxt, p)
   745  			p.As = obj.ARET
   746  
   747  			// If there are instructions following
   748  			// this ARET, they come from a branch
   749  			// with the same stackframe, so undo
   750  			// the cleanup.
   751  			p.Spadj = +autoffset
   752  		}
   753  
   754  		if p.To.Sym != nil { // retjmp
   755  			p.As = obj.AJMP
   756  		}
   757  	}
   758  }
   759  
   760  func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
   761  	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   762  		a.Type = obj.TYPE_MEM
   763  		a.Reg = REG_R15
   764  		a.Index = REG_CX
   765  		a.Scale = 1
   766  		return
   767  	}
   768  
   769  	a.Type = obj.TYPE_MEM
   770  	a.Reg = REG_CX
   771  }
   772  
   773  // Append code to p to load g into cx.
   774  // Overwrites p with the first instruction (no first appendp).
   775  // Overwriting p is unusual but it lets use this in both the
   776  // prologue (caller must call appendp first) and in the epilogue.
   777  // Returns last new instruction.
   778  func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
   779  	p.As = AMOVQ
   780  	if ctxt.Arch.Ptrsize == 4 {
   781  		p.As = AMOVL
   782  	}
   783  	p.From.Type = obj.TYPE_MEM
   784  	p.From.Reg = REG_TLS
   785  	p.From.Offset = 0
   786  	p.To.Type = obj.TYPE_REG
   787  	p.To.Reg = REG_CX
   788  
   789  	next := p.Link
   790  	progedit(ctxt, p)
   791  	for p.Link != next {
   792  		p = p.Link
   793  	}
   794  
   795  	if p.From.Index == REG_TLS {
   796  		p.From.Scale = 2
   797  	}
   798  
   799  	return p
   800  }
   801  
   802  // Append code to p to check for stack split.
   803  // Appends to (does not overwrite) p.
   804  // Assumes g is in CX.
   805  // Returns last new instruction.
   806  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog {
   807  	cmp := ACMPQ
   808  	lea := ALEAQ
   809  	mov := AMOVQ
   810  	sub := ASUBQ
   811  
   812  	if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   813  		cmp = ACMPL
   814  		lea = ALEAL
   815  		mov = AMOVL
   816  		sub = ASUBL
   817  	}
   818  
   819  	var q1 *obj.Prog
   820  	if framesize <= obj.StackSmall {
   821  		// small stack: SP <= stackguard
   822  		//	CMPQ SP, stackguard
   823  		p = obj.Appendp(ctxt, p)
   824  
   825  		p.As = int16(cmp)
   826  		p.From.Type = obj.TYPE_REG
   827  		p.From.Reg = REG_SP
   828  		indir_cx(ctxt, p, &p.To)
   829  		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   830  		if ctxt.Cursym.Cfunc != 0 {
   831  			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   832  		}
   833  	} else if framesize <= obj.StackBig {
   834  		// large stack: SP-framesize <= stackguard-StackSmall
   835  		//	LEAQ -xxx(SP), AX
   836  		//	CMPQ AX, stackguard
   837  		p = obj.Appendp(ctxt, p)
   838  
   839  		p.As = int16(lea)
   840  		p.From.Type = obj.TYPE_MEM
   841  		p.From.Reg = REG_SP
   842  		p.From.Offset = -(int64(framesize) - obj.StackSmall)
   843  		p.To.Type = obj.TYPE_REG
   844  		p.To.Reg = REG_AX
   845  
   846  		p = obj.Appendp(ctxt, p)
   847  		p.As = int16(cmp)
   848  		p.From.Type = obj.TYPE_REG
   849  		p.From.Reg = REG_AX
   850  		indir_cx(ctxt, p, &p.To)
   851  		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   852  		if ctxt.Cursym.Cfunc != 0 {
   853  			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   854  		}
   855  	} else {
   856  		// Such a large stack we need to protect against wraparound.
   857  		// If SP is close to zero:
   858  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   859  		// The +StackGuard on both sides is required to keep the left side positive:
   860  		// SP is allowed to be slightly below stackguard. See stack.h.
   861  		//
   862  		// Preemption sets stackguard to StackPreempt, a very large value.
   863  		// That breaks the math above, so we have to check for that explicitly.
   864  		//	MOVQ	stackguard, CX
   865  		//	CMPQ	CX, $StackPreempt
   866  		//	JEQ	label-of-call-to-morestack
   867  		//	LEAQ	StackGuard(SP), AX
   868  		//	SUBQ	CX, AX
   869  		//	CMPQ	AX, $(framesize+(StackGuard-StackSmall))
   870  
   871  		p = obj.Appendp(ctxt, p)
   872  
   873  		p.As = int16(mov)
   874  		indir_cx(ctxt, p, &p.From)
   875  		p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   876  		if ctxt.Cursym.Cfunc != 0 {
   877  			p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   878  		}
   879  		p.To.Type = obj.TYPE_REG
   880  		p.To.Reg = REG_SI
   881  
   882  		p = obj.Appendp(ctxt, p)
   883  		p.As = int16(cmp)
   884  		p.From.Type = obj.TYPE_REG
   885  		p.From.Reg = REG_SI
   886  		p.To.Type = obj.TYPE_CONST
   887  		p.To.Offset = obj.StackPreempt
   888  		if p.Mode == 32 {
   889  			p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
   890  		}
   891  
   892  		p = obj.Appendp(ctxt, p)
   893  		p.As = AJEQ
   894  		p.To.Type = obj.TYPE_BRANCH
   895  		q1 = p
   896  
   897  		p = obj.Appendp(ctxt, p)
   898  		p.As = int16(lea)
   899  		p.From.Type = obj.TYPE_MEM
   900  		p.From.Reg = REG_SP
   901  		p.From.Offset = obj.StackGuard
   902  		p.To.Type = obj.TYPE_REG
   903  		p.To.Reg = REG_AX
   904  
   905  		p = obj.Appendp(ctxt, p)
   906  		p.As = int16(sub)
   907  		p.From.Type = obj.TYPE_REG
   908  		p.From.Reg = REG_SI
   909  		p.To.Type = obj.TYPE_REG
   910  		p.To.Reg = REG_AX
   911  
   912  		p = obj.Appendp(ctxt, p)
   913  		p.As = int16(cmp)
   914  		p.From.Type = obj.TYPE_REG
   915  		p.From.Reg = REG_AX
   916  		p.To.Type = obj.TYPE_CONST
   917  		p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
   918  	}
   919  
   920  	// common
   921  	jls := obj.Appendp(ctxt, p)
   922  	jls.As = AJLS
   923  	jls.To.Type = obj.TYPE_BRANCH
   924  
   925  	var last *obj.Prog
   926  	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
   927  	}
   928  
   929  	call := obj.Appendp(ctxt, last)
   930  	call.Lineno = ctxt.Cursym.Text.Lineno
   931  	call.Mode = ctxt.Cursym.Text.Mode
   932  	call.As = obj.ACALL
   933  	call.To.Type = obj.TYPE_BRANCH
   934  	morestack := "runtime.morestack"
   935  	switch {
   936  	case ctxt.Cursym.Cfunc != 0:
   937  		morestack = "runtime.morestackc"
   938  	case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
   939  		morestack = "runtime.morestack_noctxt"
   940  	}
   941  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
   942  
   943  	jmp := obj.Appendp(ctxt, call)
   944  	jmp.As = obj.AJMP
   945  	jmp.To.Type = obj.TYPE_BRANCH
   946  	jmp.Pcond = ctxt.Cursym.Text.Link
   947  
   948  	jls.Pcond = call
   949  	if q1 != nil {
   950  		q1.Pcond = call
   951  	}
   952  
   953  	return jls
   954  }
   955  
   956  func follow(ctxt *obj.Link, s *obj.LSym) {
   957  	ctxt.Cursym = s
   958  
   959  	firstp := ctxt.NewProg()
   960  	lastp := firstp
   961  	xfol(ctxt, s.Text, &lastp)
   962  	lastp.Link = nil
   963  	s.Text = firstp.Link
   964  }
   965  
   966  func nofollow(a int) bool {
   967  	switch a {
   968  	case obj.AJMP,
   969  		obj.ARET,
   970  		AIRETL,
   971  		AIRETQ,
   972  		AIRETW,
   973  		ARETFL,
   974  		ARETFQ,
   975  		ARETFW,
   976  		obj.AUNDEF:
   977  		return true
   978  	}
   979  
   980  	return false
   981  }
   982  
   983  func pushpop(a int) bool {
   984  	switch a {
   985  	case APUSHL,
   986  		APUSHFL,
   987  		APUSHQ,
   988  		APUSHFQ,
   989  		APUSHW,
   990  		APUSHFW,
   991  		APOPL,
   992  		APOPFL,
   993  		APOPQ,
   994  		APOPFQ,
   995  		APOPW,
   996  		APOPFW:
   997  		return true
   998  	}
   999  
  1000  	return false
  1001  }
  1002  
  1003  func relinv(a int16) int16 {
  1004  	switch a {
  1005  	case AJEQ:
  1006  		return AJNE
  1007  	case AJNE:
  1008  		return AJEQ
  1009  	case AJLE:
  1010  		return AJGT
  1011  	case AJLS:
  1012  		return AJHI
  1013  	case AJLT:
  1014  		return AJGE
  1015  	case AJMI:
  1016  		return AJPL
  1017  	case AJGE:
  1018  		return AJLT
  1019  	case AJPL:
  1020  		return AJMI
  1021  	case AJGT:
  1022  		return AJLE
  1023  	case AJHI:
  1024  		return AJLS
  1025  	case AJCS:
  1026  		return AJCC
  1027  	case AJCC:
  1028  		return AJCS
  1029  	case AJPS:
  1030  		return AJPC
  1031  	case AJPC:
  1032  		return AJPS
  1033  	case AJOS:
  1034  		return AJOC
  1035  	case AJOC:
  1036  		return AJOS
  1037  	}
  1038  
  1039  	log.Fatalf("unknown relation: %s", obj.Aconv(int(a)))
  1040  	return 0
  1041  }
  1042  
  1043  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1044  	var q *obj.Prog
  1045  	var i int
  1046  	var a int
  1047  
  1048  loop:
  1049  	if p == nil {
  1050  		return
  1051  	}
  1052  	if p.As == obj.AJMP {
  1053  		q = p.Pcond
  1054  		if q != nil && q.As != obj.ATEXT {
  1055  			/* mark instruction as done and continue layout at target of jump */
  1056  			p.Mark = 1
  1057  
  1058  			p = q
  1059  			if p.Mark == 0 {
  1060  				goto loop
  1061  			}
  1062  		}
  1063  	}
  1064  
  1065  	if p.Mark != 0 {
  1066  		/*
  1067  		 * p goes here, but already used it elsewhere.
  1068  		 * copy up to 4 instructions or else branch to other copy.
  1069  		 */
  1070  		i = 0
  1071  		q = p
  1072  		for ; i < 4; i, q = i+1, q.Link {
  1073  			if q == nil {
  1074  				break
  1075  			}
  1076  			if q == *last {
  1077  				break
  1078  			}
  1079  			a = int(q.As)
  1080  			if a == obj.ANOP {
  1081  				i--
  1082  				continue
  1083  			}
  1084  
  1085  			if nofollow(a) || pushpop(a) {
  1086  				break // NOTE(rsc): arm does goto copy
  1087  			}
  1088  			if q.Pcond == nil || q.Pcond.Mark != 0 {
  1089  				continue
  1090  			}
  1091  			if a == obj.ACALL || a == ALOOP {
  1092  				continue
  1093  			}
  1094  			for {
  1095  				if p.As == obj.ANOP {
  1096  					p = p.Link
  1097  					continue
  1098  				}
  1099  
  1100  				q = obj.Copyp(ctxt, p)
  1101  				p = p.Link
  1102  				q.Mark = 1
  1103  				(*last).Link = q
  1104  				*last = q
  1105  				if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark != 0 {
  1106  					continue
  1107  				}
  1108  
  1109  				q.As = relinv(q.As)
  1110  				p = q.Pcond
  1111  				q.Pcond = q.Link
  1112  				q.Link = p
  1113  				xfol(ctxt, q.Link, last)
  1114  				p = q.Link
  1115  				if p.Mark != 0 {
  1116  					return
  1117  				}
  1118  				goto loop
  1119  				/* */
  1120  			}
  1121  		}
  1122  		q = ctxt.NewProg()
  1123  		q.As = obj.AJMP
  1124  		q.Lineno = p.Lineno
  1125  		q.To.Type = obj.TYPE_BRANCH
  1126  		q.To.Offset = p.Pc
  1127  		q.Pcond = p
  1128  		p = q
  1129  	}
  1130  
  1131  	/* emit p */
  1132  	p.Mark = 1
  1133  
  1134  	(*last).Link = p
  1135  	*last = p
  1136  	a = int(p.As)
  1137  
  1138  	/* continue loop with what comes after p */
  1139  	if nofollow(a) {
  1140  		return
  1141  	}
  1142  	if p.Pcond != nil && a != obj.ACALL {
  1143  		/*
  1144  		 * some kind of conditional branch.
  1145  		 * recurse to follow one path.
  1146  		 * continue loop on the other.
  1147  		 */
  1148  		q = obj.Brchain(ctxt, p.Pcond)
  1149  		if q != nil {
  1150  			p.Pcond = q
  1151  		}
  1152  		q = obj.Brchain(ctxt, p.Link)
  1153  		if q != nil {
  1154  			p.Link = q
  1155  		}
  1156  		if p.From.Type == obj.TYPE_CONST {
  1157  			if p.From.Offset == 1 {
  1158  				/*
  1159  				 * expect conditional jump to be taken.
  1160  				 * rewrite so that's the fall-through case.
  1161  				 */
  1162  				p.As = relinv(int16(a))
  1163  
  1164  				q = p.Link
  1165  				p.Link = p.Pcond
  1166  				p.Pcond = q
  1167  			}
  1168  		} else {
  1169  			q = p.Link
  1170  			if q.Mark != 0 {
  1171  				if a != ALOOP {
  1172  					p.As = relinv(int16(a))
  1173  					p.Link = p.Pcond
  1174  					p.Pcond = q
  1175  				}
  1176  			}
  1177  		}
  1178  
  1179  		xfol(ctxt, p.Link, last)
  1180  		if p.Pcond.Mark != 0 {
  1181  			return
  1182  		}
  1183  		p = p.Pcond
  1184  		goto loop
  1185  	}
  1186  
  1187  	p = p.Link
  1188  	goto loop
  1189  }
  1190  
  1191  var unaryDst = map[int]bool{
  1192  	ABSWAPL:    true,
  1193  	ABSWAPQ:    true,
  1194  	ACMPXCHG8B: true,
  1195  	ADECB:      true,
  1196  	ADECL:      true,
  1197  	ADECQ:      true,
  1198  	ADECW:      true,
  1199  	AINCB:      true,
  1200  	AINCL:      true,
  1201  	AINCQ:      true,
  1202  	AINCW:      true,
  1203  	ANEGB:      true,
  1204  	ANEGL:      true,
  1205  	ANEGQ:      true,
  1206  	ANEGW:      true,
  1207  	ANOTB:      true,
  1208  	ANOTL:      true,
  1209  	ANOTQ:      true,
  1210  	ANOTW:      true,
  1211  	APOPL:      true,
  1212  	APOPQ:      true,
  1213  	APOPW:      true,
  1214  	ASETCC:     true,
  1215  	ASETCS:     true,
  1216  	ASETEQ:     true,
  1217  	ASETGE:     true,
  1218  	ASETGT:     true,
  1219  	ASETHI:     true,
  1220  	ASETLE:     true,
  1221  	ASETLS:     true,
  1222  	ASETLT:     true,
  1223  	ASETMI:     true,
  1224  	ASETNE:     true,
  1225  	ASETOC:     true,
  1226  	ASETOS:     true,
  1227  	ASETPC:     true,
  1228  	ASETPL:     true,
  1229  	ASETPS:     true,
  1230  	AFFREE:     true,
  1231  	AFLDENV:    true,
  1232  	AFSAVE:     true,
  1233  	AFSTCW:     true,
  1234  	AFSTENV:    true,
  1235  	AFSTSW:     true,
  1236  	AFXSAVE:    true,
  1237  	AFXSAVE64:  true,
  1238  	ASTMXCSR:   true,
  1239  }
  1240  
  1241  var Linkamd64 = obj.LinkArch{
  1242  	ByteOrder:  binary.LittleEndian,
  1243  	Name:       "amd64",
  1244  	Thechar:    '6',
  1245  	Preprocess: preprocess,
  1246  	Assemble:   span6,
  1247  	Follow:     follow,
  1248  	Progedit:   progedit,
  1249  	UnaryDst:   unaryDst,
  1250  	Minlc:      1,
  1251  	Ptrsize:    8,
  1252  	Regsize:    8,
  1253  }
  1254  
  1255  var Linkamd64p32 = obj.LinkArch{
  1256  	ByteOrder:  binary.LittleEndian,
  1257  	Name:       "amd64p32",
  1258  	Thechar:    '6',
  1259  	Preprocess: preprocess,
  1260  	Assemble:   span6,
  1261  	Follow:     follow,
  1262  	Progedit:   progedit,
  1263  	UnaryDst:   unaryDst,
  1264  	Minlc:      1,
  1265  	Ptrsize:    4,
  1266  	Regsize:    8,
  1267  }
  1268  
  1269  var Link386 = obj.LinkArch{
  1270  	ByteOrder:  binary.LittleEndian,
  1271  	Name:       "386",
  1272  	Thechar:    '8',
  1273  	Preprocess: preprocess,
  1274  	Assemble:   span6,
  1275  	Follow:     follow,
  1276  	Progedit:   progedit,
  1277  	UnaryDst:   unaryDst,
  1278  	Minlc:      1,
  1279  	Ptrsize:    4,
  1280  	Regsize:    4,
  1281  }