github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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  	"encoding/binary"
    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  			ASTWCCC,
   305  			AECIWX,
   306  			AECOWX,
   307  			AEIEIO,
   308  			AICBI,
   309  			AISYNC,
   310  			ATLBIE,
   311  			ATLBIEL,
   312  			ASLBIA,
   313  			ASLBIE,
   314  			ASLBMFEE,
   315  			ASLBMFEV,
   316  			ASLBMTE,
   317  			ADCBF,
   318  			ADCBI,
   319  			ADCBST,
   320  			ADCBT,
   321  			ADCBTST,
   322  			ADCBZ,
   323  			ASYNC,
   324  			ATLBSYNC,
   325  			APTESYNC,
   326  			ATW,
   327  			AWORD,
   328  			ARFI,
   329  			ARFCI,
   330  			ARFID,
   331  			AHRFID:
   332  			q = p
   333  			p.Mark |= LABEL | SYNC
   334  			continue
   335  
   336  		case AMOVW, AMOVWZ, AMOVD:
   337  			q = p
   338  			if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
   339  				p.Mark |= LABEL | SYNC
   340  			}
   341  			continue
   342  
   343  		case AFABS,
   344  			AFABSCC,
   345  			AFADD,
   346  			AFADDCC,
   347  			AFCTIW,
   348  			AFCTIWCC,
   349  			AFCTIWZ,
   350  			AFCTIWZCC,
   351  			AFDIV,
   352  			AFDIVCC,
   353  			AFMADD,
   354  			AFMADDCC,
   355  			AFMOVD,
   356  			AFMOVDU,
   357  			/* case AFMOVDS: */
   358  			AFMOVS,
   359  			AFMOVSU,
   360  
   361  			/* case AFMOVSD: */
   362  			AFMSUB,
   363  			AFMSUBCC,
   364  			AFMUL,
   365  			AFMULCC,
   366  			AFNABS,
   367  			AFNABSCC,
   368  			AFNEG,
   369  			AFNEGCC,
   370  			AFNMADD,
   371  			AFNMADDCC,
   372  			AFNMSUB,
   373  			AFNMSUBCC,
   374  			AFRSP,
   375  			AFRSPCC,
   376  			AFSUB,
   377  			AFSUBCC:
   378  			q = p
   379  
   380  			p.Mark |= FLOAT
   381  			continue
   382  
   383  		case ABL,
   384  			ABCL,
   385  			obj.ADUFFZERO,
   386  			obj.ADUFFCOPY:
   387  			cursym.Text.Mark &^= LEAF
   388  			fallthrough
   389  
   390  		case ABC,
   391  			ABEQ,
   392  			ABGE,
   393  			ABGT,
   394  			ABLE,
   395  			ABLT,
   396  			ABNE,
   397  			ABR,
   398  			ABVC,
   399  			ABVS:
   400  			p.Mark |= BRANCH
   401  			q = p
   402  			q1 = p.Pcond
   403  			if q1 != nil {
   404  				for q1.As == obj.ANOP {
   405  					q1 = q1.Link
   406  					p.Pcond = q1
   407  				}
   408  
   409  				if q1.Mark&LEAF == 0 {
   410  					q1.Mark |= LABEL
   411  				}
   412  			} else {
   413  				p.Mark |= LABEL
   414  			}
   415  			q1 = p.Link
   416  			if q1 != nil {
   417  				q1.Mark |= LABEL
   418  			}
   419  			continue
   420  
   421  		case AFCMPO, AFCMPU:
   422  			q = p
   423  			p.Mark |= FCMP | FLOAT
   424  			continue
   425  
   426  		case obj.ARET:
   427  			q = p
   428  			if p.Link != nil {
   429  				p.Link.Mark |= LABEL
   430  			}
   431  			continue
   432  
   433  		case obj.ANOP:
   434  			q1 = p.Link
   435  			q.Link = q1 /* q is non-nop */
   436  			q1.Mark |= p.Mark
   437  			continue
   438  
   439  		default:
   440  			q = p
   441  			continue
   442  		}
   443  	}
   444  
   445  	autosize := int32(0)
   446  	var aoffset int
   447  	var mov obj.As
   448  	var p1 *obj.Prog
   449  	var p2 *obj.Prog
   450  	for p := cursym.Text; p != nil; p = p.Link {
   451  		o := p.As
   452  		switch o {
   453  		case obj.ATEXT:
   454  			mov = AMOVD
   455  			aoffset = 0
   456  			autosize = int32(textstksiz)
   457  
   458  			if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 {
   459  				// A leaf function with no locals has no frame.
   460  				p.From3.Offset |= obj.NOFRAME
   461  			}
   462  
   463  			if p.From3.Offset&obj.NOFRAME == 0 {
   464  				// If there is a stack frame at all, it includes
   465  				// space to save the LR.
   466  				autosize += int32(ctxt.FixedFrameSize())
   467  			}
   468  
   469  			p.To.Offset = int64(autosize)
   470  
   471  			q = p
   472  
   473  			if ctxt.Flag_shared != 0 && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" {
   474  				// When compiling Go into PIC, all functions must start
   475  				// with instructions to load the TOC pointer into r2:
   476  				//
   477  				//	addis r2, r12, .TOC.-func@ha
   478  				//	addi r2, r2, .TOC.-func@l+4
   479  				//
   480  				// We could probably skip this prologue in some situations
   481  				// but it's a bit subtle. However, it is both safe and
   482  				// necessary to leave the prologue off duffzero and
   483  				// duffcopy as we rely on being able to jump to a specific
   484  				// instruction offset for them, and stackBarrier is only
   485  				// ever called from an overwritten LR-save slot on the
   486  				// stack (when r12 will not be remotely the right thing)
   487  				// but fortunately does not access global data.
   488  				//
   489  				// These are AWORDS because there is no (afaict) way to
   490  				// generate the addis instruction except as part of the
   491  				// load of a large constant, and in that case there is no
   492  				// way to use r12 as the source.
   493  				q = obj.Appendp(ctxt, q)
   494  				q.As = AWORD
   495  				q.Lineno = p.Lineno
   496  				q.From.Type = obj.TYPE_CONST
   497  				q.From.Offset = 0x3c4c0000
   498  				q = obj.Appendp(ctxt, q)
   499  				q.As = AWORD
   500  				q.Lineno = p.Lineno
   501  				q.From.Type = obj.TYPE_CONST
   502  				q.From.Offset = 0x38420000
   503  				rel := obj.Addrel(ctxt.Cursym)
   504  				rel.Off = 0
   505  				rel.Siz = 8
   506  				rel.Sym = obj.Linklookup(ctxt, ".TOC.", 0)
   507  				rel.Type = obj.R_ADDRPOWER_PCREL
   508  			}
   509  
   510  			if cursym.Text.From3.Offset&obj.NOSPLIT == 0 {
   511  				q = stacksplit(ctxt, q, autosize) // emit split check
   512  			}
   513  
   514  			if autosize != 0 {
   515  				/* use MOVDU to adjust R1 when saving R31, if autosize is small */
   516  				if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG {
   517  					mov = AMOVDU
   518  					aoffset = int(-autosize)
   519  				} else {
   520  					q = obj.Appendp(ctxt, q)
   521  					q.As = AADD
   522  					q.Lineno = p.Lineno
   523  					q.From.Type = obj.TYPE_CONST
   524  					q.From.Offset = int64(-autosize)
   525  					q.To.Type = obj.TYPE_REG
   526  					q.To.Reg = REGSP
   527  					q.Spadj = +autosize
   528  				}
   529  			} else if cursym.Text.Mark&LEAF == 0 {
   530  				// A very few functions that do not return to their caller
   531  				// (e.g. gogo) are not identified as leaves but still have
   532  				// no frame.
   533  				cursym.Text.Mark |= LEAF
   534  			}
   535  
   536  			if cursym.Text.Mark&LEAF != 0 {
   537  				cursym.Leaf = true
   538  				break
   539  			}
   540  
   541  			q = obj.Appendp(ctxt, q)
   542  			q.As = AMOVD
   543  			q.Lineno = p.Lineno
   544  			q.From.Type = obj.TYPE_REG
   545  			q.From.Reg = REG_LR
   546  			q.To.Type = obj.TYPE_REG
   547  			q.To.Reg = REGTMP
   548  
   549  			q = obj.Appendp(ctxt, q)
   550  			q.As = mov
   551  			q.Lineno = p.Lineno
   552  			q.From.Type = obj.TYPE_REG
   553  			q.From.Reg = REGTMP
   554  			q.To.Type = obj.TYPE_MEM
   555  			q.To.Offset = int64(aoffset)
   556  			q.To.Reg = REGSP
   557  			if q.As == AMOVDU {
   558  				q.Spadj = int32(-aoffset)
   559  			}
   560  
   561  			if ctxt.Flag_shared != 0 {
   562  				q = obj.Appendp(ctxt, q)
   563  				q.As = AMOVD
   564  				q.Lineno = p.Lineno
   565  				q.From.Type = obj.TYPE_REG
   566  				q.From.Reg = REG_R2
   567  				q.To.Type = obj.TYPE_MEM
   568  				q.To.Reg = REGSP
   569  				q.To.Offset = 24
   570  			}
   571  
   572  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   573  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   574  				//
   575  				//	MOVD g_panic(g), R3
   576  				//	CMP R0, R3
   577  				//	BEQ end
   578  				//	MOVD panic_argp(R3), R4
   579  				//	ADD $(autosize+8), R1, R5
   580  				//	CMP R4, R5
   581  				//	BNE end
   582  				//	ADD $8, R1, R6
   583  				//	MOVD R6, panic_argp(R3)
   584  				// end:
   585  				//	NOP
   586  				//
   587  				// The NOP is needed to give the jumps somewhere to land.
   588  				// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
   589  
   590  				q = obj.Appendp(ctxt, q)
   591  
   592  				q.As = AMOVD
   593  				q.From.Type = obj.TYPE_MEM
   594  				q.From.Reg = REGG
   595  				q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   596  				q.To.Type = obj.TYPE_REG
   597  				q.To.Reg = REG_R3
   598  
   599  				q = obj.Appendp(ctxt, q)
   600  				q.As = ACMP
   601  				q.From.Type = obj.TYPE_REG
   602  				q.From.Reg = REG_R0
   603  				q.To.Type = obj.TYPE_REG
   604  				q.To.Reg = REG_R3
   605  
   606  				q = obj.Appendp(ctxt, q)
   607  				q.As = ABEQ
   608  				q.To.Type = obj.TYPE_BRANCH
   609  				p1 = q
   610  
   611  				q = obj.Appendp(ctxt, q)
   612  				q.As = AMOVD
   613  				q.From.Type = obj.TYPE_MEM
   614  				q.From.Reg = REG_R3
   615  				q.From.Offset = 0 // Panic.argp
   616  				q.To.Type = obj.TYPE_REG
   617  				q.To.Reg = REG_R4
   618  
   619  				q = obj.Appendp(ctxt, q)
   620  				q.As = AADD
   621  				q.From.Type = obj.TYPE_CONST
   622  				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
   623  				q.Reg = REGSP
   624  				q.To.Type = obj.TYPE_REG
   625  				q.To.Reg = REG_R5
   626  
   627  				q = obj.Appendp(ctxt, q)
   628  				q.As = ACMP
   629  				q.From.Type = obj.TYPE_REG
   630  				q.From.Reg = REG_R4
   631  				q.To.Type = obj.TYPE_REG
   632  				q.To.Reg = REG_R5
   633  
   634  				q = obj.Appendp(ctxt, q)
   635  				q.As = ABNE
   636  				q.To.Type = obj.TYPE_BRANCH
   637  				p2 = q
   638  
   639  				q = obj.Appendp(ctxt, q)
   640  				q.As = AADD
   641  				q.From.Type = obj.TYPE_CONST
   642  				q.From.Offset = ctxt.FixedFrameSize()
   643  				q.Reg = REGSP
   644  				q.To.Type = obj.TYPE_REG
   645  				q.To.Reg = REG_R6
   646  
   647  				q = obj.Appendp(ctxt, q)
   648  				q.As = AMOVD
   649  				q.From.Type = obj.TYPE_REG
   650  				q.From.Reg = REG_R6
   651  				q.To.Type = obj.TYPE_MEM
   652  				q.To.Reg = REG_R3
   653  				q.To.Offset = 0 // Panic.argp
   654  
   655  				q = obj.Appendp(ctxt, q)
   656  
   657  				q.As = obj.ANOP
   658  				p1.Pcond = q
   659  				p2.Pcond = q
   660  			}
   661  
   662  		case obj.ARET:
   663  			if p.From.Type == obj.TYPE_CONST {
   664  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   665  				break
   666  			}
   667  
   668  			retTarget := p.To.Sym
   669  
   670  			if cursym.Text.Mark&LEAF != 0 {
   671  				if autosize == 0 {
   672  					p.As = ABR
   673  					p.From = obj.Addr{}
   674  					if retTarget == nil {
   675  						p.To.Type = obj.TYPE_REG
   676  						p.To.Reg = REG_LR
   677  					} else {
   678  						p.To.Type = obj.TYPE_BRANCH
   679  						p.To.Sym = retTarget
   680  					}
   681  					p.Mark |= BRANCH
   682  					break
   683  				}
   684  
   685  				p.As = AADD
   686  				p.From.Type = obj.TYPE_CONST
   687  				p.From.Offset = int64(autosize)
   688  				p.To.Type = obj.TYPE_REG
   689  				p.To.Reg = REGSP
   690  				p.Spadj = -autosize
   691  
   692  				q = ctxt.NewProg()
   693  				q.As = ABR
   694  				q.Lineno = p.Lineno
   695  				q.To.Type = obj.TYPE_REG
   696  				q.To.Reg = REG_LR
   697  				q.Mark |= BRANCH
   698  				q.Spadj = +autosize
   699  
   700  				q.Link = p.Link
   701  				p.Link = q
   702  				break
   703  			}
   704  
   705  			p.As = AMOVD
   706  			p.From.Type = obj.TYPE_MEM
   707  			p.From.Offset = 0
   708  			p.From.Reg = REGSP
   709  			p.To.Type = obj.TYPE_REG
   710  			p.To.Reg = REGTMP
   711  
   712  			q = ctxt.NewProg()
   713  			q.As = AMOVD
   714  			q.Lineno = p.Lineno
   715  			q.From.Type = obj.TYPE_REG
   716  			q.From.Reg = REGTMP
   717  			q.To.Type = obj.TYPE_REG
   718  			q.To.Reg = REG_LR
   719  
   720  			q.Link = p.Link
   721  			p.Link = q
   722  			p = q
   723  
   724  			if false {
   725  				// Debug bad returns
   726  				q = ctxt.NewProg()
   727  
   728  				q.As = AMOVD
   729  				q.Lineno = p.Lineno
   730  				q.From.Type = obj.TYPE_MEM
   731  				q.From.Offset = 0
   732  				q.From.Reg = REGTMP
   733  				q.To.Type = obj.TYPE_REG
   734  				q.To.Reg = REGTMP
   735  
   736  				q.Link = p.Link
   737  				p.Link = q
   738  				p = q
   739  			}
   740  
   741  			if autosize != 0 {
   742  				q = ctxt.NewProg()
   743  				q.As = AADD
   744  				q.Lineno = p.Lineno
   745  				q.From.Type = obj.TYPE_CONST
   746  				q.From.Offset = int64(autosize)
   747  				q.To.Type = obj.TYPE_REG
   748  				q.To.Reg = REGSP
   749  				q.Spadj = -autosize
   750  
   751  				q.Link = p.Link
   752  				p.Link = q
   753  			}
   754  
   755  			q1 = ctxt.NewProg()
   756  			q1.As = ABR
   757  			q1.Lineno = p.Lineno
   758  			if retTarget == nil {
   759  				q1.To.Type = obj.TYPE_REG
   760  				q1.To.Reg = REG_LR
   761  			} else {
   762  				q1.To.Type = obj.TYPE_BRANCH
   763  				q1.To.Sym = retTarget
   764  			}
   765  			q1.Mark |= BRANCH
   766  			q1.Spadj = +autosize
   767  
   768  			q1.Link = q.Link
   769  			q.Link = q1
   770  		case AADD:
   771  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   772  				p.Spadj = int32(-p.From.Offset)
   773  			}
   774  		}
   775  	}
   776  }
   777  
   778  /*
   779  // instruction scheduling
   780  	if(debug['Q'] == 0)
   781  		return;
   782  
   783  	curtext = nil;
   784  	q = nil;	// p - 1
   785  	q1 = firstp;	// top of block
   786  	o = 0;		// count of instructions
   787  	for(p = firstp; p != nil; p = p1) {
   788  		p1 = p->link;
   789  		o++;
   790  		if(p->mark & NOSCHED){
   791  			if(q1 != p){
   792  				sched(q1, q);
   793  			}
   794  			for(; p != nil; p = p->link){
   795  				if(!(p->mark & NOSCHED))
   796  					break;
   797  				q = p;
   798  			}
   799  			p1 = p;
   800  			q1 = p;
   801  			o = 0;
   802  			continue;
   803  		}
   804  		if(p->mark & (LABEL|SYNC)) {
   805  			if(q1 != p)
   806  				sched(q1, q);
   807  			q1 = p;
   808  			o = 1;
   809  		}
   810  		if(p->mark & (BRANCH|SYNC)) {
   811  			sched(q1, p);
   812  			q1 = p1;
   813  			o = 0;
   814  		}
   815  		if(o >= NSCHED) {
   816  			sched(q1, p);
   817  			q1 = p1;
   818  			o = 0;
   819  		}
   820  		q = p;
   821  	}
   822  */
   823  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
   824  	// MOVD	g_stackguard(g), R3
   825  	p = obj.Appendp(ctxt, p)
   826  
   827  	p.As = AMOVD
   828  	p.From.Type = obj.TYPE_MEM
   829  	p.From.Reg = REGG
   830  	p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   831  	if ctxt.Cursym.Cfunc {
   832  		p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   833  	}
   834  	p.To.Type = obj.TYPE_REG
   835  	p.To.Reg = REG_R3
   836  
   837  	var q *obj.Prog
   838  	if framesize <= obj.StackSmall {
   839  		// small stack: SP < stackguard
   840  		//	CMP	stackguard, SP
   841  		p = obj.Appendp(ctxt, p)
   842  
   843  		p.As = ACMPU
   844  		p.From.Type = obj.TYPE_REG
   845  		p.From.Reg = REG_R3
   846  		p.To.Type = obj.TYPE_REG
   847  		p.To.Reg = REGSP
   848  	} else if framesize <= obj.StackBig {
   849  		// large stack: SP-framesize < stackguard-StackSmall
   850  		//	ADD $-framesize, SP, R4
   851  		//	CMP stackguard, R4
   852  		p = obj.Appendp(ctxt, p)
   853  
   854  		p.As = AADD
   855  		p.From.Type = obj.TYPE_CONST
   856  		p.From.Offset = int64(-framesize)
   857  		p.Reg = REGSP
   858  		p.To.Type = obj.TYPE_REG
   859  		p.To.Reg = REG_R4
   860  
   861  		p = obj.Appendp(ctxt, p)
   862  		p.As = ACMPU
   863  		p.From.Type = obj.TYPE_REG
   864  		p.From.Reg = REG_R3
   865  		p.To.Type = obj.TYPE_REG
   866  		p.To.Reg = REG_R4
   867  	} else {
   868  		// Such a large stack we need to protect against wraparound.
   869  		// If SP is close to zero:
   870  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   871  		// The +StackGuard on both sides is required to keep the left side positive:
   872  		// SP is allowed to be slightly below stackguard. See stack.h.
   873  		//
   874  		// Preemption sets stackguard to StackPreempt, a very large value.
   875  		// That breaks the math above, so we have to check for that explicitly.
   876  		//	// stackguard is R3
   877  		//	CMP	R3, $StackPreempt
   878  		//	BEQ	label-of-call-to-morestack
   879  		//	ADD	$StackGuard, SP, R4
   880  		//	SUB	R3, R4
   881  		//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
   882  		//	CMPU	R31, R4
   883  		p = obj.Appendp(ctxt, p)
   884  
   885  		p.As = ACMP
   886  		p.From.Type = obj.TYPE_REG
   887  		p.From.Reg = REG_R3
   888  		p.To.Type = obj.TYPE_CONST
   889  		p.To.Offset = obj.StackPreempt
   890  
   891  		p = obj.Appendp(ctxt, p)
   892  		q = p
   893  		p.As = ABEQ
   894  		p.To.Type = obj.TYPE_BRANCH
   895  
   896  		p = obj.Appendp(ctxt, p)
   897  		p.As = AADD
   898  		p.From.Type = obj.TYPE_CONST
   899  		p.From.Offset = obj.StackGuard
   900  		p.Reg = REGSP
   901  		p.To.Type = obj.TYPE_REG
   902  		p.To.Reg = REG_R4
   903  
   904  		p = obj.Appendp(ctxt, p)
   905  		p.As = ASUB
   906  		p.From.Type = obj.TYPE_REG
   907  		p.From.Reg = REG_R3
   908  		p.To.Type = obj.TYPE_REG
   909  		p.To.Reg = REG_R4
   910  
   911  		p = obj.Appendp(ctxt, p)
   912  		p.As = AMOVD
   913  		p.From.Type = obj.TYPE_CONST
   914  		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
   915  		p.To.Type = obj.TYPE_REG
   916  		p.To.Reg = REGTMP
   917  
   918  		p = obj.Appendp(ctxt, p)
   919  		p.As = ACMPU
   920  		p.From.Type = obj.TYPE_REG
   921  		p.From.Reg = REGTMP
   922  		p.To.Type = obj.TYPE_REG
   923  		p.To.Reg = REG_R4
   924  	}
   925  
   926  	// q1: BLT	done
   927  	p = obj.Appendp(ctxt, p)
   928  	q1 := p
   929  
   930  	p.As = ABLT
   931  	p.To.Type = obj.TYPE_BRANCH
   932  
   933  	// MOVD	LR, R5
   934  	p = obj.Appendp(ctxt, p)
   935  
   936  	p.As = AMOVD
   937  	p.From.Type = obj.TYPE_REG
   938  	p.From.Reg = REG_LR
   939  	p.To.Type = obj.TYPE_REG
   940  	p.To.Reg = REG_R5
   941  	if q != nil {
   942  		q.Pcond = p
   943  	}
   944  
   945  	var morestacksym *obj.LSym
   946  	if ctxt.Cursym.Cfunc {
   947  		morestacksym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
   948  	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
   949  		morestacksym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
   950  	} else {
   951  		morestacksym = obj.Linklookup(ctxt, "runtime.morestack", 0)
   952  	}
   953  
   954  	if ctxt.Flag_dynlink {
   955  		// Avoid calling morestack via a PLT when dynamically linking. The
   956  		// PLT stubs generated by the system linker on ppc64le when "std r2,
   957  		// 24(r1)" to save the TOC pointer in their callers stack
   958  		// frame. Unfortunately (and necessarily) morestack is called before
   959  		// the function that calls it sets up its frame and so the PLT ends
   960  		// up smashing the saved TOC pointer for its caller's caller.
   961  		//
   962  		// According to the ABI documentation there is a mechanism to avoid
   963  		// the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
   964  		// relocation on the nop after the call to morestack) but at the time
   965  		// of writing it is not supported at all by gold and my attempt to
   966  		// use it with ld.bfd caused an internal linker error. So this hack
   967  		// seems preferable.
   968  
   969  		// MOVD $runtime.morestack(SB), R12
   970  		p = obj.Appendp(ctxt, p)
   971  		p.As = AMOVD
   972  		p.From.Type = obj.TYPE_MEM
   973  		p.From.Sym = morestacksym
   974  		p.From.Name = obj.NAME_GOTREF
   975  		p.To.Type = obj.TYPE_REG
   976  		p.To.Reg = REG_R12
   977  
   978  		// MOVD R12, CTR
   979  		p = obj.Appendp(ctxt, p)
   980  		p.As = AMOVD
   981  		p.From.Type = obj.TYPE_REG
   982  		p.From.Reg = REG_R12
   983  		p.To.Type = obj.TYPE_REG
   984  		p.To.Reg = REG_CTR
   985  
   986  		// BL CTR
   987  		p = obj.Appendp(ctxt, p)
   988  		p.As = obj.ACALL
   989  		p.From.Type = obj.TYPE_REG
   990  		p.From.Reg = REG_R12
   991  		p.To.Type = obj.TYPE_REG
   992  		p.To.Reg = REG_CTR
   993  	} else {
   994  		// BL	runtime.morestack(SB)
   995  		p = obj.Appendp(ctxt, p)
   996  
   997  		p.As = ABL
   998  		p.To.Type = obj.TYPE_BRANCH
   999  		p.To.Sym = morestacksym
  1000  	}
  1001  	// BR	start
  1002  	p = obj.Appendp(ctxt, p)
  1003  
  1004  	p.As = ABR
  1005  	p.To.Type = obj.TYPE_BRANCH
  1006  	p.Pcond = ctxt.Cursym.Text.Link
  1007  
  1008  	// placeholder for q1's jump target
  1009  	p = obj.Appendp(ctxt, p)
  1010  
  1011  	p.As = obj.ANOP // zero-width place holder
  1012  	q1.Pcond = p
  1013  
  1014  	return p
  1015  }
  1016  
  1017  func follow(ctxt *obj.Link, s *obj.LSym) {
  1018  	ctxt.Cursym = s
  1019  
  1020  	firstp := ctxt.NewProg()
  1021  	lastp := firstp
  1022  	xfol(ctxt, s.Text, &lastp)
  1023  	lastp.Link = nil
  1024  	s.Text = firstp.Link
  1025  }
  1026  
  1027  func relinv(a obj.As) obj.As {
  1028  	switch a {
  1029  	case ABEQ:
  1030  		return ABNE
  1031  	case ABNE:
  1032  		return ABEQ
  1033  
  1034  	case ABGE:
  1035  		return ABLT
  1036  	case ABLT:
  1037  		return ABGE
  1038  
  1039  	case ABGT:
  1040  		return ABLE
  1041  	case ABLE:
  1042  		return ABGT
  1043  
  1044  	case ABVC:
  1045  		return ABVS
  1046  	case ABVS:
  1047  		return ABVC
  1048  	}
  1049  
  1050  	return 0
  1051  }
  1052  
  1053  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1054  	var q *obj.Prog
  1055  	var r *obj.Prog
  1056  	var b obj.As
  1057  	var i int
  1058  
  1059  loop:
  1060  	if p == nil {
  1061  		return
  1062  	}
  1063  	a := p.As
  1064  	if a == ABR {
  1065  		q = p.Pcond
  1066  		if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) {
  1067  			p.Mark |= FOLL
  1068  			(*last).Link = p
  1069  			*last = p
  1070  			p = p.Link
  1071  			xfol(ctxt, p, last)
  1072  			p = q
  1073  			if p != nil && p.Mark&FOLL == 0 {
  1074  				goto loop
  1075  			}
  1076  			return
  1077  		}
  1078  
  1079  		if q != nil {
  1080  			p.Mark |= FOLL
  1081  			p = q
  1082  			if p.Mark&FOLL == 0 {
  1083  				goto loop
  1084  			}
  1085  		}
  1086  	}
  1087  
  1088  	if p.Mark&FOLL != 0 {
  1089  		i = 0
  1090  		q = p
  1091  		for ; i < 4; i, q = i+1, q.Link {
  1092  			if q == *last || (q.Mark&NOSCHED != 0) {
  1093  				break
  1094  			}
  1095  			b = 0 /* set */
  1096  			a = q.As
  1097  			if a == obj.ANOP {
  1098  				i--
  1099  				continue
  1100  			}
  1101  
  1102  			if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
  1103  				goto copy
  1104  			}
  1105  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
  1106  				continue
  1107  			}
  1108  			b = relinv(a)
  1109  			if b == 0 {
  1110  				continue
  1111  			}
  1112  
  1113  		copy:
  1114  			for {
  1115  				r = ctxt.NewProg()
  1116  				*r = *p
  1117  				if r.Mark&FOLL == 0 {
  1118  					fmt.Printf("can't happen 1\n")
  1119  				}
  1120  				r.Mark |= FOLL
  1121  				if p != q {
  1122  					p = p.Link
  1123  					(*last).Link = r
  1124  					*last = r
  1125  					continue
  1126  				}
  1127  
  1128  				(*last).Link = r
  1129  				*last = r
  1130  				if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
  1131  					return
  1132  				}
  1133  				r.As = b
  1134  				r.Pcond = p.Link
  1135  				r.Link = p.Pcond
  1136  				if r.Link.Mark&FOLL == 0 {
  1137  					xfol(ctxt, r.Link, last)
  1138  				}
  1139  				if r.Pcond.Mark&FOLL == 0 {
  1140  					fmt.Printf("can't happen 2\n")
  1141  				}
  1142  				return
  1143  			}
  1144  		}
  1145  
  1146  		a = ABR
  1147  		q = ctxt.NewProg()
  1148  		q.As = a
  1149  		q.Lineno = p.Lineno
  1150  		q.To.Type = obj.TYPE_BRANCH
  1151  		q.To.Offset = p.Pc
  1152  		q.Pcond = p
  1153  		p = q
  1154  	}
  1155  
  1156  	p.Mark |= FOLL
  1157  	(*last).Link = p
  1158  	*last = p
  1159  	if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
  1160  		if p.Mark&NOSCHED != 0 {
  1161  			p = p.Link
  1162  			goto loop
  1163  		}
  1164  
  1165  		return
  1166  	}
  1167  
  1168  	if p.Pcond != nil {
  1169  		if a != ABL && p.Link != nil {
  1170  			xfol(ctxt, p.Link, last)
  1171  			p = p.Pcond
  1172  			if p == nil || (p.Mark&FOLL != 0) {
  1173  				return
  1174  			}
  1175  			goto loop
  1176  		}
  1177  	}
  1178  
  1179  	p = p.Link
  1180  	goto loop
  1181  }
  1182  
  1183  var Linkppc64 = obj.LinkArch{
  1184  	ByteOrder:  binary.BigEndian,
  1185  	Name:       "ppc64",
  1186  	Thechar:    '9',
  1187  	Preprocess: preprocess,
  1188  	Assemble:   span9,
  1189  	Follow:     follow,
  1190  	Progedit:   progedit,
  1191  	Minlc:      4,
  1192  	Ptrsize:    8,
  1193  	Regsize:    8,
  1194  }
  1195  
  1196  var Linkppc64le = obj.LinkArch{
  1197  	ByteOrder:  binary.LittleEndian,
  1198  	Name:       "ppc64le",
  1199  	Thechar:    '9',
  1200  	Preprocess: preprocess,
  1201  	Assemble:   span9,
  1202  	Follow:     follow,
  1203  	Progedit:   progedit,
  1204  	Minlc:      4,
  1205  	Ptrsize:    8,
  1206  	Regsize:    8,
  1207  }