github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/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  	"fmt"
    36  	"math"
    37  )
    38  
    39  func progedit(ctxt *obj.Link, p *obj.Prog) {
    40  	p.From.Class = 0
    41  	p.To.Class = 0
    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  			i32 := math.Float32bits(f32)
    61  			literal := fmt.Sprintf("$f32.%08x", i32)
    62  			s := obj.Linklookup(ctxt, literal, 0)
    63  			s.Size = 4
    64  			p.From.Type = obj.TYPE_MEM
    65  			p.From.Sym = s
    66  			p.From.Sym.Local = true
    67  			p.From.Name = obj.NAME_EXTERN
    68  			p.From.Offset = 0
    69  		}
    70  
    71  	case AFMOVD:
    72  		if p.From.Type == obj.TYPE_FCONST {
    73  			i64 := math.Float64bits(p.From.Val.(float64))
    74  			literal := fmt.Sprintf("$f64.%016x", i64)
    75  			s := obj.Linklookup(ctxt, literal, 0)
    76  			s.Size = 8
    77  			p.From.Type = obj.TYPE_MEM
    78  			p.From.Sym = s
    79  			p.From.Sym.Local = true
    80  			p.From.Name = obj.NAME_EXTERN
    81  			p.From.Offset = 0
    82  		}
    83  
    84  		// Put >32-bit constants in memory and load them
    85  	case AMOVD:
    86  		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 {
    87  			literal := fmt.Sprintf("$i64.%016x", uint64(p.From.Offset))
    88  			s := obj.Linklookup(ctxt, literal, 0)
    89  			s.Size = 8
    90  			p.From.Type = obj.TYPE_MEM
    91  			p.From.Sym = s
    92  			p.From.Sym.Local = true
    93  			p.From.Name = obj.NAME_EXTERN
    94  			p.From.Offset = 0
    95  		}
    96  	}
    97  
    98  	// Rewrite SUB constants into ADD.
    99  	switch p.As {
   100  	case ASUBC:
   101  		if p.From.Type == obj.TYPE_CONST {
   102  			p.From.Offset = -p.From.Offset
   103  			p.As = AADDC
   104  		}
   105  
   106  	case ASUBCCC:
   107  		if p.From.Type == obj.TYPE_CONST {
   108  			p.From.Offset = -p.From.Offset
   109  			p.As = AADDCCC
   110  		}
   111  
   112  	case ASUB:
   113  		if p.From.Type == obj.TYPE_CONST {
   114  			p.From.Offset = -p.From.Offset
   115  			p.As = AADD
   116  		}
   117  	}
   118  	if ctxt.Flag_dynlink {
   119  		rewriteToUseGot(ctxt, p)
   120  	}
   121  }
   122  
   123  // Rewrite p, if necessary, to access global data via the global offset table.
   124  func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
   125  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   126  		//     ADUFFxxx $offset
   127  		// becomes
   128  		//     MOVD runtime.duffxxx@GOT, R12
   129  		//     ADD $offset, R12
   130  		//     MOVD R12, CTR
   131  		//     BL (CTR)
   132  		var sym *obj.LSym
   133  		if p.As == obj.ADUFFZERO {
   134  			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
   135  		} else {
   136  			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
   137  		}
   138  		offset := p.To.Offset
   139  		p.As = AMOVD
   140  		p.From.Type = obj.TYPE_MEM
   141  		p.From.Name = obj.NAME_GOTREF
   142  		p.From.Sym = sym
   143  		p.To.Type = obj.TYPE_REG
   144  		p.To.Reg = REG_R12
   145  		p.To.Name = obj.NAME_NONE
   146  		p.To.Offset = 0
   147  		p.To.Sym = nil
   148  		p1 := obj.Appendp(ctxt, p)
   149  		p1.As = AADD
   150  		p1.From.Type = obj.TYPE_CONST
   151  		p1.From.Offset = offset
   152  		p1.To.Type = obj.TYPE_REG
   153  		p1.To.Reg = REG_R12
   154  		p2 := obj.Appendp(ctxt, p1)
   155  		p2.As = AMOVD
   156  		p2.From.Type = obj.TYPE_REG
   157  		p2.From.Reg = REG_R12
   158  		p2.To.Type = obj.TYPE_REG
   159  		p2.To.Reg = REG_CTR
   160  		p3 := obj.Appendp(ctxt, p2)
   161  		p3.As = obj.ACALL
   162  		p3.From.Type = obj.TYPE_REG
   163  		p3.From.Reg = REG_R12
   164  		p3.To.Type = obj.TYPE_REG
   165  		p3.To.Reg = REG_CTR
   166  	}
   167  
   168  	// We only care about global data: NAME_EXTERN means a global
   169  	// symbol in the Go sense, and p.Sym.Local is true for a few
   170  	// internally defined symbols.
   171  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   172  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   173  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   174  		if p.As != AMOVD {
   175  			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   176  		}
   177  		if p.To.Type != obj.TYPE_REG {
   178  			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   179  		}
   180  		p.From.Type = obj.TYPE_MEM
   181  		p.From.Name = obj.NAME_GOTREF
   182  		if p.From.Offset != 0 {
   183  			q := obj.Appendp(ctxt, p)
   184  			q.As = AADD
   185  			q.From.Type = obj.TYPE_CONST
   186  			q.From.Offset = p.From.Offset
   187  			q.To = p.To
   188  			p.From.Offset = 0
   189  		}
   190  	}
   191  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   192  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   193  	}
   194  	var source *obj.Addr
   195  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   196  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP)
   197  	// An addition may be inserted between the two MOVs if there is an offset.
   198  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   199  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   200  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   201  		}
   202  		source = &p.From
   203  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   204  		source = &p.To
   205  	} else {
   206  		return
   207  	}
   208  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   209  		return
   210  	}
   211  	if source.Sym.Type == obj.STLSBSS {
   212  		return
   213  	}
   214  	if source.Type != obj.TYPE_MEM {
   215  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   216  	}
   217  	p1 := obj.Appendp(ctxt, p)
   218  	p2 := obj.Appendp(ctxt, p1)
   219  
   220  	p1.As = AMOVD
   221  	p1.From.Type = obj.TYPE_MEM
   222  	p1.From.Sym = source.Sym
   223  	p1.From.Name = obj.NAME_GOTREF
   224  	p1.To.Type = obj.TYPE_REG
   225  	p1.To.Reg = REGTMP
   226  
   227  	p2.As = p.As
   228  	p2.From = p.From
   229  	p2.To = p.To
   230  	if p.From.Name == obj.NAME_EXTERN {
   231  		p2.From.Reg = REGTMP
   232  		p2.From.Name = obj.NAME_NONE
   233  		p2.From.Sym = nil
   234  	} else if p.To.Name == obj.NAME_EXTERN {
   235  		p2.To.Reg = REGTMP
   236  		p2.To.Name = obj.NAME_NONE
   237  		p2.To.Sym = nil
   238  	} else {
   239  		return
   240  	}
   241  	obj.Nopout(p)
   242  }
   243  
   244  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   245  	// TODO(minux): add morestack short-cuts with small fixed frame-size.
   246  	ctxt.Cursym = cursym
   247  
   248  	if cursym.Text == nil || cursym.Text.Link == nil {
   249  		return
   250  	}
   251  
   252  	p := cursym.Text
   253  	textstksiz := p.To.Offset
   254  	if textstksiz == -8 {
   255  		// Compatibility hack.
   256  		p.From3.Offset |= obj.NOFRAME
   257  		textstksiz = 0
   258  	}
   259  	if textstksiz%8 != 0 {
   260  		ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
   261  	}
   262  	if p.From3.Offset&obj.NOFRAME != 0 {
   263  		if textstksiz != 0 {
   264  			ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   265  		}
   266  	}
   267  
   268  	cursym.Args = p.To.Val.(int32)
   269  	cursym.Locals = int32(textstksiz)
   270  
   271  	/*
   272  	 * find leaf subroutines
   273  	 * strip NOPs
   274  	 * expand RET
   275  	 * expand BECOME pseudo
   276  	 */
   277  	if ctxt.Debugvlog != 0 {
   278  		fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime())
   279  	}
   280  	ctxt.Bso.Flush()
   281  
   282  	var q *obj.Prog
   283  	var q1 *obj.Prog
   284  	for p := cursym.Text; p != nil; p = p.Link {
   285  		switch p.As {
   286  		/* too hard, just leave alone */
   287  		case obj.ATEXT:
   288  			q = p
   289  
   290  			p.Mark |= LABEL | LEAF | SYNC
   291  			if p.Link != nil {
   292  				p.Link.Mark |= LABEL
   293  			}
   294  
   295  		case ANOR:
   296  			q = p
   297  			if p.To.Type == obj.TYPE_REG {
   298  				if p.To.Reg == REGZERO {
   299  					p.Mark |= LABEL | SYNC
   300  				}
   301  			}
   302  
   303  		case ALWAR,
   304  			ALBAR,
   305  			ASTBCCC,
   306  			ASTWCCC,
   307  			AECIWX,
   308  			AECOWX,
   309  			AEIEIO,
   310  			AICBI,
   311  			AISYNC,
   312  			ATLBIE,
   313  			ATLBIEL,
   314  			ASLBIA,
   315  			ASLBIE,
   316  			ASLBMFEE,
   317  			ASLBMFEV,
   318  			ASLBMTE,
   319  			ADCBF,
   320  			ADCBI,
   321  			ADCBST,
   322  			ADCBT,
   323  			ADCBTST,
   324  			ADCBZ,
   325  			ASYNC,
   326  			ATLBSYNC,
   327  			APTESYNC,
   328  			ALWSYNC,
   329  			ATW,
   330  			AWORD,
   331  			ARFI,
   332  			ARFCI,
   333  			ARFID,
   334  			AHRFID:
   335  			q = p
   336  			p.Mark |= LABEL | SYNC
   337  			continue
   338  
   339  		case AMOVW, AMOVWZ, AMOVD:
   340  			q = p
   341  			if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
   342  				p.Mark |= LABEL | SYNC
   343  			}
   344  			continue
   345  
   346  		case AFABS,
   347  			AFABSCC,
   348  			AFADD,
   349  			AFADDCC,
   350  			AFCTIW,
   351  			AFCTIWCC,
   352  			AFCTIWZ,
   353  			AFCTIWZCC,
   354  			AFDIV,
   355  			AFDIVCC,
   356  			AFMADD,
   357  			AFMADDCC,
   358  			AFMOVD,
   359  			AFMOVDU,
   360  			/* case AFMOVDS: */
   361  			AFMOVS,
   362  			AFMOVSU,
   363  
   364  			/* case AFMOVSD: */
   365  			AFMSUB,
   366  			AFMSUBCC,
   367  			AFMUL,
   368  			AFMULCC,
   369  			AFNABS,
   370  			AFNABSCC,
   371  			AFNEG,
   372  			AFNEGCC,
   373  			AFNMADD,
   374  			AFNMADDCC,
   375  			AFNMSUB,
   376  			AFNMSUBCC,
   377  			AFRSP,
   378  			AFRSPCC,
   379  			AFSUB,
   380  			AFSUBCC:
   381  			q = p
   382  
   383  			p.Mark |= FLOAT
   384  			continue
   385  
   386  		case ABL,
   387  			ABCL,
   388  			obj.ADUFFZERO,
   389  			obj.ADUFFCOPY:
   390  			cursym.Text.Mark &^= LEAF
   391  			fallthrough
   392  
   393  		case ABC,
   394  			ABEQ,
   395  			ABGE,
   396  			ABGT,
   397  			ABLE,
   398  			ABLT,
   399  			ABNE,
   400  			ABR,
   401  			ABVC,
   402  			ABVS:
   403  			p.Mark |= BRANCH
   404  			q = p
   405  			q1 = p.Pcond
   406  			if q1 != nil {
   407  				for q1.As == obj.ANOP {
   408  					q1 = q1.Link
   409  					p.Pcond = q1
   410  				}
   411  
   412  				if q1.Mark&LEAF == 0 {
   413  					q1.Mark |= LABEL
   414  				}
   415  			} else {
   416  				p.Mark |= LABEL
   417  			}
   418  			q1 = p.Link
   419  			if q1 != nil {
   420  				q1.Mark |= LABEL
   421  			}
   422  			continue
   423  
   424  		case AFCMPO, AFCMPU:
   425  			q = p
   426  			p.Mark |= FCMP | FLOAT
   427  			continue
   428  
   429  		case obj.ARET:
   430  			q = p
   431  			if p.Link != nil {
   432  				p.Link.Mark |= LABEL
   433  			}
   434  			continue
   435  
   436  		case obj.ANOP:
   437  			q1 = p.Link
   438  			q.Link = q1 /* q is non-nop */
   439  			q1.Mark |= p.Mark
   440  			continue
   441  
   442  		default:
   443  			q = p
   444  			continue
   445  		}
   446  	}
   447  
   448  	autosize := int32(0)
   449  	var aoffset int
   450  	var mov obj.As
   451  	var p1 *obj.Prog
   452  	var p2 *obj.Prog
   453  	for p := cursym.Text; p != nil; p = p.Link {
   454  		o := p.As
   455  		switch o {
   456  		case obj.ATEXT:
   457  			mov = AMOVD
   458  			aoffset = 0
   459  			autosize = int32(textstksiz)
   460  
   461  			if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 {
   462  				// A leaf function with no locals has no frame.
   463  				p.From3.Offset |= obj.NOFRAME
   464  			}
   465  
   466  			if p.From3.Offset&obj.NOFRAME == 0 {
   467  				// If there is a stack frame at all, it includes
   468  				// space to save the LR.
   469  				autosize += int32(ctxt.FixedFrameSize())
   470  			}
   471  
   472  			p.To.Offset = int64(autosize)
   473  
   474  			q = p
   475  
   476  			if ctxt.Flag_shared && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" {
   477  				// When compiling Go into PIC, all functions must start
   478  				// with instructions to load the TOC pointer into r2:
   479  				//
   480  				//	addis r2, r12, .TOC.-func@ha
   481  				//	addi r2, r2, .TOC.-func@l+4
   482  				//
   483  				// We could probably skip this prologue in some situations
   484  				// but it's a bit subtle. However, it is both safe and
   485  				// necessary to leave the prologue off duffzero and
   486  				// duffcopy as we rely on being able to jump to a specific
   487  				// instruction offset for them, and stackBarrier is only
   488  				// ever called from an overwritten LR-save slot on the
   489  				// stack (when r12 will not be remotely the right thing)
   490  				// but fortunately does not access global data.
   491  				//
   492  				// These are AWORDS because there is no (afaict) way to
   493  				// generate the addis instruction except as part of the
   494  				// load of a large constant, and in that case there is no
   495  				// way to use r12 as the source.
   496  				q = obj.Appendp(ctxt, q)
   497  				q.As = AWORD
   498  				q.Lineno = p.Lineno
   499  				q.From.Type = obj.TYPE_CONST
   500  				q.From.Offset = 0x3c4c0000
   501  				q = obj.Appendp(ctxt, q)
   502  				q.As = AWORD
   503  				q.Lineno = p.Lineno
   504  				q.From.Type = obj.TYPE_CONST
   505  				q.From.Offset = 0x38420000
   506  				rel := obj.Addrel(ctxt.Cursym)
   507  				rel.Off = 0
   508  				rel.Siz = 8
   509  				rel.Sym = obj.Linklookup(ctxt, ".TOC.", 0)
   510  				rel.Type = obj.R_ADDRPOWER_PCREL
   511  			}
   512  
   513  			if cursym.Text.From3.Offset&obj.NOSPLIT == 0 {
   514  				q = stacksplit(ctxt, q, autosize) // emit split check
   515  			}
   516  
   517  			if autosize != 0 {
   518  				/* use MOVDU to adjust R1 when saving R31, if autosize is small */
   519  				if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG {
   520  					mov = AMOVDU
   521  					aoffset = int(-autosize)
   522  				} else {
   523  					q = obj.Appendp(ctxt, q)
   524  					q.As = AADD
   525  					q.Lineno = p.Lineno
   526  					q.From.Type = obj.TYPE_CONST
   527  					q.From.Offset = int64(-autosize)
   528  					q.To.Type = obj.TYPE_REG
   529  					q.To.Reg = REGSP
   530  					q.Spadj = +autosize
   531  				}
   532  			} else if cursym.Text.Mark&LEAF == 0 {
   533  				// A very few functions that do not return to their caller
   534  				// (e.g. gogo) are not identified as leaves but still have
   535  				// no frame.
   536  				cursym.Text.Mark |= LEAF
   537  			}
   538  
   539  			if cursym.Text.Mark&LEAF != 0 {
   540  				cursym.Leaf = true
   541  				break
   542  			}
   543  
   544  			q = obj.Appendp(ctxt, q)
   545  			q.As = AMOVD
   546  			q.Lineno = p.Lineno
   547  			q.From.Type = obj.TYPE_REG
   548  			q.From.Reg = REG_LR
   549  			q.To.Type = obj.TYPE_REG
   550  			q.To.Reg = REGTMP
   551  
   552  			q = obj.Appendp(ctxt, q)
   553  			q.As = mov
   554  			q.Lineno = p.Lineno
   555  			q.From.Type = obj.TYPE_REG
   556  			q.From.Reg = REGTMP
   557  			q.To.Type = obj.TYPE_MEM
   558  			q.To.Offset = int64(aoffset)
   559  			q.To.Reg = REGSP
   560  			if q.As == AMOVDU {
   561  				q.Spadj = int32(-aoffset)
   562  			}
   563  
   564  			if ctxt.Flag_shared {
   565  				q = obj.Appendp(ctxt, q)
   566  				q.As = AMOVD
   567  				q.Lineno = p.Lineno
   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 cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   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(ctxt, q)
   594  
   595  				q.As = AMOVD
   596  				q.From.Type = obj.TYPE_MEM
   597  				q.From.Reg = REGG
   598  				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   599  				q.To.Type = obj.TYPE_REG
   600  				q.To.Reg = REG_R3
   601  
   602  				q = obj.Appendp(ctxt, q)
   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(ctxt, q)
   610  				q.As = ABEQ
   611  				q.To.Type = obj.TYPE_BRANCH
   612  				p1 = q
   613  
   614  				q = obj.Appendp(ctxt, q)
   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(ctxt, q)
   623  				q.As = AADD
   624  				q.From.Type = obj.TYPE_CONST
   625  				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
   626  				q.Reg = REGSP
   627  				q.To.Type = obj.TYPE_REG
   628  				q.To.Reg = REG_R5
   629  
   630  				q = obj.Appendp(ctxt, q)
   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(ctxt, q)
   638  				q.As = ABNE
   639  				q.To.Type = obj.TYPE_BRANCH
   640  				p2 = q
   641  
   642  				q = obj.Appendp(ctxt, q)
   643  				q.As = AADD
   644  				q.From.Type = obj.TYPE_CONST
   645  				q.From.Offset = ctxt.FixedFrameSize()
   646  				q.Reg = REGSP
   647  				q.To.Type = obj.TYPE_REG
   648  				q.To.Reg = REG_R6
   649  
   650  				q = obj.Appendp(ctxt, q)
   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(ctxt, q)
   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  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   668  				break
   669  			}
   670  
   671  			retTarget := p.To.Sym
   672  
   673  			if 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 = ctxt.NewProg()
   696  				q.As = ABR
   697  				q.Lineno = p.Lineno
   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 = ctxt.NewProg()
   716  			q.As = AMOVD
   717  			q.Lineno = p.Lineno
   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 = ctxt.NewProg()
   730  
   731  				q.As = AMOVD
   732  				q.Lineno = p.Lineno
   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 = ctxt.NewProg()
   746  				q.As = AADD
   747  				q.Lineno = p.Lineno
   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 = ctxt.NewProg()
   759  			q1.As = ABR
   760  			q1.Lineno = p.Lineno
   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 stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
   827  	// MOVD	g_stackguard(g), R3
   828  	p = obj.Appendp(ctxt, p)
   829  
   830  	p.As = AMOVD
   831  	p.From.Type = obj.TYPE_MEM
   832  	p.From.Reg = REGG
   833  	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
   834  	if ctxt.Cursym.Cfunc {
   835  		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
   836  	}
   837  	p.To.Type = obj.TYPE_REG
   838  	p.To.Reg = REG_R3
   839  
   840  	var q *obj.Prog
   841  	if framesize <= obj.StackSmall {
   842  		// small stack: SP < stackguard
   843  		//	CMP	stackguard, SP
   844  		p = obj.Appendp(ctxt, p)
   845  
   846  		p.As = ACMPU
   847  		p.From.Type = obj.TYPE_REG
   848  		p.From.Reg = REG_R3
   849  		p.To.Type = obj.TYPE_REG
   850  		p.To.Reg = REGSP
   851  	} else if framesize <= obj.StackBig {
   852  		// large stack: SP-framesize < stackguard-StackSmall
   853  		//	ADD $-framesize, SP, R4
   854  		//	CMP stackguard, R4
   855  		p = obj.Appendp(ctxt, p)
   856  
   857  		p.As = AADD
   858  		p.From.Type = obj.TYPE_CONST
   859  		p.From.Offset = int64(-framesize)
   860  		p.Reg = REGSP
   861  		p.To.Type = obj.TYPE_REG
   862  		p.To.Reg = REG_R4
   863  
   864  		p = obj.Appendp(ctxt, p)
   865  		p.As = ACMPU
   866  		p.From.Type = obj.TYPE_REG
   867  		p.From.Reg = REG_R3
   868  		p.To.Type = obj.TYPE_REG
   869  		p.To.Reg = REG_R4
   870  	} else {
   871  		// Such a large stack we need to protect against wraparound.
   872  		// If SP is close to zero:
   873  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   874  		// The +StackGuard on both sides is required to keep the left side positive:
   875  		// SP is allowed to be slightly below stackguard. See stack.h.
   876  		//
   877  		// Preemption sets stackguard to StackPreempt, a very large value.
   878  		// That breaks the math above, so we have to check for that explicitly.
   879  		//	// stackguard is R3
   880  		//	CMP	R3, $StackPreempt
   881  		//	BEQ	label-of-call-to-morestack
   882  		//	ADD	$StackGuard, SP, R4
   883  		//	SUB	R3, R4
   884  		//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
   885  		//	CMPU	R31, R4
   886  		p = obj.Appendp(ctxt, p)
   887  
   888  		p.As = ACMP
   889  		p.From.Type = obj.TYPE_REG
   890  		p.From.Reg = REG_R3
   891  		p.To.Type = obj.TYPE_CONST
   892  		p.To.Offset = obj.StackPreempt
   893  
   894  		p = obj.Appendp(ctxt, p)
   895  		q = p
   896  		p.As = ABEQ
   897  		p.To.Type = obj.TYPE_BRANCH
   898  
   899  		p = obj.Appendp(ctxt, p)
   900  		p.As = AADD
   901  		p.From.Type = obj.TYPE_CONST
   902  		p.From.Offset = obj.StackGuard
   903  		p.Reg = REGSP
   904  		p.To.Type = obj.TYPE_REG
   905  		p.To.Reg = REG_R4
   906  
   907  		p = obj.Appendp(ctxt, p)
   908  		p.As = ASUB
   909  		p.From.Type = obj.TYPE_REG
   910  		p.From.Reg = REG_R3
   911  		p.To.Type = obj.TYPE_REG
   912  		p.To.Reg = REG_R4
   913  
   914  		p = obj.Appendp(ctxt, p)
   915  		p.As = AMOVD
   916  		p.From.Type = obj.TYPE_CONST
   917  		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
   918  		p.To.Type = obj.TYPE_REG
   919  		p.To.Reg = REGTMP
   920  
   921  		p = obj.Appendp(ctxt, p)
   922  		p.As = ACMPU
   923  		p.From.Type = obj.TYPE_REG
   924  		p.From.Reg = REGTMP
   925  		p.To.Type = obj.TYPE_REG
   926  		p.To.Reg = REG_R4
   927  	}
   928  
   929  	// q1: BLT	done
   930  	p = obj.Appendp(ctxt, p)
   931  	q1 := p
   932  
   933  	p.As = ABLT
   934  	p.To.Type = obj.TYPE_BRANCH
   935  
   936  	// MOVD	LR, R5
   937  	p = obj.Appendp(ctxt, p)
   938  
   939  	p.As = AMOVD
   940  	p.From.Type = obj.TYPE_REG
   941  	p.From.Reg = REG_LR
   942  	p.To.Type = obj.TYPE_REG
   943  	p.To.Reg = REG_R5
   944  	if q != nil {
   945  		q.Pcond = p
   946  	}
   947  
   948  	var morestacksym *obj.LSym
   949  	if ctxt.Cursym.Cfunc {
   950  		morestacksym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
   951  	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
   952  		morestacksym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
   953  	} else {
   954  		morestacksym = obj.Linklookup(ctxt, "runtime.morestack", 0)
   955  	}
   956  
   957  	if ctxt.Flag_dynlink {
   958  		// Avoid calling morestack via a PLT when dynamically linking. The
   959  		// PLT stubs generated by the system linker on ppc64le when "std r2,
   960  		// 24(r1)" to save the TOC pointer in their callers stack
   961  		// frame. Unfortunately (and necessarily) morestack is called before
   962  		// the function that calls it sets up its frame and so the PLT ends
   963  		// up smashing the saved TOC pointer for its caller's caller.
   964  		//
   965  		// According to the ABI documentation there is a mechanism to avoid
   966  		// the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
   967  		// relocation on the nop after the call to morestack) but at the time
   968  		// of writing it is not supported at all by gold and my attempt to
   969  		// use it with ld.bfd caused an internal linker error. So this hack
   970  		// seems preferable.
   971  
   972  		// MOVD $runtime.morestack(SB), R12
   973  		p = obj.Appendp(ctxt, p)
   974  		p.As = AMOVD
   975  		p.From.Type = obj.TYPE_MEM
   976  		p.From.Sym = morestacksym
   977  		p.From.Name = obj.NAME_GOTREF
   978  		p.To.Type = obj.TYPE_REG
   979  		p.To.Reg = REG_R12
   980  
   981  		// MOVD R12, CTR
   982  		p = obj.Appendp(ctxt, p)
   983  		p.As = AMOVD
   984  		p.From.Type = obj.TYPE_REG
   985  		p.From.Reg = REG_R12
   986  		p.To.Type = obj.TYPE_REG
   987  		p.To.Reg = REG_CTR
   988  
   989  		// BL CTR
   990  		p = obj.Appendp(ctxt, p)
   991  		p.As = obj.ACALL
   992  		p.From.Type = obj.TYPE_REG
   993  		p.From.Reg = REG_R12
   994  		p.To.Type = obj.TYPE_REG
   995  		p.To.Reg = REG_CTR
   996  	} else {
   997  		// BL	runtime.morestack(SB)
   998  		p = obj.Appendp(ctxt, p)
   999  
  1000  		p.As = ABL
  1001  		p.To.Type = obj.TYPE_BRANCH
  1002  		p.To.Sym = morestacksym
  1003  	}
  1004  	// BR	start
  1005  	p = obj.Appendp(ctxt, p)
  1006  
  1007  	p.As = ABR
  1008  	p.To.Type = obj.TYPE_BRANCH
  1009  	p.Pcond = ctxt.Cursym.Text.Link
  1010  
  1011  	// placeholder for q1's jump target
  1012  	p = obj.Appendp(ctxt, p)
  1013  
  1014  	p.As = obj.ANOP // zero-width place holder
  1015  	q1.Pcond = p
  1016  
  1017  	return p
  1018  }
  1019  
  1020  func follow(ctxt *obj.Link, s *obj.LSym) {
  1021  	ctxt.Cursym = s
  1022  
  1023  	firstp := ctxt.NewProg()
  1024  	lastp := firstp
  1025  	xfol(ctxt, s.Text, &lastp)
  1026  	lastp.Link = nil
  1027  	s.Text = firstp.Link
  1028  }
  1029  
  1030  func relinv(a obj.As) obj.As {
  1031  	switch a {
  1032  	case ABEQ:
  1033  		return ABNE
  1034  	case ABNE:
  1035  		return ABEQ
  1036  
  1037  	case ABGE:
  1038  		return ABLT
  1039  	case ABLT:
  1040  		return ABGE
  1041  
  1042  	case ABGT:
  1043  		return ABLE
  1044  	case ABLE:
  1045  		return ABGT
  1046  
  1047  	case ABVC:
  1048  		return ABVS
  1049  	case ABVS:
  1050  		return ABVC
  1051  	}
  1052  
  1053  	return 0
  1054  }
  1055  
  1056  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1057  	var q *obj.Prog
  1058  	var r *obj.Prog
  1059  	var b obj.As
  1060  	var i int
  1061  
  1062  loop:
  1063  	if p == nil {
  1064  		return
  1065  	}
  1066  	a := p.As
  1067  	if a == ABR {
  1068  		q = p.Pcond
  1069  		if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) {
  1070  			p.Mark |= FOLL
  1071  			(*last).Link = p
  1072  			*last = p
  1073  			p = p.Link
  1074  			xfol(ctxt, p, last)
  1075  			p = q
  1076  			if p != nil && p.Mark&FOLL == 0 {
  1077  				goto loop
  1078  			}
  1079  			return
  1080  		}
  1081  
  1082  		if q != nil {
  1083  			p.Mark |= FOLL
  1084  			p = q
  1085  			if p.Mark&FOLL == 0 {
  1086  				goto loop
  1087  			}
  1088  		}
  1089  	}
  1090  
  1091  	if p.Mark&FOLL != 0 {
  1092  		i = 0
  1093  		q = p
  1094  		for ; i < 4; i, q = i+1, q.Link {
  1095  			if q == *last || (q.Mark&NOSCHED != 0) {
  1096  				break
  1097  			}
  1098  			b = 0 /* set */
  1099  			a = q.As
  1100  			if a == obj.ANOP {
  1101  				i--
  1102  				continue
  1103  			}
  1104  
  1105  			if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
  1106  				goto copy
  1107  			}
  1108  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
  1109  				continue
  1110  			}
  1111  			b = relinv(a)
  1112  			if b == 0 {
  1113  				continue
  1114  			}
  1115  
  1116  		copy:
  1117  			for {
  1118  				r = ctxt.NewProg()
  1119  				*r = *p
  1120  				if r.Mark&FOLL == 0 {
  1121  					fmt.Printf("can't happen 1\n")
  1122  				}
  1123  				r.Mark |= FOLL
  1124  				if p != q {
  1125  					p = p.Link
  1126  					(*last).Link = r
  1127  					*last = r
  1128  					continue
  1129  				}
  1130  
  1131  				(*last).Link = r
  1132  				*last = r
  1133  				if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
  1134  					return
  1135  				}
  1136  				r.As = b
  1137  				r.Pcond = p.Link
  1138  				r.Link = p.Pcond
  1139  				if r.Link.Mark&FOLL == 0 {
  1140  					xfol(ctxt, r.Link, last)
  1141  				}
  1142  				if r.Pcond.Mark&FOLL == 0 {
  1143  					fmt.Printf("can't happen 2\n")
  1144  				}
  1145  				return
  1146  			}
  1147  		}
  1148  
  1149  		a = ABR
  1150  		q = ctxt.NewProg()
  1151  		q.As = a
  1152  		q.Lineno = p.Lineno
  1153  		q.To.Type = obj.TYPE_BRANCH
  1154  		q.To.Offset = p.Pc
  1155  		q.Pcond = p
  1156  		p = q
  1157  	}
  1158  
  1159  	p.Mark |= FOLL
  1160  	(*last).Link = p
  1161  	*last = p
  1162  	if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
  1163  		if p.Mark&NOSCHED != 0 {
  1164  			p = p.Link
  1165  			goto loop
  1166  		}
  1167  
  1168  		return
  1169  	}
  1170  
  1171  	if p.Pcond != nil {
  1172  		if a != ABL && p.Link != nil {
  1173  			xfol(ctxt, p.Link, last)
  1174  			p = p.Pcond
  1175  			if p == nil || (p.Mark&FOLL != 0) {
  1176  				return
  1177  			}
  1178  			goto loop
  1179  		}
  1180  	}
  1181  
  1182  	p = p.Link
  1183  	goto loop
  1184  }
  1185  
  1186  var Linkppc64 = obj.LinkArch{
  1187  	Arch:       sys.ArchPPC64,
  1188  	Preprocess: preprocess,
  1189  	Assemble:   span9,
  1190  	Follow:     follow,
  1191  	Progedit:   progedit,
  1192  }
  1193  
  1194  var Linkppc64le = obj.LinkArch{
  1195  	Arch:       sys.ArchPPC64LE,
  1196  	Preprocess: preprocess,
  1197  	Assemble:   span9,
  1198  	Follow:     follow,
  1199  	Progedit:   progedit,
  1200  }