github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/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  		ARETURN,
    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.Name = obj.NAME_EXTERN
    67  			p.From.Offset = 0
    68  		}
    69  
    70  	case AFMOVD:
    71  		if p.From.Type == obj.TYPE_FCONST {
    72  			i64 := math.Float64bits(p.From.Val.(float64))
    73  			literal := fmt.Sprintf("$f64.%016x", i64)
    74  			s := obj.Linklookup(ctxt, literal, 0)
    75  			s.Size = 8
    76  			p.From.Type = obj.TYPE_MEM
    77  			p.From.Sym = s
    78  			p.From.Name = obj.NAME_EXTERN
    79  			p.From.Offset = 0
    80  		}
    81  
    82  		// Put >32-bit constants in memory and load them
    83  	case AMOVD:
    84  		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 {
    85  			literal := fmt.Sprintf("$i64.%016x", uint64(p.From.Offset))
    86  			s := obj.Linklookup(ctxt, literal, 0)
    87  			s.Size = 8
    88  			p.From.Type = obj.TYPE_MEM
    89  			p.From.Sym = s
    90  			p.From.Name = obj.NAME_EXTERN
    91  			p.From.Offset = 0
    92  		}
    93  	}
    94  
    95  	// Rewrite SUB constants into ADD.
    96  	switch p.As {
    97  	case ASUBC:
    98  		if p.From.Type == obj.TYPE_CONST {
    99  			p.From.Offset = -p.From.Offset
   100  			p.As = AADDC
   101  		}
   102  
   103  	case ASUBCCC:
   104  		if p.From.Type == obj.TYPE_CONST {
   105  			p.From.Offset = -p.From.Offset
   106  			p.As = AADDCCC
   107  		}
   108  
   109  	case ASUB:
   110  		if p.From.Type == obj.TYPE_CONST {
   111  			p.From.Offset = -p.From.Offset
   112  			p.As = AADD
   113  		}
   114  	}
   115  }
   116  
   117  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   118  	if ctxt.Symmorestack[0] == nil {
   119  		ctxt.Symmorestack[0] = obj.Linklookup(ctxt, "runtime.morestack", 0)
   120  		ctxt.Symmorestack[1] = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
   121  	}
   122  
   123  	// TODO(minux): add morestack short-cuts with small fixed frame-size.
   124  	ctxt.Cursym = cursym
   125  
   126  	if cursym.Text == nil || cursym.Text.Link == nil {
   127  		return
   128  	}
   129  
   130  	p := cursym.Text
   131  	textstksiz := p.To.Offset
   132  
   133  	cursym.Args = p.To.Val.(int32)
   134  	cursym.Locals = int32(textstksiz)
   135  
   136  	/*
   137  	 * find leaf subroutines
   138  	 * strip NOPs
   139  	 * expand RET
   140  	 * expand BECOME pseudo
   141  	 */
   142  	if ctxt.Debugvlog != 0 {
   143  		fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime())
   144  	}
   145  	obj.Bflush(ctxt.Bso)
   146  
   147  	var q *obj.Prog
   148  	var q1 *obj.Prog
   149  	for p := cursym.Text; p != nil; p = p.Link {
   150  		switch p.As {
   151  		/* too hard, just leave alone */
   152  		case obj.ATEXT:
   153  			q = p
   154  
   155  			p.Mark |= LABEL | LEAF | SYNC
   156  			if p.Link != nil {
   157  				p.Link.Mark |= LABEL
   158  			}
   159  
   160  		case ANOR:
   161  			q = p
   162  			if p.To.Type == obj.TYPE_REG {
   163  				if p.To.Reg == REGZERO {
   164  					p.Mark |= LABEL | SYNC
   165  				}
   166  			}
   167  
   168  		case ALWAR,
   169  			ASTWCCC,
   170  			AECIWX,
   171  			AECOWX,
   172  			AEIEIO,
   173  			AICBI,
   174  			AISYNC,
   175  			ATLBIE,
   176  			ATLBIEL,
   177  			ASLBIA,
   178  			ASLBIE,
   179  			ASLBMFEE,
   180  			ASLBMFEV,
   181  			ASLBMTE,
   182  			ADCBF,
   183  			ADCBI,
   184  			ADCBST,
   185  			ADCBT,
   186  			ADCBTST,
   187  			ADCBZ,
   188  			ASYNC,
   189  			ATLBSYNC,
   190  			APTESYNC,
   191  			ATW,
   192  			AWORD,
   193  			ARFI,
   194  			ARFCI,
   195  			ARFID,
   196  			AHRFID:
   197  			q = p
   198  			p.Mark |= LABEL | SYNC
   199  			continue
   200  
   201  		case AMOVW, AMOVWZ, AMOVD:
   202  			q = p
   203  			if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
   204  				p.Mark |= LABEL | SYNC
   205  			}
   206  			continue
   207  
   208  		case AFABS,
   209  			AFABSCC,
   210  			AFADD,
   211  			AFADDCC,
   212  			AFCTIW,
   213  			AFCTIWCC,
   214  			AFCTIWZ,
   215  			AFCTIWZCC,
   216  			AFDIV,
   217  			AFDIVCC,
   218  			AFMADD,
   219  			AFMADDCC,
   220  			AFMOVD,
   221  			AFMOVDU,
   222  			/* case AFMOVDS: */
   223  			AFMOVS,
   224  			AFMOVSU,
   225  
   226  			/* case AFMOVSD: */
   227  			AFMSUB,
   228  			AFMSUBCC,
   229  			AFMUL,
   230  			AFMULCC,
   231  			AFNABS,
   232  			AFNABSCC,
   233  			AFNEG,
   234  			AFNEGCC,
   235  			AFNMADD,
   236  			AFNMADDCC,
   237  			AFNMSUB,
   238  			AFNMSUBCC,
   239  			AFRSP,
   240  			AFRSPCC,
   241  			AFSUB,
   242  			AFSUBCC:
   243  			q = p
   244  
   245  			p.Mark |= FLOAT
   246  			continue
   247  
   248  		case ABL,
   249  			ABCL,
   250  			obj.ADUFFZERO,
   251  			obj.ADUFFCOPY:
   252  			cursym.Text.Mark &^= LEAF
   253  			fallthrough
   254  
   255  		case ABC,
   256  			ABEQ,
   257  			ABGE,
   258  			ABGT,
   259  			ABLE,
   260  			ABLT,
   261  			ABNE,
   262  			ABR,
   263  			ABVC,
   264  			ABVS:
   265  			p.Mark |= BRANCH
   266  			q = p
   267  			q1 = p.Pcond
   268  			if q1 != nil {
   269  				for q1.As == obj.ANOP {
   270  					q1 = q1.Link
   271  					p.Pcond = q1
   272  				}
   273  
   274  				if q1.Mark&LEAF == 0 {
   275  					q1.Mark |= LABEL
   276  				}
   277  			} else {
   278  				p.Mark |= LABEL
   279  			}
   280  			q1 = p.Link
   281  			if q1 != nil {
   282  				q1.Mark |= LABEL
   283  			}
   284  			continue
   285  
   286  		case AFCMPO, AFCMPU:
   287  			q = p
   288  			p.Mark |= FCMP | FLOAT
   289  			continue
   290  
   291  		case ARETURN:
   292  			q = p
   293  			if p.Link != nil {
   294  				p.Link.Mark |= LABEL
   295  			}
   296  			continue
   297  
   298  		case obj.ANOP:
   299  			q1 = p.Link
   300  			q.Link = q1 /* q is non-nop */
   301  			q1.Mark |= p.Mark
   302  			continue
   303  
   304  		default:
   305  			q = p
   306  			continue
   307  		}
   308  	}
   309  
   310  	autosize := int32(0)
   311  	var aoffset int
   312  	var mov int
   313  	var o int
   314  	var p1 *obj.Prog
   315  	var p2 *obj.Prog
   316  	for p := cursym.Text; p != nil; p = p.Link {
   317  		o = int(p.As)
   318  		switch o {
   319  		case obj.ATEXT:
   320  			mov = AMOVD
   321  			aoffset = 0
   322  			autosize = int32(textstksiz + 8)
   323  			if (p.Mark&LEAF != 0) && autosize <= 8 {
   324  				autosize = 0
   325  			} else if autosize&4 != 0 {
   326  				autosize += 4
   327  			}
   328  			p.To.Offset = int64(autosize) - 8
   329  
   330  			if p.From3.Offset&obj.NOSPLIT == 0 {
   331  				p = stacksplit(ctxt, p, autosize, cursym.Text.From3.Offset&obj.NEEDCTXT == 0) // emit split check
   332  			}
   333  
   334  			q = p
   335  
   336  			if autosize != 0 {
   337  				/* use MOVDU to adjust R1 when saving R31, if autosize is small */
   338  				if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG {
   339  					mov = AMOVDU
   340  					aoffset = int(-autosize)
   341  				} else {
   342  					q = obj.Appendp(ctxt, p)
   343  					q.As = AADD
   344  					q.Lineno = p.Lineno
   345  					q.From.Type = obj.TYPE_CONST
   346  					q.From.Offset = int64(-autosize)
   347  					q.To.Type = obj.TYPE_REG
   348  					q.To.Reg = REGSP
   349  					q.Spadj = +autosize
   350  				}
   351  			} else if cursym.Text.Mark&LEAF == 0 {
   352  				if ctxt.Debugvlog != 0 {
   353  					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name)
   354  					obj.Bflush(ctxt.Bso)
   355  				}
   356  
   357  				cursym.Text.Mark |= LEAF
   358  			}
   359  
   360  			if cursym.Text.Mark&LEAF != 0 {
   361  				cursym.Leaf = 1
   362  				break
   363  			}
   364  
   365  			q = obj.Appendp(ctxt, q)
   366  			q.As = AMOVD
   367  			q.Lineno = p.Lineno
   368  			q.From.Type = obj.TYPE_REG
   369  			q.From.Reg = REG_LR
   370  			q.To.Type = obj.TYPE_REG
   371  			q.To.Reg = REGTMP
   372  
   373  			q = obj.Appendp(ctxt, q)
   374  			q.As = int16(mov)
   375  			q.Lineno = p.Lineno
   376  			q.From.Type = obj.TYPE_REG
   377  			q.From.Reg = REGTMP
   378  			q.To.Type = obj.TYPE_MEM
   379  			q.To.Offset = int64(aoffset)
   380  			q.To.Reg = REGSP
   381  			if q.As == AMOVDU {
   382  				q.Spadj = int32(-aoffset)
   383  			}
   384  
   385  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   386  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   387  				//
   388  				//	MOVD g_panic(g), R3
   389  				//	CMP R0, R3
   390  				//	BEQ end
   391  				//	MOVD panic_argp(R3), R4
   392  				//	ADD $(autosize+8), R1, R5
   393  				//	CMP R4, R5
   394  				//	BNE end
   395  				//	ADD $8, R1, R6
   396  				//	MOVD R6, panic_argp(R3)
   397  				// end:
   398  				//	NOP
   399  				//
   400  				// The NOP is needed to give the jumps somewhere to land.
   401  				// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
   402  
   403  				q = obj.Appendp(ctxt, q)
   404  
   405  				q.As = AMOVD
   406  				q.From.Type = obj.TYPE_MEM
   407  				q.From.Reg = REGG
   408  				q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   409  				q.To.Type = obj.TYPE_REG
   410  				q.To.Reg = REG_R3
   411  
   412  				q = obj.Appendp(ctxt, q)
   413  				q.As = ACMP
   414  				q.From.Type = obj.TYPE_REG
   415  				q.From.Reg = REG_R0
   416  				q.To.Type = obj.TYPE_REG
   417  				q.To.Reg = REG_R3
   418  
   419  				q = obj.Appendp(ctxt, q)
   420  				q.As = ABEQ
   421  				q.To.Type = obj.TYPE_BRANCH
   422  				p1 = q
   423  
   424  				q = obj.Appendp(ctxt, q)
   425  				q.As = AMOVD
   426  				q.From.Type = obj.TYPE_MEM
   427  				q.From.Reg = REG_R3
   428  				q.From.Offset = 0 // Panic.argp
   429  				q.To.Type = obj.TYPE_REG
   430  				q.To.Reg = REG_R4
   431  
   432  				q = obj.Appendp(ctxt, q)
   433  				q.As = AADD
   434  				q.From.Type = obj.TYPE_CONST
   435  				q.From.Offset = int64(autosize) + 8
   436  				q.Reg = REGSP
   437  				q.To.Type = obj.TYPE_REG
   438  				q.To.Reg = REG_R5
   439  
   440  				q = obj.Appendp(ctxt, q)
   441  				q.As = ACMP
   442  				q.From.Type = obj.TYPE_REG
   443  				q.From.Reg = REG_R4
   444  				q.To.Type = obj.TYPE_REG
   445  				q.To.Reg = REG_R5
   446  
   447  				q = obj.Appendp(ctxt, q)
   448  				q.As = ABNE
   449  				q.To.Type = obj.TYPE_BRANCH
   450  				p2 = q
   451  
   452  				q = obj.Appendp(ctxt, q)
   453  				q.As = AADD
   454  				q.From.Type = obj.TYPE_CONST
   455  				q.From.Offset = 8
   456  				q.Reg = REGSP
   457  				q.To.Type = obj.TYPE_REG
   458  				q.To.Reg = REG_R6
   459  
   460  				q = obj.Appendp(ctxt, q)
   461  				q.As = AMOVD
   462  				q.From.Type = obj.TYPE_REG
   463  				q.From.Reg = REG_R6
   464  				q.To.Type = obj.TYPE_MEM
   465  				q.To.Reg = REG_R3
   466  				q.To.Offset = 0 // Panic.argp
   467  
   468  				q = obj.Appendp(ctxt, q)
   469  
   470  				q.As = obj.ANOP
   471  				p1.Pcond = q
   472  				p2.Pcond = q
   473  			}
   474  
   475  		case ARETURN:
   476  			if p.From.Type == obj.TYPE_CONST {
   477  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   478  				break
   479  			}
   480  
   481  			if p.To.Sym != nil { // retjmp
   482  				p.As = ABR
   483  				p.To.Type = obj.TYPE_BRANCH
   484  				break
   485  			}
   486  
   487  			if cursym.Text.Mark&LEAF != 0 {
   488  				if autosize == 0 {
   489  					p.As = ABR
   490  					p.From = obj.Addr{}
   491  					p.To.Type = obj.TYPE_REG
   492  					p.To.Reg = REG_LR
   493  					p.Mark |= BRANCH
   494  					break
   495  				}
   496  
   497  				p.As = AADD
   498  				p.From.Type = obj.TYPE_CONST
   499  				p.From.Offset = int64(autosize)
   500  				p.To.Type = obj.TYPE_REG
   501  				p.To.Reg = REGSP
   502  				p.Spadj = -autosize
   503  
   504  				q = ctxt.NewProg()
   505  				q.As = ABR
   506  				q.Lineno = p.Lineno
   507  				q.To.Type = obj.TYPE_REG
   508  				q.To.Reg = REG_LR
   509  				q.Mark |= BRANCH
   510  				q.Spadj = +autosize
   511  
   512  				q.Link = p.Link
   513  				p.Link = q
   514  				break
   515  			}
   516  
   517  			p.As = AMOVD
   518  			p.From.Type = obj.TYPE_MEM
   519  			p.From.Offset = 0
   520  			p.From.Reg = REGSP
   521  			p.To.Type = obj.TYPE_REG
   522  			p.To.Reg = REGTMP
   523  
   524  			q = ctxt.NewProg()
   525  			q.As = AMOVD
   526  			q.Lineno = p.Lineno
   527  			q.From.Type = obj.TYPE_REG
   528  			q.From.Reg = REGTMP
   529  			q.To.Type = obj.TYPE_REG
   530  			q.To.Reg = REG_LR
   531  
   532  			q.Link = p.Link
   533  			p.Link = q
   534  			p = q
   535  
   536  			if false {
   537  				// Debug bad returns
   538  				q = ctxt.NewProg()
   539  
   540  				q.As = AMOVD
   541  				q.Lineno = p.Lineno
   542  				q.From.Type = obj.TYPE_MEM
   543  				q.From.Offset = 0
   544  				q.From.Reg = REGTMP
   545  				q.To.Type = obj.TYPE_REG
   546  				q.To.Reg = REGTMP
   547  
   548  				q.Link = p.Link
   549  				p.Link = q
   550  				p = q
   551  			}
   552  
   553  			if autosize != 0 {
   554  				q = ctxt.NewProg()
   555  				q.As = AADD
   556  				q.Lineno = p.Lineno
   557  				q.From.Type = obj.TYPE_CONST
   558  				q.From.Offset = int64(autosize)
   559  				q.To.Type = obj.TYPE_REG
   560  				q.To.Reg = REGSP
   561  				q.Spadj = -autosize
   562  
   563  				q.Link = p.Link
   564  				p.Link = q
   565  			}
   566  
   567  			q1 = ctxt.NewProg()
   568  			q1.As = ABR
   569  			q1.Lineno = p.Lineno
   570  			q1.To.Type = obj.TYPE_REG
   571  			q1.To.Reg = REG_LR
   572  			q1.Mark |= BRANCH
   573  			q1.Spadj = +autosize
   574  
   575  			q1.Link = q.Link
   576  			q.Link = q1
   577  
   578  		case AADD:
   579  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   580  				p.Spadj = int32(-p.From.Offset)
   581  			}
   582  		}
   583  	}
   584  }
   585  
   586  /*
   587  // instruction scheduling
   588  	if(debug['Q'] == 0)
   589  		return;
   590  
   591  	curtext = nil;
   592  	q = nil;	// p - 1
   593  	q1 = firstp;	// top of block
   594  	o = 0;		// count of instructions
   595  	for(p = firstp; p != nil; p = p1) {
   596  		p1 = p->link;
   597  		o++;
   598  		if(p->mark & NOSCHED){
   599  			if(q1 != p){
   600  				sched(q1, q);
   601  			}
   602  			for(; p != nil; p = p->link){
   603  				if(!(p->mark & NOSCHED))
   604  					break;
   605  				q = p;
   606  			}
   607  			p1 = p;
   608  			q1 = p;
   609  			o = 0;
   610  			continue;
   611  		}
   612  		if(p->mark & (LABEL|SYNC)) {
   613  			if(q1 != p)
   614  				sched(q1, q);
   615  			q1 = p;
   616  			o = 1;
   617  		}
   618  		if(p->mark & (BRANCH|SYNC)) {
   619  			sched(q1, p);
   620  			q1 = p1;
   621  			o = 0;
   622  		}
   623  		if(o >= NSCHED) {
   624  			sched(q1, p);
   625  			q1 = p1;
   626  			o = 0;
   627  		}
   628  		q = p;
   629  	}
   630  */
   631  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, noctxt bool) *obj.Prog {
   632  	// MOVD	g_stackguard(g), R3
   633  	p = obj.Appendp(ctxt, p)
   634  
   635  	p.As = AMOVD
   636  	p.From.Type = obj.TYPE_MEM
   637  	p.From.Reg = REGG
   638  	p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   639  	if ctxt.Cursym.Cfunc != 0 {
   640  		p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   641  	}
   642  	p.To.Type = obj.TYPE_REG
   643  	p.To.Reg = REG_R3
   644  
   645  	var q *obj.Prog
   646  	if framesize <= obj.StackSmall {
   647  		// small stack: SP < stackguard
   648  		//	CMP	stackguard, SP
   649  		p = obj.Appendp(ctxt, p)
   650  
   651  		p.As = ACMPU
   652  		p.From.Type = obj.TYPE_REG
   653  		p.From.Reg = REG_R3
   654  		p.To.Type = obj.TYPE_REG
   655  		p.To.Reg = REGSP
   656  	} else if framesize <= obj.StackBig {
   657  		// large stack: SP-framesize < stackguard-StackSmall
   658  		//	ADD $-framesize, SP, R4
   659  		//	CMP stackguard, R4
   660  		p = obj.Appendp(ctxt, p)
   661  
   662  		p.As = AADD
   663  		p.From.Type = obj.TYPE_CONST
   664  		p.From.Offset = int64(-framesize)
   665  		p.Reg = REGSP
   666  		p.To.Type = obj.TYPE_REG
   667  		p.To.Reg = REG_R4
   668  
   669  		p = obj.Appendp(ctxt, p)
   670  		p.As = ACMPU
   671  		p.From.Type = obj.TYPE_REG
   672  		p.From.Reg = REG_R3
   673  		p.To.Type = obj.TYPE_REG
   674  		p.To.Reg = REG_R4
   675  	} else {
   676  		// Such a large stack we need to protect against wraparound.
   677  		// If SP is close to zero:
   678  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   679  		// The +StackGuard on both sides is required to keep the left side positive:
   680  		// SP is allowed to be slightly below stackguard. See stack.h.
   681  		//
   682  		// Preemption sets stackguard to StackPreempt, a very large value.
   683  		// That breaks the math above, so we have to check for that explicitly.
   684  		//	// stackguard is R3
   685  		//	CMP	R3, $StackPreempt
   686  		//	BEQ	label-of-call-to-morestack
   687  		//	ADD	$StackGuard, SP, R4
   688  		//	SUB	R3, R4
   689  		//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
   690  		//	CMPU	R31, R4
   691  		p = obj.Appendp(ctxt, p)
   692  
   693  		p.As = ACMP
   694  		p.From.Type = obj.TYPE_REG
   695  		p.From.Reg = REG_R3
   696  		p.To.Type = obj.TYPE_CONST
   697  		p.To.Offset = obj.StackPreempt
   698  
   699  		p = obj.Appendp(ctxt, p)
   700  		q = p
   701  		p.As = ABEQ
   702  		p.To.Type = obj.TYPE_BRANCH
   703  
   704  		p = obj.Appendp(ctxt, p)
   705  		p.As = AADD
   706  		p.From.Type = obj.TYPE_CONST
   707  		p.From.Offset = obj.StackGuard
   708  		p.Reg = REGSP
   709  		p.To.Type = obj.TYPE_REG
   710  		p.To.Reg = REG_R4
   711  
   712  		p = obj.Appendp(ctxt, p)
   713  		p.As = ASUB
   714  		p.From.Type = obj.TYPE_REG
   715  		p.From.Reg = REG_R3
   716  		p.To.Type = obj.TYPE_REG
   717  		p.To.Reg = REG_R4
   718  
   719  		p = obj.Appendp(ctxt, p)
   720  		p.As = AMOVD
   721  		p.From.Type = obj.TYPE_CONST
   722  		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
   723  		p.To.Type = obj.TYPE_REG
   724  		p.To.Reg = REGTMP
   725  
   726  		p = obj.Appendp(ctxt, p)
   727  		p.As = ACMPU
   728  		p.From.Type = obj.TYPE_REG
   729  		p.From.Reg = REGTMP
   730  		p.To.Type = obj.TYPE_REG
   731  		p.To.Reg = REG_R4
   732  	}
   733  
   734  	// q1: BLT	done
   735  	p = obj.Appendp(ctxt, p)
   736  	q1 := p
   737  
   738  	p.As = ABLT
   739  	p.To.Type = obj.TYPE_BRANCH
   740  
   741  	// MOVD	LR, R5
   742  	p = obj.Appendp(ctxt, p)
   743  
   744  	p.As = AMOVD
   745  	p.From.Type = obj.TYPE_REG
   746  	p.From.Reg = REG_LR
   747  	p.To.Type = obj.TYPE_REG
   748  	p.To.Reg = REG_R5
   749  	if q != nil {
   750  		q.Pcond = p
   751  	}
   752  
   753  	// BL	runtime.morestack(SB)
   754  	p = obj.Appendp(ctxt, p)
   755  
   756  	p.As = ABL
   757  	p.To.Type = obj.TYPE_BRANCH
   758  	if ctxt.Cursym.Cfunc != 0 {
   759  		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
   760  	} else {
   761  		p.To.Sym = ctxt.Symmorestack[bool2int(noctxt)]
   762  	}
   763  
   764  	// BR	start
   765  	p = obj.Appendp(ctxt, p)
   766  
   767  	p.As = ABR
   768  	p.To.Type = obj.TYPE_BRANCH
   769  	p.Pcond = ctxt.Cursym.Text.Link
   770  
   771  	// placeholder for q1's jump target
   772  	p = obj.Appendp(ctxt, p)
   773  
   774  	p.As = obj.ANOP // zero-width place holder
   775  	q1.Pcond = p
   776  
   777  	return p
   778  }
   779  
   780  func follow(ctxt *obj.Link, s *obj.LSym) {
   781  	ctxt.Cursym = s
   782  
   783  	firstp := ctxt.NewProg()
   784  	lastp := firstp
   785  	xfol(ctxt, s.Text, &lastp)
   786  	lastp.Link = nil
   787  	s.Text = firstp.Link
   788  }
   789  
   790  func relinv(a int) int {
   791  	switch a {
   792  	case ABEQ:
   793  		return ABNE
   794  	case ABNE:
   795  		return ABEQ
   796  
   797  	case ABGE:
   798  		return ABLT
   799  	case ABLT:
   800  		return ABGE
   801  
   802  	case ABGT:
   803  		return ABLE
   804  	case ABLE:
   805  		return ABGT
   806  
   807  	case ABVC:
   808  		return ABVS
   809  	case ABVS:
   810  		return ABVC
   811  	}
   812  
   813  	return 0
   814  }
   815  
   816  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   817  	var q *obj.Prog
   818  	var r *obj.Prog
   819  	var a int
   820  	var b int
   821  	var i int
   822  
   823  loop:
   824  	if p == nil {
   825  		return
   826  	}
   827  	a = int(p.As)
   828  	if a == ABR {
   829  		q = p.Pcond
   830  		if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) {
   831  			p.Mark |= FOLL
   832  			(*last).Link = p
   833  			*last = p
   834  			p = p.Link
   835  			xfol(ctxt, p, last)
   836  			p = q
   837  			if p != nil && p.Mark&FOLL == 0 {
   838  				goto loop
   839  			}
   840  			return
   841  		}
   842  
   843  		if q != nil {
   844  			p.Mark |= FOLL
   845  			p = q
   846  			if p.Mark&FOLL == 0 {
   847  				goto loop
   848  			}
   849  		}
   850  	}
   851  
   852  	if p.Mark&FOLL != 0 {
   853  		i = 0
   854  		q = p
   855  		for ; i < 4; i, q = i+1, q.Link {
   856  			if q == *last || (q.Mark&NOSCHED != 0) {
   857  				break
   858  			}
   859  			b = 0 /* set */
   860  			a = int(q.As)
   861  			if a == obj.ANOP {
   862  				i--
   863  				continue
   864  			}
   865  
   866  			if a == ABR || a == ARETURN || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
   867  				goto copy
   868  			}
   869  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   870  				continue
   871  			}
   872  			b = relinv(a)
   873  			if b == 0 {
   874  				continue
   875  			}
   876  
   877  		copy:
   878  			for {
   879  				r = ctxt.NewProg()
   880  				*r = *p
   881  				if r.Mark&FOLL == 0 {
   882  					fmt.Printf("cant happen 1\n")
   883  				}
   884  				r.Mark |= FOLL
   885  				if p != q {
   886  					p = p.Link
   887  					(*last).Link = r
   888  					*last = r
   889  					continue
   890  				}
   891  
   892  				(*last).Link = r
   893  				*last = r
   894  				if a == ABR || a == ARETURN || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
   895  					return
   896  				}
   897  				r.As = int16(b)
   898  				r.Pcond = p.Link
   899  				r.Link = p.Pcond
   900  				if r.Link.Mark&FOLL == 0 {
   901  					xfol(ctxt, r.Link, last)
   902  				}
   903  				if r.Pcond.Mark&FOLL == 0 {
   904  					fmt.Printf("cant happen 2\n")
   905  				}
   906  				return
   907  			}
   908  		}
   909  
   910  		a = ABR
   911  		q = ctxt.NewProg()
   912  		q.As = int16(a)
   913  		q.Lineno = p.Lineno
   914  		q.To.Type = obj.TYPE_BRANCH
   915  		q.To.Offset = p.Pc
   916  		q.Pcond = p
   917  		p = q
   918  	}
   919  
   920  	p.Mark |= FOLL
   921  	(*last).Link = p
   922  	*last = p
   923  	if a == ABR || a == ARETURN || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
   924  		if p.Mark&NOSCHED != 0 {
   925  			p = p.Link
   926  			goto loop
   927  		}
   928  
   929  		return
   930  	}
   931  
   932  	if p.Pcond != nil {
   933  		if a != ABL && p.Link != nil {
   934  			xfol(ctxt, p.Link, last)
   935  			p = p.Pcond
   936  			if p == nil || (p.Mark&FOLL != 0) {
   937  				return
   938  			}
   939  			goto loop
   940  		}
   941  	}
   942  
   943  	p = p.Link
   944  	goto loop
   945  }
   946  
   947  var Linkppc64 = obj.LinkArch{
   948  	ByteOrder:  binary.BigEndian,
   949  	Name:       "ppc64",
   950  	Thechar:    '9',
   951  	Preprocess: preprocess,
   952  	Assemble:   span9,
   953  	Follow:     follow,
   954  	Progedit:   progedit,
   955  	Minlc:      4,
   956  	Ptrsize:    8,
   957  	Regsize:    8,
   958  }
   959  
   960  var Linkppc64le = obj.LinkArch{
   961  	ByteOrder:  binary.LittleEndian,
   962  	Name:       "ppc64le",
   963  	Thechar:    '9',
   964  	Preprocess: preprocess,
   965  	Assemble:   span9,
   966  	Follow:     follow,
   967  	Progedit:   progedit,
   968  	Minlc:      4,
   969  	Ptrsize:    8,
   970  	Regsize:    8,
   971  }