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