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