gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

obj9.go (31672B)


      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 }