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