github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/src/cmd/internal/obj/x86/obj6.go (about)

     1  // Inferno utils/6l/pass.c
     2  // https://bitbucket.org/inferno-os/inferno-os/src/default/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  			obj.Hwindowsgui:
    60  			return false
    61  		}
    62  
    63  		return true
    64  	}
    65  
    66  	switch ctxt.Headtype {
    67  	case obj.Hplan9, obj.Hwindows, obj.Hwindowsgui:
    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 || ctxt.Headtype == obj.Hwindowsgui) && 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.Set(obj.AttrLocal, 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.Set(obj.AttrLocal, 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.Set(obj.AttrLocal, 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  				// Treat common runtime calls that take no arguments
   664  				// the same as duffcopy and duffzero.
   665  				if !isZeroArgRuntimeCall(q.To.Sym) {
   666  					leaf = false
   667  					break LeafSearch
   668  				}
   669  				fallthrough
   670  			case obj.ADUFFCOPY, obj.ADUFFZERO:
   671  				if autoffset >= obj.StackSmall-8 {
   672  					leaf = false
   673  					break LeafSearch
   674  				}
   675  			}
   676  		}
   677  
   678  		if leaf {
   679  			p.From3.Offset |= obj.NOSPLIT
   680  		}
   681  	}
   682  
   683  	if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 {
   684  		p = obj.Appendp(ctxt, p)
   685  		p = load_g_cx(ctxt, p) // load g into CX
   686  	}
   687  
   688  	if cursym.Text.From3Offset()&obj.NOSPLIT == 0 {
   689  		p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check
   690  	}
   691  
   692  	if autoffset != 0 {
   693  		if autoffset%int32(ctxt.Arch.RegSize) != 0 {
   694  			ctxt.Diag("unaligned stack size %d", autoffset)
   695  		}
   696  		p = obj.Appendp(ctxt, p)
   697  		p.As = AADJSP
   698  		p.From.Type = obj.TYPE_CONST
   699  		p.From.Offset = int64(autoffset)
   700  		p.Spadj = autoffset
   701  	}
   702  
   703  	deltasp := autoffset
   704  
   705  	if bpsize > 0 {
   706  		// Save caller's BP
   707  		p = obj.Appendp(ctxt, p)
   708  
   709  		p.As = AMOVQ
   710  		p.From.Type = obj.TYPE_REG
   711  		p.From.Reg = REG_BP
   712  		p.To.Type = obj.TYPE_MEM
   713  		p.To.Reg = REG_SP
   714  		p.To.Scale = 1
   715  		p.To.Offset = int64(autoffset) - int64(bpsize)
   716  
   717  		// Move current frame to BP
   718  		p = obj.Appendp(ctxt, p)
   719  
   720  		p.As = ALEAQ
   721  		p.From.Type = obj.TYPE_MEM
   722  		p.From.Reg = REG_SP
   723  		p.From.Scale = 1
   724  		p.From.Offset = int64(autoffset) - int64(bpsize)
   725  		p.To.Type = obj.TYPE_REG
   726  		p.To.Reg = REG_BP
   727  	}
   728  
   729  	if cursym.Text.From3Offset()&obj.WRAPPER != 0 {
   730  		// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   731  		//
   732  		//	MOVQ g_panic(CX), BX
   733  		//	TESTQ BX, BX
   734  		//	JEQ end
   735  		//	LEAQ (autoffset+8)(SP), DI
   736  		//	CMPQ panic_argp(BX), DI
   737  		//	JNE end
   738  		//	MOVQ SP, panic_argp(BX)
   739  		// end:
   740  		//	NOP
   741  		//
   742  		// The NOP is needed to give the jumps somewhere to land.
   743  		// It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
   744  
   745  		p = obj.Appendp(ctxt, p)
   746  
   747  		p.As = AMOVQ
   748  		p.From.Type = obj.TYPE_MEM
   749  		p.From.Reg = REG_CX
   750  		p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   751  		p.To.Type = obj.TYPE_REG
   752  		p.To.Reg = REG_BX
   753  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   754  			p.As = AMOVL
   755  			p.From.Type = obj.TYPE_MEM
   756  			p.From.Reg = REG_R15
   757  			p.From.Scale = 1
   758  			p.From.Index = REG_CX
   759  		}
   760  		if p.Mode == 32 {
   761  			p.As = AMOVL
   762  		}
   763  
   764  		p = obj.Appendp(ctxt, p)
   765  		p.As = ATESTQ
   766  		p.From.Type = obj.TYPE_REG
   767  		p.From.Reg = REG_BX
   768  		p.To.Type = obj.TYPE_REG
   769  		p.To.Reg = REG_BX
   770  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   771  			p.As = ATESTL
   772  		}
   773  
   774  		p = obj.Appendp(ctxt, p)
   775  		p.As = AJEQ
   776  		p.To.Type = obj.TYPE_BRANCH
   777  		p1 := p
   778  
   779  		p = obj.Appendp(ctxt, p)
   780  		p.As = ALEAQ
   781  		p.From.Type = obj.TYPE_MEM
   782  		p.From.Reg = REG_SP
   783  		p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize)
   784  		p.To.Type = obj.TYPE_REG
   785  		p.To.Reg = REG_DI
   786  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   787  			p.As = ALEAL
   788  		}
   789  
   790  		p = obj.Appendp(ctxt, p)
   791  		p.As = ACMPQ
   792  		p.From.Type = obj.TYPE_MEM
   793  		p.From.Reg = REG_BX
   794  		p.From.Offset = 0 // Panic.argp
   795  		p.To.Type = obj.TYPE_REG
   796  		p.To.Reg = REG_DI
   797  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   798  			p.As = ACMPL
   799  			p.From.Type = obj.TYPE_MEM
   800  			p.From.Reg = REG_R15
   801  			p.From.Scale = 1
   802  			p.From.Index = REG_BX
   803  		}
   804  		if p.Mode == 32 {
   805  			p.As = ACMPL
   806  		}
   807  
   808  		p = obj.Appendp(ctxt, p)
   809  		p.As = AJNE
   810  		p.To.Type = obj.TYPE_BRANCH
   811  		p2 := p
   812  
   813  		p = obj.Appendp(ctxt, p)
   814  		p.As = AMOVQ
   815  		p.From.Type = obj.TYPE_REG
   816  		p.From.Reg = REG_SP
   817  		p.To.Type = obj.TYPE_MEM
   818  		p.To.Reg = REG_BX
   819  		p.To.Offset = 0 // Panic.argp
   820  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   821  			p.As = AMOVL
   822  			p.To.Type = obj.TYPE_MEM
   823  			p.To.Reg = REG_R15
   824  			p.To.Scale = 1
   825  			p.To.Index = REG_BX
   826  		}
   827  		if p.Mode == 32 {
   828  			p.As = AMOVL
   829  		}
   830  
   831  		p = obj.Appendp(ctxt, p)
   832  		p.As = obj.ANOP
   833  		p1.Pcond = p
   834  		p2.Pcond = p
   835  	}
   836  
   837  	for ; p != nil; p = p.Link {
   838  		pcsize := int(p.Mode) / 8
   839  		switch p.From.Name {
   840  		case obj.NAME_AUTO:
   841  			p.From.Offset += int64(deltasp) - int64(bpsize)
   842  		case obj.NAME_PARAM:
   843  			p.From.Offset += int64(deltasp) + int64(pcsize)
   844  		}
   845  		if p.From3 != nil {
   846  			switch p.From3.Name {
   847  			case obj.NAME_AUTO:
   848  				p.From3.Offset += int64(deltasp) - int64(bpsize)
   849  			case obj.NAME_PARAM:
   850  				p.From3.Offset += int64(deltasp) + int64(pcsize)
   851  			}
   852  		}
   853  		switch p.To.Name {
   854  		case obj.NAME_AUTO:
   855  			p.To.Offset += int64(deltasp) - int64(bpsize)
   856  		case obj.NAME_PARAM:
   857  			p.To.Offset += int64(deltasp) + int64(pcsize)
   858  		}
   859  
   860  		switch p.As {
   861  		default:
   862  			continue
   863  
   864  		case APUSHL, APUSHFL:
   865  			deltasp += 4
   866  			p.Spadj = 4
   867  			continue
   868  
   869  		case APUSHQ, APUSHFQ:
   870  			deltasp += 8
   871  			p.Spadj = 8
   872  			continue
   873  
   874  		case APUSHW, APUSHFW:
   875  			deltasp += 2
   876  			p.Spadj = 2
   877  			continue
   878  
   879  		case APOPL, APOPFL:
   880  			deltasp -= 4
   881  			p.Spadj = -4
   882  			continue
   883  
   884  		case APOPQ, APOPFQ:
   885  			deltasp -= 8
   886  			p.Spadj = -8
   887  			continue
   888  
   889  		case APOPW, APOPFW:
   890  			deltasp -= 2
   891  			p.Spadj = -2
   892  			continue
   893  
   894  		case obj.ARET:
   895  			// do nothing
   896  		}
   897  
   898  		if autoffset != deltasp {
   899  			ctxt.Diag("unbalanced PUSH/POP")
   900  		}
   901  
   902  		if autoffset != 0 {
   903  			if bpsize > 0 {
   904  				// Restore caller's BP
   905  				p.As = AMOVQ
   906  
   907  				p.From.Type = obj.TYPE_MEM
   908  				p.From.Reg = REG_SP
   909  				p.From.Scale = 1
   910  				p.From.Offset = int64(autoffset) - int64(bpsize)
   911  				p.To.Type = obj.TYPE_REG
   912  				p.To.Reg = REG_BP
   913  				p = obj.Appendp(ctxt, p)
   914  			}
   915  
   916  			p.As = AADJSP
   917  			p.From.Type = obj.TYPE_CONST
   918  			p.From.Offset = int64(-autoffset)
   919  			p.Spadj = -autoffset
   920  			p = obj.Appendp(ctxt, p)
   921  			p.As = obj.ARET
   922  
   923  			// If there are instructions following
   924  			// this ARET, they come from a branch
   925  			// with the same stackframe, so undo
   926  			// the cleanup.
   927  			p.Spadj = +autoffset
   928  		}
   929  
   930  		if p.To.Sym != nil { // retjmp
   931  			p.As = obj.AJMP
   932  		}
   933  	}
   934  }
   935  
   936  func isZeroArgRuntimeCall(s *obj.LSym) bool {
   937  	if s == nil {
   938  		return false
   939  	}
   940  	switch s.Name {
   941  	case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide":
   942  		return true
   943  	}
   944  	return false
   945  }
   946  
   947  func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
   948  	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   949  		a.Type = obj.TYPE_MEM
   950  		a.Reg = REG_R15
   951  		a.Index = REG_CX
   952  		a.Scale = 1
   953  		return
   954  	}
   955  
   956  	a.Type = obj.TYPE_MEM
   957  	a.Reg = REG_CX
   958  }
   959  
   960  // Append code to p to load g into cx.
   961  // Overwrites p with the first instruction (no first appendp).
   962  // Overwriting p is unusual but it lets use this in both the
   963  // prologue (caller must call appendp first) and in the epilogue.
   964  // Returns last new instruction.
   965  func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
   966  	p.As = AMOVQ
   967  	if ctxt.Arch.PtrSize == 4 {
   968  		p.As = AMOVL
   969  	}
   970  	p.From.Type = obj.TYPE_MEM
   971  	p.From.Reg = REG_TLS
   972  	p.From.Offset = 0
   973  	p.To.Type = obj.TYPE_REG
   974  	p.To.Reg = REG_CX
   975  
   976  	next := p.Link
   977  	progedit(ctxt, p)
   978  	for p.Link != next {
   979  		p = p.Link
   980  	}
   981  
   982  	if p.From.Index == REG_TLS {
   983  		p.From.Scale = 2
   984  	}
   985  
   986  	return p
   987  }
   988  
   989  // Append code to p to check for stack split.
   990  // Appends to (does not overwrite) p.
   991  // Assumes g is in CX.
   992  // Returns last new instruction.
   993  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog {
   994  	cmp := ACMPQ
   995  	lea := ALEAQ
   996  	mov := AMOVQ
   997  	sub := ASUBQ
   998  
   999  	if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
  1000  		cmp = ACMPL
  1001  		lea = ALEAL
  1002  		mov = AMOVL
  1003  		sub = ASUBL
  1004  	}
  1005  
  1006  	var q1 *obj.Prog
  1007  	if framesize <= obj.StackSmall {
  1008  		// small stack: SP <= stackguard
  1009  		//	CMPQ SP, stackguard
  1010  		p = obj.Appendp(ctxt, p)
  1011  
  1012  		p.As = cmp
  1013  		p.From.Type = obj.TYPE_REG
  1014  		p.From.Reg = REG_SP
  1015  		indir_cx(ctxt, p, &p.To)
  1016  		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
  1017  		if ctxt.Cursym.CFunc() {
  1018  			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
  1019  		}
  1020  	} else if framesize <= obj.StackBig {
  1021  		// large stack: SP-framesize <= stackguard-StackSmall
  1022  		//	LEAQ -xxx(SP), AX
  1023  		//	CMPQ AX, stackguard
  1024  		p = obj.Appendp(ctxt, p)
  1025  
  1026  		p.As = lea
  1027  		p.From.Type = obj.TYPE_MEM
  1028  		p.From.Reg = REG_SP
  1029  		p.From.Offset = -(int64(framesize) - obj.StackSmall)
  1030  		p.To.Type = obj.TYPE_REG
  1031  		p.To.Reg = REG_AX
  1032  
  1033  		p = obj.Appendp(ctxt, p)
  1034  		p.As = cmp
  1035  		p.From.Type = obj.TYPE_REG
  1036  		p.From.Reg = REG_AX
  1037  		indir_cx(ctxt, p, &p.To)
  1038  		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
  1039  		if ctxt.Cursym.CFunc() {
  1040  			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
  1041  		}
  1042  	} else {
  1043  		// Such a large stack we need to protect against wraparound.
  1044  		// If SP is close to zero:
  1045  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
  1046  		// The +StackGuard on both sides is required to keep the left side positive:
  1047  		// SP is allowed to be slightly below stackguard. See stack.h.
  1048  		//
  1049  		// Preemption sets stackguard to StackPreempt, a very large value.
  1050  		// That breaks the math above, so we have to check for that explicitly.
  1051  		//	MOVQ	stackguard, CX
  1052  		//	CMPQ	CX, $StackPreempt
  1053  		//	JEQ	label-of-call-to-morestack
  1054  		//	LEAQ	StackGuard(SP), AX
  1055  		//	SUBQ	CX, AX
  1056  		//	CMPQ	AX, $(framesize+(StackGuard-StackSmall))
  1057  
  1058  		p = obj.Appendp(ctxt, p)
  1059  
  1060  		p.As = mov
  1061  		indir_cx(ctxt, p, &p.From)
  1062  		p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
  1063  		if ctxt.Cursym.CFunc() {
  1064  			p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
  1065  		}
  1066  		p.To.Type = obj.TYPE_REG
  1067  		p.To.Reg = REG_SI
  1068  
  1069  		p = obj.Appendp(ctxt, p)
  1070  		p.As = cmp
  1071  		p.From.Type = obj.TYPE_REG
  1072  		p.From.Reg = REG_SI
  1073  		p.To.Type = obj.TYPE_CONST
  1074  		p.To.Offset = obj.StackPreempt
  1075  		if p.Mode == 32 {
  1076  			p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
  1077  		}
  1078  
  1079  		p = obj.Appendp(ctxt, p)
  1080  		p.As = AJEQ
  1081  		p.To.Type = obj.TYPE_BRANCH
  1082  		q1 = p
  1083  
  1084  		p = obj.Appendp(ctxt, p)
  1085  		p.As = lea
  1086  		p.From.Type = obj.TYPE_MEM
  1087  		p.From.Reg = REG_SP
  1088  		p.From.Offset = obj.StackGuard
  1089  		p.To.Type = obj.TYPE_REG
  1090  		p.To.Reg = REG_AX
  1091  
  1092  		p = obj.Appendp(ctxt, p)
  1093  		p.As = sub
  1094  		p.From.Type = obj.TYPE_REG
  1095  		p.From.Reg = REG_SI
  1096  		p.To.Type = obj.TYPE_REG
  1097  		p.To.Reg = REG_AX
  1098  
  1099  		p = obj.Appendp(ctxt, p)
  1100  		p.As = cmp
  1101  		p.From.Type = obj.TYPE_REG
  1102  		p.From.Reg = REG_AX
  1103  		p.To.Type = obj.TYPE_CONST
  1104  		p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
  1105  	}
  1106  
  1107  	// common
  1108  	jls := obj.Appendp(ctxt, p)
  1109  	jls.As = AJLS
  1110  	jls.To.Type = obj.TYPE_BRANCH
  1111  
  1112  	var last *obj.Prog
  1113  	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
  1114  	}
  1115  
  1116  	// Now we are at the end of the function, but logically
  1117  	// we are still in function prologue. We need to fix the
  1118  	// SP data and PCDATA.
  1119  	spfix := obj.Appendp(ctxt, last)
  1120  	spfix.As = obj.ANOP
  1121  	spfix.Spadj = -framesize
  1122  
  1123  	pcdata := obj.Appendp(ctxt, spfix)
  1124  	pcdata.Lineno = ctxt.Cursym.Text.Lineno
  1125  	pcdata.Mode = ctxt.Cursym.Text.Mode
  1126  	pcdata.As = obj.APCDATA
  1127  	pcdata.From.Type = obj.TYPE_CONST
  1128  	pcdata.From.Offset = obj.PCDATA_StackMapIndex
  1129  	pcdata.To.Type = obj.TYPE_CONST
  1130  	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
  1131  
  1132  	call := obj.Appendp(ctxt, pcdata)
  1133  	call.Lineno = ctxt.Cursym.Text.Lineno
  1134  	call.Mode = ctxt.Cursym.Text.Mode
  1135  	call.As = obj.ACALL
  1136  	call.To.Type = obj.TYPE_BRANCH
  1137  	call.To.Name = obj.NAME_EXTERN
  1138  	morestack := "runtime.morestack"
  1139  	switch {
  1140  	case ctxt.Cursym.CFunc():
  1141  		morestack = "runtime.morestackc"
  1142  	case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
  1143  		morestack = "runtime.morestack_noctxt"
  1144  	}
  1145  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
  1146  	// When compiling 386 code for dynamic linking, the call needs to be adjusted
  1147  	// to follow PIC rules. This in turn can insert more instructions, so we need
  1148  	// to keep track of the start of the call (where the jump will be to) and the
  1149  	// end (which following instructions are appended to).
  1150  	callend := call
  1151  	progedit(ctxt, callend)
  1152  	for ; callend.Link != nil; callend = callend.Link {
  1153  		progedit(ctxt, callend.Link)
  1154  	}
  1155  
  1156  	jmp := obj.Appendp(ctxt, callend)
  1157  	jmp.As = obj.AJMP
  1158  	jmp.To.Type = obj.TYPE_BRANCH
  1159  	jmp.Pcond = ctxt.Cursym.Text.Link
  1160  	jmp.Spadj = +framesize
  1161  
  1162  	jls.Pcond = call
  1163  	if q1 != nil {
  1164  		q1.Pcond = call
  1165  	}
  1166  
  1167  	return jls
  1168  }
  1169  
  1170  func follow(ctxt *obj.Link, s *obj.LSym) {
  1171  	ctxt.Cursym = s
  1172  
  1173  	firstp := ctxt.NewProg()
  1174  	lastp := firstp
  1175  	xfol(ctxt, s.Text, &lastp)
  1176  	lastp.Link = nil
  1177  	s.Text = firstp.Link
  1178  }
  1179  
  1180  func nofollow(a obj.As) bool {
  1181  	switch a {
  1182  	case obj.AJMP,
  1183  		obj.ARET,
  1184  		AIRETL,
  1185  		AIRETQ,
  1186  		AIRETW,
  1187  		ARETFL,
  1188  		ARETFQ,
  1189  		ARETFW,
  1190  		obj.AUNDEF:
  1191  		return true
  1192  	}
  1193  
  1194  	return false
  1195  }
  1196  
  1197  func pushpop(a obj.As) bool {
  1198  	switch a {
  1199  	case APUSHL,
  1200  		APUSHFL,
  1201  		APUSHQ,
  1202  		APUSHFQ,
  1203  		APUSHW,
  1204  		APUSHFW,
  1205  		APOPL,
  1206  		APOPFL,
  1207  		APOPQ,
  1208  		APOPFQ,
  1209  		APOPW,
  1210  		APOPFW:
  1211  		return true
  1212  	}
  1213  
  1214  	return false
  1215  }
  1216  
  1217  func relinv(a obj.As) obj.As {
  1218  	switch a {
  1219  	case AJEQ:
  1220  		return AJNE
  1221  	case AJNE:
  1222  		return AJEQ
  1223  	case AJLE:
  1224  		return AJGT
  1225  	case AJLS:
  1226  		return AJHI
  1227  	case AJLT:
  1228  		return AJGE
  1229  	case AJMI:
  1230  		return AJPL
  1231  	case AJGE:
  1232  		return AJLT
  1233  	case AJPL:
  1234  		return AJMI
  1235  	case AJGT:
  1236  		return AJLE
  1237  	case AJHI:
  1238  		return AJLS
  1239  	case AJCS:
  1240  		return AJCC
  1241  	case AJCC:
  1242  		return AJCS
  1243  	case AJPS:
  1244  		return AJPC
  1245  	case AJPC:
  1246  		return AJPS
  1247  	case AJOS:
  1248  		return AJOC
  1249  	case AJOC:
  1250  		return AJOS
  1251  	}
  1252  
  1253  	log.Fatalf("unknown relation: %s", a)
  1254  	return 0
  1255  }
  1256  
  1257  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1258  	var q *obj.Prog
  1259  	var i int
  1260  	var a obj.As
  1261  
  1262  loop:
  1263  	if p == nil {
  1264  		return
  1265  	}
  1266  	if p.As == obj.AJMP {
  1267  		q = p.Pcond
  1268  		if q != nil && q.As != obj.ATEXT {
  1269  			/* mark instruction as done and continue layout at target of jump */
  1270  			p.Mark |= DONE
  1271  
  1272  			p = q
  1273  			if p.Mark&DONE == 0 {
  1274  				goto loop
  1275  			}
  1276  		}
  1277  	}
  1278  
  1279  	if p.Mark&DONE != 0 {
  1280  		/*
  1281  		 * p goes here, but already used it elsewhere.
  1282  		 * copy up to 4 instructions or else branch to other copy.
  1283  		 */
  1284  		i = 0
  1285  		q = p
  1286  		for ; i < 4; i, q = i+1, q.Link {
  1287  			if q == nil {
  1288  				break
  1289  			}
  1290  			if q == *last {
  1291  				break
  1292  			}
  1293  			a = q.As
  1294  			if a == obj.ANOP {
  1295  				i--
  1296  				continue
  1297  			}
  1298  
  1299  			if nofollow(a) || pushpop(a) {
  1300  				break // NOTE(rsc): arm does goto copy
  1301  			}
  1302  			if q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
  1303  				continue
  1304  			}
  1305  			if a == obj.ACALL || a == ALOOP {
  1306  				continue
  1307  			}
  1308  			for {
  1309  				if p.As == obj.ANOP {
  1310  					p = p.Link
  1311  					continue
  1312  				}
  1313  
  1314  				q = obj.Copyp(ctxt, p)
  1315  				p = p.Link
  1316  				q.Mark |= DONE
  1317  				(*last).Link = q
  1318  				*last = q
  1319  				if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
  1320  					continue
  1321  				}
  1322  
  1323  				q.As = relinv(q.As)
  1324  				p = q.Pcond
  1325  				q.Pcond = q.Link
  1326  				q.Link = p
  1327  				xfol(ctxt, q.Link, last)
  1328  				p = q.Link
  1329  				if p.Mark&DONE != 0 {
  1330  					return
  1331  				}
  1332  				goto loop
  1333  				/* */
  1334  			}
  1335  		}
  1336  		q = ctxt.NewProg()
  1337  		q.As = obj.AJMP
  1338  		q.Lineno = p.Lineno
  1339  		q.To.Type = obj.TYPE_BRANCH
  1340  		q.To.Offset = p.Pc
  1341  		q.Pcond = p
  1342  		p = q
  1343  	}
  1344  
  1345  	/* emit p */
  1346  	p.Mark |= DONE
  1347  
  1348  	(*last).Link = p
  1349  	*last = p
  1350  	a = p.As
  1351  
  1352  	/* continue loop with what comes after p */
  1353  	if nofollow(a) {
  1354  		return
  1355  	}
  1356  	if p.Pcond != nil && a != obj.ACALL {
  1357  		/*
  1358  		 * some kind of conditional branch.
  1359  		 * recurse to follow one path.
  1360  		 * continue loop on the other.
  1361  		 */
  1362  		q = obj.Brchain(ctxt, p.Pcond)
  1363  		if q != nil {
  1364  			p.Pcond = q
  1365  		}
  1366  		q = obj.Brchain(ctxt, p.Link)
  1367  		if q != nil {
  1368  			p.Link = q
  1369  		}
  1370  		if p.From.Type == obj.TYPE_CONST {
  1371  			if p.From.Offset == 1 {
  1372  				/*
  1373  				 * expect conditional jump to be taken.
  1374  				 * rewrite so that's the fall-through case.
  1375  				 */
  1376  				p.As = relinv(a)
  1377  
  1378  				q = p.Link
  1379  				p.Link = p.Pcond
  1380  				p.Pcond = q
  1381  			}
  1382  		} else {
  1383  			q = p.Link
  1384  			if q.Mark&DONE != 0 {
  1385  				if a != ALOOP {
  1386  					p.As = relinv(a)
  1387  					p.Link = p.Pcond
  1388  					p.Pcond = q
  1389  				}
  1390  			}
  1391  		}
  1392  
  1393  		xfol(ctxt, p.Link, last)
  1394  		if p.Pcond.Mark&DONE != 0 {
  1395  			return
  1396  		}
  1397  		p = p.Pcond
  1398  		goto loop
  1399  	}
  1400  
  1401  	p = p.Link
  1402  	goto loop
  1403  }
  1404  
  1405  var unaryDst = map[obj.As]bool{
  1406  	ABSWAPL:    true,
  1407  	ABSWAPQ:    true,
  1408  	ACMPXCHG8B: true,
  1409  	ADECB:      true,
  1410  	ADECL:      true,
  1411  	ADECQ:      true,
  1412  	ADECW:      true,
  1413  	AINCB:      true,
  1414  	AINCL:      true,
  1415  	AINCQ:      true,
  1416  	AINCW:      true,
  1417  	ANEGB:      true,
  1418  	ANEGL:      true,
  1419  	ANEGQ:      true,
  1420  	ANEGW:      true,
  1421  	ANOTB:      true,
  1422  	ANOTL:      true,
  1423  	ANOTQ:      true,
  1424  	ANOTW:      true,
  1425  	APOPL:      true,
  1426  	APOPQ:      true,
  1427  	APOPW:      true,
  1428  	ASETCC:     true,
  1429  	ASETCS:     true,
  1430  	ASETEQ:     true,
  1431  	ASETGE:     true,
  1432  	ASETGT:     true,
  1433  	ASETHI:     true,
  1434  	ASETLE:     true,
  1435  	ASETLS:     true,
  1436  	ASETLT:     true,
  1437  	ASETMI:     true,
  1438  	ASETNE:     true,
  1439  	ASETOC:     true,
  1440  	ASETOS:     true,
  1441  	ASETPC:     true,
  1442  	ASETPL:     true,
  1443  	ASETPS:     true,
  1444  	AFFREE:     true,
  1445  	AFLDENV:    true,
  1446  	AFSAVE:     true,
  1447  	AFSTCW:     true,
  1448  	AFSTENV:    true,
  1449  	AFSTSW:     true,
  1450  	AFXSAVE:    true,
  1451  	AFXSAVE64:  true,
  1452  	ASTMXCSR:   true,
  1453  }
  1454  
  1455  var Linkamd64 = obj.LinkArch{
  1456  	Arch:       sys.ArchAMD64,
  1457  	Preprocess: preprocess,
  1458  	Assemble:   span6,
  1459  	Follow:     follow,
  1460  	Progedit:   progedit,
  1461  	UnaryDst:   unaryDst,
  1462  }
  1463  
  1464  var Linkamd64p32 = obj.LinkArch{
  1465  	Arch:       sys.ArchAMD64P32,
  1466  	Preprocess: preprocess,
  1467  	Assemble:   span6,
  1468  	Follow:     follow,
  1469  	Progedit:   progedit,
  1470  	UnaryDst:   unaryDst,
  1471  }
  1472  
  1473  var Link386 = obj.LinkArch{
  1474  	Arch:       sys.Arch386,
  1475  	Preprocess: preprocess,
  1476  	Assemble:   span6,
  1477  	Follow:     follow,
  1478  	Progedit:   progedit,
  1479  	UnaryDst:   unaryDst,
  1480  }