github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/cmd/internal/obj/x86/obj6.go (about)

     1  // Inferno utils/6l/pass.c
     2  // http://code.google.com/p/inferno-os/source/browse/utils/6l/pass.c
     3  //
     4  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     5  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     6  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     7  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     8  //	Portions Copyright © 2004,2006 Bruce Ellis
     9  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    10  //	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    11  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
    12  //
    13  // Permission is hereby granted, free of charge, to any person obtaining a copy
    14  // of this software and associated documentation files (the "Software"), to deal
    15  // in the Software without restriction, including without limitation the rights
    16  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    17  // copies of the Software, and to permit persons to whom the Software is
    18  // furnished to do so, subject to the following conditions:
    19  //
    20  // The above copyright notice and this permission notice shall be included in
    21  // all copies or substantial portions of the Software.
    22  //
    23  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    26  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    29  // THE SOFTWARE.
    30  
    31  package x86
    32  
    33  import (
    34  	"cmd/internal/obj"
    35  	"cmd/internal/sys"
    36  	"fmt"
    37  	"log"
    38  	"math"
    39  )
    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  			return false
    59  		}
    60  
    61  		return true
    62  	}
    63  
    64  	switch ctxt.Headtype {
    65  	case obj.Hplan9,
    66  		obj.Hwindows:
    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 && 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.Local = 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.Local = 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  	}
   337  
   338  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   339  		//     ADUFFxxx $offset
   340  		// becomes
   341  		//     $MOV runtime.duffxxx@GOT, $reg
   342  		//     $ADD $offset, $reg
   343  		//     CALL $reg
   344  		var sym *obj.LSym
   345  		if p.As == obj.ADUFFZERO {
   346  			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
   347  		} else {
   348  			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
   349  		}
   350  		offset := p.To.Offset
   351  		p.As = mov
   352  		p.From.Type = obj.TYPE_MEM
   353  		p.From.Name = obj.NAME_GOTREF
   354  		p.From.Sym = sym
   355  		p.To.Type = obj.TYPE_REG
   356  		p.To.Reg = reg
   357  		p.To.Offset = 0
   358  		p.To.Sym = nil
   359  		p1 := obj.Appendp(ctxt, p)
   360  		p1.As = add
   361  		p1.From.Type = obj.TYPE_CONST
   362  		p1.From.Offset = offset
   363  		p1.To.Type = obj.TYPE_REG
   364  		p1.To.Reg = reg
   365  		p2 := obj.Appendp(ctxt, p1)
   366  		p2.As = obj.ACALL
   367  		p2.To.Type = obj.TYPE_REG
   368  		p2.To.Reg = reg
   369  	}
   370  
   371  	// We only care about global data: NAME_EXTERN means a global
   372  	// symbol in the Go sense, and p.Sym.Local is true for a few
   373  	// internally defined symbols.
   374  	if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   375  		// $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below
   376  		p.As = mov
   377  		p.From.Type = obj.TYPE_ADDR
   378  	}
   379  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   380  		// $MOV $sym, Rx becomes $MOV sym@GOT, Rx
   381  		// $MOV $sym+<off>, Rx becomes $MOV sym@GOT, Rx; $LEA <off>(Rx), Rx
   382  		// On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX
   383  		cmplxdest := false
   384  		pAs := p.As
   385  		var dest obj.Addr
   386  		if p.To.Type != obj.TYPE_REG || pAs != mov {
   387  			if p.Mode == 64 {
   388  				ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
   389  			}
   390  			cmplxdest = true
   391  			dest = p.To
   392  			p.As = mov
   393  			p.To.Type = obj.TYPE_REG
   394  			p.To.Reg = REG_CX
   395  			p.To.Sym = nil
   396  			p.To.Name = obj.NAME_NONE
   397  		}
   398  		p.From.Type = obj.TYPE_MEM
   399  		p.From.Name = obj.NAME_GOTREF
   400  		q := p
   401  		if p.From.Offset != 0 {
   402  			q = obj.Appendp(ctxt, p)
   403  			q.As = lea
   404  			q.From.Type = obj.TYPE_MEM
   405  			q.From.Reg = p.To.Reg
   406  			q.From.Offset = p.From.Offset
   407  			q.To = p.To
   408  			p.From.Offset = 0
   409  		}
   410  		if cmplxdest {
   411  			q = obj.Appendp(ctxt, q)
   412  			q.As = pAs
   413  			q.To = dest
   414  			q.From.Type = obj.TYPE_REG
   415  			q.From.Reg = REG_CX
   416  		}
   417  	}
   418  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   419  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   420  	}
   421  	var source *obj.Addr
   422  	// MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry
   423  	// MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15)
   424  	// An addition may be inserted between the two MOVs if there is an offset.
   425  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   426  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   427  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   428  		}
   429  		source = &p.From
   430  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   431  		source = &p.To
   432  	} else {
   433  		return
   434  	}
   435  	if p.As == obj.ACALL {
   436  		// When dynlinking on 386, almost any call might end up being a call
   437  		// to a PLT, so make sure the GOT pointer is loaded into BX.
   438  		// RegTo2 is set on the replacement call insn to stop it being
   439  		// processed when it is in turn passed to progedit.
   440  		if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local) || p.RegTo2 != 0 {
   441  			return
   442  		}
   443  		p1 := obj.Appendp(ctxt, p)
   444  		p2 := obj.Appendp(ctxt, p1)
   445  
   446  		p1.As = ALEAL
   447  		p1.From.Type = obj.TYPE_MEM
   448  		p1.From.Name = obj.NAME_STATIC
   449  		p1.From.Sym = obj.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
   450  		p1.To.Type = obj.TYPE_REG
   451  		p1.To.Reg = REG_BX
   452  
   453  		p2.As = p.As
   454  		p2.Scond = p.Scond
   455  		p2.From = p.From
   456  		p2.From3 = p.From3
   457  		p2.Reg = p.Reg
   458  		p2.To = p.To
   459  		// p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr
   460  		// in ../pass.go complain, so set it back to TYPE_MEM here, until p2
   461  		// itself gets passed to progedit.
   462  		p2.To.Type = obj.TYPE_MEM
   463  		p2.RegTo2 = 1
   464  
   465  		obj.Nopout(p)
   466  		return
   467  
   468  	}
   469  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP {
   470  		return
   471  	}
   472  	if source.Type != obj.TYPE_MEM {
   473  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   474  	}
   475  	p1 := obj.Appendp(ctxt, p)
   476  	p2 := obj.Appendp(ctxt, p1)
   477  
   478  	p1.As = mov
   479  	p1.From.Type = obj.TYPE_MEM
   480  	p1.From.Sym = source.Sym
   481  	p1.From.Name = obj.NAME_GOTREF
   482  	p1.To.Type = obj.TYPE_REG
   483  	p1.To.Reg = reg
   484  
   485  	p2.As = p.As
   486  	p2.From = p.From
   487  	p2.To = p.To
   488  	if p.From.Name == obj.NAME_EXTERN {
   489  		p2.From.Reg = reg
   490  		p2.From.Name = obj.NAME_NONE
   491  		p2.From.Sym = nil
   492  	} else if p.To.Name == obj.NAME_EXTERN {
   493  		p2.To.Reg = reg
   494  		p2.To.Name = obj.NAME_NONE
   495  		p2.To.Sym = nil
   496  	} else {
   497  		return
   498  	}
   499  	obj.Nopout(p)
   500  }
   501  
   502  func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
   503  	// RegTo2 is set on the instructions we insert here so they don't get
   504  	// processed twice.
   505  	if p.RegTo2 != 0 {
   506  		return
   507  	}
   508  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   509  		return
   510  	}
   511  	// Any Prog (aside from the above special cases) with an Addr with Name ==
   512  	// NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.cx
   513  	// inserted before it.
   514  	isName := func(a *obj.Addr) bool {
   515  		if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 {
   516  			return false
   517  		}
   518  		if a.Sym.Type == obj.STLSBSS {
   519  			return false
   520  		}
   521  		return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF
   522  	}
   523  
   524  	if isName(&p.From) && p.From.Type == obj.TYPE_ADDR {
   525  		// Handle things like "MOVL $sym, (SP)" or "PUSHL $sym" by rewriting
   526  		// to "MOVL $sym, CX; MOVL CX, (SP)" or "MOVL $sym, CX; PUSHL CX"
   527  		// respectively.
   528  		if p.To.Type != obj.TYPE_REG {
   529  			q := obj.Appendp(ctxt, p)
   530  			q.As = p.As
   531  			q.From.Type = obj.TYPE_REG
   532  			q.From.Reg = REG_CX
   533  			q.To = p.To
   534  			p.As = AMOVL
   535  			p.To.Type = obj.TYPE_REG
   536  			p.To.Reg = REG_CX
   537  			p.To.Sym = nil
   538  			p.To.Name = obj.NAME_NONE
   539  		}
   540  	}
   541  
   542  	if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) {
   543  		return
   544  	}
   545  	q := obj.Appendp(ctxt, p)
   546  	q.RegTo2 = 1
   547  	r := obj.Appendp(ctxt, q)
   548  	r.RegTo2 = 1
   549  	q.As = obj.ACALL
   550  	q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0)
   551  	q.To.Type = obj.TYPE_MEM
   552  	q.To.Name = obj.NAME_EXTERN
   553  	q.To.Sym.Local = true
   554  	r.As = p.As
   555  	r.Scond = p.Scond
   556  	r.From = p.From
   557  	r.From3 = p.From3
   558  	r.Reg = p.Reg
   559  	r.To = p.To
   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) {
   596  	if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil {
   597  		ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0)
   598  	}
   599  
   600  	ctxt.Cursym = cursym
   601  
   602  	if cursym.Text == nil || cursym.Text.Link == nil {
   603  		return
   604  	}
   605  
   606  	p := cursym.Text
   607  	autoffset := int32(p.To.Offset)
   608  	if autoffset < 0 {
   609  		autoffset = 0
   610  	}
   611  
   612  	var bpsize int
   613  	if p.Mode == 64 && ctxt.Framepointer_enabled && autoffset > 0 && p.From3.Offset&obj.NOFRAME == 0 {
   614  		// Make room for to save a base pointer. If autoffset == 0,
   615  		// this might do something special like a tail jump to
   616  		// another function, so in that case we omit this.
   617  		bpsize = ctxt.Arch.PtrSize
   618  		autoffset += int32(bpsize)
   619  		p.To.Offset += int64(bpsize)
   620  	} else {
   621  		bpsize = 0
   622  	}
   623  
   624  	textarg := int64(p.To.Val.(int32))
   625  	cursym.Args = int32(textarg)
   626  	cursym.Locals = int32(p.To.Offset)
   627  
   628  	// TODO(rsc): Remove.
   629  	if p.Mode == 32 && cursym.Locals < 0 {
   630  		cursym.Locals = 0
   631  	}
   632  
   633  	// TODO(rsc): Remove 'p.Mode == 64 &&'.
   634  	if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 {
   635  		for q := p; q != nil; q = q.Link {
   636  			if q.As == obj.ACALL {
   637  				goto noleaf
   638  			}
   639  			if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 {
   640  				goto noleaf
   641  			}
   642  		}
   643  
   644  		p.From3.Offset |= obj.NOSPLIT
   645  	noleaf:
   646  	}
   647  
   648  	if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 {
   649  		p = obj.Appendp(ctxt, p)
   650  		p = load_g_cx(ctxt, p) // load g into CX
   651  	}
   652  
   653  	if cursym.Text.From3Offset()&obj.NOSPLIT == 0 {
   654  		p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check
   655  	}
   656  
   657  	if autoffset != 0 {
   658  		if autoffset%int32(ctxt.Arch.RegSize) != 0 {
   659  			ctxt.Diag("unaligned stack size %d", autoffset)
   660  		}
   661  		p = obj.Appendp(ctxt, p)
   662  		p.As = AADJSP
   663  		p.From.Type = obj.TYPE_CONST
   664  		p.From.Offset = int64(autoffset)
   665  		p.Spadj = autoffset
   666  	} else {
   667  		// zero-byte stack adjustment.
   668  		// Insert a fake non-zero adjustment so that stkcheck can
   669  		// recognize the end of the stack-splitting prolog.
   670  		p = obj.Appendp(ctxt, p)
   671  
   672  		p.As = obj.ANOP
   673  		p.Spadj = int32(-ctxt.Arch.PtrSize)
   674  		p = obj.Appendp(ctxt, p)
   675  		p.As = obj.ANOP
   676  		p.Spadj = int32(ctxt.Arch.PtrSize)
   677  	}
   678  
   679  	deltasp := autoffset
   680  
   681  	if bpsize > 0 {
   682  		// Save caller's BP
   683  		p = obj.Appendp(ctxt, p)
   684  
   685  		p.As = AMOVQ
   686  		p.From.Type = obj.TYPE_REG
   687  		p.From.Reg = REG_BP
   688  		p.To.Type = obj.TYPE_MEM
   689  		p.To.Reg = REG_SP
   690  		p.To.Scale = 1
   691  		p.To.Offset = int64(autoffset) - int64(bpsize)
   692  
   693  		// Move current frame to BP
   694  		p = obj.Appendp(ctxt, p)
   695  
   696  		p.As = ALEAQ
   697  		p.From.Type = obj.TYPE_MEM
   698  		p.From.Reg = REG_SP
   699  		p.From.Scale = 1
   700  		p.From.Offset = int64(autoffset) - int64(bpsize)
   701  		p.To.Type = obj.TYPE_REG
   702  		p.To.Reg = REG_BP
   703  	}
   704  
   705  	if cursym.Text.From3Offset()&obj.WRAPPER != 0 {
   706  		// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   707  		//
   708  		//	MOVQ g_panic(CX), BX
   709  		//	TESTQ BX, BX
   710  		//	JEQ end
   711  		//	LEAQ (autoffset+8)(SP), DI
   712  		//	CMPQ panic_argp(BX), DI
   713  		//	JNE end
   714  		//	MOVQ SP, panic_argp(BX)
   715  		// end:
   716  		//	NOP
   717  		//
   718  		// The NOP is needed to give the jumps somewhere to land.
   719  		// It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
   720  
   721  		p = obj.Appendp(ctxt, p)
   722  
   723  		p.As = AMOVQ
   724  		p.From.Type = obj.TYPE_MEM
   725  		p.From.Reg = REG_CX
   726  		p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   727  		p.To.Type = obj.TYPE_REG
   728  		p.To.Reg = REG_BX
   729  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   730  			p.As = AMOVL
   731  			p.From.Type = obj.TYPE_MEM
   732  			p.From.Reg = REG_R15
   733  			p.From.Scale = 1
   734  			p.From.Index = REG_CX
   735  		}
   736  		if p.Mode == 32 {
   737  			p.As = AMOVL
   738  		}
   739  
   740  		p = obj.Appendp(ctxt, p)
   741  		p.As = ATESTQ
   742  		p.From.Type = obj.TYPE_REG
   743  		p.From.Reg = REG_BX
   744  		p.To.Type = obj.TYPE_REG
   745  		p.To.Reg = REG_BX
   746  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   747  			p.As = ATESTL
   748  		}
   749  
   750  		p = obj.Appendp(ctxt, p)
   751  		p.As = AJEQ
   752  		p.To.Type = obj.TYPE_BRANCH
   753  		p1 := p
   754  
   755  		p = obj.Appendp(ctxt, p)
   756  		p.As = ALEAQ
   757  		p.From.Type = obj.TYPE_MEM
   758  		p.From.Reg = REG_SP
   759  		p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize)
   760  		p.To.Type = obj.TYPE_REG
   761  		p.To.Reg = REG_DI
   762  		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   763  			p.As = ALEAL
   764  		}
   765  
   766  		p = obj.Appendp(ctxt, p)
   767  		p.As = ACMPQ
   768  		p.From.Type = obj.TYPE_MEM
   769  		p.From.Reg = REG_BX
   770  		p.From.Offset = 0 // Panic.argp
   771  		p.To.Type = obj.TYPE_REG
   772  		p.To.Reg = REG_DI
   773  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   774  			p.As = ACMPL
   775  			p.From.Type = obj.TYPE_MEM
   776  			p.From.Reg = REG_R15
   777  			p.From.Scale = 1
   778  			p.From.Index = REG_BX
   779  		}
   780  		if p.Mode == 32 {
   781  			p.As = ACMPL
   782  		}
   783  
   784  		p = obj.Appendp(ctxt, p)
   785  		p.As = AJNE
   786  		p.To.Type = obj.TYPE_BRANCH
   787  		p2 := p
   788  
   789  		p = obj.Appendp(ctxt, p)
   790  		p.As = AMOVQ
   791  		p.From.Type = obj.TYPE_REG
   792  		p.From.Reg = REG_SP
   793  		p.To.Type = obj.TYPE_MEM
   794  		p.To.Reg = REG_BX
   795  		p.To.Offset = 0 // Panic.argp
   796  		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   797  			p.As = AMOVL
   798  			p.To.Type = obj.TYPE_MEM
   799  			p.To.Reg = REG_R15
   800  			p.To.Scale = 1
   801  			p.To.Index = REG_BX
   802  		}
   803  		if p.Mode == 32 {
   804  			p.As = AMOVL
   805  		}
   806  
   807  		p = obj.Appendp(ctxt, p)
   808  		p.As = obj.ANOP
   809  		p1.Pcond = p
   810  		p2.Pcond = p
   811  	}
   812  
   813  	var a int
   814  	var pcsize int
   815  	for ; p != nil; p = p.Link {
   816  		pcsize = int(p.Mode) / 8
   817  		a = int(p.From.Name)
   818  		if a == obj.NAME_AUTO {
   819  			p.From.Offset += int64(deltasp) - int64(bpsize)
   820  		}
   821  		if a == obj.NAME_PARAM {
   822  			p.From.Offset += int64(deltasp) + int64(pcsize)
   823  		}
   824  		if p.From3 != nil {
   825  			a = int(p.From3.Name)
   826  			if a == obj.NAME_AUTO {
   827  				p.From3.Offset += int64(deltasp) - int64(bpsize)
   828  			}
   829  			if a == obj.NAME_PARAM {
   830  				p.From3.Offset += int64(deltasp) + int64(pcsize)
   831  			}
   832  		}
   833  		a = int(p.To.Name)
   834  		if a == obj.NAME_AUTO {
   835  			p.To.Offset += int64(deltasp) - int64(bpsize)
   836  		}
   837  		if a == obj.NAME_PARAM {
   838  			p.To.Offset += int64(deltasp) + int64(pcsize)
   839  		}
   840  
   841  		switch p.As {
   842  		default:
   843  			continue
   844  
   845  		case APUSHL, APUSHFL:
   846  			deltasp += 4
   847  			p.Spadj = 4
   848  			continue
   849  
   850  		case APUSHQ, APUSHFQ:
   851  			deltasp += 8
   852  			p.Spadj = 8
   853  			continue
   854  
   855  		case APUSHW, APUSHFW:
   856  			deltasp += 2
   857  			p.Spadj = 2
   858  			continue
   859  
   860  		case APOPL, APOPFL:
   861  			deltasp -= 4
   862  			p.Spadj = -4
   863  			continue
   864  
   865  		case APOPQ, APOPFQ:
   866  			deltasp -= 8
   867  			p.Spadj = -8
   868  			continue
   869  
   870  		case APOPW, APOPFW:
   871  			deltasp -= 2
   872  			p.Spadj = -2
   873  			continue
   874  
   875  		case obj.ARET:
   876  			break
   877  		}
   878  
   879  		if autoffset != deltasp {
   880  			ctxt.Diag("unbalanced PUSH/POP")
   881  		}
   882  
   883  		if autoffset != 0 {
   884  			if bpsize > 0 {
   885  				// Restore caller's BP
   886  				p.As = AMOVQ
   887  
   888  				p.From.Type = obj.TYPE_MEM
   889  				p.From.Reg = REG_SP
   890  				p.From.Scale = 1
   891  				p.From.Offset = int64(autoffset) - int64(bpsize)
   892  				p.To.Type = obj.TYPE_REG
   893  				p.To.Reg = REG_BP
   894  				p = obj.Appendp(ctxt, p)
   895  			}
   896  
   897  			p.As = AADJSP
   898  			p.From.Type = obj.TYPE_CONST
   899  			p.From.Offset = int64(-autoffset)
   900  			p.Spadj = -autoffset
   901  			p = obj.Appendp(ctxt, p)
   902  			p.As = obj.ARET
   903  
   904  			// If there are instructions following
   905  			// this ARET, they come from a branch
   906  			// with the same stackframe, so undo
   907  			// the cleanup.
   908  			p.Spadj = +autoffset
   909  		}
   910  
   911  		if p.To.Sym != nil { // retjmp
   912  			p.As = obj.AJMP
   913  		}
   914  	}
   915  }
   916  
   917  func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
   918  	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
   919  		a.Type = obj.TYPE_MEM
   920  		a.Reg = REG_R15
   921  		a.Index = REG_CX
   922  		a.Scale = 1
   923  		return
   924  	}
   925  
   926  	a.Type = obj.TYPE_MEM
   927  	a.Reg = REG_CX
   928  }
   929  
   930  // Append code to p to load g into cx.
   931  // Overwrites p with the first instruction (no first appendp).
   932  // Overwriting p is unusual but it lets use this in both the
   933  // prologue (caller must call appendp first) and in the epilogue.
   934  // Returns last new instruction.
   935  func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
   936  	p.As = AMOVQ
   937  	if ctxt.Arch.PtrSize == 4 {
   938  		p.As = AMOVL
   939  	}
   940  	p.From.Type = obj.TYPE_MEM
   941  	p.From.Reg = REG_TLS
   942  	p.From.Offset = 0
   943  	p.To.Type = obj.TYPE_REG
   944  	p.To.Reg = REG_CX
   945  
   946  	next := p.Link
   947  	progedit(ctxt, p)
   948  	for p.Link != next {
   949  		p = p.Link
   950  	}
   951  
   952  	if p.From.Index == REG_TLS {
   953  		p.From.Scale = 2
   954  	}
   955  
   956  	return p
   957  }
   958  
   959  // Append code to p to check for stack split.
   960  // Appends to (does not overwrite) p.
   961  // Assumes g is in CX.
   962  // Returns last new instruction.
   963  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog {
   964  	cmp := ACMPQ
   965  	lea := ALEAQ
   966  	mov := AMOVQ
   967  	sub := ASUBQ
   968  
   969  	if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
   970  		cmp = ACMPL
   971  		lea = ALEAL
   972  		mov = AMOVL
   973  		sub = ASUBL
   974  	}
   975  
   976  	var q1 *obj.Prog
   977  	if framesize <= obj.StackSmall {
   978  		// small stack: SP <= stackguard
   979  		//	CMPQ SP, stackguard
   980  		p = obj.Appendp(ctxt, p)
   981  
   982  		p.As = cmp
   983  		p.From.Type = obj.TYPE_REG
   984  		p.From.Reg = REG_SP
   985  		indir_cx(ctxt, p, &p.To)
   986  		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
   987  		if ctxt.Cursym.Cfunc {
   988  			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
   989  		}
   990  	} else if framesize <= obj.StackBig {
   991  		// large stack: SP-framesize <= stackguard-StackSmall
   992  		//	LEAQ -xxx(SP), AX
   993  		//	CMPQ AX, stackguard
   994  		p = obj.Appendp(ctxt, p)
   995  
   996  		p.As = lea
   997  		p.From.Type = obj.TYPE_MEM
   998  		p.From.Reg = REG_SP
   999  		p.From.Offset = -(int64(framesize) - obj.StackSmall)
  1000  		p.To.Type = obj.TYPE_REG
  1001  		p.To.Reg = REG_AX
  1002  
  1003  		p = obj.Appendp(ctxt, p)
  1004  		p.As = cmp
  1005  		p.From.Type = obj.TYPE_REG
  1006  		p.From.Reg = REG_AX
  1007  		indir_cx(ctxt, p, &p.To)
  1008  		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
  1009  		if ctxt.Cursym.Cfunc {
  1010  			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
  1011  		}
  1012  	} else {
  1013  		// Such a large stack we need to protect against wraparound.
  1014  		// If SP is close to zero:
  1015  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
  1016  		// The +StackGuard on both sides is required to keep the left side positive:
  1017  		// SP is allowed to be slightly below stackguard. See stack.h.
  1018  		//
  1019  		// Preemption sets stackguard to StackPreempt, a very large value.
  1020  		// That breaks the math above, so we have to check for that explicitly.
  1021  		//	MOVQ	stackguard, CX
  1022  		//	CMPQ	CX, $StackPreempt
  1023  		//	JEQ	label-of-call-to-morestack
  1024  		//	LEAQ	StackGuard(SP), AX
  1025  		//	SUBQ	CX, AX
  1026  		//	CMPQ	AX, $(framesize+(StackGuard-StackSmall))
  1027  
  1028  		p = obj.Appendp(ctxt, p)
  1029  
  1030  		p.As = mov
  1031  		indir_cx(ctxt, p, &p.From)
  1032  		p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
  1033  		if ctxt.Cursym.Cfunc {
  1034  			p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
  1035  		}
  1036  		p.To.Type = obj.TYPE_REG
  1037  		p.To.Reg = REG_SI
  1038  
  1039  		p = obj.Appendp(ctxt, p)
  1040  		p.As = cmp
  1041  		p.From.Type = obj.TYPE_REG
  1042  		p.From.Reg = REG_SI
  1043  		p.To.Type = obj.TYPE_CONST
  1044  		p.To.Offset = obj.StackPreempt
  1045  		if p.Mode == 32 {
  1046  			p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
  1047  		}
  1048  
  1049  		p = obj.Appendp(ctxt, p)
  1050  		p.As = AJEQ
  1051  		p.To.Type = obj.TYPE_BRANCH
  1052  		q1 = p
  1053  
  1054  		p = obj.Appendp(ctxt, p)
  1055  		p.As = lea
  1056  		p.From.Type = obj.TYPE_MEM
  1057  		p.From.Reg = REG_SP
  1058  		p.From.Offset = obj.StackGuard
  1059  		p.To.Type = obj.TYPE_REG
  1060  		p.To.Reg = REG_AX
  1061  
  1062  		p = obj.Appendp(ctxt, p)
  1063  		p.As = sub
  1064  		p.From.Type = obj.TYPE_REG
  1065  		p.From.Reg = REG_SI
  1066  		p.To.Type = obj.TYPE_REG
  1067  		p.To.Reg = REG_AX
  1068  
  1069  		p = obj.Appendp(ctxt, p)
  1070  		p.As = cmp
  1071  		p.From.Type = obj.TYPE_REG
  1072  		p.From.Reg = REG_AX
  1073  		p.To.Type = obj.TYPE_CONST
  1074  		p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
  1075  	}
  1076  
  1077  	// common
  1078  	jls := obj.Appendp(ctxt, p)
  1079  	jls.As = AJLS
  1080  	jls.To.Type = obj.TYPE_BRANCH
  1081  
  1082  	var last *obj.Prog
  1083  	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
  1084  	}
  1085  
  1086  	spfix := obj.Appendp(ctxt, last)
  1087  	spfix.As = obj.ANOP
  1088  	spfix.Spadj = -framesize
  1089  
  1090  	call := obj.Appendp(ctxt, spfix)
  1091  	call.Lineno = ctxt.Cursym.Text.Lineno
  1092  	call.Mode = ctxt.Cursym.Text.Mode
  1093  	call.As = obj.ACALL
  1094  	call.To.Type = obj.TYPE_BRANCH
  1095  	call.To.Name = obj.NAME_EXTERN
  1096  	morestack := "runtime.morestack"
  1097  	switch {
  1098  	case ctxt.Cursym.Cfunc:
  1099  		morestack = "runtime.morestackc"
  1100  	case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
  1101  		morestack = "runtime.morestack_noctxt"
  1102  	}
  1103  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
  1104  	// When compiling 386 code for dynamic linking, the call needs to be adjusted
  1105  	// to follow PIC rules. This in turn can insert more instructions, so we need
  1106  	// to keep track of the start of the call (where the jump will be to) and the
  1107  	// end (which following instructions are appended to).
  1108  	callend := call
  1109  	progedit(ctxt, callend)
  1110  	for ; callend.Link != nil; callend = callend.Link {
  1111  		progedit(ctxt, callend.Link)
  1112  	}
  1113  
  1114  	jmp := obj.Appendp(ctxt, callend)
  1115  	jmp.As = obj.AJMP
  1116  	jmp.To.Type = obj.TYPE_BRANCH
  1117  	jmp.Pcond = ctxt.Cursym.Text.Link
  1118  	jmp.Spadj = +framesize
  1119  
  1120  	jls.Pcond = call
  1121  	if q1 != nil {
  1122  		q1.Pcond = call
  1123  	}
  1124  
  1125  	return jls
  1126  }
  1127  
  1128  func follow(ctxt *obj.Link, s *obj.LSym) {
  1129  	ctxt.Cursym = s
  1130  
  1131  	firstp := ctxt.NewProg()
  1132  	lastp := firstp
  1133  	xfol(ctxt, s.Text, &lastp)
  1134  	lastp.Link = nil
  1135  	s.Text = firstp.Link
  1136  }
  1137  
  1138  func nofollow(a obj.As) bool {
  1139  	switch a {
  1140  	case obj.AJMP,
  1141  		obj.ARET,
  1142  		AIRETL,
  1143  		AIRETQ,
  1144  		AIRETW,
  1145  		ARETFL,
  1146  		ARETFQ,
  1147  		ARETFW,
  1148  		obj.AUNDEF:
  1149  		return true
  1150  	}
  1151  
  1152  	return false
  1153  }
  1154  
  1155  func pushpop(a obj.As) bool {
  1156  	switch a {
  1157  	case APUSHL,
  1158  		APUSHFL,
  1159  		APUSHQ,
  1160  		APUSHFQ,
  1161  		APUSHW,
  1162  		APUSHFW,
  1163  		APOPL,
  1164  		APOPFL,
  1165  		APOPQ,
  1166  		APOPFQ,
  1167  		APOPW,
  1168  		APOPFW:
  1169  		return true
  1170  	}
  1171  
  1172  	return false
  1173  }
  1174  
  1175  func relinv(a obj.As) obj.As {
  1176  	switch a {
  1177  	case AJEQ:
  1178  		return AJNE
  1179  	case AJNE:
  1180  		return AJEQ
  1181  	case AJLE:
  1182  		return AJGT
  1183  	case AJLS:
  1184  		return AJHI
  1185  	case AJLT:
  1186  		return AJGE
  1187  	case AJMI:
  1188  		return AJPL
  1189  	case AJGE:
  1190  		return AJLT
  1191  	case AJPL:
  1192  		return AJMI
  1193  	case AJGT:
  1194  		return AJLE
  1195  	case AJHI:
  1196  		return AJLS
  1197  	case AJCS:
  1198  		return AJCC
  1199  	case AJCC:
  1200  		return AJCS
  1201  	case AJPS:
  1202  		return AJPC
  1203  	case AJPC:
  1204  		return AJPS
  1205  	case AJOS:
  1206  		return AJOC
  1207  	case AJOC:
  1208  		return AJOS
  1209  	}
  1210  
  1211  	log.Fatalf("unknown relation: %s", obj.Aconv(a))
  1212  	return 0
  1213  }
  1214  
  1215  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1216  	var q *obj.Prog
  1217  	var i int
  1218  	var a obj.As
  1219  
  1220  loop:
  1221  	if p == nil {
  1222  		return
  1223  	}
  1224  	if p.As == obj.AJMP {
  1225  		q = p.Pcond
  1226  		if q != nil && q.As != obj.ATEXT {
  1227  			/* mark instruction as done and continue layout at target of jump */
  1228  			p.Mark |= DONE
  1229  
  1230  			p = q
  1231  			if p.Mark&DONE == 0 {
  1232  				goto loop
  1233  			}
  1234  		}
  1235  	}
  1236  
  1237  	if p.Mark&DONE != 0 {
  1238  		/*
  1239  		 * p goes here, but already used it elsewhere.
  1240  		 * copy up to 4 instructions or else branch to other copy.
  1241  		 */
  1242  		i = 0
  1243  		q = p
  1244  		for ; i < 4; i, q = i+1, q.Link {
  1245  			if q == nil {
  1246  				break
  1247  			}
  1248  			if q == *last {
  1249  				break
  1250  			}
  1251  			a = q.As
  1252  			if a == obj.ANOP {
  1253  				i--
  1254  				continue
  1255  			}
  1256  
  1257  			if nofollow(a) || pushpop(a) {
  1258  				break // NOTE(rsc): arm does goto copy
  1259  			}
  1260  			if q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
  1261  				continue
  1262  			}
  1263  			if a == obj.ACALL || a == ALOOP {
  1264  				continue
  1265  			}
  1266  			for {
  1267  				if p.As == obj.ANOP {
  1268  					p = p.Link
  1269  					continue
  1270  				}
  1271  
  1272  				q = obj.Copyp(ctxt, p)
  1273  				p = p.Link
  1274  				q.Mark |= DONE
  1275  				(*last).Link = q
  1276  				*last = q
  1277  				if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
  1278  					continue
  1279  				}
  1280  
  1281  				q.As = relinv(q.As)
  1282  				p = q.Pcond
  1283  				q.Pcond = q.Link
  1284  				q.Link = p
  1285  				xfol(ctxt, q.Link, last)
  1286  				p = q.Link
  1287  				if p.Mark&DONE != 0 {
  1288  					return
  1289  				}
  1290  				goto loop
  1291  				/* */
  1292  			}
  1293  		}
  1294  		q = ctxt.NewProg()
  1295  		q.As = obj.AJMP
  1296  		q.Lineno = p.Lineno
  1297  		q.To.Type = obj.TYPE_BRANCH
  1298  		q.To.Offset = p.Pc
  1299  		q.Pcond = p
  1300  		p = q
  1301  	}
  1302  
  1303  	/* emit p */
  1304  	p.Mark |= DONE
  1305  
  1306  	(*last).Link = p
  1307  	*last = p
  1308  	a = p.As
  1309  
  1310  	/* continue loop with what comes after p */
  1311  	if nofollow(a) {
  1312  		return
  1313  	}
  1314  	if p.Pcond != nil && a != obj.ACALL {
  1315  		/*
  1316  		 * some kind of conditional branch.
  1317  		 * recurse to follow one path.
  1318  		 * continue loop on the other.
  1319  		 */
  1320  		q = obj.Brchain(ctxt, p.Pcond)
  1321  		if q != nil {
  1322  			p.Pcond = q
  1323  		}
  1324  		q = obj.Brchain(ctxt, p.Link)
  1325  		if q != nil {
  1326  			p.Link = q
  1327  		}
  1328  		if p.From.Type == obj.TYPE_CONST {
  1329  			if p.From.Offset == 1 {
  1330  				/*
  1331  				 * expect conditional jump to be taken.
  1332  				 * rewrite so that's the fall-through case.
  1333  				 */
  1334  				p.As = relinv(a)
  1335  
  1336  				q = p.Link
  1337  				p.Link = p.Pcond
  1338  				p.Pcond = q
  1339  			}
  1340  		} else {
  1341  			q = p.Link
  1342  			if q.Mark&DONE != 0 {
  1343  				if a != ALOOP {
  1344  					p.As = relinv(a)
  1345  					p.Link = p.Pcond
  1346  					p.Pcond = q
  1347  				}
  1348  			}
  1349  		}
  1350  
  1351  		xfol(ctxt, p.Link, last)
  1352  		if p.Pcond.Mark&DONE != 0 {
  1353  			return
  1354  		}
  1355  		p = p.Pcond
  1356  		goto loop
  1357  	}
  1358  
  1359  	p = p.Link
  1360  	goto loop
  1361  }
  1362  
  1363  var unaryDst = map[obj.As]bool{
  1364  	ABSWAPL:    true,
  1365  	ABSWAPQ:    true,
  1366  	ACMPXCHG8B: true,
  1367  	ADECB:      true,
  1368  	ADECL:      true,
  1369  	ADECQ:      true,
  1370  	ADECW:      true,
  1371  	AINCB:      true,
  1372  	AINCL:      true,
  1373  	AINCQ:      true,
  1374  	AINCW:      true,
  1375  	ANEGB:      true,
  1376  	ANEGL:      true,
  1377  	ANEGQ:      true,
  1378  	ANEGW:      true,
  1379  	ANOTB:      true,
  1380  	ANOTL:      true,
  1381  	ANOTQ:      true,
  1382  	ANOTW:      true,
  1383  	APOPL:      true,
  1384  	APOPQ:      true,
  1385  	APOPW:      true,
  1386  	ASETCC:     true,
  1387  	ASETCS:     true,
  1388  	ASETEQ:     true,
  1389  	ASETGE:     true,
  1390  	ASETGT:     true,
  1391  	ASETHI:     true,
  1392  	ASETLE:     true,
  1393  	ASETLS:     true,
  1394  	ASETLT:     true,
  1395  	ASETMI:     true,
  1396  	ASETNE:     true,
  1397  	ASETOC:     true,
  1398  	ASETOS:     true,
  1399  	ASETPC:     true,
  1400  	ASETPL:     true,
  1401  	ASETPS:     true,
  1402  	AFFREE:     true,
  1403  	AFLDENV:    true,
  1404  	AFSAVE:     true,
  1405  	AFSTCW:     true,
  1406  	AFSTENV:    true,
  1407  	AFSTSW:     true,
  1408  	AFXSAVE:    true,
  1409  	AFXSAVE64:  true,
  1410  	ASTMXCSR:   true,
  1411  }
  1412  
  1413  var Linkamd64 = obj.LinkArch{
  1414  	Arch:       sys.ArchAMD64,
  1415  	Preprocess: preprocess,
  1416  	Assemble:   span6,
  1417  	Follow:     follow,
  1418  	Progedit:   progedit,
  1419  	UnaryDst:   unaryDst,
  1420  }
  1421  
  1422  var Linkamd64p32 = obj.LinkArch{
  1423  	Arch:       sys.ArchAMD64P32,
  1424  	Preprocess: preprocess,
  1425  	Assemble:   span6,
  1426  	Follow:     follow,
  1427  	Progedit:   progedit,
  1428  	UnaryDst:   unaryDst,
  1429  }
  1430  
  1431  var Link386 = obj.LinkArch{
  1432  	Arch:       sys.Arch386,
  1433  	Preprocess: preprocess,
  1434  	Assemble:   span6,
  1435  	Follow:     follow,
  1436  	Progedit:   progedit,
  1437  	UnaryDst:   unaryDst,
  1438  }