github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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 argument 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  			//  f == 0 can't be used here due to -0, so use Float64bits
   235  			if f := p.From.Val.(float64); math.Float64bits(f) == 0 {
   236  				if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
   237  					p.As = AXORPS
   238  					p.From = p.To
   239  					break
   240  				}
   241  			}
   242  		}
   243  		fallthrough
   244  
   245  	case AFMOVF,
   246  		AFADDF,
   247  		AFSUBF,
   248  		AFSUBRF,
   249  		AFMULF,
   250  		AFDIVF,
   251  		AFDIVRF,
   252  		AFCOMF,
   253  		AFCOMFP,
   254  		AADDSS,
   255  		ASUBSS,
   256  		AMULSS,
   257  		ADIVSS,
   258  		ACOMISS,
   259  		AUCOMISS:
   260  		if p.From.Type == obj.TYPE_FCONST {
   261  			f32 := float32(p.From.Val.(float64))
   262  			i32 := math.Float32bits(f32)
   263  			literal := fmt.Sprintf("$f32.%08x", i32)
   264  			s := obj.Linklookup(ctxt, literal, 0)
   265  			p.From.Type = obj.TYPE_MEM
   266  			p.From.Name = obj.NAME_EXTERN
   267  			p.From.Sym = s
   268  			p.From.Sym.Local = true
   269  			p.From.Offset = 0
   270  		}
   271  
   272  	case AMOVSD:
   273  		// Convert AMOVSD $(0), Xx to AXORPS Xx, Xx
   274  		if p.From.Type == obj.TYPE_FCONST {
   275  			//  f == 0 can't be used here due to -0, so use Float64bits
   276  			if f := p.From.Val.(float64); math.Float64bits(f) == 0 {
   277  				if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
   278  					p.As = AXORPS
   279  					p.From = p.To
   280  					break
   281  				}
   282  			}
   283  		}
   284  		fallthrough
   285  
   286  	case AFMOVD,
   287  		AFADDD,
   288  		AFSUBD,
   289  		AFSUBRD,
   290  		AFMULD,
   291  		AFDIVD,
   292  		AFDIVRD,
   293  		AFCOMD,
   294  		AFCOMDP,
   295  		AADDSD,
   296  		ASUBSD,
   297  		AMULSD,
   298  		ADIVSD,
   299  		ACOMISD,
   300  		AUCOMISD:
   301  		if p.From.Type == obj.TYPE_FCONST {
   302  			i64 := math.Float64bits(p.From.Val.(float64))
   303  			literal := fmt.Sprintf("$f64.%016x", i64)
   304  			s := obj.Linklookup(ctxt, literal, 0)
   305  			p.From.Type = obj.TYPE_MEM
   306  			p.From.Name = obj.NAME_EXTERN
   307  			p.From.Sym = s
   308  			p.From.Sym.Local = true
   309  			p.From.Offset = 0
   310  		}
   311  	}
   312  
   313  	if ctxt.Flag_dynlink {
   314  		rewriteToUseGot(ctxt, p)
   315  	}
   316  
   317  	if ctxt.Flag_shared != 0 && p.Mode == 32 {
   318  		rewriteToPcrel(ctxt, p)
   319  	}
   320  }
   321  
   322  // Rewrite p, if necessary, to access global data via the global offset table.
   323  func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
   324  	var add, lea, mov obj.As
   325  	var reg int16
   326  	if p.Mode == 64 {
   327  		add = AADDQ
   328  		lea = ALEAQ
   329  		mov = AMOVQ
   330  		reg = REG_R15
   331  	} else {
   332  		add = AADDL
   333  		lea = ALEAL
   334  		mov = AMOVL
   335  		reg = REG_CX
   336  	}
   337  
   338  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   339  		//     ADUFFxxx $offset
   340  		// becomes
   341  		//     $MOV runtime.duffxxx@GOT, $reg
   342  		//     $ADD $offset, $reg
   343  		//     CALL $reg
   344  		var sym *obj.LSym
   345  		if p.As == obj.ADUFFZERO {
   346  			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
   347  		} else {
   348  			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
   349  		}
   350  		offset := p.To.Offset
   351  		p.As = mov
   352  		p.From.Type = obj.TYPE_MEM
   353  		p.From.Name = obj.NAME_GOTREF
   354  		p.From.Sym = sym
   355  		p.To.Type = obj.TYPE_REG
   356  		p.To.Reg = reg
   357  		p.To.Offset = 0
   358  		p.To.Sym = nil
   359  		p1 := obj.Appendp(ctxt, p)
   360  		p1.As = add
   361  		p1.From.Type = obj.TYPE_CONST
   362  		p1.From.Offset = offset
   363  		p1.To.Type = obj.TYPE_REG
   364  		p1.To.Reg = reg
   365  		p2 := obj.Appendp(ctxt, p1)
   366  		p2.As = obj.ACALL
   367  		p2.To.Type = obj.TYPE_REG
   368  		p2.To.Reg = reg
   369  	}
   370  
   371  	// We only care about global data: NAME_EXTERN means a global
   372  	// symbol in the Go sense, and p.Sym.Local is true for a few
   373  	// internally defined symbols.
   374  	if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   375  		// $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below
   376  		p.As = mov
   377  		p.From.Type = obj.TYPE_ADDR
   378  	}
   379  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   380  		// $MOV $sym, Rx becomes $MOV sym@GOT, Rx
   381  		// $MOV $sym+<off>, Rx becomes $MOV sym@GOT, Rx; $LEA <off>(Rx), Rx
   382  		// On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX
   383  		cmplxdest := false
   384  		pAs := p.As
   385  		var dest obj.Addr
   386  		if p.To.Type != obj.TYPE_REG || pAs != mov {
   387  			if p.Mode == 64 {
   388  				ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
   389  			}
   390  			cmplxdest = true
   391  			dest = p.To
   392  			p.As = mov
   393  			p.To.Type = obj.TYPE_REG
   394  			p.To.Reg = REG_CX
   395  			p.To.Sym = nil
   396  			p.To.Name = obj.NAME_NONE
   397  		}
   398  		p.From.Type = obj.TYPE_MEM
   399  		p.From.Name = obj.NAME_GOTREF
   400  		q := p
   401  		if p.From.Offset != 0 {
   402  			q = obj.Appendp(ctxt, p)
   403  			q.As = lea
   404  			q.From.Type = obj.TYPE_MEM
   405  			q.From.Reg = p.To.Reg
   406  			q.From.Offset = p.From.Offset
   407  			q.To = p.To
   408  			p.From.Offset = 0
   409  		}
   410  		if cmplxdest {
   411  			q = obj.Appendp(ctxt, q)
   412  			q.As = pAs
   413  			q.To = dest
   414  			q.From.Type = obj.TYPE_REG
   415  			q.From.Reg = REG_CX
   416  		}
   417  	}
   418  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   419  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   420  	}
   421  	var source *obj.Addr
   422  	// MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry
   423  	// MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15)
   424  	// An addition may be inserted between the two MOVs if there is an offset.
   425  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   426  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   427  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   428  		}
   429  		source = &p.From
   430  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   431  		source = &p.To
   432  	} else {
   433  		return
   434  	}
   435  	if p.As == obj.ACALL {
   436  		// When dynlinking on 386, almost any call might end up being a call
   437  		// to a PLT, so make sure the GOT pointer is loaded into BX.
   438  		// RegTo2 is set on the replacement call insn to stop it being
   439  		// processed when it is in turn passed to progedit.
   440  		if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local) || p.RegTo2 != 0 {
   441  			return
   442  		}
   443  		p1 := obj.Appendp(ctxt, p)
   444  		p2 := obj.Appendp(ctxt, p1)
   445  
   446  		p1.As = ALEAL
   447  		p1.From.Type = obj.TYPE_MEM
   448  		p1.From.Name = obj.NAME_STATIC
   449  		p1.From.Sym = obj.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
   450  		p1.To.Type = obj.TYPE_REG
   451  		p1.To.Reg = REG_BX
   452  
   453  		p2.As = p.As
   454  		p2.Scond = p.Scond
   455  		p2.From = p.From
   456  		p2.From3 = p.From3
   457  		p2.Reg = p.Reg
   458  		p2.To = p.To
   459  		// p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr
   460  		// in ../pass.go complain, so set it back to TYPE_MEM here, until p2
   461  		// itself gets passed to progedit.
   462  		p2.To.Type = obj.TYPE_MEM
   463  		p2.RegTo2 = 1
   464  
   465  		obj.Nopout(p)
   466  		return
   467  
   468  	}
   469  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP {
   470  		return
   471  	}
   472  	if source.Type != obj.TYPE_MEM {
   473  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   474  	}
   475  	p1 := obj.Appendp(ctxt, p)
   476  	p2 := obj.Appendp(ctxt, p1)
   477  
   478  	p1.As = mov
   479  	p1.From.Type = obj.TYPE_MEM
   480  	p1.From.Sym = source.Sym
   481  	p1.From.Name = obj.NAME_GOTREF
   482  	p1.To.Type = obj.TYPE_REG
   483  	p1.To.Reg = reg
   484  
   485  	p2.As = p.As
   486  	p2.From = p.From
   487  	p2.To = p.To
   488  	if p.From.Name == obj.NAME_EXTERN {
   489  		p2.From.Reg = reg
   490  		p2.From.Name = obj.NAME_NONE
   491  		p2.From.Sym = nil
   492  	} else if p.To.Name == obj.NAME_EXTERN {
   493  		p2.To.Reg = reg
   494  		p2.To.Name = obj.NAME_NONE
   495  		p2.To.Sym = nil
   496  	} else {
   497  		return
   498  	}
   499  	obj.Nopout(p)
   500  }
   501  
   502  func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
   503  	// RegTo2 is set on the instructions we insert here so they don't get
   504  	// processed twice.
   505  	if p.RegTo2 != 0 {
   506  		return
   507  	}
   508  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   509  		return
   510  	}
   511  	// Any Prog (aside from the above special cases) with an Addr with Name ==
   512  	// NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.cx
   513  	// inserted before it.
   514  	isName := func(a *obj.Addr) bool {
   515  		if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 {
   516  			return false
   517  		}
   518  		if a.Sym.Type == obj.STLSBSS {
   519  			return false
   520  		}
   521  		return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF
   522  	}
   523  
   524  	if isName(&p.From) && p.From.Type == obj.TYPE_ADDR {
   525  		// Handle things like "MOVL $sym, (SP)" or "PUSHL $sym" by rewriting
   526  		// to "MOVL $sym, CX; MOVL CX, (SP)" or "MOVL $sym, CX; PUSHL CX"
   527  		// respectively.
   528  		if p.To.Type != obj.TYPE_REG {
   529  			q := obj.Appendp(ctxt, p)
   530  			q.As = p.As
   531  			q.From.Type = obj.TYPE_REG
   532  			q.From.Reg = REG_CX
   533  			q.To = p.To
   534  			p.As = AMOVL
   535  			p.To.Type = obj.TYPE_REG
   536  			p.To.Reg = REG_CX
   537  			p.To.Sym = nil
   538  			p.To.Name = obj.NAME_NONE
   539  		}
   540  	}
   541  
   542  	if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) {
   543  		return
   544  	}
   545  	q := obj.Appendp(ctxt, p)
   546  	q.RegTo2 = 1
   547  	r := obj.Appendp(ctxt, q)
   548  	r.RegTo2 = 1
   549  	q.As = obj.ACALL
   550  	q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0)
   551  	q.To.Type = obj.TYPE_MEM
   552  	q.To.Name = obj.NAME_EXTERN
   553  	q.To.Sym.Local = true
   554  	r.As = p.As
   555  	r.Scond = p.Scond
   556  	r.From = p.From
   557  	r.From3 = p.From3
   558  	r.Reg = p.Reg
   559  	r.To = p.To
   560  	obj.Nopout(p)
   561  }
   562  
   563  func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
   564  	if p.As == ALEAL || p.As == ALEAQ {
   565  		return
   566  	}
   567  
   568  	if a.Reg == REG_BP {
   569  		ctxt.Diag("invalid address: %v", p)
   570  		return
   571  	}
   572  
   573  	if a.Reg == REG_TLS {
   574  		a.Reg = REG_BP
   575  	}
   576  	if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE {
   577  		switch a.Reg {
   578  		// all ok
   579  		case REG_BP, REG_SP, REG_R15:
   580  			break
   581  
   582  		default:
   583  			if a.Index != REG_NONE {
   584  				ctxt.Diag("invalid address %v", p)
   585  			}
   586  			a.Index = a.Reg
   587  			if a.Index != REG_NONE {
   588  				a.Scale = 1
   589  			}
   590  			a.Reg = REG_R15
   591  		}
   592  	}
   593  }
   594  
   595  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   596  	if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil {
   597  		ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0)
   598  	}
   599  
   600  	ctxt.Cursym = cursym
   601  
   602  	if cursym.Text == nil || cursym.Text.Link == nil {
   603  		return
   604  	}
   605  
   606  	p := cursym.Text
   607  	autoffset := int32(p.To.Offset)
   608  	if autoffset < 0 {
   609  		autoffset = 0
   610  	}
   611  
   612  	var bpsize int
   613  	if p.Mode == 64 && obj.Framepointer_enabled != 0 && autoffset > 0 {
   614  		// Make room for to save a base pointer. If autoffset == 0,
   615  		// this might do something special like a tail jump to
   616  		// another function, so in that case we omit this.
   617  		bpsize = ctxt.Arch.Ptrsize
   618  
   619  		autoffset += int32(bpsize)
   620  		p.To.Offset += int64(bpsize)
   621  	} else {
   622  		bpsize = 0
   623  	}
   624  
   625  	textarg := int64(p.To.Val.(int32))
   626  	cursym.Args = int32(textarg)
   627  	cursym.Locals = int32(p.To.Offset)
   628  
   629  	// TODO(rsc): Remove.
   630  	if p.Mode == 32 && cursym.Locals < 0 {
   631  		cursym.Locals = 0
   632  	}
   633  
   634  	// TODO(rsc): Remove 'p.Mode == 64 &&'.
   635  	if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 {
   636  		for q := p; q != nil; q = q.Link {
   637  			if q.As == obj.ACALL {
   638  				goto noleaf
   639  			}
   640  			if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 {
   641  				goto noleaf
   642  			}
   643  		}
   644  
   645  		p.From3.Offset |= obj.NOSPLIT
   646  	noleaf:
   647  	}
   648  
   649  	if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 {
   650  		p = obj.Appendp(ctxt, p)
   651  		p = load_g_cx(ctxt, p) // load g into CX
   652  	}
   653  
   654  	if cursym.Text.From3Offset()&obj.NOSPLIT == 0 {
   655  		p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check
   656  	}
   657  
   658  	if autoffset != 0 {
   659  		if autoffset%int32(ctxt.Arch.Regsize) != 0 {
   660  			ctxt.Diag("unaligned stack size %d", autoffset)
   661  		}
   662  		p = obj.Appendp(ctxt, p)
   663  		p.As = AADJSP
   664  		p.From.Type = obj.TYPE_CONST
   665  		p.From.Offset = int64(autoffset)
   666  		p.Spadj = autoffset
   667  	} else {
   668  		// zero-byte stack adjustment.
   669  		// Insert a fake non-zero adjustment so that stkcheck can
   670  		// recognize the end of the stack-splitting prolog.
   671  		p = obj.Appendp(ctxt, p)
   672  
   673  		p.As = obj.ANOP
   674  		p.Spadj = int32(-ctxt.Arch.Ptrsize)
   675  		p = obj.Appendp(ctxt, p)
   676  		p.As = obj.ANOP
   677  		p.Spadj = int32(ctxt.Arch.Ptrsize)
   678  	}
   679  
   680  	deltasp := autoffset
   681  
   682  	if bpsize > 0 {
   683  		// Save caller's BP
   684  		p = obj.Appendp(ctxt, p)
   685  
   686  		p.As = AMOVQ
   687  		p.From.Type = obj.TYPE_REG
   688  		p.From.Reg = REG_BP
   689  		p.To.Type = obj.TYPE_MEM
   690  		p.To.Reg = REG_SP
   691  		p.To.Scale = 1
   692  		p.To.Offset = int64(autoffset) - int64(bpsize)
   693  
   694  		// Move current frame to BP
   695  		p = obj.Appendp(ctxt, p)
   696  
   697  		p.As = ALEAQ
   698  		p.From.Type = obj.TYPE_MEM
   699  		p.From.Reg = REG_SP
   700  		p.From.Scale = 1
   701  		p.From.Offset = int64(autoffset) - int64(bpsize)
   702  		p.To.Type = obj.TYPE_REG
   703  		p.To.Reg = REG_BP
   704  	}
   705  
   706  	if cursym.Text.From3Offset()&obj.WRAPPER != 0 {
   707  		// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   708  		//
   709  		//	MOVQ g_panic(CX), BX
   710  		//	TESTQ BX, BX
   711  		//	JEQ end
   712  		//	LEAQ (autoffset+8)(SP), DI
   713  		//	CMPQ panic_argp(BX), DI
   714  		//	JNE end
   715  		//	MOVQ SP, panic_argp(BX)
   716  		// end:
   717  		//	NOP
   718  		//
   719  		// The NOP is needed to give the jumps somewhere to land.
   720  		// It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
   721  
   722  		p = obj.Appendp(ctxt, p)
   723  
   724  		p.As = AMOVQ
   725  		p.From.Type = obj.TYPE_MEM
   726  		p.From.Reg = REG_CX
   727  		p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   728  		p.To.Type = obj.TYPE_REG
   729  		p.To.Reg = REG_BX
   730  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   731  			p.As = AMOVL
   732  			p.From.Type = obj.TYPE_MEM
   733  			p.From.Reg = REG_R15
   734  			p.From.Scale = 1
   735  			p.From.Index = REG_CX
   736  		}
   737  		if p.Mode == 32 {
   738  			p.As = AMOVL
   739  		}
   740  
   741  		p = obj.Appendp(ctxt, p)
   742  		p.As = ATESTQ
   743  		p.From.Type = obj.TYPE_REG
   744  		p.From.Reg = REG_BX
   745  		p.To.Type = obj.TYPE_REG
   746  		p.To.Reg = REG_BX
   747  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   748  			p.As = ATESTL
   749  		}
   750  
   751  		p = obj.Appendp(ctxt, p)
   752  		p.As = AJEQ
   753  		p.To.Type = obj.TYPE_BRANCH
   754  		p1 := p
   755  
   756  		p = obj.Appendp(ctxt, p)
   757  		p.As = ALEAQ
   758  		p.From.Type = obj.TYPE_MEM
   759  		p.From.Reg = REG_SP
   760  		p.From.Offset = int64(autoffset) + int64(ctxt.Arch.Regsize)
   761  		p.To.Type = obj.TYPE_REG
   762  		p.To.Reg = REG_DI
   763  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   764  			p.As = ALEAL
   765  		}
   766  
   767  		p = obj.Appendp(ctxt, p)
   768  		p.As = ACMPQ
   769  		p.From.Type = obj.TYPE_MEM
   770  		p.From.Reg = REG_BX
   771  		p.From.Offset = 0 // Panic.argp
   772  		p.To.Type = obj.TYPE_REG
   773  		p.To.Reg = REG_DI
   774  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   775  			p.As = ACMPL
   776  			p.From.Type = obj.TYPE_MEM
   777  			p.From.Reg = REG_R15
   778  			p.From.Scale = 1
   779  			p.From.Index = REG_BX
   780  		}
   781  		if p.Mode == 32 {
   782  			p.As = ACMPL
   783  		}
   784  
   785  		p = obj.Appendp(ctxt, p)
   786  		p.As = AJNE
   787  		p.To.Type = obj.TYPE_BRANCH
   788  		p2 := p
   789  
   790  		p = obj.Appendp(ctxt, p)
   791  		p.As = AMOVQ
   792  		p.From.Type = obj.TYPE_REG
   793  		p.From.Reg = REG_SP
   794  		p.To.Type = obj.TYPE_MEM
   795  		p.To.Reg = REG_BX
   796  		p.To.Offset = 0 // Panic.argp
   797  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   798  			p.As = AMOVL
   799  			p.To.Type = obj.TYPE_MEM
   800  			p.To.Reg = REG_R15
   801  			p.To.Scale = 1
   802  			p.To.Index = REG_BX
   803  		}
   804  		if p.Mode == 32 {
   805  			p.As = AMOVL
   806  		}
   807  
   808  		p = obj.Appendp(ctxt, p)
   809  		p.As = obj.ANOP
   810  		p1.Pcond = p
   811  		p2.Pcond = p
   812  	}
   813  
   814  	var a int
   815  	var pcsize int
   816  	for ; p != nil; p = p.Link {
   817  		pcsize = int(p.Mode) / 8
   818  		a = int(p.From.Name)
   819  		if a == obj.NAME_AUTO {
   820  			p.From.Offset += int64(deltasp) - int64(bpsize)
   821  		}
   822  		if a == obj.NAME_PARAM {
   823  			p.From.Offset += int64(deltasp) + int64(pcsize)
   824  		}
   825  		if p.From3 != nil {
   826  			a = int(p.From3.Name)
   827  			if a == obj.NAME_AUTO {
   828  				p.From3.Offset += int64(deltasp) - int64(bpsize)
   829  			}
   830  			if a == obj.NAME_PARAM {
   831  				p.From3.Offset += int64(deltasp) + int64(pcsize)
   832  			}
   833  		}
   834  		a = int(p.To.Name)
   835  		if a == obj.NAME_AUTO {
   836  			p.To.Offset += int64(deltasp) - int64(bpsize)
   837  		}
   838  		if a == obj.NAME_PARAM {
   839  			p.To.Offset += int64(deltasp) + int64(pcsize)
   840  		}
   841  
   842  		switch p.As {
   843  		default:
   844  			continue
   845  
   846  		case APUSHL, APUSHFL:
   847  			deltasp += 4
   848  			p.Spadj = 4
   849  			continue
   850  
   851  		case APUSHQ, APUSHFQ:
   852  			deltasp += 8
   853  			p.Spadj = 8
   854  			continue
   855  
   856  		case APUSHW, APUSHFW:
   857  			deltasp += 2
   858  			p.Spadj = 2
   859  			continue
   860  
   861  		case APOPL, APOPFL:
   862  			deltasp -= 4
   863  			p.Spadj = -4
   864  			continue
   865  
   866  		case APOPQ, APOPFQ:
   867  			deltasp -= 8
   868  			p.Spadj = -8
   869  			continue
   870  
   871  		case APOPW, APOPFW:
   872  			deltasp -= 2
   873  			p.Spadj = -2
   874  			continue
   875  
   876  		case obj.ARET:
   877  			break
   878  		}
   879  
   880  		if autoffset != deltasp {
   881  			ctxt.Diag("unbalanced PUSH/POP")
   882  		}
   883  
   884  		if autoffset != 0 {
   885  			if bpsize > 0 {
   886  				// Restore caller's BP
   887  				p.As = AMOVQ
   888  
   889  				p.From.Type = obj.TYPE_MEM
   890  				p.From.Reg = REG_SP
   891  				p.From.Scale = 1
   892  				p.From.Offset = int64(autoffset) - int64(bpsize)
   893  				p.To.Type = obj.TYPE_REG
   894  				p.To.Reg = REG_BP
   895  				p = obj.Appendp(ctxt, p)
   896  			}
   897  
   898  			p.As = AADJSP
   899  			p.From.Type = obj.TYPE_CONST
   900  			p.From.Offset = int64(-autoffset)
   901  			p.Spadj = -autoffset
   902  			p = obj.Appendp(ctxt, p)
   903  			p.As = obj.ARET
   904  
   905  			// If there are instructions following
   906  			// this ARET, they come from a branch
   907  			// with the same stackframe, so undo
   908  			// the cleanup.
   909  			p.Spadj = +autoffset
   910  		}
   911  
   912  		if p.To.Sym != nil { // retjmp
   913  			p.As = obj.AJMP
   914  		}
   915  	}
   916  }
   917  
   918  func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
   919  	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   920  		a.Type = obj.TYPE_MEM
   921  		a.Reg = REG_R15
   922  		a.Index = REG_CX
   923  		a.Scale = 1
   924  		return
   925  	}
   926  
   927  	a.Type = obj.TYPE_MEM
   928  	a.Reg = REG_CX
   929  }
   930  
   931  // Append code to p to load g into cx.
   932  // Overwrites p with the first instruction (no first appendp).
   933  // Overwriting p is unusual but it lets use this in both the
   934  // prologue (caller must call appendp first) and in the epilogue.
   935  // Returns last new instruction.
   936  func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
   937  	p.As = AMOVQ
   938  	if ctxt.Arch.Ptrsize == 4 {
   939  		p.As = AMOVL
   940  	}
   941  	p.From.Type = obj.TYPE_MEM
   942  	p.From.Reg = REG_TLS
   943  	p.From.Offset = 0
   944  	p.To.Type = obj.TYPE_REG
   945  	p.To.Reg = REG_CX
   946  
   947  	next := p.Link
   948  	progedit(ctxt, p)
   949  	for p.Link != next {
   950  		p = p.Link
   951  	}
   952  
   953  	if p.From.Index == REG_TLS {
   954  		p.From.Scale = 2
   955  	}
   956  
   957  	return p
   958  }
   959  
   960  // Append code to p to check for stack split.
   961  // Appends to (does not overwrite) p.
   962  // Assumes g is in CX.
   963  // Returns last new instruction.
   964  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog {
   965  	cmp := ACMPQ
   966  	lea := ALEAQ
   967  	mov := AMOVQ
   968  	sub := ASUBQ
   969  
   970  	if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   971  		cmp = ACMPL
   972  		lea = ALEAL
   973  		mov = AMOVL
   974  		sub = ASUBL
   975  	}
   976  
   977  	var q1 *obj.Prog
   978  	if framesize <= obj.StackSmall {
   979  		// small stack: SP <= stackguard
   980  		//	CMPQ SP, stackguard
   981  		p = obj.Appendp(ctxt, p)
   982  
   983  		p.As = cmp
   984  		p.From.Type = obj.TYPE_REG
   985  		p.From.Reg = REG_SP
   986  		indir_cx(ctxt, p, &p.To)
   987  		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   988  		if ctxt.Cursym.Cfunc {
   989  			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   990  		}
   991  	} else if framesize <= obj.StackBig {
   992  		// large stack: SP-framesize <= stackguard-StackSmall
   993  		//	LEAQ -xxx(SP), AX
   994  		//	CMPQ AX, stackguard
   995  		p = obj.Appendp(ctxt, p)
   996  
   997  		p.As = lea
   998  		p.From.Type = obj.TYPE_MEM
   999  		p.From.Reg = REG_SP
  1000  		p.From.Offset = -(int64(framesize) - obj.StackSmall)
  1001  		p.To.Type = obj.TYPE_REG
  1002  		p.To.Reg = REG_AX
  1003  
  1004  		p = obj.Appendp(ctxt, p)
  1005  		p.As = cmp
  1006  		p.From.Type = obj.TYPE_REG
  1007  		p.From.Reg = REG_AX
  1008  		indir_cx(ctxt, p, &p.To)
  1009  		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
  1010  		if ctxt.Cursym.Cfunc {
  1011  			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
  1012  		}
  1013  	} else {
  1014  		// Such a large stack we need to protect against wraparound.
  1015  		// If SP is close to zero:
  1016  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
  1017  		// The +StackGuard on both sides is required to keep the left side positive:
  1018  		// SP is allowed to be slightly below stackguard. See stack.h.
  1019  		//
  1020  		// Preemption sets stackguard to StackPreempt, a very large value.
  1021  		// That breaks the math above, so we have to check for that explicitly.
  1022  		//	MOVQ	stackguard, CX
  1023  		//	CMPQ	CX, $StackPreempt
  1024  		//	JEQ	label-of-call-to-morestack
  1025  		//	LEAQ	StackGuard(SP), AX
  1026  		//	SUBQ	CX, AX
  1027  		//	CMPQ	AX, $(framesize+(StackGuard-StackSmall))
  1028  
  1029  		p = obj.Appendp(ctxt, p)
  1030  
  1031  		p.As = mov
  1032  		indir_cx(ctxt, p, &p.From)
  1033  		p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
  1034  		if ctxt.Cursym.Cfunc {
  1035  			p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
  1036  		}
  1037  		p.To.Type = obj.TYPE_REG
  1038  		p.To.Reg = REG_SI
  1039  
  1040  		p = obj.Appendp(ctxt, p)
  1041  		p.As = cmp
  1042  		p.From.Type = obj.TYPE_REG
  1043  		p.From.Reg = REG_SI
  1044  		p.To.Type = obj.TYPE_CONST
  1045  		p.To.Offset = obj.StackPreempt
  1046  		if p.Mode == 32 {
  1047  			p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
  1048  		}
  1049  
  1050  		p = obj.Appendp(ctxt, p)
  1051  		p.As = AJEQ
  1052  		p.To.Type = obj.TYPE_BRANCH
  1053  		q1 = p
  1054  
  1055  		p = obj.Appendp(ctxt, p)
  1056  		p.As = lea
  1057  		p.From.Type = obj.TYPE_MEM
  1058  		p.From.Reg = REG_SP
  1059  		p.From.Offset = obj.StackGuard
  1060  		p.To.Type = obj.TYPE_REG
  1061  		p.To.Reg = REG_AX
  1062  
  1063  		p = obj.Appendp(ctxt, p)
  1064  		p.As = sub
  1065  		p.From.Type = obj.TYPE_REG
  1066  		p.From.Reg = REG_SI
  1067  		p.To.Type = obj.TYPE_REG
  1068  		p.To.Reg = REG_AX
  1069  
  1070  		p = obj.Appendp(ctxt, p)
  1071  		p.As = cmp
  1072  		p.From.Type = obj.TYPE_REG
  1073  		p.From.Reg = REG_AX
  1074  		p.To.Type = obj.TYPE_CONST
  1075  		p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
  1076  	}
  1077  
  1078  	// common
  1079  	jls := obj.Appendp(ctxt, p)
  1080  	jls.As = AJLS
  1081  	jls.To.Type = obj.TYPE_BRANCH
  1082  
  1083  	var last *obj.Prog
  1084  	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
  1085  	}
  1086  
  1087  	spfix := obj.Appendp(ctxt, last)
  1088  	spfix.As = obj.ANOP
  1089  	spfix.Spadj = -framesize
  1090  
  1091  	call := obj.Appendp(ctxt, spfix)
  1092  	call.Lineno = ctxt.Cursym.Text.Lineno
  1093  	call.Mode = ctxt.Cursym.Text.Mode
  1094  	call.As = obj.ACALL
  1095  	call.To.Type = obj.TYPE_BRANCH
  1096  	morestack := "runtime.morestack"
  1097  	switch {
  1098  	case ctxt.Cursym.Cfunc:
  1099  		morestack = "runtime.morestackc"
  1100  	case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
  1101  		morestack = "runtime.morestack_noctxt"
  1102  	}
  1103  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
  1104  
  1105  	jmp := obj.Appendp(ctxt, call)
  1106  	jmp.As = obj.AJMP
  1107  	jmp.To.Type = obj.TYPE_BRANCH
  1108  	jmp.Pcond = ctxt.Cursym.Text.Link
  1109  	jmp.Spadj = +framesize
  1110  
  1111  	jls.Pcond = call
  1112  	if q1 != nil {
  1113  		q1.Pcond = call
  1114  	}
  1115  
  1116  	return jls
  1117  }
  1118  
  1119  func follow(ctxt *obj.Link, s *obj.LSym) {
  1120  	ctxt.Cursym = s
  1121  
  1122  	firstp := ctxt.NewProg()
  1123  	lastp := firstp
  1124  	xfol(ctxt, s.Text, &lastp)
  1125  	lastp.Link = nil
  1126  	s.Text = firstp.Link
  1127  }
  1128  
  1129  func nofollow(a obj.As) bool {
  1130  	switch a {
  1131  	case obj.AJMP,
  1132  		obj.ARET,
  1133  		AIRETL,
  1134  		AIRETQ,
  1135  		AIRETW,
  1136  		ARETFL,
  1137  		ARETFQ,
  1138  		ARETFW,
  1139  		obj.AUNDEF:
  1140  		return true
  1141  	}
  1142  
  1143  	return false
  1144  }
  1145  
  1146  func pushpop(a obj.As) bool {
  1147  	switch a {
  1148  	case APUSHL,
  1149  		APUSHFL,
  1150  		APUSHQ,
  1151  		APUSHFQ,
  1152  		APUSHW,
  1153  		APUSHFW,
  1154  		APOPL,
  1155  		APOPFL,
  1156  		APOPQ,
  1157  		APOPFQ,
  1158  		APOPW,
  1159  		APOPFW:
  1160  		return true
  1161  	}
  1162  
  1163  	return false
  1164  }
  1165  
  1166  func relinv(a obj.As) obj.As {
  1167  	switch a {
  1168  	case AJEQ:
  1169  		return AJNE
  1170  	case AJNE:
  1171  		return AJEQ
  1172  	case AJLE:
  1173  		return AJGT
  1174  	case AJLS:
  1175  		return AJHI
  1176  	case AJLT:
  1177  		return AJGE
  1178  	case AJMI:
  1179  		return AJPL
  1180  	case AJGE:
  1181  		return AJLT
  1182  	case AJPL:
  1183  		return AJMI
  1184  	case AJGT:
  1185  		return AJLE
  1186  	case AJHI:
  1187  		return AJLS
  1188  	case AJCS:
  1189  		return AJCC
  1190  	case AJCC:
  1191  		return AJCS
  1192  	case AJPS:
  1193  		return AJPC
  1194  	case AJPC:
  1195  		return AJPS
  1196  	case AJOS:
  1197  		return AJOC
  1198  	case AJOC:
  1199  		return AJOS
  1200  	}
  1201  
  1202  	log.Fatalf("unknown relation: %s", obj.Aconv(a))
  1203  	return 0
  1204  }
  1205  
  1206  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1207  	var q *obj.Prog
  1208  	var i int
  1209  	var a obj.As
  1210  
  1211  loop:
  1212  	if p == nil {
  1213  		return
  1214  	}
  1215  	if p.As == obj.AJMP {
  1216  		q = p.Pcond
  1217  		if q != nil && q.As != obj.ATEXT {
  1218  			/* mark instruction as done and continue layout at target of jump */
  1219  			p.Mark |= DONE
  1220  
  1221  			p = q
  1222  			if p.Mark&DONE == 0 {
  1223  				goto loop
  1224  			}
  1225  		}
  1226  	}
  1227  
  1228  	if p.Mark&DONE != 0 {
  1229  		/*
  1230  		 * p goes here, but already used it elsewhere.
  1231  		 * copy up to 4 instructions or else branch to other copy.
  1232  		 */
  1233  		i = 0
  1234  		q = p
  1235  		for ; i < 4; i, q = i+1, q.Link {
  1236  			if q == nil {
  1237  				break
  1238  			}
  1239  			if q == *last {
  1240  				break
  1241  			}
  1242  			a = q.As
  1243  			if a == obj.ANOP {
  1244  				i--
  1245  				continue
  1246  			}
  1247  
  1248  			if nofollow(a) || pushpop(a) {
  1249  				break // NOTE(rsc): arm does goto copy
  1250  			}
  1251  			if q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
  1252  				continue
  1253  			}
  1254  			if a == obj.ACALL || a == ALOOP {
  1255  				continue
  1256  			}
  1257  			for {
  1258  				if p.As == obj.ANOP {
  1259  					p = p.Link
  1260  					continue
  1261  				}
  1262  
  1263  				q = obj.Copyp(ctxt, p)
  1264  				p = p.Link
  1265  				q.Mark |= DONE
  1266  				(*last).Link = q
  1267  				*last = q
  1268  				if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
  1269  					continue
  1270  				}
  1271  
  1272  				q.As = relinv(q.As)
  1273  				p = q.Pcond
  1274  				q.Pcond = q.Link
  1275  				q.Link = p
  1276  				xfol(ctxt, q.Link, last)
  1277  				p = q.Link
  1278  				if p.Mark&DONE != 0 {
  1279  					return
  1280  				}
  1281  				goto loop
  1282  				/* */
  1283  			}
  1284  		}
  1285  		q = ctxt.NewProg()
  1286  		q.As = obj.AJMP
  1287  		q.Lineno = p.Lineno
  1288  		q.To.Type = obj.TYPE_BRANCH
  1289  		q.To.Offset = p.Pc
  1290  		q.Pcond = p
  1291  		p = q
  1292  	}
  1293  
  1294  	/* emit p */
  1295  	p.Mark |= DONE
  1296  
  1297  	(*last).Link = p
  1298  	*last = p
  1299  	a = p.As
  1300  
  1301  	/* continue loop with what comes after p */
  1302  	if nofollow(a) {
  1303  		return
  1304  	}
  1305  	if p.Pcond != nil && a != obj.ACALL {
  1306  		/*
  1307  		 * some kind of conditional branch.
  1308  		 * recurse to follow one path.
  1309  		 * continue loop on the other.
  1310  		 */
  1311  		q = obj.Brchain(ctxt, p.Pcond)
  1312  		if q != nil {
  1313  			p.Pcond = q
  1314  		}
  1315  		q = obj.Brchain(ctxt, p.Link)
  1316  		if q != nil {
  1317  			p.Link = q
  1318  		}
  1319  		if p.From.Type == obj.TYPE_CONST {
  1320  			if p.From.Offset == 1 {
  1321  				/*
  1322  				 * expect conditional jump to be taken.
  1323  				 * rewrite so that's the fall-through case.
  1324  				 */
  1325  				p.As = relinv(a)
  1326  
  1327  				q = p.Link
  1328  				p.Link = p.Pcond
  1329  				p.Pcond = q
  1330  			}
  1331  		} else {
  1332  			q = p.Link
  1333  			if q.Mark&DONE != 0 {
  1334  				if a != ALOOP {
  1335  					p.As = relinv(a)
  1336  					p.Link = p.Pcond
  1337  					p.Pcond = q
  1338  				}
  1339  			}
  1340  		}
  1341  
  1342  		xfol(ctxt, p.Link, last)
  1343  		if p.Pcond.Mark&DONE != 0 {
  1344  			return
  1345  		}
  1346  		p = p.Pcond
  1347  		goto loop
  1348  	}
  1349  
  1350  	p = p.Link
  1351  	goto loop
  1352  }
  1353  
  1354  var unaryDst = map[obj.As]bool{
  1355  	ABSWAPL:    true,
  1356  	ABSWAPQ:    true,
  1357  	ACMPXCHG8B: true,
  1358  	ADECB:      true,
  1359  	ADECL:      true,
  1360  	ADECQ:      true,
  1361  	ADECW:      true,
  1362  	AINCB:      true,
  1363  	AINCL:      true,
  1364  	AINCQ:      true,
  1365  	AINCW:      true,
  1366  	ANEGB:      true,
  1367  	ANEGL:      true,
  1368  	ANEGQ:      true,
  1369  	ANEGW:      true,
  1370  	ANOTB:      true,
  1371  	ANOTL:      true,
  1372  	ANOTQ:      true,
  1373  	ANOTW:      true,
  1374  	APOPL:      true,
  1375  	APOPQ:      true,
  1376  	APOPW:      true,
  1377  	ASETCC:     true,
  1378  	ASETCS:     true,
  1379  	ASETEQ:     true,
  1380  	ASETGE:     true,
  1381  	ASETGT:     true,
  1382  	ASETHI:     true,
  1383  	ASETLE:     true,
  1384  	ASETLS:     true,
  1385  	ASETLT:     true,
  1386  	ASETMI:     true,
  1387  	ASETNE:     true,
  1388  	ASETOC:     true,
  1389  	ASETOS:     true,
  1390  	ASETPC:     true,
  1391  	ASETPL:     true,
  1392  	ASETPS:     true,
  1393  	AFFREE:     true,
  1394  	AFLDENV:    true,
  1395  	AFSAVE:     true,
  1396  	AFSTCW:     true,
  1397  	AFSTENV:    true,
  1398  	AFSTSW:     true,
  1399  	AFXSAVE:    true,
  1400  	AFXSAVE64:  true,
  1401  	ASTMXCSR:   true,
  1402  }
  1403  
  1404  var Linkamd64 = obj.LinkArch{
  1405  	ByteOrder:  binary.LittleEndian,
  1406  	Name:       "amd64",
  1407  	Thechar:    '6',
  1408  	Preprocess: preprocess,
  1409  	Assemble:   span6,
  1410  	Follow:     follow,
  1411  	Progedit:   progedit,
  1412  	UnaryDst:   unaryDst,
  1413  	Minlc:      1,
  1414  	Ptrsize:    8,
  1415  	Regsize:    8,
  1416  }
  1417  
  1418  var Linkamd64p32 = obj.LinkArch{
  1419  	ByteOrder:  binary.LittleEndian,
  1420  	Name:       "amd64p32",
  1421  	Thechar:    '6',
  1422  	Preprocess: preprocess,
  1423  	Assemble:   span6,
  1424  	Follow:     follow,
  1425  	Progedit:   progedit,
  1426  	UnaryDst:   unaryDst,
  1427  	Minlc:      1,
  1428  	Ptrsize:    4,
  1429  	Regsize:    8,
  1430  }
  1431  
  1432  var Link386 = obj.LinkArch{
  1433  	ByteOrder:  binary.LittleEndian,
  1434  	Name:       "386",
  1435  	Thechar:    '8',
  1436  	Preprocess: preprocess,
  1437  	Assemble:   span6,
  1438  	Follow:     follow,
  1439  	Progedit:   progedit,
  1440  	UnaryDst:   unaryDst,
  1441  	Minlc:      1,
  1442  	Ptrsize:    4,
  1443  	Regsize:    4,
  1444  }