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