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