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