github.com/muesli/go@v0.0.0-20170208044820-e410d2a81ef2/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) g->panic->argp = bottom-of-frame
   746  		//
   747  		//	MOVQ g_panic(CX), BX
   748  		//	TESTQ BX, BX
   749  		//	JEQ end
   750  		//	LEAQ (autoffset+8)(SP), DI
   751  		//	CMPQ panic_argp(BX), DI
   752  		//	JNE end
   753  		//	MOVQ SP, panic_argp(BX)
   754  		// end:
   755  		//	NOP
   756  		//
   757  		// The NOP is needed to give the jumps somewhere to land.
   758  		// It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
   759  
   760  		p = obj.Appendp(ctxt, p)
   761  
   762  		p.As = AMOVQ
   763  		p.From.Type = obj.TYPE_MEM
   764  		p.From.Reg = REG_CX
   765  		p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   766  		p.To.Type = obj.TYPE_REG
   767  		p.To.Reg = REG_BX
   768  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   769  			p.As = AMOVL
   770  			p.From.Type = obj.TYPE_MEM
   771  			p.From.Reg = REG_R15
   772  			p.From.Scale = 1
   773  			p.From.Index = REG_CX
   774  		}
   775  		if p.Mode == 32 {
   776  			p.As = AMOVL
   777  		}
   778  
   779  		p = obj.Appendp(ctxt, p)
   780  		p.As = ATESTQ
   781  		p.From.Type = obj.TYPE_REG
   782  		p.From.Reg = REG_BX
   783  		p.To.Type = obj.TYPE_REG
   784  		p.To.Reg = REG_BX
   785  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   786  			p.As = ATESTL
   787  		}
   788  
   789  		p = obj.Appendp(ctxt, p)
   790  		p.As = AJEQ
   791  		p.To.Type = obj.TYPE_BRANCH
   792  		p1 := p
   793  
   794  		p = obj.Appendp(ctxt, p)
   795  		p.As = ALEAQ
   796  		p.From.Type = obj.TYPE_MEM
   797  		p.From.Reg = REG_SP
   798  		p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize)
   799  		p.To.Type = obj.TYPE_REG
   800  		p.To.Reg = REG_DI
   801  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   802  			p.As = ALEAL
   803  		}
   804  
   805  		p = obj.Appendp(ctxt, p)
   806  		p.As = ACMPQ
   807  		p.From.Type = obj.TYPE_MEM
   808  		p.From.Reg = REG_BX
   809  		p.From.Offset = 0 // Panic.argp
   810  		p.To.Type = obj.TYPE_REG
   811  		p.To.Reg = REG_DI
   812  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   813  			p.As = ACMPL
   814  			p.From.Type = obj.TYPE_MEM
   815  			p.From.Reg = REG_R15
   816  			p.From.Scale = 1
   817  			p.From.Index = REG_BX
   818  		}
   819  		if p.Mode == 32 {
   820  			p.As = ACMPL
   821  		}
   822  
   823  		p = obj.Appendp(ctxt, p)
   824  		p.As = AJNE
   825  		p.To.Type = obj.TYPE_BRANCH
   826  		p2 := p
   827  
   828  		p = obj.Appendp(ctxt, p)
   829  		p.As = AMOVQ
   830  		p.From.Type = obj.TYPE_REG
   831  		p.From.Reg = REG_SP
   832  		p.To.Type = obj.TYPE_MEM
   833  		p.To.Reg = REG_BX
   834  		p.To.Offset = 0 // Panic.argp
   835  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   836  			p.As = AMOVL
   837  			p.To.Type = obj.TYPE_MEM
   838  			p.To.Reg = REG_R15
   839  			p.To.Scale = 1
   840  			p.To.Index = REG_BX
   841  		}
   842  		if p.Mode == 32 {
   843  			p.As = AMOVL
   844  		}
   845  
   846  		p = obj.Appendp(ctxt, p)
   847  		p.As = obj.ANOP
   848  		p1.Pcond = p
   849  		p2.Pcond = p
   850  	}
   851  
   852  	for ; p != nil; p = p.Link {
   853  		pcsize := int(p.Mode) / 8
   854  		switch p.From.Name {
   855  		case obj.NAME_AUTO:
   856  			p.From.Offset += int64(deltasp) - int64(bpsize)
   857  		case obj.NAME_PARAM:
   858  			p.From.Offset += int64(deltasp) + int64(pcsize)
   859  		}
   860  		if p.From3 != nil {
   861  			switch p.From3.Name {
   862  			case obj.NAME_AUTO:
   863  				p.From3.Offset += int64(deltasp) - int64(bpsize)
   864  			case obj.NAME_PARAM:
   865  				p.From3.Offset += int64(deltasp) + int64(pcsize)
   866  			}
   867  		}
   868  		switch p.To.Name {
   869  		case obj.NAME_AUTO:
   870  			p.To.Offset += int64(deltasp) - int64(bpsize)
   871  		case obj.NAME_PARAM:
   872  			p.To.Offset += int64(deltasp) + int64(pcsize)
   873  		}
   874  
   875  		switch p.As {
   876  		default:
   877  			continue
   878  
   879  		case APUSHL, APUSHFL:
   880  			deltasp += 4
   881  			p.Spadj = 4
   882  			continue
   883  
   884  		case APUSHQ, APUSHFQ:
   885  			deltasp += 8
   886  			p.Spadj = 8
   887  			continue
   888  
   889  		case APUSHW, APUSHFW:
   890  			deltasp += 2
   891  			p.Spadj = 2
   892  			continue
   893  
   894  		case APOPL, APOPFL:
   895  			deltasp -= 4
   896  			p.Spadj = -4
   897  			continue
   898  
   899  		case APOPQ, APOPFQ:
   900  			deltasp -= 8
   901  			p.Spadj = -8
   902  			continue
   903  
   904  		case APOPW, APOPFW:
   905  			deltasp -= 2
   906  			p.Spadj = -2
   907  			continue
   908  
   909  		case obj.ARET:
   910  			// do nothing
   911  		}
   912  
   913  		if autoffset != deltasp {
   914  			ctxt.Diag("unbalanced PUSH/POP")
   915  		}
   916  
   917  		if autoffset != 0 {
   918  			if bpsize > 0 {
   919  				// Restore caller's BP
   920  				p.As = AMOVQ
   921  
   922  				p.From.Type = obj.TYPE_MEM
   923  				p.From.Reg = REG_SP
   924  				p.From.Scale = 1
   925  				p.From.Offset = int64(autoffset) - int64(bpsize)
   926  				p.To.Type = obj.TYPE_REG
   927  				p.To.Reg = REG_BP
   928  				p = obj.Appendp(ctxt, p)
   929  			}
   930  
   931  			p.As = AADJSP
   932  			p.From.Type = obj.TYPE_CONST
   933  			p.From.Offset = int64(-autoffset)
   934  			p.Spadj = -autoffset
   935  			p = obj.Appendp(ctxt, p)
   936  			p.As = obj.ARET
   937  
   938  			// If there are instructions following
   939  			// this ARET, they come from a branch
   940  			// with the same stackframe, so undo
   941  			// the cleanup.
   942  			p.Spadj = +autoffset
   943  		}
   944  
   945  		if p.To.Sym != nil { // retjmp
   946  			p.As = obj.AJMP
   947  		}
   948  	}
   949  }
   950  
   951  func isZeroArgRuntimeCall(s *obj.LSym) bool {
   952  	if s == nil {
   953  		return false
   954  	}
   955  	switch s.Name {
   956  	case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide":
   957  		return true
   958  	}
   959  	return false
   960  }
   961  
   962  func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
   963  	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   964  		a.Type = obj.TYPE_MEM
   965  		a.Reg = REG_R15
   966  		a.Index = REG_CX
   967  		a.Scale = 1
   968  		return
   969  	}
   970  
   971  	a.Type = obj.TYPE_MEM
   972  	a.Reg = REG_CX
   973  }
   974  
   975  // Append code to p to load g into cx.
   976  // Overwrites p with the first instruction (no first appendp).
   977  // Overwriting p is unusual but it lets use this in both the
   978  // prologue (caller must call appendp first) and in the epilogue.
   979  // Returns last new instruction.
   980  func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
   981  	p.As = AMOVQ
   982  	if ctxt.Arch.PtrSize == 4 {
   983  		p.As = AMOVL
   984  	}
   985  	p.From.Type = obj.TYPE_MEM
   986  	p.From.Reg = REG_TLS
   987  	p.From.Offset = 0
   988  	p.To.Type = obj.TYPE_REG
   989  	p.To.Reg = REG_CX
   990  
   991  	next := p.Link
   992  	progedit(ctxt, p)
   993  	for p.Link != next {
   994  		p = p.Link
   995  	}
   996  
   997  	if p.From.Index == REG_TLS {
   998  		p.From.Scale = 2
   999  	}
  1000  
  1001  	return p
  1002  }
  1003  
  1004  // Append code to p to check for stack split.
  1005  // Appends to (does not overwrite) p.
  1006  // Assumes g is in CX.
  1007  // Returns last new instruction.
  1008  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog {
  1009  	cmp := ACMPQ
  1010  	lea := ALEAQ
  1011  	mov := AMOVQ
  1012  	sub := ASUBQ
  1013  
  1014  	if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
  1015  		cmp = ACMPL
  1016  		lea = ALEAL
  1017  		mov = AMOVL
  1018  		sub = ASUBL
  1019  	}
  1020  
  1021  	var q1 *obj.Prog
  1022  	if framesize <= obj.StackSmall {
  1023  		// small stack: SP <= stackguard
  1024  		//	CMPQ SP, stackguard
  1025  		p = obj.Appendp(ctxt, p)
  1026  
  1027  		p.As = cmp
  1028  		p.From.Type = obj.TYPE_REG
  1029  		p.From.Reg = REG_SP
  1030  		indir_cx(ctxt, p, &p.To)
  1031  		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
  1032  		if ctxt.Cursym.CFunc() {
  1033  			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
  1034  		}
  1035  	} else if framesize <= obj.StackBig {
  1036  		// large stack: SP-framesize <= stackguard-StackSmall
  1037  		//	LEAQ -xxx(SP), AX
  1038  		//	CMPQ AX, stackguard
  1039  		p = obj.Appendp(ctxt, p)
  1040  
  1041  		p.As = lea
  1042  		p.From.Type = obj.TYPE_MEM
  1043  		p.From.Reg = REG_SP
  1044  		p.From.Offset = -(int64(framesize) - obj.StackSmall)
  1045  		p.To.Type = obj.TYPE_REG
  1046  		p.To.Reg = REG_AX
  1047  
  1048  		p = obj.Appendp(ctxt, p)
  1049  		p.As = cmp
  1050  		p.From.Type = obj.TYPE_REG
  1051  		p.From.Reg = REG_AX
  1052  		indir_cx(ctxt, p, &p.To)
  1053  		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
  1054  		if ctxt.Cursym.CFunc() {
  1055  			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
  1056  		}
  1057  	} else {
  1058  		// Such a large stack we need to protect against wraparound.
  1059  		// If SP is close to zero:
  1060  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
  1061  		// The +StackGuard on both sides is required to keep the left side positive:
  1062  		// SP is allowed to be slightly below stackguard. See stack.h.
  1063  		//
  1064  		// Preemption sets stackguard to StackPreempt, a very large value.
  1065  		// That breaks the math above, so we have to check for that explicitly.
  1066  		//	MOVQ	stackguard, CX
  1067  		//	CMPQ	CX, $StackPreempt
  1068  		//	JEQ	label-of-call-to-morestack
  1069  		//	LEAQ	StackGuard(SP), AX
  1070  		//	SUBQ	CX, AX
  1071  		//	CMPQ	AX, $(framesize+(StackGuard-StackSmall))
  1072  
  1073  		p = obj.Appendp(ctxt, p)
  1074  
  1075  		p.As = mov
  1076  		indir_cx(ctxt, p, &p.From)
  1077  		p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
  1078  		if ctxt.Cursym.CFunc() {
  1079  			p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
  1080  		}
  1081  		p.To.Type = obj.TYPE_REG
  1082  		p.To.Reg = REG_SI
  1083  
  1084  		p = obj.Appendp(ctxt, p)
  1085  		p.As = cmp
  1086  		p.From.Type = obj.TYPE_REG
  1087  		p.From.Reg = REG_SI
  1088  		p.To.Type = obj.TYPE_CONST
  1089  		p.To.Offset = obj.StackPreempt
  1090  		if p.Mode == 32 {
  1091  			p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
  1092  		}
  1093  
  1094  		p = obj.Appendp(ctxt, p)
  1095  		p.As = AJEQ
  1096  		p.To.Type = obj.TYPE_BRANCH
  1097  		q1 = p
  1098  
  1099  		p = obj.Appendp(ctxt, p)
  1100  		p.As = lea
  1101  		p.From.Type = obj.TYPE_MEM
  1102  		p.From.Reg = REG_SP
  1103  		p.From.Offset = obj.StackGuard
  1104  		p.To.Type = obj.TYPE_REG
  1105  		p.To.Reg = REG_AX
  1106  
  1107  		p = obj.Appendp(ctxt, p)
  1108  		p.As = sub
  1109  		p.From.Type = obj.TYPE_REG
  1110  		p.From.Reg = REG_SI
  1111  		p.To.Type = obj.TYPE_REG
  1112  		p.To.Reg = REG_AX
  1113  
  1114  		p = obj.Appendp(ctxt, p)
  1115  		p.As = cmp
  1116  		p.From.Type = obj.TYPE_REG
  1117  		p.From.Reg = REG_AX
  1118  		p.To.Type = obj.TYPE_CONST
  1119  		p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
  1120  	}
  1121  
  1122  	// common
  1123  	jls := obj.Appendp(ctxt, p)
  1124  	jls.As = AJLS
  1125  	jls.To.Type = obj.TYPE_BRANCH
  1126  
  1127  	var last *obj.Prog
  1128  	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
  1129  	}
  1130  
  1131  	// Now we are at the end of the function, but logically
  1132  	// we are still in function prologue. We need to fix the
  1133  	// SP data and PCDATA.
  1134  	spfix := obj.Appendp(ctxt, last)
  1135  	spfix.As = obj.ANOP
  1136  	spfix.Spadj = -framesize
  1137  
  1138  	pcdata := obj.Appendp(ctxt, spfix)
  1139  	pcdata.Pos = ctxt.Cursym.Text.Pos
  1140  	pcdata.Mode = ctxt.Cursym.Text.Mode
  1141  	pcdata.As = obj.APCDATA
  1142  	pcdata.From.Type = obj.TYPE_CONST
  1143  	pcdata.From.Offset = obj.PCDATA_StackMapIndex
  1144  	pcdata.To.Type = obj.TYPE_CONST
  1145  	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
  1146  
  1147  	call := obj.Appendp(ctxt, pcdata)
  1148  	call.Pos = ctxt.Cursym.Text.Pos
  1149  	call.Mode = ctxt.Cursym.Text.Mode
  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 ctxt.Cursym.CFunc():
  1156  		morestack = "runtime.morestackc"
  1157  	case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
  1158  		morestack = "runtime.morestack_noctxt"
  1159  	}
  1160  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
  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)
  1167  	for ; callend.Link != nil; callend = callend.Link {
  1168  		progedit(ctxt, callend.Link)
  1169  	}
  1170  
  1171  	jmp := obj.Appendp(ctxt, callend)
  1172  	jmp.As = obj.AJMP
  1173  	jmp.To.Type = obj.TYPE_BRANCH
  1174  	jmp.Pcond = ctxt.Cursym.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  	ACMPXCHG8B: true,
  1189  	ADECB:      true,
  1190  	ADECL:      true,
  1191  	ADECQ:      true,
  1192  	ADECW:      true,
  1193  	AINCB:      true,
  1194  	AINCL:      true,
  1195  	AINCQ:      true,
  1196  	AINCW:      true,
  1197  	ANEGB:      true,
  1198  	ANEGL:      true,
  1199  	ANEGQ:      true,
  1200  	ANEGW:      true,
  1201  	ANOTB:      true,
  1202  	ANOTL:      true,
  1203  	ANOTQ:      true,
  1204  	ANOTW:      true,
  1205  	APOPL:      true,
  1206  	APOPQ:      true,
  1207  	APOPW:      true,
  1208  	ASETCC:     true,
  1209  	ASETCS:     true,
  1210  	ASETEQ:     true,
  1211  	ASETGE:     true,
  1212  	ASETGT:     true,
  1213  	ASETHI:     true,
  1214  	ASETLE:     true,
  1215  	ASETLS:     true,
  1216  	ASETLT:     true,
  1217  	ASETMI:     true,
  1218  	ASETNE:     true,
  1219  	ASETOC:     true,
  1220  	ASETOS:     true,
  1221  	ASETPC:     true,
  1222  	ASETPL:     true,
  1223  	ASETPS:     true,
  1224  	AFFREE:     true,
  1225  	AFLDENV:    true,
  1226  	AFSAVE:     true,
  1227  	AFSTCW:     true,
  1228  	AFSTENV:    true,
  1229  	AFSTSW:     true,
  1230  	AFXSAVE:    true,
  1231  	AFXSAVE64:  true,
  1232  	ASTMXCSR:   true,
  1233  }
  1234  
  1235  var Linkamd64 = obj.LinkArch{
  1236  	Arch:       sys.ArchAMD64,
  1237  	Preprocess: preprocess,
  1238  	Assemble:   span6,
  1239  	Progedit:   progedit,
  1240  	UnaryDst:   unaryDst,
  1241  }
  1242  
  1243  var Linkamd64p32 = obj.LinkArch{
  1244  	Arch:       sys.ArchAMD64P32,
  1245  	Preprocess: preprocess,
  1246  	Assemble:   span6,
  1247  	Progedit:   progedit,
  1248  	UnaryDst:   unaryDst,
  1249  }
  1250  
  1251  var Link386 = obj.LinkArch{
  1252  	Arch:       sys.Arch386,
  1253  	Preprocess: preprocess,
  1254  	Assemble:   span6,
  1255  	Progedit:   progedit,
  1256  	UnaryDst:   unaryDst,
  1257  }