obj9.go raw

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