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