github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/internal/obj/ppc64/obj9.go (about)

     1  // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
     2  //
     3  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     4  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     5  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     6  //	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
     7  //	Portions Copyright © 2004,2006 Bruce Ellis
     8  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
     9  //	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
    10  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
    11  //
    12  // Permission is hereby granted, free of charge, to any person obtaining a copy
    13  // of this software and associated documentation files (the "Software"), to deal
    14  // in the Software without restriction, including without limitation the rights
    15  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    16  // copies of the Software, and to permit persons to whom the Software is
    17  // furnished to do so, subject to the following conditions:
    18  //
    19  // The above copyright notice and this permission notice shall be included in
    20  // all copies or substantial portions of the Software.
    21  //
    22  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    23  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    24  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    25  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    26  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    27  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    28  // THE SOFTWARE.
    29  
    30  package ppc64
    31  
    32  import (
    33  	"github.com/gagliardetto/golang-go/cmd/internal/obj"
    34  	"github.com/gagliardetto/golang-go/cmd/internal/objabi"
    35  	"github.com/gagliardetto/golang-go/cmd/internal/sys"
    36  )
    37  
    38  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    39  	p.From.Class = 0
    40  	p.To.Class = 0
    41  
    42  	c := ctxt9{ctxt: ctxt, newprog: newprog}
    43  
    44  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
    45  	switch p.As {
    46  	case ABR,
    47  		ABL,
    48  		obj.ARET,
    49  		obj.ADUFFZERO,
    50  		obj.ADUFFCOPY:
    51  		if p.To.Sym != nil {
    52  			p.To.Type = obj.TYPE_BRANCH
    53  		}
    54  	}
    55  
    56  	// Rewrite float constants to values stored in memory.
    57  	switch p.As {
    58  	case AFMOVS:
    59  		if p.From.Type == obj.TYPE_FCONST {
    60  			f32 := float32(p.From.Val.(float64))
    61  			p.From.Type = obj.TYPE_MEM
    62  			p.From.Sym = ctxt.Float32Sym(f32)
    63  			p.From.Name = obj.NAME_EXTERN
    64  			p.From.Offset = 0
    65  		}
    66  
    67  	case AFMOVD:
    68  		if p.From.Type == obj.TYPE_FCONST {
    69  			f64 := p.From.Val.(float64)
    70  			// Constant not needed in memory for float +/- 0
    71  			if f64 != 0 {
    72  				p.From.Type = obj.TYPE_MEM
    73  				p.From.Sym = ctxt.Float64Sym(f64)
    74  				p.From.Name = obj.NAME_EXTERN
    75  				p.From.Offset = 0
    76  			}
    77  		}
    78  
    79  		// Put >32-bit constants in memory and load them
    80  	case AMOVD:
    81  		if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset {
    82  			p.From.Type = obj.TYPE_MEM
    83  			p.From.Sym = ctxt.Int64Sym(p.From.Offset)
    84  			p.From.Name = obj.NAME_EXTERN
    85  			p.From.Offset = 0
    86  		}
    87  	}
    88  
    89  	// Rewrite SUB constants into ADD.
    90  	switch p.As {
    91  	case ASUBC:
    92  		if p.From.Type == obj.TYPE_CONST {
    93  			p.From.Offset = -p.From.Offset
    94  			p.As = AADDC
    95  		}
    96  
    97  	case ASUBCCC:
    98  		if p.From.Type == obj.TYPE_CONST {
    99  			p.From.Offset = -p.From.Offset
   100  			p.As = AADDCCC
   101  		}
   102  
   103  	case ASUB:
   104  		if p.From.Type == obj.TYPE_CONST {
   105  			p.From.Offset = -p.From.Offset
   106  			p.As = AADD
   107  		}
   108  	}
   109  	if c.ctxt.Headtype == objabi.Haix {
   110  		c.rewriteToUseTOC(p)
   111  	} else if c.ctxt.Flag_dynlink {
   112  		c.rewriteToUseGot(p)
   113  	}
   114  }
   115  
   116  // Rewrite p, if necessary, to access a symbol using its TOC anchor.
   117  // This code is for AIX only.
   118  func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) {
   119  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   120  		return
   121  	}
   122  
   123  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   124  		// ADUFFZERO/ADUFFCOPY is considered as an ABL except in dynamic
   125  		// link where it should be an indirect call.
   126  		if !c.ctxt.Flag_dynlink {
   127  			return
   128  		}
   129  		//     ADUFFxxx $offset
   130  		// becomes
   131  		//     MOVD runtime.duffxxx@TOC, R12
   132  		//     ADD $offset, R12
   133  		//     MOVD R12, LR
   134  		//     BL (LR)
   135  		var sym *obj.LSym
   136  		if p.As == obj.ADUFFZERO {
   137  			sym = c.ctxt.Lookup("runtime.duffzero")
   138  		} else {
   139  			sym = c.ctxt.Lookup("runtime.duffcopy")
   140  		}
   141  		// Retrieve or create the TOC anchor.
   142  		symtoc := c.ctxt.LookupInit("TOC."+sym.Name, func(s *obj.LSym) {
   143  			s.Type = objabi.SDATA
   144  			s.Set(obj.AttrDuplicateOK, true)
   145  			c.ctxt.Data = append(c.ctxt.Data, s)
   146  			s.WriteAddr(c.ctxt, 0, 8, sym, 0)
   147  		})
   148  
   149  		offset := p.To.Offset
   150  		p.As = AMOVD
   151  		p.From.Type = obj.TYPE_MEM
   152  		p.From.Name = obj.NAME_TOCREF
   153  		p.From.Sym = symtoc
   154  		p.To.Type = obj.TYPE_REG
   155  		p.To.Reg = REG_R12
   156  		p.To.Name = obj.NAME_NONE
   157  		p.To.Offset = 0
   158  		p.To.Sym = nil
   159  		p1 := obj.Appendp(p, c.newprog)
   160  		p1.As = AADD
   161  		p1.From.Type = obj.TYPE_CONST
   162  		p1.From.Offset = offset
   163  		p1.To.Type = obj.TYPE_REG
   164  		p1.To.Reg = REG_R12
   165  		p2 := obj.Appendp(p1, c.newprog)
   166  		p2.As = AMOVD
   167  		p2.From.Type = obj.TYPE_REG
   168  		p2.From.Reg = REG_R12
   169  		p2.To.Type = obj.TYPE_REG
   170  		p2.To.Reg = REG_LR
   171  		p3 := obj.Appendp(p2, c.newprog)
   172  		p3.As = obj.ACALL
   173  		p3.To.Type = obj.TYPE_REG
   174  		p3.To.Reg = REG_LR
   175  	}
   176  
   177  	var source *obj.Addr
   178  	if p.From.Name == obj.NAME_EXTERN || p.From.Name == obj.NAME_STATIC {
   179  		if p.From.Type == obj.TYPE_ADDR {
   180  			if p.As == ADWORD {
   181  				// ADWORD $sym doesn't need TOC anchor
   182  				return
   183  			}
   184  			if p.As != AMOVD {
   185  				c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v", p)
   186  				return
   187  			}
   188  			if p.To.Type != obj.TYPE_REG {
   189  				c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v", p)
   190  				return
   191  			}
   192  		} else if p.From.Type != obj.TYPE_MEM {
   193  			c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
   194  			return
   195  		}
   196  		source = &p.From
   197  
   198  	} else if p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC {
   199  		if p.To.Type != obj.TYPE_MEM {
   200  			c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
   201  			return
   202  		}
   203  		if source != nil {
   204  			c.ctxt.Diag("cannot handle symbols on both sides in %v", p)
   205  			return
   206  		}
   207  		source = &p.To
   208  	} else {
   209  		return
   210  
   211  	}
   212  
   213  	if source.Sym == nil {
   214  		c.ctxt.Diag("do not know how to handle nil symbol in %v", p)
   215  		return
   216  	}
   217  
   218  	if source.Sym.Type == objabi.STLSBSS {
   219  		return
   220  	}
   221  
   222  	// Retrieve or create the TOC anchor.
   223  	symtoc := c.ctxt.LookupInit("TOC."+source.Sym.Name, func(s *obj.LSym) {
   224  		s.Type = objabi.SDATA
   225  		s.Set(obj.AttrDuplicateOK, true)
   226  		c.ctxt.Data = append(c.ctxt.Data, s)
   227  		s.WriteAddr(c.ctxt, 0, 8, source.Sym, 0)
   228  	})
   229  
   230  	if source.Type == obj.TYPE_ADDR {
   231  		// MOVD $sym, Rx becomes MOVD symtoc, Rx
   232  		// MOVD $sym+<off>, Rx becomes MOVD symtoc, Rx; ADD <off>, Rx
   233  		p.From.Type = obj.TYPE_MEM
   234  		p.From.Sym = symtoc
   235  		p.From.Name = obj.NAME_TOCREF
   236  
   237  		if p.From.Offset != 0 {
   238  			q := obj.Appendp(p, c.newprog)
   239  			q.As = AADD
   240  			q.From.Type = obj.TYPE_CONST
   241  			q.From.Offset = p.From.Offset
   242  			p.From.Offset = 0
   243  			q.To = p.To
   244  		}
   245  		return
   246  
   247  	}
   248  
   249  	// MOVx sym, Ry becomes MOVD symtoc, REGTMP; MOVx (REGTMP), Ry
   250  	// MOVx Ry, sym becomes MOVD symtoc, REGTMP; MOVx Ry, (REGTMP)
   251  	// An addition may be inserted between the two MOVs if there is an offset.
   252  
   253  	q := obj.Appendp(p, c.newprog)
   254  	q.As = AMOVD
   255  	q.From.Type = obj.TYPE_MEM
   256  	q.From.Sym = symtoc
   257  	q.From.Name = obj.NAME_TOCREF
   258  	q.To.Type = obj.TYPE_REG
   259  	q.To.Reg = REGTMP
   260  
   261  	q = obj.Appendp(q, c.newprog)
   262  	q.As = p.As
   263  	q.From = p.From
   264  	q.To = p.To
   265  	if p.From.Name != obj.NAME_NONE {
   266  		q.From.Type = obj.TYPE_MEM
   267  		q.From.Reg = REGTMP
   268  		q.From.Name = obj.NAME_NONE
   269  		q.From.Sym = nil
   270  	} else if p.To.Name != obj.NAME_NONE {
   271  		q.To.Type = obj.TYPE_MEM
   272  		q.To.Reg = REGTMP
   273  		q.To.Name = obj.NAME_NONE
   274  		q.To.Sym = nil
   275  	} else {
   276  		c.ctxt.Diag("unreachable case in rewriteToUseTOC with %v", p)
   277  	}
   278  
   279  	obj.Nopout(p)
   280  }
   281  
   282  // Rewrite p, if necessary, to access global data via the global offset table.
   283  func (c *ctxt9) rewriteToUseGot(p *obj.Prog) {
   284  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   285  		//     ADUFFxxx $offset
   286  		// becomes
   287  		//     MOVD runtime.duffxxx@GOT, R12
   288  		//     ADD $offset, R12
   289  		//     MOVD R12, LR
   290  		//     BL (LR)
   291  		var sym *obj.LSym
   292  		if p.As == obj.ADUFFZERO {
   293  			sym = c.ctxt.Lookup("runtime.duffzero")
   294  		} else {
   295  			sym = c.ctxt.Lookup("runtime.duffcopy")
   296  		}
   297  		offset := p.To.Offset
   298  		p.As = AMOVD
   299  		p.From.Type = obj.TYPE_MEM
   300  		p.From.Name = obj.NAME_GOTREF
   301  		p.From.Sym = sym
   302  		p.To.Type = obj.TYPE_REG
   303  		p.To.Reg = REG_R12
   304  		p.To.Name = obj.NAME_NONE
   305  		p.To.Offset = 0
   306  		p.To.Sym = nil
   307  		p1 := obj.Appendp(p, c.newprog)
   308  		p1.As = AADD
   309  		p1.From.Type = obj.TYPE_CONST
   310  		p1.From.Offset = offset
   311  		p1.To.Type = obj.TYPE_REG
   312  		p1.To.Reg = REG_R12
   313  		p2 := obj.Appendp(p1, c.newprog)
   314  		p2.As = AMOVD
   315  		p2.From.Type = obj.TYPE_REG
   316  		p2.From.Reg = REG_R12
   317  		p2.To.Type = obj.TYPE_REG
   318  		p2.To.Reg = REG_LR
   319  		p3 := obj.Appendp(p2, c.newprog)
   320  		p3.As = obj.ACALL
   321  		p3.To.Type = obj.TYPE_REG
   322  		p3.To.Reg = REG_LR
   323  	}
   324  
   325  	// We only care about global data: NAME_EXTERN means a global
   326  	// symbol in the Go sense, and p.Sym.Local is true for a few
   327  	// internally defined symbols.
   328  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   329  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   330  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   331  		if p.As != AMOVD {
   332  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   333  		}
   334  		if p.To.Type != obj.TYPE_REG {
   335  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   336  		}
   337  		p.From.Type = obj.TYPE_MEM
   338  		p.From.Name = obj.NAME_GOTREF
   339  		if p.From.Offset != 0 {
   340  			q := obj.Appendp(p, c.newprog)
   341  			q.As = AADD
   342  			q.From.Type = obj.TYPE_CONST
   343  			q.From.Offset = p.From.Offset
   344  			q.To = p.To
   345  			p.From.Offset = 0
   346  		}
   347  	}
   348  	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   349  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   350  	}
   351  	var source *obj.Addr
   352  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   353  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP)
   354  	// An addition may be inserted between the two MOVs if there is an offset.
   355  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   356  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   357  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   358  		}
   359  		source = &p.From
   360  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   361  		source = &p.To
   362  	} else {
   363  		return
   364  	}
   365  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   366  		return
   367  	}
   368  	if source.Sym.Type == objabi.STLSBSS {
   369  		return
   370  	}
   371  	if source.Type != obj.TYPE_MEM {
   372  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   373  	}
   374  	p1 := obj.Appendp(p, c.newprog)
   375  	p2 := obj.Appendp(p1, c.newprog)
   376  
   377  	p1.As = AMOVD
   378  	p1.From.Type = obj.TYPE_MEM
   379  	p1.From.Sym = source.Sym
   380  	p1.From.Name = obj.NAME_GOTREF
   381  	p1.To.Type = obj.TYPE_REG
   382  	p1.To.Reg = REGTMP
   383  
   384  	p2.As = p.As
   385  	p2.From = p.From
   386  	p2.To = p.To
   387  	if p.From.Name == obj.NAME_EXTERN {
   388  		p2.From.Reg = REGTMP
   389  		p2.From.Name = obj.NAME_NONE
   390  		p2.From.Sym = nil
   391  	} else if p.To.Name == obj.NAME_EXTERN {
   392  		p2.To.Reg = REGTMP
   393  		p2.To.Name = obj.NAME_NONE
   394  		p2.To.Sym = nil
   395  	} else {
   396  		return
   397  	}
   398  	obj.Nopout(p)
   399  }
   400  
   401  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   402  	// TODO(minux): add morestack short-cuts with small fixed frame-size.
   403  	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
   404  		return
   405  	}
   406  
   407  	c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog}
   408  
   409  	p := c.cursym.Func.Text
   410  	textstksiz := p.To.Offset
   411  	if textstksiz == -8 {
   412  		// Compatibility hack.
   413  		p.From.Sym.Set(obj.AttrNoFrame, true)
   414  		textstksiz = 0
   415  	}
   416  	if textstksiz%8 != 0 {
   417  		c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
   418  	}
   419  	if p.From.Sym.NoFrame() {
   420  		if textstksiz != 0 {
   421  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   422  		}
   423  	}
   424  
   425  	c.cursym.Func.Args = p.To.Val.(int32)
   426  	c.cursym.Func.Locals = int32(textstksiz)
   427  
   428  	/*
   429  	 * find leaf subroutines
   430  	 * strip NOPs
   431  	 * expand RET
   432  	 * expand BECOME pseudo
   433  	 */
   434  
   435  	var q *obj.Prog
   436  	var q1 *obj.Prog
   437  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   438  		switch p.As {
   439  		/* too hard, just leave alone */
   440  		case obj.ATEXT:
   441  			q = p
   442  
   443  			p.Mark |= LABEL | LEAF | SYNC
   444  			if p.Link != nil {
   445  				p.Link.Mark |= LABEL
   446  			}
   447  
   448  		case ANOR:
   449  			q = p
   450  			if p.To.Type == obj.TYPE_REG {
   451  				if p.To.Reg == REGZERO {
   452  					p.Mark |= LABEL | SYNC
   453  				}
   454  			}
   455  
   456  		case ALWAR,
   457  			ALBAR,
   458  			ASTBCCC,
   459  			ASTWCCC,
   460  			AECIWX,
   461  			AECOWX,
   462  			AEIEIO,
   463  			AICBI,
   464  			AISYNC,
   465  			ATLBIE,
   466  			ATLBIEL,
   467  			ASLBIA,
   468  			ASLBIE,
   469  			ASLBMFEE,
   470  			ASLBMFEV,
   471  			ASLBMTE,
   472  			ADCBF,
   473  			ADCBI,
   474  			ADCBST,
   475  			ADCBT,
   476  			ADCBTST,
   477  			ADCBZ,
   478  			ASYNC,
   479  			ATLBSYNC,
   480  			APTESYNC,
   481  			ALWSYNC,
   482  			ATW,
   483  			AWORD,
   484  			ARFI,
   485  			ARFCI,
   486  			ARFID,
   487  			AHRFID:
   488  			q = p
   489  			p.Mark |= LABEL | SYNC
   490  			continue
   491  
   492  		case AMOVW, AMOVWZ, AMOVD:
   493  			q = p
   494  			if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
   495  				p.Mark |= LABEL | SYNC
   496  			}
   497  			continue
   498  
   499  		case AFABS,
   500  			AFABSCC,
   501  			AFADD,
   502  			AFADDCC,
   503  			AFCTIW,
   504  			AFCTIWCC,
   505  			AFCTIWZ,
   506  			AFCTIWZCC,
   507  			AFDIV,
   508  			AFDIVCC,
   509  			AFMADD,
   510  			AFMADDCC,
   511  			AFMOVD,
   512  			AFMOVDU,
   513  			/* case AFMOVDS: */
   514  			AFMOVS,
   515  			AFMOVSU,
   516  
   517  			/* case AFMOVSD: */
   518  			AFMSUB,
   519  			AFMSUBCC,
   520  			AFMUL,
   521  			AFMULCC,
   522  			AFNABS,
   523  			AFNABSCC,
   524  			AFNEG,
   525  			AFNEGCC,
   526  			AFNMADD,
   527  			AFNMADDCC,
   528  			AFNMSUB,
   529  			AFNMSUBCC,
   530  			AFRSP,
   531  			AFRSPCC,
   532  			AFSUB,
   533  			AFSUBCC:
   534  			q = p
   535  
   536  			p.Mark |= FLOAT
   537  			continue
   538  
   539  		case ABL,
   540  			ABCL,
   541  			obj.ADUFFZERO,
   542  			obj.ADUFFCOPY:
   543  			c.cursym.Func.Text.Mark &^= LEAF
   544  			fallthrough
   545  
   546  		case ABC,
   547  			ABEQ,
   548  			ABGE,
   549  			ABGT,
   550  			ABLE,
   551  			ABLT,
   552  			ABNE,
   553  			ABR,
   554  			ABVC,
   555  			ABVS:
   556  			p.Mark |= BRANCH
   557  			q = p
   558  			q1 = p.Pcond
   559  			if q1 != nil {
   560  				for q1.As == obj.ANOP {
   561  					q1 = q1.Link
   562  					p.Pcond = q1
   563  				}
   564  
   565  				if q1.Mark&LEAF == 0 {
   566  					q1.Mark |= LABEL
   567  				}
   568  			} else {
   569  				p.Mark |= LABEL
   570  			}
   571  			q1 = p.Link
   572  			if q1 != nil {
   573  				q1.Mark |= LABEL
   574  			}
   575  			continue
   576  
   577  		case AFCMPO, AFCMPU:
   578  			q = p
   579  			p.Mark |= FCMP | FLOAT
   580  			continue
   581  
   582  		case obj.ARET:
   583  			q = p
   584  			if p.Link != nil {
   585  				p.Link.Mark |= LABEL
   586  			}
   587  			continue
   588  
   589  		case obj.ANOP:
   590  			q1 = p.Link
   591  			q.Link = q1 /* q is non-nop */
   592  			q1.Mark |= p.Mark
   593  			continue
   594  
   595  		default:
   596  			q = p
   597  			continue
   598  		}
   599  	}
   600  
   601  	autosize := int32(0)
   602  	var p1 *obj.Prog
   603  	var p2 *obj.Prog
   604  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   605  		o := p.As
   606  		switch o {
   607  		case obj.ATEXT:
   608  			autosize = int32(textstksiz)
   609  
   610  			if p.Mark&LEAF != 0 && autosize == 0 {
   611  				// A leaf function with no locals has no frame.
   612  				p.From.Sym.Set(obj.AttrNoFrame, true)
   613  			}
   614  
   615  			if !p.From.Sym.NoFrame() {
   616  				// If there is a stack frame at all, it includes
   617  				// space to save the LR.
   618  				autosize += int32(c.ctxt.FixedFrameSize())
   619  			}
   620  
   621  			if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
   622  				// A leaf function with a small stack can be marked
   623  				// NOSPLIT, avoiding a stack check.
   624  				p.From.Sym.Set(obj.AttrNoSplit, true)
   625  			}
   626  
   627  			p.To.Offset = int64(autosize)
   628  
   629  			q = p
   630  
   631  			if c.ctxt.Flag_shared && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" {
   632  				// When compiling Go into PIC, all functions must start
   633  				// with instructions to load the TOC pointer into r2:
   634  				//
   635  				//	addis r2, r12, .TOC.-func@ha
   636  				//	addi r2, r2, .TOC.-func@l+4
   637  				//
   638  				// We could probably skip this prologue in some situations
   639  				// but it's a bit subtle. However, it is both safe and
   640  				// necessary to leave the prologue off duffzero and
   641  				// duffcopy as we rely on being able to jump to a specific
   642  				// instruction offset for them.
   643  				//
   644  				// These are AWORDS because there is no (afaict) way to
   645  				// generate the addis instruction except as part of the
   646  				// load of a large constant, and in that case there is no
   647  				// way to use r12 as the source.
   648  				//
   649  				// Note that the same condition is tested in
   650  				// putelfsym in cmd/link/internal/ld/symtab.go
   651  				// where we set the st_other field to indicate
   652  				// the presence of these instructions.
   653  				q = obj.Appendp(q, c.newprog)
   654  				q.As = AWORD
   655  				q.Pos = p.Pos
   656  				q.From.Type = obj.TYPE_CONST
   657  				q.From.Offset = 0x3c4c0000
   658  				q = obj.Appendp(q, c.newprog)
   659  				q.As = AWORD
   660  				q.Pos = p.Pos
   661  				q.From.Type = obj.TYPE_CONST
   662  				q.From.Offset = 0x38420000
   663  				rel := obj.Addrel(c.cursym)
   664  				rel.Off = 0
   665  				rel.Siz = 8
   666  				rel.Sym = c.ctxt.Lookup(".TOC.")
   667  				rel.Type = objabi.R_ADDRPOWER_PCREL
   668  			}
   669  
   670  			if !c.cursym.Func.Text.From.Sym.NoSplit() {
   671  				q = c.stacksplit(q, autosize) // emit split check
   672  			}
   673  
   674  			// Special handling of the racecall thunk. Assume that its asm code will
   675  			// save the link register and update the stack, since that code is
   676  			// called directly from C/C++ and can't clobber REGTMP (R31).
   677  			if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" {
   678  				// Save the link register and update the SP.  MOVDU is used unless
   679  				// the frame size is too large.  The link register must be saved
   680  				// even for non-empty leaf functions so that traceback works.
   681  				if autosize >= -BIG && autosize <= BIG {
   682  					// Use MOVDU to adjust R1 when saving R31, if autosize is small.
   683  					q = obj.Appendp(q, c.newprog)
   684  					q.As = AMOVD
   685  					q.Pos = p.Pos
   686  					q.From.Type = obj.TYPE_REG
   687  					q.From.Reg = REG_LR
   688  					q.To.Type = obj.TYPE_REG
   689  					q.To.Reg = REGTMP
   690  
   691  					q = obj.Appendp(q, c.newprog)
   692  					q.As = AMOVDU
   693  					q.Pos = p.Pos
   694  					q.From.Type = obj.TYPE_REG
   695  					q.From.Reg = REGTMP
   696  					q.To.Type = obj.TYPE_MEM
   697  					q.To.Offset = int64(-autosize)
   698  					q.To.Reg = REGSP
   699  					q.Spadj = autosize
   700  				} else {
   701  					// Frame size is too large for a MOVDU instruction.
   702  					// Store link register before decrementing SP, so if a signal comes
   703  					// during the execution of the function prologue, the traceback
   704  					// code will not see a half-updated stack frame.
   705  					// This sequence is not async preemptible, as if we open a frame
   706  					// at the current SP, it will clobber the saved LR.
   707  					q = obj.Appendp(q, c.newprog)
   708  					q.As = AMOVD
   709  					q.Pos = p.Pos
   710  					q.From.Type = obj.TYPE_REG
   711  					q.From.Reg = REG_LR
   712  					q.To.Type = obj.TYPE_REG
   713  					q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
   714  
   715  					q = c.ctxt.StartUnsafePoint(q, c.newprog)
   716  
   717  					q = obj.Appendp(q, c.newprog)
   718  					q.As = AMOVD
   719  					q.Pos = p.Pos
   720  					q.From.Type = obj.TYPE_REG
   721  					q.From.Reg = REG_R29
   722  					q.To.Type = obj.TYPE_MEM
   723  					q.To.Offset = int64(-autosize)
   724  					q.To.Reg = REGSP
   725  
   726  					q = obj.Appendp(q, c.newprog)
   727  					q.As = AADD
   728  					q.Pos = p.Pos
   729  					q.From.Type = obj.TYPE_CONST
   730  					q.From.Offset = int64(-autosize)
   731  					q.To.Type = obj.TYPE_REG
   732  					q.To.Reg = REGSP
   733  					q.Spadj = +autosize
   734  
   735  					q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
   736  
   737  				}
   738  			} else if c.cursym.Func.Text.Mark&LEAF == 0 {
   739  				// A very few functions that do not return to their caller
   740  				// (e.g. gogo) are not identified as leaves but still have
   741  				// no frame.
   742  				c.cursym.Func.Text.Mark |= LEAF
   743  			}
   744  
   745  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   746  				c.cursym.Set(obj.AttrLeaf, true)
   747  				break
   748  			}
   749  
   750  			if c.ctxt.Flag_shared {
   751  				q = obj.Appendp(q, c.newprog)
   752  				q.As = AMOVD
   753  				q.Pos = p.Pos
   754  				q.From.Type = obj.TYPE_REG
   755  				q.From.Reg = REG_R2
   756  				q.To.Type = obj.TYPE_MEM
   757  				q.To.Reg = REGSP
   758  				q.To.Offset = 24
   759  			}
   760  
   761  			if c.cursym.Func.Text.From.Sym.Wrapper() {
   762  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   763  				//
   764  				//	MOVD g_panic(g), R3
   765  				//	CMP R0, R3
   766  				//	BEQ end
   767  				//	MOVD panic_argp(R3), R4
   768  				//	ADD $(autosize+8), R1, R5
   769  				//	CMP R4, R5
   770  				//	BNE end
   771  				//	ADD $8, R1, R6
   772  				//	MOVD R6, panic_argp(R3)
   773  				// end:
   774  				//	NOP
   775  				//
   776  				// The NOP is needed to give the jumps somewhere to land.
   777  				// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
   778  
   779  				q = obj.Appendp(q, c.newprog)
   780  
   781  				q.As = AMOVD
   782  				q.From.Type = obj.TYPE_MEM
   783  				q.From.Reg = REGG
   784  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   785  				q.To.Type = obj.TYPE_REG
   786  				q.To.Reg = REG_R3
   787  
   788  				q = obj.Appendp(q, c.newprog)
   789  				q.As = ACMP
   790  				q.From.Type = obj.TYPE_REG
   791  				q.From.Reg = REG_R0
   792  				q.To.Type = obj.TYPE_REG
   793  				q.To.Reg = REG_R3
   794  
   795  				q = obj.Appendp(q, c.newprog)
   796  				q.As = ABEQ
   797  				q.To.Type = obj.TYPE_BRANCH
   798  				p1 = q
   799  
   800  				q = obj.Appendp(q, c.newprog)
   801  				q.As = AMOVD
   802  				q.From.Type = obj.TYPE_MEM
   803  				q.From.Reg = REG_R3
   804  				q.From.Offset = 0 // Panic.argp
   805  				q.To.Type = obj.TYPE_REG
   806  				q.To.Reg = REG_R4
   807  
   808  				q = obj.Appendp(q, c.newprog)
   809  				q.As = AADD
   810  				q.From.Type = obj.TYPE_CONST
   811  				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
   812  				q.Reg = REGSP
   813  				q.To.Type = obj.TYPE_REG
   814  				q.To.Reg = REG_R5
   815  
   816  				q = obj.Appendp(q, c.newprog)
   817  				q.As = ACMP
   818  				q.From.Type = obj.TYPE_REG
   819  				q.From.Reg = REG_R4
   820  				q.To.Type = obj.TYPE_REG
   821  				q.To.Reg = REG_R5
   822  
   823  				q = obj.Appendp(q, c.newprog)
   824  				q.As = ABNE
   825  				q.To.Type = obj.TYPE_BRANCH
   826  				p2 = q
   827  
   828  				q = obj.Appendp(q, c.newprog)
   829  				q.As = AADD
   830  				q.From.Type = obj.TYPE_CONST
   831  				q.From.Offset = c.ctxt.FixedFrameSize()
   832  				q.Reg = REGSP
   833  				q.To.Type = obj.TYPE_REG
   834  				q.To.Reg = REG_R6
   835  
   836  				q = obj.Appendp(q, c.newprog)
   837  				q.As = AMOVD
   838  				q.From.Type = obj.TYPE_REG
   839  				q.From.Reg = REG_R6
   840  				q.To.Type = obj.TYPE_MEM
   841  				q.To.Reg = REG_R3
   842  				q.To.Offset = 0 // Panic.argp
   843  
   844  				q = obj.Appendp(q, c.newprog)
   845  
   846  				q.As = obj.ANOP
   847  				p1.Pcond = q
   848  				p2.Pcond = q
   849  			}
   850  
   851  		case obj.ARET:
   852  			if p.From.Type == obj.TYPE_CONST {
   853  				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   854  				break
   855  			}
   856  
   857  			retTarget := p.To.Sym
   858  
   859  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   860  				if autosize == 0 || c.cursym.Name == "runtime.racecallbackthunk" {
   861  					p.As = ABR
   862  					p.From = obj.Addr{}
   863  					if retTarget == nil {
   864  						p.To.Type = obj.TYPE_REG
   865  						p.To.Reg = REG_LR
   866  					} else {
   867  						p.To.Type = obj.TYPE_BRANCH
   868  						p.To.Sym = retTarget
   869  					}
   870  					p.Mark |= BRANCH
   871  					break
   872  				}
   873  
   874  				p.As = AADD
   875  				p.From.Type = obj.TYPE_CONST
   876  				p.From.Offset = int64(autosize)
   877  				p.To.Type = obj.TYPE_REG
   878  				p.To.Reg = REGSP
   879  				p.Spadj = -autosize
   880  
   881  				q = c.newprog()
   882  				q.As = ABR
   883  				q.Pos = p.Pos
   884  				q.To.Type = obj.TYPE_REG
   885  				q.To.Reg = REG_LR
   886  				q.Mark |= BRANCH
   887  				q.Spadj = +autosize
   888  
   889  				q.Link = p.Link
   890  				p.Link = q
   891  				break
   892  			}
   893  
   894  			p.As = AMOVD
   895  			p.From.Type = obj.TYPE_MEM
   896  			p.From.Offset = 0
   897  			p.From.Reg = REGSP
   898  			p.To.Type = obj.TYPE_REG
   899  			p.To.Reg = REGTMP
   900  
   901  			q = c.newprog()
   902  			q.As = AMOVD
   903  			q.Pos = p.Pos
   904  			q.From.Type = obj.TYPE_REG
   905  			q.From.Reg = REGTMP
   906  			q.To.Type = obj.TYPE_REG
   907  			q.To.Reg = REG_LR
   908  
   909  			q.Link = p.Link
   910  			p.Link = q
   911  			p = q
   912  
   913  			if false {
   914  				// Debug bad returns
   915  				q = c.newprog()
   916  
   917  				q.As = AMOVD
   918  				q.Pos = p.Pos
   919  				q.From.Type = obj.TYPE_MEM
   920  				q.From.Offset = 0
   921  				q.From.Reg = REGTMP
   922  				q.To.Type = obj.TYPE_REG
   923  				q.To.Reg = REGTMP
   924  
   925  				q.Link = p.Link
   926  				p.Link = q
   927  				p = q
   928  			}
   929  			prev := p
   930  			if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" {
   931  				q = c.newprog()
   932  				q.As = AADD
   933  				q.Pos = p.Pos
   934  				q.From.Type = obj.TYPE_CONST
   935  				q.From.Offset = int64(autosize)
   936  				q.To.Type = obj.TYPE_REG
   937  				q.To.Reg = REGSP
   938  				q.Spadj = -autosize
   939  
   940  				q.Link = p.Link
   941  				prev.Link = q
   942  				prev = q
   943  			}
   944  
   945  			q1 = c.newprog()
   946  			q1.As = ABR
   947  			q1.Pos = p.Pos
   948  			if retTarget == nil {
   949  				q1.To.Type = obj.TYPE_REG
   950  				q1.To.Reg = REG_LR
   951  			} else {
   952  				q1.To.Type = obj.TYPE_BRANCH
   953  				q1.To.Sym = retTarget
   954  			}
   955  			q1.Mark |= BRANCH
   956  			q1.Spadj = +autosize
   957  
   958  			q1.Link = q.Link
   959  			prev.Link = q1
   960  		case AADD:
   961  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   962  				p.Spadj = int32(-p.From.Offset)
   963  			}
   964  		case AMOVDU:
   965  			if p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
   966  				p.Spadj = int32(-p.To.Offset)
   967  			}
   968  			if p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP {
   969  				p.Spadj = int32(-p.From.Offset)
   970  			}
   971  		case obj.AGETCALLERPC:
   972  			if cursym.Leaf() {
   973  				/* MOVD LR, Rd */
   974  				p.As = AMOVD
   975  				p.From.Type = obj.TYPE_REG
   976  				p.From.Reg = REG_LR
   977  			} else {
   978  				/* MOVD (RSP), Rd */
   979  				p.As = AMOVD
   980  				p.From.Type = obj.TYPE_MEM
   981  				p.From.Reg = REGSP
   982  			}
   983  		}
   984  	}
   985  }
   986  
   987  /*
   988  // instruction scheduling
   989  	if(debug['Q'] == 0)
   990  		return;
   991  
   992  	curtext = nil;
   993  	q = nil;	// p - 1
   994  	q1 = firstp;	// top of block
   995  	o = 0;		// count of instructions
   996  	for(p = firstp; p != nil; p = p1) {
   997  		p1 = p->link;
   998  		o++;
   999  		if(p->mark & NOSCHED){
  1000  			if(q1 != p){
  1001  				sched(q1, q);
  1002  			}
  1003  			for(; p != nil; p = p->link){
  1004  				if(!(p->mark & NOSCHED))
  1005  					break;
  1006  				q = p;
  1007  			}
  1008  			p1 = p;
  1009  			q1 = p;
  1010  			o = 0;
  1011  			continue;
  1012  		}
  1013  		if(p->mark & (LABEL|SYNC)) {
  1014  			if(q1 != p)
  1015  				sched(q1, q);
  1016  			q1 = p;
  1017  			o = 1;
  1018  		}
  1019  		if(p->mark & (BRANCH|SYNC)) {
  1020  			sched(q1, p);
  1021  			q1 = p1;
  1022  			o = 0;
  1023  		}
  1024  		if(o >= NSCHED) {
  1025  			sched(q1, p);
  1026  			q1 = p1;
  1027  			o = 0;
  1028  		}
  1029  		q = p;
  1030  	}
  1031  */
  1032  func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
  1033  	p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode
  1034  
  1035  	// MOVD	g_stackguard(g), R3
  1036  	p = obj.Appendp(p, c.newprog)
  1037  
  1038  	p.As = AMOVD
  1039  	p.From.Type = obj.TYPE_MEM
  1040  	p.From.Reg = REGG
  1041  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
  1042  	if c.cursym.CFunc() {
  1043  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
  1044  	}
  1045  	p.To.Type = obj.TYPE_REG
  1046  	p.To.Reg = REG_R3
  1047  
  1048  	// Mark the stack bound check and morestack call async nonpreemptible.
  1049  	// If we get preempted here, when resumed the preemption request is
  1050  	// cleared, but we'll still call morestack, which will double the stack
  1051  	// unnecessarily. See issue #35470.
  1052  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
  1053  
  1054  	var q *obj.Prog
  1055  	if framesize <= objabi.StackSmall {
  1056  		// small stack: SP < stackguard
  1057  		//	CMP	stackguard, SP
  1058  		p = obj.Appendp(p, c.newprog)
  1059  
  1060  		p.As = ACMPU
  1061  		p.From.Type = obj.TYPE_REG
  1062  		p.From.Reg = REG_R3
  1063  		p.To.Type = obj.TYPE_REG
  1064  		p.To.Reg = REGSP
  1065  	} else if framesize <= objabi.StackBig {
  1066  		// large stack: SP-framesize < stackguard-StackSmall
  1067  		//	ADD $-(framesize-StackSmall), SP, R4
  1068  		//	CMP stackguard, R4
  1069  		p = obj.Appendp(p, c.newprog)
  1070  
  1071  		p.As = AADD
  1072  		p.From.Type = obj.TYPE_CONST
  1073  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
  1074  		p.Reg = REGSP
  1075  		p.To.Type = obj.TYPE_REG
  1076  		p.To.Reg = REG_R4
  1077  
  1078  		p = obj.Appendp(p, c.newprog)
  1079  		p.As = ACMPU
  1080  		p.From.Type = obj.TYPE_REG
  1081  		p.From.Reg = REG_R3
  1082  		p.To.Type = obj.TYPE_REG
  1083  		p.To.Reg = REG_R4
  1084  	} else {
  1085  		// Such a large stack we need to protect against wraparound.
  1086  		// If SP is close to zero:
  1087  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
  1088  		// The +StackGuard on both sides is required to keep the left side positive:
  1089  		// SP is allowed to be slightly below stackguard. See stack.h.
  1090  		//
  1091  		// Preemption sets stackguard to StackPreempt, a very large value.
  1092  		// That breaks the math above, so we have to check for that explicitly.
  1093  		//	// stackguard is R3
  1094  		//	CMP	R3, $StackPreempt
  1095  		//	BEQ	label-of-call-to-morestack
  1096  		//	ADD	$StackGuard, SP, R4
  1097  		//	SUB	R3, R4
  1098  		//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
  1099  		//	CMPU	R31, R4
  1100  		p = obj.Appendp(p, c.newprog)
  1101  
  1102  		p.As = ACMP
  1103  		p.From.Type = obj.TYPE_REG
  1104  		p.From.Reg = REG_R3
  1105  		p.To.Type = obj.TYPE_CONST
  1106  		p.To.Offset = objabi.StackPreempt
  1107  
  1108  		p = obj.Appendp(p, c.newprog)
  1109  		q = p
  1110  		p.As = ABEQ
  1111  		p.To.Type = obj.TYPE_BRANCH
  1112  
  1113  		p = obj.Appendp(p, c.newprog)
  1114  		p.As = AADD
  1115  		p.From.Type = obj.TYPE_CONST
  1116  		p.From.Offset = int64(objabi.StackGuard)
  1117  		p.Reg = REGSP
  1118  		p.To.Type = obj.TYPE_REG
  1119  		p.To.Reg = REG_R4
  1120  
  1121  		p = obj.Appendp(p, c.newprog)
  1122  		p.As = ASUB
  1123  		p.From.Type = obj.TYPE_REG
  1124  		p.From.Reg = REG_R3
  1125  		p.To.Type = obj.TYPE_REG
  1126  		p.To.Reg = REG_R4
  1127  
  1128  		p = obj.Appendp(p, c.newprog)
  1129  		p.As = AMOVD
  1130  		p.From.Type = obj.TYPE_CONST
  1131  		p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
  1132  		p.To.Type = obj.TYPE_REG
  1133  		p.To.Reg = REGTMP
  1134  
  1135  		p = obj.Appendp(p, c.newprog)
  1136  		p.As = ACMPU
  1137  		p.From.Type = obj.TYPE_REG
  1138  		p.From.Reg = REGTMP
  1139  		p.To.Type = obj.TYPE_REG
  1140  		p.To.Reg = REG_R4
  1141  	}
  1142  
  1143  	// q1: BLT	done
  1144  	p = obj.Appendp(p, c.newprog)
  1145  	q1 := p
  1146  
  1147  	p.As = ABLT
  1148  	p.To.Type = obj.TYPE_BRANCH
  1149  
  1150  	// MOVD	LR, R5
  1151  	p = obj.Appendp(p, c.newprog)
  1152  
  1153  	p.As = AMOVD
  1154  	p.From.Type = obj.TYPE_REG
  1155  	p.From.Reg = REG_LR
  1156  	p.To.Type = obj.TYPE_REG
  1157  	p.To.Reg = REG_R5
  1158  	if q != nil {
  1159  		q.Pcond = p
  1160  	}
  1161  
  1162  	p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
  1163  
  1164  	var morestacksym *obj.LSym
  1165  	if c.cursym.CFunc() {
  1166  		morestacksym = c.ctxt.Lookup("runtime.morestackc")
  1167  	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
  1168  		morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt")
  1169  	} else {
  1170  		morestacksym = c.ctxt.Lookup("runtime.morestack")
  1171  	}
  1172  
  1173  	if c.ctxt.Flag_shared {
  1174  		// In PPC64 PIC code, R2 is used as TOC pointer derived from R12
  1175  		// which is the address of function entry point when entering
  1176  		// the function. We need to preserve R2 across call to morestack.
  1177  		// Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
  1178  		// the caller's frame, but not used (0(SP) is caller's saved LR,
  1179  		// 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
  1180  
  1181  		// MOVD R12, 8(SP)
  1182  		p = obj.Appendp(p, c.newprog)
  1183  		p.As = AMOVD
  1184  		p.From.Type = obj.TYPE_REG
  1185  		p.From.Reg = REG_R2
  1186  		p.To.Type = obj.TYPE_MEM
  1187  		p.To.Reg = REGSP
  1188  		p.To.Offset = 8
  1189  	}
  1190  
  1191  	if c.ctxt.Flag_dynlink {
  1192  		// Avoid calling morestack via a PLT when dynamically linking. The
  1193  		// PLT stubs generated by the system linker on ppc64le when "std r2,
  1194  		// 24(r1)" to save the TOC pointer in their callers stack
  1195  		// frame. Unfortunately (and necessarily) morestack is called before
  1196  		// the function that calls it sets up its frame and so the PLT ends
  1197  		// up smashing the saved TOC pointer for its caller's caller.
  1198  		//
  1199  		// According to the ABI documentation there is a mechanism to avoid
  1200  		// the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
  1201  		// relocation on the nop after the call to morestack) but at the time
  1202  		// of writing it is not supported at all by gold and my attempt to
  1203  		// use it with ld.bfd caused an internal linker error. So this hack
  1204  		// seems preferable.
  1205  
  1206  		// MOVD $runtime.morestack(SB), R12
  1207  		p = obj.Appendp(p, c.newprog)
  1208  		p.As = AMOVD
  1209  		p.From.Type = obj.TYPE_MEM
  1210  		p.From.Sym = morestacksym
  1211  		p.From.Name = obj.NAME_GOTREF
  1212  		p.To.Type = obj.TYPE_REG
  1213  		p.To.Reg = REG_R12
  1214  
  1215  		// MOVD R12, LR
  1216  		p = obj.Appendp(p, c.newprog)
  1217  		p.As = AMOVD
  1218  		p.From.Type = obj.TYPE_REG
  1219  		p.From.Reg = REG_R12
  1220  		p.To.Type = obj.TYPE_REG
  1221  		p.To.Reg = REG_LR
  1222  
  1223  		// BL LR
  1224  		p = obj.Appendp(p, c.newprog)
  1225  		p.As = obj.ACALL
  1226  		p.To.Type = obj.TYPE_REG
  1227  		p.To.Reg = REG_LR
  1228  	} else {
  1229  		// BL	runtime.morestack(SB)
  1230  		p = obj.Appendp(p, c.newprog)
  1231  
  1232  		p.As = ABL
  1233  		p.To.Type = obj.TYPE_BRANCH
  1234  		p.To.Sym = morestacksym
  1235  	}
  1236  
  1237  	if c.ctxt.Flag_shared {
  1238  		// MOVD 8(SP), R2
  1239  		p = obj.Appendp(p, c.newprog)
  1240  		p.As = AMOVD
  1241  		p.From.Type = obj.TYPE_MEM
  1242  		p.From.Reg = REGSP
  1243  		p.From.Offset = 8
  1244  		p.To.Type = obj.TYPE_REG
  1245  		p.To.Reg = REG_R2
  1246  	}
  1247  
  1248  	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
  1249  
  1250  	// BR	start
  1251  	p = obj.Appendp(p, c.newprog)
  1252  	p.As = ABR
  1253  	p.To.Type = obj.TYPE_BRANCH
  1254  	p.Pcond = p0.Link
  1255  
  1256  	// placeholder for q1's jump target
  1257  	p = obj.Appendp(p, c.newprog)
  1258  
  1259  	p.As = obj.ANOP // zero-width place holder
  1260  	q1.Pcond = p
  1261  
  1262  	return p
  1263  }
  1264  
  1265  var Linkppc64 = obj.LinkArch{
  1266  	Arch:           sys.ArchPPC64,
  1267  	Init:           buildop,
  1268  	Preprocess:     preprocess,
  1269  	Assemble:       span9,
  1270  	Progedit:       progedit,
  1271  	DWARFRegisters: PPC64DWARFRegisters,
  1272  }
  1273  
  1274  var Linkppc64le = obj.LinkArch{
  1275  	Arch:           sys.ArchPPC64LE,
  1276  	Init:           buildop,
  1277  	Preprocess:     preprocess,
  1278  	Assemble:       span9,
  1279  	Progedit:       progedit,
  1280  	DWARFRegisters: PPC64DWARFRegisters,
  1281  }