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