github.com/aloncn/graphics-go@v0.0.1/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 int
   448  	var o int
   449  	var p1 *obj.Prog
   450  	var p2 *obj.Prog
   451  	for p := cursym.Text; p != nil; p = p.Link {
   452  		o = int(p.As)
   453  		switch o {
   454  		case obj.ATEXT:
   455  			mov = AMOVD
   456  			aoffset = 0
   457  			autosize = int32(textstksiz)
   458  
   459  			if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 {
   460  				// A leaf function with no locals has no frame.
   461  				p.From3.Offset |= obj.NOFRAME
   462  			}
   463  
   464  			if p.From3.Offset&obj.NOFRAME == 0 {
   465  				// If there is a stack frame at all, it includes
   466  				// space to save the LR.
   467  				autosize += int32(ctxt.FixedFrameSize())
   468  			}
   469  
   470  			p.To.Offset = int64(autosize)
   471  
   472  			q = p
   473  
   474  			if ctxt.Flag_shared != 0 && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" {
   475  				// When compiling Go into PIC, all functions must start
   476  				// with instructions to load the TOC pointer into r2:
   477  				//
   478  				//	addis r2, r12, .TOC.-func@ha
   479  				//	addi r2, r2, .TOC.-func@l+4
   480  				//
   481  				// We could probably skip this prologue in some situations
   482  				// but it's a bit subtle. However, it is both safe and
   483  				// necessary to leave the prologue off duffzero and
   484  				// duffcopy as we rely on being able to jump to a specific
   485  				// instruction offset for them, and stackBarrier is only
   486  				// ever called from an overwritten LR-save slot on the
   487  				// stack (when r12 will not be remotely the right thing)
   488  				// but fortunately does not access global data.
   489  				//
   490  				// These are AWORDS because there is no (afaict) way to
   491  				// generate the addis instruction except as part of the
   492  				// load of a large constant, and in that case there is no
   493  				// way to use r12 as the source.
   494  				q = obj.Appendp(ctxt, q)
   495  				q.As = AWORD
   496  				q.Lineno = p.Lineno
   497  				q.From.Type = obj.TYPE_CONST
   498  				q.From.Offset = 0x3c4c0000
   499  				q = obj.Appendp(ctxt, q)
   500  				q.As = AWORD
   501  				q.Lineno = p.Lineno
   502  				q.From.Type = obj.TYPE_CONST
   503  				q.From.Offset = 0x38420000
   504  				rel := obj.Addrel(ctxt.Cursym)
   505  				rel.Off = 0
   506  				rel.Siz = 8
   507  				rel.Sym = obj.Linklookup(ctxt, ".TOC.", 0)
   508  				rel.Type = obj.R_ADDRPOWER_PCREL
   509  			}
   510  
   511  			if cursym.Text.From3.Offset&obj.NOSPLIT == 0 {
   512  				q = stacksplit(ctxt, q, autosize) // emit split check
   513  			}
   514  
   515  			if autosize != 0 {
   516  				/* use MOVDU to adjust R1 when saving R31, if autosize is small */
   517  				if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG {
   518  					mov = AMOVDU
   519  					aoffset = int(-autosize)
   520  				} else {
   521  					q = obj.Appendp(ctxt, q)
   522  					q.As = AADD
   523  					q.Lineno = p.Lineno
   524  					q.From.Type = obj.TYPE_CONST
   525  					q.From.Offset = int64(-autosize)
   526  					q.To.Type = obj.TYPE_REG
   527  					q.To.Reg = REGSP
   528  					q.Spadj = +autosize
   529  				}
   530  			} else if cursym.Text.Mark&LEAF == 0 {
   531  				// A very few functions that do not return to their caller
   532  				// (e.g. gogo) are not identified as leaves but still have
   533  				// no frame.
   534  				cursym.Text.Mark |= LEAF
   535  			}
   536  
   537  			if cursym.Text.Mark&LEAF != 0 {
   538  				cursym.Leaf = 1
   539  				break
   540  			}
   541  
   542  			q = obj.Appendp(ctxt, q)
   543  			q.As = AMOVD
   544  			q.Lineno = p.Lineno
   545  			q.From.Type = obj.TYPE_REG
   546  			q.From.Reg = REG_LR
   547  			q.To.Type = obj.TYPE_REG
   548  			q.To.Reg = REGTMP
   549  
   550  			q = obj.Appendp(ctxt, q)
   551  			q.As = int16(mov)
   552  			q.Lineno = p.Lineno
   553  			q.From.Type = obj.TYPE_REG
   554  			q.From.Reg = REGTMP
   555  			q.To.Type = obj.TYPE_MEM
   556  			q.To.Offset = int64(aoffset)
   557  			q.To.Reg = REGSP
   558  			if q.As == AMOVDU {
   559  				q.Spadj = int32(-aoffset)
   560  			}
   561  
   562  			if ctxt.Flag_shared != 0 {
   563  				q = obj.Appendp(ctxt, q)
   564  				q.As = AMOVD
   565  				q.Lineno = p.Lineno
   566  				q.From.Type = obj.TYPE_REG
   567  				q.From.Reg = REG_R2
   568  				q.To.Type = obj.TYPE_MEM
   569  				q.To.Reg = REGSP
   570  				q.To.Offset = 24
   571  			}
   572  
   573  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   574  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   575  				//
   576  				//	MOVD g_panic(g), R3
   577  				//	CMP R0, R3
   578  				//	BEQ end
   579  				//	MOVD panic_argp(R3), R4
   580  				//	ADD $(autosize+8), R1, R5
   581  				//	CMP R4, R5
   582  				//	BNE end
   583  				//	ADD $8, R1, R6
   584  				//	MOVD R6, panic_argp(R3)
   585  				// end:
   586  				//	NOP
   587  				//
   588  				// The NOP is needed to give the jumps somewhere to land.
   589  				// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
   590  
   591  				q = obj.Appendp(ctxt, q)
   592  
   593  				q.As = AMOVD
   594  				q.From.Type = obj.TYPE_MEM
   595  				q.From.Reg = REGG
   596  				q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   597  				q.To.Type = obj.TYPE_REG
   598  				q.To.Reg = REG_R3
   599  
   600  				q = obj.Appendp(ctxt, q)
   601  				q.As = ACMP
   602  				q.From.Type = obj.TYPE_REG
   603  				q.From.Reg = REG_R0
   604  				q.To.Type = obj.TYPE_REG
   605  				q.To.Reg = REG_R3
   606  
   607  				q = obj.Appendp(ctxt, q)
   608  				q.As = ABEQ
   609  				q.To.Type = obj.TYPE_BRANCH
   610  				p1 = q
   611  
   612  				q = obj.Appendp(ctxt, q)
   613  				q.As = AMOVD
   614  				q.From.Type = obj.TYPE_MEM
   615  				q.From.Reg = REG_R3
   616  				q.From.Offset = 0 // Panic.argp
   617  				q.To.Type = obj.TYPE_REG
   618  				q.To.Reg = REG_R4
   619  
   620  				q = obj.Appendp(ctxt, q)
   621  				q.As = AADD
   622  				q.From.Type = obj.TYPE_CONST
   623  				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
   624  				q.Reg = REGSP
   625  				q.To.Type = obj.TYPE_REG
   626  				q.To.Reg = REG_R5
   627  
   628  				q = obj.Appendp(ctxt, q)
   629  				q.As = ACMP
   630  				q.From.Type = obj.TYPE_REG
   631  				q.From.Reg = REG_R4
   632  				q.To.Type = obj.TYPE_REG
   633  				q.To.Reg = REG_R5
   634  
   635  				q = obj.Appendp(ctxt, q)
   636  				q.As = ABNE
   637  				q.To.Type = obj.TYPE_BRANCH
   638  				p2 = q
   639  
   640  				q = obj.Appendp(ctxt, q)
   641  				q.As = AADD
   642  				q.From.Type = obj.TYPE_CONST
   643  				q.From.Offset = ctxt.FixedFrameSize()
   644  				q.Reg = REGSP
   645  				q.To.Type = obj.TYPE_REG
   646  				q.To.Reg = REG_R6
   647  
   648  				q = obj.Appendp(ctxt, q)
   649  				q.As = AMOVD
   650  				q.From.Type = obj.TYPE_REG
   651  				q.From.Reg = REG_R6
   652  				q.To.Type = obj.TYPE_MEM
   653  				q.To.Reg = REG_R3
   654  				q.To.Offset = 0 // Panic.argp
   655  
   656  				q = obj.Appendp(ctxt, q)
   657  
   658  				q.As = obj.ANOP
   659  				p1.Pcond = q
   660  				p2.Pcond = q
   661  			}
   662  
   663  		case obj.ARET:
   664  			if p.From.Type == obj.TYPE_CONST {
   665  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   666  				break
   667  			}
   668  
   669  			retTarget := p.To.Sym
   670  
   671  			if cursym.Text.Mark&LEAF != 0 {
   672  				if autosize == 0 {
   673  					p.As = ABR
   674  					p.From = obj.Addr{}
   675  					if retTarget == nil {
   676  						p.To.Type = obj.TYPE_REG
   677  						p.To.Reg = REG_LR
   678  					} else {
   679  						p.To.Type = obj.TYPE_BRANCH
   680  						p.To.Sym = retTarget
   681  					}
   682  					p.Mark |= BRANCH
   683  					break
   684  				}
   685  
   686  				p.As = AADD
   687  				p.From.Type = obj.TYPE_CONST
   688  				p.From.Offset = int64(autosize)
   689  				p.To.Type = obj.TYPE_REG
   690  				p.To.Reg = REGSP
   691  				p.Spadj = -autosize
   692  
   693  				q = ctxt.NewProg()
   694  				q.As = ABR
   695  				q.Lineno = p.Lineno
   696  				q.To.Type = obj.TYPE_REG
   697  				q.To.Reg = REG_LR
   698  				q.Mark |= BRANCH
   699  				q.Spadj = +autosize
   700  
   701  				q.Link = p.Link
   702  				p.Link = q
   703  				break
   704  			}
   705  
   706  			p.As = AMOVD
   707  			p.From.Type = obj.TYPE_MEM
   708  			p.From.Offset = 0
   709  			p.From.Reg = REGSP
   710  			p.To.Type = obj.TYPE_REG
   711  			p.To.Reg = REGTMP
   712  
   713  			q = ctxt.NewProg()
   714  			q.As = AMOVD
   715  			q.Lineno = p.Lineno
   716  			q.From.Type = obj.TYPE_REG
   717  			q.From.Reg = REGTMP
   718  			q.To.Type = obj.TYPE_REG
   719  			q.To.Reg = REG_LR
   720  
   721  			q.Link = p.Link
   722  			p.Link = q
   723  			p = q
   724  
   725  			if false {
   726  				// Debug bad returns
   727  				q = ctxt.NewProg()
   728  
   729  				q.As = AMOVD
   730  				q.Lineno = p.Lineno
   731  				q.From.Type = obj.TYPE_MEM
   732  				q.From.Offset = 0
   733  				q.From.Reg = REGTMP
   734  				q.To.Type = obj.TYPE_REG
   735  				q.To.Reg = REGTMP
   736  
   737  				q.Link = p.Link
   738  				p.Link = q
   739  				p = q
   740  			}
   741  
   742  			if autosize != 0 {
   743  				q = ctxt.NewProg()
   744  				q.As = AADD
   745  				q.Lineno = p.Lineno
   746  				q.From.Type = obj.TYPE_CONST
   747  				q.From.Offset = int64(autosize)
   748  				q.To.Type = obj.TYPE_REG
   749  				q.To.Reg = REGSP
   750  				q.Spadj = -autosize
   751  
   752  				q.Link = p.Link
   753  				p.Link = q
   754  			}
   755  
   756  			q1 = ctxt.NewProg()
   757  			q1.As = ABR
   758  			q1.Lineno = p.Lineno
   759  			if retTarget == nil {
   760  				q1.To.Type = obj.TYPE_REG
   761  				q1.To.Reg = REG_LR
   762  			} else {
   763  				q1.To.Type = obj.TYPE_BRANCH
   764  				q1.To.Sym = retTarget
   765  			}
   766  			q1.Mark |= BRANCH
   767  			q1.Spadj = +autosize
   768  
   769  			q1.Link = q.Link
   770  			q.Link = q1
   771  		case AADD:
   772  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   773  				p.Spadj = int32(-p.From.Offset)
   774  			}
   775  		}
   776  	}
   777  }
   778  
   779  /*
   780  // instruction scheduling
   781  	if(debug['Q'] == 0)
   782  		return;
   783  
   784  	curtext = nil;
   785  	q = nil;	// p - 1
   786  	q1 = firstp;	// top of block
   787  	o = 0;		// count of instructions
   788  	for(p = firstp; p != nil; p = p1) {
   789  		p1 = p->link;
   790  		o++;
   791  		if(p->mark & NOSCHED){
   792  			if(q1 != p){
   793  				sched(q1, q);
   794  			}
   795  			for(; p != nil; p = p->link){
   796  				if(!(p->mark & NOSCHED))
   797  					break;
   798  				q = p;
   799  			}
   800  			p1 = p;
   801  			q1 = p;
   802  			o = 0;
   803  			continue;
   804  		}
   805  		if(p->mark & (LABEL|SYNC)) {
   806  			if(q1 != p)
   807  				sched(q1, q);
   808  			q1 = p;
   809  			o = 1;
   810  		}
   811  		if(p->mark & (BRANCH|SYNC)) {
   812  			sched(q1, p);
   813  			q1 = p1;
   814  			o = 0;
   815  		}
   816  		if(o >= NSCHED) {
   817  			sched(q1, p);
   818  			q1 = p1;
   819  			o = 0;
   820  		}
   821  		q = p;
   822  	}
   823  */
   824  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
   825  	// MOVD	g_stackguard(g), R3
   826  	p = obj.Appendp(ctxt, p)
   827  
   828  	p.As = AMOVD
   829  	p.From.Type = obj.TYPE_MEM
   830  	p.From.Reg = REGG
   831  	p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
   832  	if ctxt.Cursym.Cfunc != 0 {
   833  		p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
   834  	}
   835  	p.To.Type = obj.TYPE_REG
   836  	p.To.Reg = REG_R3
   837  
   838  	var q *obj.Prog
   839  	if framesize <= obj.StackSmall {
   840  		// small stack: SP < stackguard
   841  		//	CMP	stackguard, SP
   842  		p = obj.Appendp(ctxt, p)
   843  
   844  		p.As = ACMPU
   845  		p.From.Type = obj.TYPE_REG
   846  		p.From.Reg = REG_R3
   847  		p.To.Type = obj.TYPE_REG
   848  		p.To.Reg = REGSP
   849  	} else if framesize <= obj.StackBig {
   850  		// large stack: SP-framesize < stackguard-StackSmall
   851  		//	ADD $-framesize, SP, R4
   852  		//	CMP stackguard, R4
   853  		p = obj.Appendp(ctxt, p)
   854  
   855  		p.As = AADD
   856  		p.From.Type = obj.TYPE_CONST
   857  		p.From.Offset = int64(-framesize)
   858  		p.Reg = REGSP
   859  		p.To.Type = obj.TYPE_REG
   860  		p.To.Reg = REG_R4
   861  
   862  		p = obj.Appendp(ctxt, p)
   863  		p.As = ACMPU
   864  		p.From.Type = obj.TYPE_REG
   865  		p.From.Reg = REG_R3
   866  		p.To.Type = obj.TYPE_REG
   867  		p.To.Reg = REG_R4
   868  	} else {
   869  		// Such a large stack we need to protect against wraparound.
   870  		// If SP is close to zero:
   871  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   872  		// The +StackGuard on both sides is required to keep the left side positive:
   873  		// SP is allowed to be slightly below stackguard. See stack.h.
   874  		//
   875  		// Preemption sets stackguard to StackPreempt, a very large value.
   876  		// That breaks the math above, so we have to check for that explicitly.
   877  		//	// stackguard is R3
   878  		//	CMP	R3, $StackPreempt
   879  		//	BEQ	label-of-call-to-morestack
   880  		//	ADD	$StackGuard, SP, R4
   881  		//	SUB	R3, R4
   882  		//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
   883  		//	CMPU	R31, R4
   884  		p = obj.Appendp(ctxt, p)
   885  
   886  		p.As = ACMP
   887  		p.From.Type = obj.TYPE_REG
   888  		p.From.Reg = REG_R3
   889  		p.To.Type = obj.TYPE_CONST
   890  		p.To.Offset = obj.StackPreempt
   891  
   892  		p = obj.Appendp(ctxt, p)
   893  		q = p
   894  		p.As = ABEQ
   895  		p.To.Type = obj.TYPE_BRANCH
   896  
   897  		p = obj.Appendp(ctxt, p)
   898  		p.As = AADD
   899  		p.From.Type = obj.TYPE_CONST
   900  		p.From.Offset = obj.StackGuard
   901  		p.Reg = REGSP
   902  		p.To.Type = obj.TYPE_REG
   903  		p.To.Reg = REG_R4
   904  
   905  		p = obj.Appendp(ctxt, p)
   906  		p.As = ASUB
   907  		p.From.Type = obj.TYPE_REG
   908  		p.From.Reg = REG_R3
   909  		p.To.Type = obj.TYPE_REG
   910  		p.To.Reg = REG_R4
   911  
   912  		p = obj.Appendp(ctxt, p)
   913  		p.As = AMOVD
   914  		p.From.Type = obj.TYPE_CONST
   915  		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
   916  		p.To.Type = obj.TYPE_REG
   917  		p.To.Reg = REGTMP
   918  
   919  		p = obj.Appendp(ctxt, p)
   920  		p.As = ACMPU
   921  		p.From.Type = obj.TYPE_REG
   922  		p.From.Reg = REGTMP
   923  		p.To.Type = obj.TYPE_REG
   924  		p.To.Reg = REG_R4
   925  	}
   926  
   927  	// q1: BLT	done
   928  	p = obj.Appendp(ctxt, p)
   929  	q1 := p
   930  
   931  	p.As = ABLT
   932  	p.To.Type = obj.TYPE_BRANCH
   933  
   934  	// MOVD	LR, R5
   935  	p = obj.Appendp(ctxt, p)
   936  
   937  	p.As = AMOVD
   938  	p.From.Type = obj.TYPE_REG
   939  	p.From.Reg = REG_LR
   940  	p.To.Type = obj.TYPE_REG
   941  	p.To.Reg = REG_R5
   942  	if q != nil {
   943  		q.Pcond = p
   944  	}
   945  
   946  	var morestacksym *obj.LSym
   947  	if ctxt.Cursym.Cfunc != 0 {
   948  		morestacksym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
   949  	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
   950  		morestacksym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
   951  	} else {
   952  		morestacksym = obj.Linklookup(ctxt, "runtime.morestack", 0)
   953  	}
   954  
   955  	if ctxt.Flag_dynlink {
   956  		// Avoid calling morestack via a PLT when dynamically linking.  The
   957  		// PLT stubs generated by the system linker on ppc64le when "std r2,
   958  		// 24(r1)" to save the TOC pointer in their callers stack
   959  		// frame. Unfortunately (and necessarily) morestack is called before
   960  		// the function that calls it sets up its frame and so the PLT ends
   961  		// up smashing the saved TOC pointer for its caller's caller.
   962  		//
   963  		// According to the ABI documentation there is a mechanism to avoid
   964  		// the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
   965  		// relocation on the nop after the call to morestack) but at the time
   966  		// of writing it is not supported at all by gold and my attempt to
   967  		// use it with ld.bfd caused an internal linker error. So this hack
   968  		// seems preferable.
   969  
   970  		// MOVD $runtime.morestack(SB), R12
   971  		p = obj.Appendp(ctxt, p)
   972  		p.As = AMOVD
   973  		p.From.Type = obj.TYPE_MEM
   974  		p.From.Sym = morestacksym
   975  		p.From.Name = obj.NAME_GOTREF
   976  		p.To.Type = obj.TYPE_REG
   977  		p.To.Reg = REG_R12
   978  
   979  		// MOVD R12, CTR
   980  		p = obj.Appendp(ctxt, p)
   981  		p.As = AMOVD
   982  		p.From.Type = obj.TYPE_REG
   983  		p.From.Reg = REG_R12
   984  		p.To.Type = obj.TYPE_REG
   985  		p.To.Reg = REG_CTR
   986  
   987  		// BL CTR
   988  		p = obj.Appendp(ctxt, p)
   989  		p.As = obj.ACALL
   990  		p.From.Type = obj.TYPE_REG
   991  		p.From.Reg = REG_R12
   992  		p.To.Type = obj.TYPE_REG
   993  		p.To.Reg = REG_CTR
   994  	} else {
   995  		// BL	runtime.morestack(SB)
   996  		p = obj.Appendp(ctxt, p)
   997  
   998  		p.As = ABL
   999  		p.To.Type = obj.TYPE_BRANCH
  1000  		p.To.Sym = morestacksym
  1001  	}
  1002  	// BR	start
  1003  	p = obj.Appendp(ctxt, p)
  1004  
  1005  	p.As = ABR
  1006  	p.To.Type = obj.TYPE_BRANCH
  1007  	p.Pcond = ctxt.Cursym.Text.Link
  1008  
  1009  	// placeholder for q1's jump target
  1010  	p = obj.Appendp(ctxt, p)
  1011  
  1012  	p.As = obj.ANOP // zero-width place holder
  1013  	q1.Pcond = p
  1014  
  1015  	return p
  1016  }
  1017  
  1018  func follow(ctxt *obj.Link, s *obj.LSym) {
  1019  	ctxt.Cursym = s
  1020  
  1021  	firstp := ctxt.NewProg()
  1022  	lastp := firstp
  1023  	xfol(ctxt, s.Text, &lastp)
  1024  	lastp.Link = nil
  1025  	s.Text = firstp.Link
  1026  }
  1027  
  1028  func relinv(a int) int {
  1029  	switch a {
  1030  	case ABEQ:
  1031  		return ABNE
  1032  	case ABNE:
  1033  		return ABEQ
  1034  
  1035  	case ABGE:
  1036  		return ABLT
  1037  	case ABLT:
  1038  		return ABGE
  1039  
  1040  	case ABGT:
  1041  		return ABLE
  1042  	case ABLE:
  1043  		return ABGT
  1044  
  1045  	case ABVC:
  1046  		return ABVS
  1047  	case ABVS:
  1048  		return ABVC
  1049  	}
  1050  
  1051  	return 0
  1052  }
  1053  
  1054  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1055  	var q *obj.Prog
  1056  	var r *obj.Prog
  1057  	var a int
  1058  	var b int
  1059  	var i int
  1060  
  1061  loop:
  1062  	if p == nil {
  1063  		return
  1064  	}
  1065  	a = int(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 = int(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("cant 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 = int16(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("cant happen 2\n")
  1143  				}
  1144  				return
  1145  			}
  1146  		}
  1147  
  1148  		a = ABR
  1149  		q = ctxt.NewProg()
  1150  		q.As = int16(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  	ByteOrder:  binary.BigEndian,
  1187  	Name:       "ppc64",
  1188  	Thechar:    '9',
  1189  	Preprocess: preprocess,
  1190  	Assemble:   span9,
  1191  	Follow:     follow,
  1192  	Progedit:   progedit,
  1193  	Minlc:      4,
  1194  	Ptrsize:    8,
  1195  	Regsize:    8,
  1196  }
  1197  
  1198  var Linkppc64le = obj.LinkArch{
  1199  	ByteOrder:  binary.LittleEndian,
  1200  	Name:       "ppc64le",
  1201  	Thechar:    '9',
  1202  	Preprocess: preprocess,
  1203  	Assemble:   span9,
  1204  	Follow:     follow,
  1205  	Progedit:   progedit,
  1206  	Minlc:      4,
  1207  	Ptrsize:    8,
  1208  	Regsize:    8,
  1209  }