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