github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/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  	"cmd/internal/obj"
    34  	"cmd/internal/objabi"
    35  	"cmd/internal/sys"
    36  )
    37  
    38  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    39  	p.From.Class = 0
    40  	p.To.Class = 0
    41  
    42  	c := ctxt9{ctxt: ctxt, newprog: newprog}
    43  
    44  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
    45  	switch p.As {
    46  	case ABR,
    47  		ABL,
    48  		obj.ARET,
    49  		obj.ADUFFZERO,
    50  		obj.ADUFFCOPY:
    51  		if p.To.Sym != nil {
    52  			p.To.Type = obj.TYPE_BRANCH
    53  		}
    54  	}
    55  
    56  	// Rewrite float constants to values stored in memory.
    57  	switch p.As {
    58  	case AFMOVS:
    59  		if p.From.Type == obj.TYPE_FCONST {
    60  			f32 := float32(p.From.Val.(float64))
    61  			p.From.Type = obj.TYPE_MEM
    62  			p.From.Sym = ctxt.Float32Sym(f32)
    63  			p.From.Name = obj.NAME_EXTERN
    64  			p.From.Offset = 0
    65  		}
    66  
    67  	case AFMOVD:
    68  		if p.From.Type == obj.TYPE_FCONST {
    69  			f64 := p.From.Val.(float64)
    70  			p.From.Type = obj.TYPE_MEM
    71  			p.From.Sym = ctxt.Float64Sym(f64)
    72  			p.From.Name = obj.NAME_EXTERN
    73  			p.From.Offset = 0
    74  		}
    75  
    76  		// Put >32-bit constants in memory and load them
    77  	case AMOVD:
    78  		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 {
    79  			p.From.Type = obj.TYPE_MEM
    80  			p.From.Sym = ctxt.Int64Sym(p.From.Offset)
    81  			p.From.Name = obj.NAME_EXTERN
    82  			p.From.Offset = 0
    83  		}
    84  	}
    85  
    86  	// Rewrite SUB constants into ADD.
    87  	switch p.As {
    88  	case ASUBC:
    89  		if p.From.Type == obj.TYPE_CONST {
    90  			p.From.Offset = -p.From.Offset
    91  			p.As = AADDC
    92  		}
    93  
    94  	case ASUBCCC:
    95  		if p.From.Type == obj.TYPE_CONST {
    96  			p.From.Offset = -p.From.Offset
    97  			p.As = AADDCCC
    98  		}
    99  
   100  	case ASUB:
   101  		if p.From.Type == obj.TYPE_CONST {
   102  			p.From.Offset = -p.From.Offset
   103  			p.As = AADD
   104  		}
   105  	}
   106  	if c.ctxt.Flag_dynlink {
   107  		c.rewriteToUseGot(p)
   108  	}
   109  }
   110  
   111  // Rewrite p, if necessary, to access global data via the global offset table.
   112  func (c *ctxt9) rewriteToUseGot(p *obj.Prog) {
   113  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   114  		//     ADUFFxxx $offset
   115  		// becomes
   116  		//     MOVD runtime.duffxxx@GOT, R12
   117  		//     ADD $offset, R12
   118  		//     MOVD R12, CTR
   119  		//     BL (CTR)
   120  		var sym *obj.LSym
   121  		if p.As == obj.ADUFFZERO {
   122  			sym = c.ctxt.Lookup("runtime.duffzero")
   123  		} else {
   124  			sym = c.ctxt.Lookup("runtime.duffcopy")
   125  		}
   126  		offset := p.To.Offset
   127  		p.As = AMOVD
   128  		p.From.Type = obj.TYPE_MEM
   129  		p.From.Name = obj.NAME_GOTREF
   130  		p.From.Sym = sym
   131  		p.To.Type = obj.TYPE_REG
   132  		p.To.Reg = REG_R12
   133  		p.To.Name = obj.NAME_NONE
   134  		p.To.Offset = 0
   135  		p.To.Sym = nil
   136  		p1 := obj.Appendp(p, c.newprog)
   137  		p1.As = AADD
   138  		p1.From.Type = obj.TYPE_CONST
   139  		p1.From.Offset = offset
   140  		p1.To.Type = obj.TYPE_REG
   141  		p1.To.Reg = REG_R12
   142  		p2 := obj.Appendp(p1, c.newprog)
   143  		p2.As = AMOVD
   144  		p2.From.Type = obj.TYPE_REG
   145  		p2.From.Reg = REG_R12
   146  		p2.To.Type = obj.TYPE_REG
   147  		p2.To.Reg = REG_CTR
   148  		p3 := obj.Appendp(p2, c.newprog)
   149  		p3.As = obj.ACALL
   150  		p3.From.Type = obj.TYPE_REG
   151  		p3.From.Reg = REG_R12
   152  		p3.To.Type = obj.TYPE_REG
   153  		p3.To.Reg = REG_CTR
   154  	}
   155  
   156  	// We only care about global data: NAME_EXTERN means a global
   157  	// symbol in the Go sense, and p.Sym.Local is true for a few
   158  	// internally defined symbols.
   159  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   160  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   161  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   162  		if p.As != AMOVD {
   163  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   164  		}
   165  		if p.To.Type != obj.TYPE_REG {
   166  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   167  		}
   168  		p.From.Type = obj.TYPE_MEM
   169  		p.From.Name = obj.NAME_GOTREF
   170  		if p.From.Offset != 0 {
   171  			q := obj.Appendp(p, c.newprog)
   172  			q.As = AADD
   173  			q.From.Type = obj.TYPE_CONST
   174  			q.From.Offset = p.From.Offset
   175  			q.To = p.To
   176  			p.From.Offset = 0
   177  		}
   178  	}
   179  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   180  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   181  	}
   182  	var source *obj.Addr
   183  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   184  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP)
   185  	// An addition may be inserted between the two MOVs if there is an offset.
   186  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   187  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   188  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   189  		}
   190  		source = &p.From
   191  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   192  		source = &p.To
   193  	} else {
   194  		return
   195  	}
   196  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   197  		return
   198  	}
   199  	if source.Sym.Type == objabi.STLSBSS {
   200  		return
   201  	}
   202  	if source.Type != obj.TYPE_MEM {
   203  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   204  	}
   205  	p1 := obj.Appendp(p, c.newprog)
   206  	p2 := obj.Appendp(p1, c.newprog)
   207  
   208  	p1.As = AMOVD
   209  	p1.From.Type = obj.TYPE_MEM
   210  	p1.From.Sym = source.Sym
   211  	p1.From.Name = obj.NAME_GOTREF
   212  	p1.To.Type = obj.TYPE_REG
   213  	p1.To.Reg = REGTMP
   214  
   215  	p2.As = p.As
   216  	p2.From = p.From
   217  	p2.To = p.To
   218  	if p.From.Name == obj.NAME_EXTERN {
   219  		p2.From.Reg = REGTMP
   220  		p2.From.Name = obj.NAME_NONE
   221  		p2.From.Sym = nil
   222  	} else if p.To.Name == obj.NAME_EXTERN {
   223  		p2.To.Reg = REGTMP
   224  		p2.To.Name = obj.NAME_NONE
   225  		p2.To.Sym = nil
   226  	} else {
   227  		return
   228  	}
   229  	obj.Nopout(p)
   230  }
   231  
   232  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   233  	// TODO(minux): add morestack short-cuts with small fixed frame-size.
   234  	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
   235  		return
   236  	}
   237  
   238  	c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog}
   239  
   240  	p := c.cursym.Func.Text
   241  	textstksiz := p.To.Offset
   242  	if textstksiz == -8 {
   243  		// Compatibility hack.
   244  		p.From.Sym.Set(obj.AttrNoFrame, true)
   245  		textstksiz = 0
   246  	}
   247  	if textstksiz%8 != 0 {
   248  		c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
   249  	}
   250  	if p.From.Sym.NoFrame() {
   251  		if textstksiz != 0 {
   252  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   253  		}
   254  	}
   255  
   256  	c.cursym.Func.Args = p.To.Val.(int32)
   257  	c.cursym.Func.Locals = int32(textstksiz)
   258  
   259  	/*
   260  	 * find leaf subroutines
   261  	 * strip NOPs
   262  	 * expand RET
   263  	 * expand BECOME pseudo
   264  	 */
   265  
   266  	var q *obj.Prog
   267  	var q1 *obj.Prog
   268  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   269  		switch p.As {
   270  		/* too hard, just leave alone */
   271  		case obj.ATEXT:
   272  			q = p
   273  
   274  			p.Mark |= LABEL | LEAF | SYNC
   275  			if p.Link != nil {
   276  				p.Link.Mark |= LABEL
   277  			}
   278  
   279  		case ANOR:
   280  			q = p
   281  			if p.To.Type == obj.TYPE_REG {
   282  				if p.To.Reg == REGZERO {
   283  					p.Mark |= LABEL | SYNC
   284  				}
   285  			}
   286  
   287  		case ALWAR,
   288  			ALBAR,
   289  			ASTBCCC,
   290  			ASTWCCC,
   291  			AECIWX,
   292  			AECOWX,
   293  			AEIEIO,
   294  			AICBI,
   295  			AISYNC,
   296  			ATLBIE,
   297  			ATLBIEL,
   298  			ASLBIA,
   299  			ASLBIE,
   300  			ASLBMFEE,
   301  			ASLBMFEV,
   302  			ASLBMTE,
   303  			ADCBF,
   304  			ADCBI,
   305  			ADCBST,
   306  			ADCBT,
   307  			ADCBTST,
   308  			ADCBZ,
   309  			ASYNC,
   310  			ATLBSYNC,
   311  			APTESYNC,
   312  			ALWSYNC,
   313  			ATW,
   314  			AWORD,
   315  			ARFI,
   316  			ARFCI,
   317  			ARFID,
   318  			AHRFID:
   319  			q = p
   320  			p.Mark |= LABEL | SYNC
   321  			continue
   322  
   323  		case AMOVW, AMOVWZ, AMOVD:
   324  			q = p
   325  			if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
   326  				p.Mark |= LABEL | SYNC
   327  			}
   328  			continue
   329  
   330  		case AFABS,
   331  			AFABSCC,
   332  			AFADD,
   333  			AFADDCC,
   334  			AFCTIW,
   335  			AFCTIWCC,
   336  			AFCTIWZ,
   337  			AFCTIWZCC,
   338  			AFDIV,
   339  			AFDIVCC,
   340  			AFMADD,
   341  			AFMADDCC,
   342  			AFMOVD,
   343  			AFMOVDU,
   344  			/* case AFMOVDS: */
   345  			AFMOVS,
   346  			AFMOVSU,
   347  
   348  			/* case AFMOVSD: */
   349  			AFMSUB,
   350  			AFMSUBCC,
   351  			AFMUL,
   352  			AFMULCC,
   353  			AFNABS,
   354  			AFNABSCC,
   355  			AFNEG,
   356  			AFNEGCC,
   357  			AFNMADD,
   358  			AFNMADDCC,
   359  			AFNMSUB,
   360  			AFNMSUBCC,
   361  			AFRSP,
   362  			AFRSPCC,
   363  			AFSUB,
   364  			AFSUBCC:
   365  			q = p
   366  
   367  			p.Mark |= FLOAT
   368  			continue
   369  
   370  		case ABL,
   371  			ABCL,
   372  			obj.ADUFFZERO,
   373  			obj.ADUFFCOPY:
   374  			c.cursym.Func.Text.Mark &^= LEAF
   375  			fallthrough
   376  
   377  		case ABC,
   378  			ABEQ,
   379  			ABGE,
   380  			ABGT,
   381  			ABLE,
   382  			ABLT,
   383  			ABNE,
   384  			ABR,
   385  			ABVC,
   386  			ABVS:
   387  			p.Mark |= BRANCH
   388  			q = p
   389  			q1 = p.Pcond
   390  			if q1 != nil {
   391  				for q1.As == obj.ANOP {
   392  					q1 = q1.Link
   393  					p.Pcond = q1
   394  				}
   395  
   396  				if q1.Mark&LEAF == 0 {
   397  					q1.Mark |= LABEL
   398  				}
   399  			} else {
   400  				p.Mark |= LABEL
   401  			}
   402  			q1 = p.Link
   403  			if q1 != nil {
   404  				q1.Mark |= LABEL
   405  			}
   406  			continue
   407  
   408  		case AFCMPO, AFCMPU:
   409  			q = p
   410  			p.Mark |= FCMP | FLOAT
   411  			continue
   412  
   413  		case obj.ARET:
   414  			q = p
   415  			if p.Link != nil {
   416  				p.Link.Mark |= LABEL
   417  			}
   418  			continue
   419  
   420  		case obj.ANOP:
   421  			q1 = p.Link
   422  			q.Link = q1 /* q is non-nop */
   423  			q1.Mark |= p.Mark
   424  			continue
   425  
   426  		default:
   427  			q = p
   428  			continue
   429  		}
   430  	}
   431  
   432  	autosize := int32(0)
   433  	var p1 *obj.Prog
   434  	var p2 *obj.Prog
   435  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   436  		o := p.As
   437  		switch o {
   438  		case obj.ATEXT:
   439  			autosize = int32(textstksiz)
   440  
   441  			if p.Mark&LEAF != 0 && autosize == 0 {
   442  				// A leaf function with no locals has no frame.
   443  				p.From.Sym.Set(obj.AttrNoFrame, true)
   444  			}
   445  
   446  			if !p.From.Sym.NoFrame() {
   447  				// If there is a stack frame at all, it includes
   448  				// space to save the LR.
   449  				autosize += int32(c.ctxt.FixedFrameSize())
   450  			}
   451  
   452  			if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
   453  				// A leaf function with a small stack can be marked
   454  				// NOSPLIT, avoiding a stack check.
   455  				p.From.Sym.Set(obj.AttrNoSplit, true)
   456  			}
   457  
   458  			p.To.Offset = int64(autosize)
   459  
   460  			q = p
   461  
   462  			if c.ctxt.Flag_shared && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" {
   463  				// When compiling Go into PIC, all functions must start
   464  				// with instructions to load the TOC pointer into r2:
   465  				//
   466  				//	addis r2, r12, .TOC.-func@ha
   467  				//	addi r2, r2, .TOC.-func@l+4
   468  				//
   469  				// We could probably skip this prologue in some situations
   470  				// but it's a bit subtle. However, it is both safe and
   471  				// necessary to leave the prologue off duffzero and
   472  				// duffcopy as we rely on being able to jump to a specific
   473  				// instruction offset for them.
   474  				//
   475  				// These are AWORDS because there is no (afaict) way to
   476  				// generate the addis instruction except as part of the
   477  				// load of a large constant, and in that case there is no
   478  				// way to use r12 as the source.
   479  				q = obj.Appendp(q, c.newprog)
   480  				q.As = AWORD
   481  				q.Pos = p.Pos
   482  				q.From.Type = obj.TYPE_CONST
   483  				q.From.Offset = 0x3c4c0000
   484  				q = obj.Appendp(q, c.newprog)
   485  				q.As = AWORD
   486  				q.Pos = p.Pos
   487  				q.From.Type = obj.TYPE_CONST
   488  				q.From.Offset = 0x38420000
   489  				rel := obj.Addrel(c.cursym)
   490  				rel.Off = 0
   491  				rel.Siz = 8
   492  				rel.Sym = c.ctxt.Lookup(".TOC.")
   493  				rel.Type = objabi.R_ADDRPOWER_PCREL
   494  			}
   495  
   496  			if !c.cursym.Func.Text.From.Sym.NoSplit() {
   497  				q = c.stacksplit(q, autosize) // emit split check
   498  			}
   499  
   500  			if autosize != 0 {
   501  				// Save the link register and update the SP.  MOVDU is used unless
   502  				// the frame size is too large.  The link register must be saved
   503  				// even for non-empty leaf functions so that traceback works.
   504  				if autosize >= -BIG && autosize <= BIG {
   505  					// Use MOVDU to adjust R1 when saving R31, if autosize is small.
   506  					q = obj.Appendp(q, c.newprog)
   507  					q.As = AMOVD
   508  					q.Pos = p.Pos
   509  					q.From.Type = obj.TYPE_REG
   510  					q.From.Reg = REG_LR
   511  					q.To.Type = obj.TYPE_REG
   512  					q.To.Reg = REGTMP
   513  
   514  					q = obj.Appendp(q, c.newprog)
   515  					q.As = AMOVDU
   516  					q.Pos = p.Pos
   517  					q.From.Type = obj.TYPE_REG
   518  					q.From.Reg = REGTMP
   519  					q.To.Type = obj.TYPE_MEM
   520  					q.To.Offset = int64(-autosize)
   521  					q.To.Reg = REGSP
   522  					q.Spadj = int32(autosize)
   523  				} else {
   524  					// Frame size is too large for a MOVDU instruction.
   525  					// Store link register before decrementing SP, so if a signal comes
   526  					// during the execution of the function prologue, the traceback
   527  					// code will not see a half-updated stack frame.
   528  					q = obj.Appendp(q, c.newprog)
   529  					q.As = AMOVD
   530  					q.Pos = p.Pos
   531  					q.From.Type = obj.TYPE_REG
   532  					q.From.Reg = REG_LR
   533  					q.To.Type = obj.TYPE_REG
   534  					q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
   535  
   536  					q = obj.Appendp(q, c.newprog)
   537  					q.As = AMOVD
   538  					q.Pos = p.Pos
   539  					q.From.Type = obj.TYPE_REG
   540  					q.From.Reg = REG_R29
   541  					q.To.Type = obj.TYPE_MEM
   542  					q.To.Offset = int64(-autosize)
   543  					q.To.Reg = REGSP
   544  
   545  					q = obj.Appendp(q, c.newprog)
   546  					q.As = AADD
   547  					q.Pos = p.Pos
   548  					q.From.Type = obj.TYPE_CONST
   549  					q.From.Offset = int64(-autosize)
   550  					q.To.Type = obj.TYPE_REG
   551  					q.To.Reg = REGSP
   552  					q.Spadj = +autosize
   553  				}
   554  			} else if c.cursym.Func.Text.Mark&LEAF == 0 {
   555  				// A very few functions that do not return to their caller
   556  				// (e.g. gogo) are not identified as leaves but still have
   557  				// no frame.
   558  				c.cursym.Func.Text.Mark |= LEAF
   559  			}
   560  
   561  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   562  				c.cursym.Set(obj.AttrLeaf, true)
   563  				break
   564  			}
   565  
   566  			if c.ctxt.Flag_shared {
   567  				q = obj.Appendp(q, c.newprog)
   568  				q.As = AMOVD
   569  				q.Pos = p.Pos
   570  				q.From.Type = obj.TYPE_REG
   571  				q.From.Reg = REG_R2
   572  				q.To.Type = obj.TYPE_MEM
   573  				q.To.Reg = REGSP
   574  				q.To.Offset = 24
   575  			}
   576  
   577  			if c.cursym.Func.Text.From.Sym.Wrapper() {
   578  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   579  				//
   580  				//	MOVD g_panic(g), R3
   581  				//	CMP R0, R3
   582  				//	BEQ end
   583  				//	MOVD panic_argp(R3), R4
   584  				//	ADD $(autosize+8), R1, R5
   585  				//	CMP R4, R5
   586  				//	BNE end
   587  				//	ADD $8, R1, R6
   588  				//	MOVD R6, panic_argp(R3)
   589  				// end:
   590  				//	NOP
   591  				//
   592  				// The NOP is needed to give the jumps somewhere to land.
   593  				// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
   594  
   595  				q = obj.Appendp(q, c.newprog)
   596  
   597  				q.As = AMOVD
   598  				q.From.Type = obj.TYPE_MEM
   599  				q.From.Reg = REGG
   600  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   601  				q.To.Type = obj.TYPE_REG
   602  				q.To.Reg = REG_R3
   603  
   604  				q = obj.Appendp(q, c.newprog)
   605  				q.As = ACMP
   606  				q.From.Type = obj.TYPE_REG
   607  				q.From.Reg = REG_R0
   608  				q.To.Type = obj.TYPE_REG
   609  				q.To.Reg = REG_R3
   610  
   611  				q = obj.Appendp(q, c.newprog)
   612  				q.As = ABEQ
   613  				q.To.Type = obj.TYPE_BRANCH
   614  				p1 = q
   615  
   616  				q = obj.Appendp(q, c.newprog)
   617  				q.As = AMOVD
   618  				q.From.Type = obj.TYPE_MEM
   619  				q.From.Reg = REG_R3
   620  				q.From.Offset = 0 // Panic.argp
   621  				q.To.Type = obj.TYPE_REG
   622  				q.To.Reg = REG_R4
   623  
   624  				q = obj.Appendp(q, c.newprog)
   625  				q.As = AADD
   626  				q.From.Type = obj.TYPE_CONST
   627  				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
   628  				q.Reg = REGSP
   629  				q.To.Type = obj.TYPE_REG
   630  				q.To.Reg = REG_R5
   631  
   632  				q = obj.Appendp(q, c.newprog)
   633  				q.As = ACMP
   634  				q.From.Type = obj.TYPE_REG
   635  				q.From.Reg = REG_R4
   636  				q.To.Type = obj.TYPE_REG
   637  				q.To.Reg = REG_R5
   638  
   639  				q = obj.Appendp(q, c.newprog)
   640  				q.As = ABNE
   641  				q.To.Type = obj.TYPE_BRANCH
   642  				p2 = q
   643  
   644  				q = obj.Appendp(q, c.newprog)
   645  				q.As = AADD
   646  				q.From.Type = obj.TYPE_CONST
   647  				q.From.Offset = c.ctxt.FixedFrameSize()
   648  				q.Reg = REGSP
   649  				q.To.Type = obj.TYPE_REG
   650  				q.To.Reg = REG_R6
   651  
   652  				q = obj.Appendp(q, c.newprog)
   653  				q.As = AMOVD
   654  				q.From.Type = obj.TYPE_REG
   655  				q.From.Reg = REG_R6
   656  				q.To.Type = obj.TYPE_MEM
   657  				q.To.Reg = REG_R3
   658  				q.To.Offset = 0 // Panic.argp
   659  
   660  				q = obj.Appendp(q, c.newprog)
   661  
   662  				q.As = obj.ANOP
   663  				p1.Pcond = q
   664  				p2.Pcond = q
   665  			}
   666  
   667  		case obj.ARET:
   668  			if p.From.Type == obj.TYPE_CONST {
   669  				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   670  				break
   671  			}
   672  
   673  			retTarget := p.To.Sym
   674  
   675  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   676  				if autosize == 0 {
   677  					p.As = ABR
   678  					p.From = obj.Addr{}
   679  					if retTarget == nil {
   680  						p.To.Type = obj.TYPE_REG
   681  						p.To.Reg = REG_LR
   682  					} else {
   683  						p.To.Type = obj.TYPE_BRANCH
   684  						p.To.Sym = retTarget
   685  					}
   686  					p.Mark |= BRANCH
   687  					break
   688  				}
   689  
   690  				p.As = AADD
   691  				p.From.Type = obj.TYPE_CONST
   692  				p.From.Offset = int64(autosize)
   693  				p.To.Type = obj.TYPE_REG
   694  				p.To.Reg = REGSP
   695  				p.Spadj = -autosize
   696  
   697  				q = c.newprog()
   698  				q.As = ABR
   699  				q.Pos = p.Pos
   700  				q.To.Type = obj.TYPE_REG
   701  				q.To.Reg = REG_LR
   702  				q.Mark |= BRANCH
   703  				q.Spadj = +autosize
   704  
   705  				q.Link = p.Link
   706  				p.Link = q
   707  				break
   708  			}
   709  
   710  			p.As = AMOVD
   711  			p.From.Type = obj.TYPE_MEM
   712  			p.From.Offset = 0
   713  			p.From.Reg = REGSP
   714  			p.To.Type = obj.TYPE_REG
   715  			p.To.Reg = REGTMP
   716  
   717  			q = c.newprog()
   718  			q.As = AMOVD
   719  			q.Pos = p.Pos
   720  			q.From.Type = obj.TYPE_REG
   721  			q.From.Reg = REGTMP
   722  			q.To.Type = obj.TYPE_REG
   723  			q.To.Reg = REG_LR
   724  
   725  			q.Link = p.Link
   726  			p.Link = q
   727  			p = q
   728  
   729  			if false {
   730  				// Debug bad returns
   731  				q = c.newprog()
   732  
   733  				q.As = AMOVD
   734  				q.Pos = p.Pos
   735  				q.From.Type = obj.TYPE_MEM
   736  				q.From.Offset = 0
   737  				q.From.Reg = REGTMP
   738  				q.To.Type = obj.TYPE_REG
   739  				q.To.Reg = REGTMP
   740  
   741  				q.Link = p.Link
   742  				p.Link = q
   743  				p = q
   744  			}
   745  
   746  			if autosize != 0 {
   747  				q = c.newprog()
   748  				q.As = AADD
   749  				q.Pos = p.Pos
   750  				q.From.Type = obj.TYPE_CONST
   751  				q.From.Offset = int64(autosize)
   752  				q.To.Type = obj.TYPE_REG
   753  				q.To.Reg = REGSP
   754  				q.Spadj = -autosize
   755  
   756  				q.Link = p.Link
   757  				p.Link = q
   758  			}
   759  
   760  			q1 = c.newprog()
   761  			q1.As = ABR
   762  			q1.Pos = p.Pos
   763  			if retTarget == nil {
   764  				q1.To.Type = obj.TYPE_REG
   765  				q1.To.Reg = REG_LR
   766  			} else {
   767  				q1.To.Type = obj.TYPE_BRANCH
   768  				q1.To.Sym = retTarget
   769  			}
   770  			q1.Mark |= BRANCH
   771  			q1.Spadj = +autosize
   772  
   773  			q1.Link = q.Link
   774  			q.Link = q1
   775  		case AADD:
   776  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   777  				p.Spadj = int32(-p.From.Offset)
   778  			}
   779  		}
   780  	}
   781  }
   782  
   783  /*
   784  // instruction scheduling
   785  	if(debug['Q'] == 0)
   786  		return;
   787  
   788  	curtext = nil;
   789  	q = nil;	// p - 1
   790  	q1 = firstp;	// top of block
   791  	o = 0;		// count of instructions
   792  	for(p = firstp; p != nil; p = p1) {
   793  		p1 = p->link;
   794  		o++;
   795  		if(p->mark & NOSCHED){
   796  			if(q1 != p){
   797  				sched(q1, q);
   798  			}
   799  			for(; p != nil; p = p->link){
   800  				if(!(p->mark & NOSCHED))
   801  					break;
   802  				q = p;
   803  			}
   804  			p1 = p;
   805  			q1 = p;
   806  			o = 0;
   807  			continue;
   808  		}
   809  		if(p->mark & (LABEL|SYNC)) {
   810  			if(q1 != p)
   811  				sched(q1, q);
   812  			q1 = p;
   813  			o = 1;
   814  		}
   815  		if(p->mark & (BRANCH|SYNC)) {
   816  			sched(q1, p);
   817  			q1 = p1;
   818  			o = 0;
   819  		}
   820  		if(o >= NSCHED) {
   821  			sched(q1, p);
   822  			q1 = p1;
   823  			o = 0;
   824  		}
   825  		q = p;
   826  	}
   827  */
   828  func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   829  	p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode
   830  
   831  	// MOVD	g_stackguard(g), R3
   832  	p = obj.Appendp(p, c.newprog)
   833  
   834  	p.As = AMOVD
   835  	p.From.Type = obj.TYPE_MEM
   836  	p.From.Reg = REGG
   837  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   838  	if c.cursym.CFunc() {
   839  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   840  	}
   841  	p.To.Type = obj.TYPE_REG
   842  	p.To.Reg = REG_R3
   843  
   844  	var q *obj.Prog
   845  	if framesize <= objabi.StackSmall {
   846  		// small stack: SP < stackguard
   847  		//	CMP	stackguard, SP
   848  		p = obj.Appendp(p, c.newprog)
   849  
   850  		p.As = ACMPU
   851  		p.From.Type = obj.TYPE_REG
   852  		p.From.Reg = REG_R3
   853  		p.To.Type = obj.TYPE_REG
   854  		p.To.Reg = REGSP
   855  	} else if framesize <= objabi.StackBig {
   856  		// large stack: SP-framesize < stackguard-StackSmall
   857  		//	ADD $-(framesize-StackSmall), SP, R4
   858  		//	CMP stackguard, R4
   859  		p = obj.Appendp(p, c.newprog)
   860  
   861  		p.As = AADD
   862  		p.From.Type = obj.TYPE_CONST
   863  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
   864  		p.Reg = REGSP
   865  		p.To.Type = obj.TYPE_REG
   866  		p.To.Reg = REG_R4
   867  
   868  		p = obj.Appendp(p, c.newprog)
   869  		p.As = ACMPU
   870  		p.From.Type = obj.TYPE_REG
   871  		p.From.Reg = REG_R3
   872  		p.To.Type = obj.TYPE_REG
   873  		p.To.Reg = REG_R4
   874  	} else {
   875  		// Such a large stack we need to protect against wraparound.
   876  		// If SP is close to zero:
   877  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   878  		// The +StackGuard on both sides is required to keep the left side positive:
   879  		// SP is allowed to be slightly below stackguard. See stack.h.
   880  		//
   881  		// Preemption sets stackguard to StackPreempt, a very large value.
   882  		// That breaks the math above, so we have to check for that explicitly.
   883  		//	// stackguard is R3
   884  		//	CMP	R3, $StackPreempt
   885  		//	BEQ	label-of-call-to-morestack
   886  		//	ADD	$StackGuard, SP, R4
   887  		//	SUB	R3, R4
   888  		//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
   889  		//	CMPU	R31, R4
   890  		p = obj.Appendp(p, c.newprog)
   891  
   892  		p.As = ACMP
   893  		p.From.Type = obj.TYPE_REG
   894  		p.From.Reg = REG_R3
   895  		p.To.Type = obj.TYPE_CONST
   896  		p.To.Offset = objabi.StackPreempt
   897  
   898  		p = obj.Appendp(p, c.newprog)
   899  		q = p
   900  		p.As = ABEQ
   901  		p.To.Type = obj.TYPE_BRANCH
   902  
   903  		p = obj.Appendp(p, c.newprog)
   904  		p.As = AADD
   905  		p.From.Type = obj.TYPE_CONST
   906  		p.From.Offset = objabi.StackGuard
   907  		p.Reg = REGSP
   908  		p.To.Type = obj.TYPE_REG
   909  		p.To.Reg = REG_R4
   910  
   911  		p = obj.Appendp(p, c.newprog)
   912  		p.As = ASUB
   913  		p.From.Type = obj.TYPE_REG
   914  		p.From.Reg = REG_R3
   915  		p.To.Type = obj.TYPE_REG
   916  		p.To.Reg = REG_R4
   917  
   918  		p = obj.Appendp(p, c.newprog)
   919  		p.As = AMOVD
   920  		p.From.Type = obj.TYPE_CONST
   921  		p.From.Offset = int64(framesize) + objabi.StackGuard - objabi.StackSmall
   922  		p.To.Type = obj.TYPE_REG
   923  		p.To.Reg = REGTMP
   924  
   925  		p = obj.Appendp(p, c.newprog)
   926  		p.As = ACMPU
   927  		p.From.Type = obj.TYPE_REG
   928  		p.From.Reg = REGTMP
   929  		p.To.Type = obj.TYPE_REG
   930  		p.To.Reg = REG_R4
   931  	}
   932  
   933  	// q1: BLT	done
   934  	p = obj.Appendp(p, c.newprog)
   935  	q1 := p
   936  
   937  	p.As = ABLT
   938  	p.To.Type = obj.TYPE_BRANCH
   939  
   940  	// MOVD	LR, R5
   941  	p = obj.Appendp(p, c.newprog)
   942  
   943  	p.As = AMOVD
   944  	p.From.Type = obj.TYPE_REG
   945  	p.From.Reg = REG_LR
   946  	p.To.Type = obj.TYPE_REG
   947  	p.To.Reg = REG_R5
   948  	if q != nil {
   949  		q.Pcond = p
   950  	}
   951  
   952  	var morestacksym *obj.LSym
   953  	if c.cursym.CFunc() {
   954  		morestacksym = c.ctxt.Lookup("runtime.morestackc")
   955  	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
   956  		morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt")
   957  	} else {
   958  		morestacksym = c.ctxt.Lookup("runtime.morestack")
   959  	}
   960  
   961  	if c.ctxt.Flag_shared {
   962  		// In PPC64 PIC code, R2 is used as TOC pointer derived from R12
   963  		// which is the address of function entry point when entering
   964  		// the function. We need to preserve R2 across call to morestack.
   965  		// Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
   966  		// the caller's frame, but not used (0(SP) is caller's saved LR,
   967  		// 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
   968  
   969  		// MOVD R12, 8(SP)
   970  		p = obj.Appendp(p, c.newprog)
   971  		p.As = AMOVD
   972  		p.From.Type = obj.TYPE_REG
   973  		p.From.Reg = REG_R2
   974  		p.To.Type = obj.TYPE_MEM
   975  		p.To.Reg = REGSP
   976  		p.To.Offset = 8
   977  	}
   978  
   979  	if c.ctxt.Flag_dynlink {
   980  		// Avoid calling morestack via a PLT when dynamically linking. The
   981  		// PLT stubs generated by the system linker on ppc64le when "std r2,
   982  		// 24(r1)" to save the TOC pointer in their callers stack
   983  		// frame. Unfortunately (and necessarily) morestack is called before
   984  		// the function that calls it sets up its frame and so the PLT ends
   985  		// up smashing the saved TOC pointer for its caller's caller.
   986  		//
   987  		// According to the ABI documentation there is a mechanism to avoid
   988  		// the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
   989  		// relocation on the nop after the call to morestack) but at the time
   990  		// of writing it is not supported at all by gold and my attempt to
   991  		// use it with ld.bfd caused an internal linker error. So this hack
   992  		// seems preferable.
   993  
   994  		// MOVD $runtime.morestack(SB), R12
   995  		p = obj.Appendp(p, c.newprog)
   996  		p.As = AMOVD
   997  		p.From.Type = obj.TYPE_MEM
   998  		p.From.Sym = morestacksym
   999  		p.From.Name = obj.NAME_GOTREF
  1000  		p.To.Type = obj.TYPE_REG
  1001  		p.To.Reg = REG_R12
  1002  
  1003  		// MOVD R12, CTR
  1004  		p = obj.Appendp(p, c.newprog)
  1005  		p.As = AMOVD
  1006  		p.From.Type = obj.TYPE_REG
  1007  		p.From.Reg = REG_R12
  1008  		p.To.Type = obj.TYPE_REG
  1009  		p.To.Reg = REG_CTR
  1010  
  1011  		// BL CTR
  1012  		p = obj.Appendp(p, c.newprog)
  1013  		p.As = obj.ACALL
  1014  		p.From.Type = obj.TYPE_REG
  1015  		p.From.Reg = REG_R12
  1016  		p.To.Type = obj.TYPE_REG
  1017  		p.To.Reg = REG_CTR
  1018  	} else {
  1019  		// BL	runtime.morestack(SB)
  1020  		p = obj.Appendp(p, c.newprog)
  1021  
  1022  		p.As = ABL
  1023  		p.To.Type = obj.TYPE_BRANCH
  1024  		p.To.Sym = morestacksym
  1025  	}
  1026  
  1027  	if c.ctxt.Flag_shared {
  1028  		// MOVD 8(SP), R2
  1029  		p = obj.Appendp(p, c.newprog)
  1030  		p.As = AMOVD
  1031  		p.From.Type = obj.TYPE_MEM
  1032  		p.From.Reg = REGSP
  1033  		p.From.Offset = 8
  1034  		p.To.Type = obj.TYPE_REG
  1035  		p.To.Reg = REG_R2
  1036  	}
  1037  
  1038  	// BR	start
  1039  	p = obj.Appendp(p, c.newprog)
  1040  	p.As = ABR
  1041  	p.To.Type = obj.TYPE_BRANCH
  1042  	p.Pcond = p0.Link
  1043  
  1044  	// placeholder for q1's jump target
  1045  	p = obj.Appendp(p, c.newprog)
  1046  
  1047  	p.As = obj.ANOP // zero-width place holder
  1048  	q1.Pcond = p
  1049  
  1050  	return p
  1051  }
  1052  
  1053  var Linkppc64 = obj.LinkArch{
  1054  	Arch:       sys.ArchPPC64,
  1055  	Init:       buildop,
  1056  	Preprocess: preprocess,
  1057  	Assemble:   span9,
  1058  	Progedit:   progedit,
  1059  }
  1060  
  1061  var Linkppc64le = obj.LinkArch{
  1062  	Arch:       sys.ArchPPC64LE,
  1063  	Init:       buildop,
  1064  	Preprocess: preprocess,
  1065  	Assemble:   span9,
  1066  	Progedit:   progedit,
  1067  }