github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/src/cmd/internal/obj/ppc64/obj9.go (about)

     1  // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
     2  //
     3  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     4  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     5  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     6  //	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
     7  //	Portions Copyright © 2004,2006 Bruce Ellis
     8  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
     9  //	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
    10  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
    11  //
    12  // Permission is hereby granted, free of charge, to any person obtaining a copy
    13  // of this software and associated documentation files (the "Software"), to deal
    14  // in the Software without restriction, including without limitation the rights
    15  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    16  // copies of the Software, and to permit persons to whom the Software is
    17  // furnished to do so, subject to the following conditions:
    18  //
    19  // The above copyright notice and this permission notice shall be included in
    20  // all copies or substantial portions of the Software.
    21  //
    22  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    23  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    24  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    25  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    26  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    27  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    28  // THE SOFTWARE.
    29  
    30  package ppc64
    31  
    32  import (
    33  	"cmd/internal/obj"
    34  	"cmd/internal/sys"
    35  	"fmt"
    36  	"math"
    37  )
    38  
    39  func progedit(ctxt *obj.Link, p *obj.Prog) {
    40  	p.From.Class = 0
    41  	p.To.Class = 0
    42  
    43  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
    44  	switch p.As {
    45  	case ABR,
    46  		ABL,
    47  		obj.ARET,
    48  		obj.ADUFFZERO,
    49  		obj.ADUFFCOPY:
    50  		if p.To.Sym != nil {
    51  			p.To.Type = obj.TYPE_BRANCH
    52  		}
    53  	}
    54  
    55  	// Rewrite float constants to values stored in memory.
    56  	switch p.As {
    57  	case AFMOVS:
    58  		if p.From.Type == obj.TYPE_FCONST {
    59  			f32 := float32(p.From.Val.(float64))
    60  			i32 := math.Float32bits(f32)
    61  			literal := fmt.Sprintf("$f32.%08x", i32)
    62  			s := obj.Linklookup(ctxt, literal, 0)
    63  			s.Size = 4
    64  			p.From.Type = obj.TYPE_MEM
    65  			p.From.Sym = s
    66  			p.From.Sym.Set(obj.AttrLocal, 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.Set(obj.AttrLocal, 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.Set(obj.AttrLocal, true)
    93  			p.From.Name = obj.NAME_EXTERN
    94  			p.From.Offset = 0
    95  		}
    96  	}
    97  
    98  	// Rewrite SUB constants into ADD.
    99  	switch p.As {
   100  	case ASUBC:
   101  		if p.From.Type == obj.TYPE_CONST {
   102  			p.From.Offset = -p.From.Offset
   103  			p.As = AADDC
   104  		}
   105  
   106  	case ASUBCCC:
   107  		if p.From.Type == obj.TYPE_CONST {
   108  			p.From.Offset = -p.From.Offset
   109  			p.As = AADDCCC
   110  		}
   111  
   112  	case ASUB:
   113  		if p.From.Type == obj.TYPE_CONST {
   114  			p.From.Offset = -p.From.Offset
   115  			p.As = AADD
   116  		}
   117  	}
   118  	if ctxt.Flag_dynlink {
   119  		rewriteToUseGot(ctxt, p)
   120  	}
   121  }
   122  
   123  // Rewrite p, if necessary, to access global data via the global offset table.
   124  func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
   125  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   126  		//     ADUFFxxx $offset
   127  		// becomes
   128  		//     MOVD runtime.duffxxx@GOT, R12
   129  		//     ADD $offset, R12
   130  		//     MOVD R12, CTR
   131  		//     BL (CTR)
   132  		var sym *obj.LSym
   133  		if p.As == obj.ADUFFZERO {
   134  			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
   135  		} else {
   136  			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
   137  		}
   138  		offset := p.To.Offset
   139  		p.As = AMOVD
   140  		p.From.Type = obj.TYPE_MEM
   141  		p.From.Name = obj.NAME_GOTREF
   142  		p.From.Sym = sym
   143  		p.To.Type = obj.TYPE_REG
   144  		p.To.Reg = REG_R12
   145  		p.To.Name = obj.NAME_NONE
   146  		p.To.Offset = 0
   147  		p.To.Sym = nil
   148  		p1 := obj.Appendp(ctxt, p)
   149  		p1.As = AADD
   150  		p1.From.Type = obj.TYPE_CONST
   151  		p1.From.Offset = offset
   152  		p1.To.Type = obj.TYPE_REG
   153  		p1.To.Reg = REG_R12
   154  		p2 := obj.Appendp(ctxt, p1)
   155  		p2.As = AMOVD
   156  		p2.From.Type = obj.TYPE_REG
   157  		p2.From.Reg = REG_R12
   158  		p2.To.Type = obj.TYPE_REG
   159  		p2.To.Reg = REG_CTR
   160  		p3 := obj.Appendp(ctxt, p2)
   161  		p3.As = obj.ACALL
   162  		p3.From.Type = obj.TYPE_REG
   163  		p3.From.Reg = REG_R12
   164  		p3.To.Type = obj.TYPE_REG
   165  		p3.To.Reg = REG_CTR
   166  	}
   167  
   168  	// We only care about global data: NAME_EXTERN means a global
   169  	// symbol in the Go sense, and p.Sym.Local is true for a few
   170  	// internally defined symbols.
   171  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   172  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   173  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   174  		if p.As != AMOVD {
   175  			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   176  		}
   177  		if p.To.Type != obj.TYPE_REG {
   178  			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   179  		}
   180  		p.From.Type = obj.TYPE_MEM
   181  		p.From.Name = obj.NAME_GOTREF
   182  		if p.From.Offset != 0 {
   183  			q := obj.Appendp(ctxt, p)
   184  			q.As = AADD
   185  			q.From.Type = obj.TYPE_CONST
   186  			q.From.Offset = p.From.Offset
   187  			q.To = p.To
   188  			p.From.Offset = 0
   189  		}
   190  	}
   191  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   192  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   193  	}
   194  	var source *obj.Addr
   195  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   196  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP)
   197  	// An addition may be inserted between the two MOVs if there is an offset.
   198  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   199  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   200  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   201  		}
   202  		source = &p.From
   203  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   204  		source = &p.To
   205  	} else {
   206  		return
   207  	}
   208  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   209  		return
   210  	}
   211  	if source.Sym.Type == obj.STLSBSS {
   212  		return
   213  	}
   214  	if source.Type != obj.TYPE_MEM {
   215  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   216  	}
   217  	p1 := obj.Appendp(ctxt, p)
   218  	p2 := obj.Appendp(ctxt, p1)
   219  
   220  	p1.As = AMOVD
   221  	p1.From.Type = obj.TYPE_MEM
   222  	p1.From.Sym = source.Sym
   223  	p1.From.Name = obj.NAME_GOTREF
   224  	p1.To.Type = obj.TYPE_REG
   225  	p1.To.Reg = REGTMP
   226  
   227  	p2.As = p.As
   228  	p2.From = p.From
   229  	p2.To = p.To
   230  	if p.From.Name == obj.NAME_EXTERN {
   231  		p2.From.Reg = REGTMP
   232  		p2.From.Name = obj.NAME_NONE
   233  		p2.From.Sym = nil
   234  	} else if p.To.Name == obj.NAME_EXTERN {
   235  		p2.To.Reg = REGTMP
   236  		p2.To.Name = obj.NAME_NONE
   237  		p2.To.Sym = nil
   238  	} else {
   239  		return
   240  	}
   241  	obj.Nopout(p)
   242  }
   243  
   244  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   245  	// TODO(minux): add morestack short-cuts with small fixed frame-size.
   246  	ctxt.Cursym = cursym
   247  
   248  	if cursym.Text == nil || cursym.Text.Link == nil {
   249  		return
   250  	}
   251  
   252  	p := cursym.Text
   253  	textstksiz := p.To.Offset
   254  	if textstksiz == -8 {
   255  		// Compatibility hack.
   256  		p.From3.Offset |= obj.NOFRAME
   257  		textstksiz = 0
   258  	}
   259  	if textstksiz%8 != 0 {
   260  		ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
   261  	}
   262  	if p.From3.Offset&obj.NOFRAME != 0 {
   263  		if textstksiz != 0 {
   264  			ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   265  		}
   266  	}
   267  
   268  	cursym.Args = p.To.Val.(int32)
   269  	cursym.Locals = int32(textstksiz)
   270  
   271  	/*
   272  	 * find leaf subroutines
   273  	 * strip NOPs
   274  	 * expand RET
   275  	 * expand BECOME pseudo
   276  	 */
   277  	if ctxt.Debugvlog != 0 {
   278  		ctxt.Logf("%5.2f noops\n", obj.Cputime())
   279  	}
   280  
   281  	var q *obj.Prog
   282  	var q1 *obj.Prog
   283  	for p := cursym.Text; p != nil; p = p.Link {
   284  		switch p.As {
   285  		/* too hard, just leave alone */
   286  		case obj.ATEXT:
   287  			q = p
   288  
   289  			p.Mark |= LABEL | LEAF | SYNC
   290  			if p.Link != nil {
   291  				p.Link.Mark |= LABEL
   292  			}
   293  
   294  		case ANOR:
   295  			q = p
   296  			if p.To.Type == obj.TYPE_REG {
   297  				if p.To.Reg == REGZERO {
   298  					p.Mark |= LABEL | SYNC
   299  				}
   300  			}
   301  
   302  		case ALWAR,
   303  			ALBAR,
   304  			ASTBCCC,
   305  			ASTWCCC,
   306  			AECIWX,
   307  			AECOWX,
   308  			AEIEIO,
   309  			AICBI,
   310  			AISYNC,
   311  			ATLBIE,
   312  			ATLBIEL,
   313  			ASLBIA,
   314  			ASLBIE,
   315  			ASLBMFEE,
   316  			ASLBMFEV,
   317  			ASLBMTE,
   318  			ADCBF,
   319  			ADCBI,
   320  			ADCBST,
   321  			ADCBT,
   322  			ADCBTST,
   323  			ADCBZ,
   324  			ASYNC,
   325  			ATLBSYNC,
   326  			APTESYNC,
   327  			ALWSYNC,
   328  			ATW,
   329  			AWORD,
   330  			ARFI,
   331  			ARFCI,
   332  			ARFID,
   333  			AHRFID:
   334  			q = p
   335  			p.Mark |= LABEL | SYNC
   336  			continue
   337  
   338  		case AMOVW, AMOVWZ, AMOVD:
   339  			q = p
   340  			if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
   341  				p.Mark |= LABEL | SYNC
   342  			}
   343  			continue
   344  
   345  		case AFABS,
   346  			AFABSCC,
   347  			AFADD,
   348  			AFADDCC,
   349  			AFCTIW,
   350  			AFCTIWCC,
   351  			AFCTIWZ,
   352  			AFCTIWZCC,
   353  			AFDIV,
   354  			AFDIVCC,
   355  			AFMADD,
   356  			AFMADDCC,
   357  			AFMOVD,
   358  			AFMOVDU,
   359  			/* case AFMOVDS: */
   360  			AFMOVS,
   361  			AFMOVSU,
   362  
   363  			/* case AFMOVSD: */
   364  			AFMSUB,
   365  			AFMSUBCC,
   366  			AFMUL,
   367  			AFMULCC,
   368  			AFNABS,
   369  			AFNABSCC,
   370  			AFNEG,
   371  			AFNEGCC,
   372  			AFNMADD,
   373  			AFNMADDCC,
   374  			AFNMSUB,
   375  			AFNMSUBCC,
   376  			AFRSP,
   377  			AFRSPCC,
   378  			AFSUB,
   379  			AFSUBCC:
   380  			q = p
   381  
   382  			p.Mark |= FLOAT
   383  			continue
   384  
   385  		case ABL,
   386  			ABCL,
   387  			obj.ADUFFZERO,
   388  			obj.ADUFFCOPY:
   389  			cursym.Text.Mark &^= LEAF
   390  			fallthrough
   391  
   392  		case ABC,
   393  			ABEQ,
   394  			ABGE,
   395  			ABGT,
   396  			ABLE,
   397  			ABLT,
   398  			ABNE,
   399  			ABR,
   400  			ABVC,
   401  			ABVS:
   402  			p.Mark |= BRANCH
   403  			q = p
   404  			q1 = p.Pcond
   405  			if q1 != nil {
   406  				for q1.As == obj.ANOP {
   407  					q1 = q1.Link
   408  					p.Pcond = q1
   409  				}
   410  
   411  				if q1.Mark&LEAF == 0 {
   412  					q1.Mark |= LABEL
   413  				}
   414  			} else {
   415  				p.Mark |= LABEL
   416  			}
   417  			q1 = p.Link
   418  			if q1 != nil {
   419  				q1.Mark |= LABEL
   420  			}
   421  			continue
   422  
   423  		case AFCMPO, AFCMPU:
   424  			q = p
   425  			p.Mark |= FCMP | FLOAT
   426  			continue
   427  
   428  		case obj.ARET:
   429  			q = p
   430  			if p.Link != nil {
   431  				p.Link.Mark |= LABEL
   432  			}
   433  			continue
   434  
   435  		case obj.ANOP:
   436  			q1 = p.Link
   437  			q.Link = q1 /* q is non-nop */
   438  			q1.Mark |= p.Mark
   439  			continue
   440  
   441  		default:
   442  			q = p
   443  			continue
   444  		}
   445  	}
   446  
   447  	autosize := int32(0)
   448  	var p1 *obj.Prog
   449  	var p2 *obj.Prog
   450  	for p := cursym.Text; p != nil; p = p.Link {
   451  		o := p.As
   452  		switch o {
   453  		case obj.ATEXT:
   454  			autosize = int32(textstksiz)
   455  
   456  			if p.Mark&LEAF != 0 && autosize == 0 {
   457  				// A leaf function with no locals has no frame.
   458  				p.From3.Offset |= obj.NOFRAME
   459  			}
   460  
   461  			if p.From3.Offset&obj.NOFRAME == 0 {
   462  				// If there is a stack frame at all, it includes
   463  				// space to save the LR.
   464  				autosize += int32(ctxt.FixedFrameSize())
   465  			}
   466  
   467  			if p.Mark&LEAF != 0 && autosize < obj.StackSmall {
   468  				// A leaf function with a small stack can be marked
   469  				// NOSPLIT, avoiding a stack check.
   470  				p.From3.Offset |= obj.NOSPLIT
   471  			}
   472  
   473  			p.To.Offset = int64(autosize)
   474  
   475  			q = p
   476  
   477  			if ctxt.Flag_shared && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" {
   478  				// When compiling Go into PIC, all functions must start
   479  				// with instructions to load the TOC pointer into r2:
   480  				//
   481  				//	addis r2, r12, .TOC.-func@ha
   482  				//	addi r2, r2, .TOC.-func@l+4
   483  				//
   484  				// We could probably skip this prologue in some situations
   485  				// but it's a bit subtle. However, it is both safe and
   486  				// necessary to leave the prologue off duffzero and
   487  				// duffcopy as we rely on being able to jump to a specific
   488  				// instruction offset for them, and stackBarrier is only
   489  				// ever called from an overwritten LR-save slot on the
   490  				// stack (when r12 will not be remotely the right thing)
   491  				// but fortunately does not access global data.
   492  				//
   493  				// These are AWORDS because there is no (afaict) way to
   494  				// generate the addis instruction except as part of the
   495  				// load of a large constant, and in that case there is no
   496  				// way to use r12 as the source.
   497  				q = obj.Appendp(ctxt, q)
   498  				q.As = AWORD
   499  				q.Lineno = p.Lineno
   500  				q.From.Type = obj.TYPE_CONST
   501  				q.From.Offset = 0x3c4c0000
   502  				q = obj.Appendp(ctxt, q)
   503  				q.As = AWORD
   504  				q.Lineno = p.Lineno
   505  				q.From.Type = obj.TYPE_CONST
   506  				q.From.Offset = 0x38420000
   507  				rel := obj.Addrel(ctxt.Cursym)
   508  				rel.Off = 0
   509  				rel.Siz = 8
   510  				rel.Sym = obj.Linklookup(ctxt, ".TOC.", 0)
   511  				rel.Type = obj.R_ADDRPOWER_PCREL
   512  			}
   513  
   514  			if cursym.Text.From3.Offset&obj.NOSPLIT == 0 {
   515  				q = stacksplit(ctxt, q, autosize) // emit split check
   516  			}
   517  
   518  			if autosize != 0 {
   519  				// Make sure to save link register for non-empty frame, even if
   520  				// it is a leaf function, so that traceback works.
   521  				if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG {
   522  					// Use MOVDU to adjust R1 when saving R31, if autosize is small.
   523  					q = obj.Appendp(ctxt, q)
   524  					q.As = AMOVD
   525  					q.Lineno = p.Lineno
   526  					q.From.Type = obj.TYPE_REG
   527  					q.From.Reg = REG_LR
   528  					q.To.Type = obj.TYPE_REG
   529  					q.To.Reg = REGTMP
   530  
   531  					q = obj.Appendp(ctxt, q)
   532  					q.As = AMOVDU
   533  					q.Lineno = p.Lineno
   534  					q.From.Type = obj.TYPE_REG
   535  					q.From.Reg = REGTMP
   536  					q.To.Type = obj.TYPE_MEM
   537  					q.To.Offset = int64(-autosize)
   538  					q.To.Reg = REGSP
   539  					q.Spadj = int32(autosize)
   540  				} else {
   541  					// Frame size is too large for a MOVDU instruction.
   542  					// Store link register before decrementing SP, so if a signal comes
   543  					// during the execution of the function prologue, the traceback
   544  					// code will not see a half-updated stack frame.
   545  					q = obj.Appendp(ctxt, q)
   546  					q.As = AMOVD
   547  					q.Lineno = p.Lineno
   548  					q.From.Type = obj.TYPE_REG
   549  					q.From.Reg = REG_LR
   550  					q.To.Type = obj.TYPE_REG
   551  					q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
   552  
   553  					q = obj.Appendp(ctxt, q)
   554  					q.As = AMOVD
   555  					q.Lineno = p.Lineno
   556  					q.From.Type = obj.TYPE_REG
   557  					q.From.Reg = REG_R29
   558  					q.To.Type = obj.TYPE_MEM
   559  					q.To.Offset = int64(-autosize)
   560  					q.To.Reg = REGSP
   561  
   562  					q = obj.Appendp(ctxt, q)
   563  					q.As = AADD
   564  					q.Lineno = p.Lineno
   565  					q.From.Type = obj.TYPE_CONST
   566  					q.From.Offset = int64(-autosize)
   567  					q.To.Type = obj.TYPE_REG
   568  					q.To.Reg = REGSP
   569  					q.Spadj = +autosize
   570  				}
   571  			} else if cursym.Text.Mark&LEAF == 0 {
   572  				// A very few functions that do not return to their caller
   573  				// (e.g. gogo) are not identified as leaves but still have
   574  				// no frame.
   575  				cursym.Text.Mark |= LEAF
   576  			}
   577  
   578  			if cursym.Text.Mark&LEAF != 0 {
   579  				cursym.Set(obj.AttrLeaf, true)
   580  				break
   581  			}
   582  
   583  			if ctxt.Flag_shared {
   584  				q = obj.Appendp(ctxt, q)
   585  				q.As = AMOVD
   586  				q.Lineno = p.Lineno
   587  				q.From.Type = obj.TYPE_REG
   588  				q.From.Reg = REG_R2
   589  				q.To.Type = obj.TYPE_MEM
   590  				q.To.Reg = REGSP
   591  				q.To.Offset = 24
   592  			}
   593  
   594  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   595  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   596  				//
   597  				//	MOVD g_panic(g), R3
   598  				//	CMP R0, R3
   599  				//	BEQ end
   600  				//	MOVD panic_argp(R3), R4
   601  				//	ADD $(autosize+8), R1, R5
   602  				//	CMP R4, R5
   603  				//	BNE end
   604  				//	ADD $8, R1, R6
   605  				//	MOVD R6, panic_argp(R3)
   606  				// end:
   607  				//	NOP
   608  				//
   609  				// The NOP is needed to give the jumps somewhere to land.
   610  				// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
   611  
   612  				q = obj.Appendp(ctxt, q)
   613  
   614  				q.As = AMOVD
   615  				q.From.Type = obj.TYPE_MEM
   616  				q.From.Reg = REGG
   617  				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   618  				q.To.Type = obj.TYPE_REG
   619  				q.To.Reg = REG_R3
   620  
   621  				q = obj.Appendp(ctxt, q)
   622  				q.As = ACMP
   623  				q.From.Type = obj.TYPE_REG
   624  				q.From.Reg = REG_R0
   625  				q.To.Type = obj.TYPE_REG
   626  				q.To.Reg = REG_R3
   627  
   628  				q = obj.Appendp(ctxt, q)
   629  				q.As = ABEQ
   630  				q.To.Type = obj.TYPE_BRANCH
   631  				p1 = q
   632  
   633  				q = obj.Appendp(ctxt, q)
   634  				q.As = AMOVD
   635  				q.From.Type = obj.TYPE_MEM
   636  				q.From.Reg = REG_R3
   637  				q.From.Offset = 0 // Panic.argp
   638  				q.To.Type = obj.TYPE_REG
   639  				q.To.Reg = REG_R4
   640  
   641  				q = obj.Appendp(ctxt, q)
   642  				q.As = AADD
   643  				q.From.Type = obj.TYPE_CONST
   644  				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
   645  				q.Reg = REGSP
   646  				q.To.Type = obj.TYPE_REG
   647  				q.To.Reg = REG_R5
   648  
   649  				q = obj.Appendp(ctxt, q)
   650  				q.As = ACMP
   651  				q.From.Type = obj.TYPE_REG
   652  				q.From.Reg = REG_R4
   653  				q.To.Type = obj.TYPE_REG
   654  				q.To.Reg = REG_R5
   655  
   656  				q = obj.Appendp(ctxt, q)
   657  				q.As = ABNE
   658  				q.To.Type = obj.TYPE_BRANCH
   659  				p2 = q
   660  
   661  				q = obj.Appendp(ctxt, q)
   662  				q.As = AADD
   663  				q.From.Type = obj.TYPE_CONST
   664  				q.From.Offset = ctxt.FixedFrameSize()
   665  				q.Reg = REGSP
   666  				q.To.Type = obj.TYPE_REG
   667  				q.To.Reg = REG_R6
   668  
   669  				q = obj.Appendp(ctxt, q)
   670  				q.As = AMOVD
   671  				q.From.Type = obj.TYPE_REG
   672  				q.From.Reg = REG_R6
   673  				q.To.Type = obj.TYPE_MEM
   674  				q.To.Reg = REG_R3
   675  				q.To.Offset = 0 // Panic.argp
   676  
   677  				q = obj.Appendp(ctxt, q)
   678  
   679  				q.As = obj.ANOP
   680  				p1.Pcond = q
   681  				p2.Pcond = q
   682  			}
   683  
   684  		case obj.ARET:
   685  			if p.From.Type == obj.TYPE_CONST {
   686  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   687  				break
   688  			}
   689  
   690  			retTarget := p.To.Sym
   691  
   692  			if cursym.Text.Mark&LEAF != 0 {
   693  				if autosize == 0 {
   694  					p.As = ABR
   695  					p.From = obj.Addr{}
   696  					if retTarget == nil {
   697  						p.To.Type = obj.TYPE_REG
   698  						p.To.Reg = REG_LR
   699  					} else {
   700  						p.To.Type = obj.TYPE_BRANCH
   701  						p.To.Sym = retTarget
   702  					}
   703  					p.Mark |= BRANCH
   704  					break
   705  				}
   706  
   707  				p.As = AADD
   708  				p.From.Type = obj.TYPE_CONST
   709  				p.From.Offset = int64(autosize)
   710  				p.To.Type = obj.TYPE_REG
   711  				p.To.Reg = REGSP
   712  				p.Spadj = -autosize
   713  
   714  				q = ctxt.NewProg()
   715  				q.As = ABR
   716  				q.Lineno = p.Lineno
   717  				q.To.Type = obj.TYPE_REG
   718  				q.To.Reg = REG_LR
   719  				q.Mark |= BRANCH
   720  				q.Spadj = +autosize
   721  
   722  				q.Link = p.Link
   723  				p.Link = q
   724  				break
   725  			}
   726  
   727  			p.As = AMOVD
   728  			p.From.Type = obj.TYPE_MEM
   729  			p.From.Offset = 0
   730  			p.From.Reg = REGSP
   731  			p.To.Type = obj.TYPE_REG
   732  			p.To.Reg = REGTMP
   733  
   734  			q = ctxt.NewProg()
   735  			q.As = AMOVD
   736  			q.Lineno = p.Lineno
   737  			q.From.Type = obj.TYPE_REG
   738  			q.From.Reg = REGTMP
   739  			q.To.Type = obj.TYPE_REG
   740  			q.To.Reg = REG_LR
   741  
   742  			q.Link = p.Link
   743  			p.Link = q
   744  			p = q
   745  
   746  			if false {
   747  				// Debug bad returns
   748  				q = ctxt.NewProg()
   749  
   750  				q.As = AMOVD
   751  				q.Lineno = p.Lineno
   752  				q.From.Type = obj.TYPE_MEM
   753  				q.From.Offset = 0
   754  				q.From.Reg = REGTMP
   755  				q.To.Type = obj.TYPE_REG
   756  				q.To.Reg = REGTMP
   757  
   758  				q.Link = p.Link
   759  				p.Link = q
   760  				p = q
   761  			}
   762  
   763  			if autosize != 0 {
   764  				q = ctxt.NewProg()
   765  				q.As = AADD
   766  				q.Lineno = p.Lineno
   767  				q.From.Type = obj.TYPE_CONST
   768  				q.From.Offset = int64(autosize)
   769  				q.To.Type = obj.TYPE_REG
   770  				q.To.Reg = REGSP
   771  				q.Spadj = -autosize
   772  
   773  				q.Link = p.Link
   774  				p.Link = q
   775  			}
   776  
   777  			q1 = ctxt.NewProg()
   778  			q1.As = ABR
   779  			q1.Lineno = p.Lineno
   780  			if retTarget == nil {
   781  				q1.To.Type = obj.TYPE_REG
   782  				q1.To.Reg = REG_LR
   783  			} else {
   784  				q1.To.Type = obj.TYPE_BRANCH
   785  				q1.To.Sym = retTarget
   786  			}
   787  			q1.Mark |= BRANCH
   788  			q1.Spadj = +autosize
   789  
   790  			q1.Link = q.Link
   791  			q.Link = q1
   792  		case AADD:
   793  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   794  				p.Spadj = int32(-p.From.Offset)
   795  			}
   796  		}
   797  	}
   798  }
   799  
   800  /*
   801  // instruction scheduling
   802  	if(debug['Q'] == 0)
   803  		return;
   804  
   805  	curtext = nil;
   806  	q = nil;	// p - 1
   807  	q1 = firstp;	// top of block
   808  	o = 0;		// count of instructions
   809  	for(p = firstp; p != nil; p = p1) {
   810  		p1 = p->link;
   811  		o++;
   812  		if(p->mark & NOSCHED){
   813  			if(q1 != p){
   814  				sched(q1, q);
   815  			}
   816  			for(; p != nil; p = p->link){
   817  				if(!(p->mark & NOSCHED))
   818  					break;
   819  				q = p;
   820  			}
   821  			p1 = p;
   822  			q1 = p;
   823  			o = 0;
   824  			continue;
   825  		}
   826  		if(p->mark & (LABEL|SYNC)) {
   827  			if(q1 != p)
   828  				sched(q1, q);
   829  			q1 = p;
   830  			o = 1;
   831  		}
   832  		if(p->mark & (BRANCH|SYNC)) {
   833  			sched(q1, p);
   834  			q1 = p1;
   835  			o = 0;
   836  		}
   837  		if(o >= NSCHED) {
   838  			sched(q1, p);
   839  			q1 = p1;
   840  			o = 0;
   841  		}
   842  		q = p;
   843  	}
   844  */
   845  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
   846  	p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode
   847  
   848  	// MOVD	g_stackguard(g), R3
   849  	p = obj.Appendp(ctxt, p)
   850  
   851  	p.As = AMOVD
   852  	p.From.Type = obj.TYPE_MEM
   853  	p.From.Reg = REGG
   854  	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
   855  	if ctxt.Cursym.CFunc() {
   856  		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
   857  	}
   858  	p.To.Type = obj.TYPE_REG
   859  	p.To.Reg = REG_R3
   860  
   861  	var q *obj.Prog
   862  	if framesize <= obj.StackSmall {
   863  		// small stack: SP < stackguard
   864  		//	CMP	stackguard, SP
   865  		p = obj.Appendp(ctxt, p)
   866  
   867  		p.As = ACMPU
   868  		p.From.Type = obj.TYPE_REG
   869  		p.From.Reg = REG_R3
   870  		p.To.Type = obj.TYPE_REG
   871  		p.To.Reg = REGSP
   872  	} else if framesize <= obj.StackBig {
   873  		// large stack: SP-framesize < stackguard-StackSmall
   874  		//	ADD $-framesize, SP, R4
   875  		//	CMP stackguard, R4
   876  		p = obj.Appendp(ctxt, p)
   877  
   878  		p.As = AADD
   879  		p.From.Type = obj.TYPE_CONST
   880  		p.From.Offset = int64(-framesize)
   881  		p.Reg = REGSP
   882  		p.To.Type = obj.TYPE_REG
   883  		p.To.Reg = REG_R4
   884  
   885  		p = obj.Appendp(ctxt, p)
   886  		p.As = ACMPU
   887  		p.From.Type = obj.TYPE_REG
   888  		p.From.Reg = REG_R3
   889  		p.To.Type = obj.TYPE_REG
   890  		p.To.Reg = REG_R4
   891  	} else {
   892  		// Such a large stack we need to protect against wraparound.
   893  		// If SP is close to zero:
   894  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   895  		// The +StackGuard on both sides is required to keep the left side positive:
   896  		// SP is allowed to be slightly below stackguard. See stack.h.
   897  		//
   898  		// Preemption sets stackguard to StackPreempt, a very large value.
   899  		// That breaks the math above, so we have to check for that explicitly.
   900  		//	// stackguard is R3
   901  		//	CMP	R3, $StackPreempt
   902  		//	BEQ	label-of-call-to-morestack
   903  		//	ADD	$StackGuard, SP, R4
   904  		//	SUB	R3, R4
   905  		//	MOVD	$(framesize+(StackGuard-StackSmall)), R31
   906  		//	CMPU	R31, R4
   907  		p = obj.Appendp(ctxt, p)
   908  
   909  		p.As = ACMP
   910  		p.From.Type = obj.TYPE_REG
   911  		p.From.Reg = REG_R3
   912  		p.To.Type = obj.TYPE_CONST
   913  		p.To.Offset = obj.StackPreempt
   914  
   915  		p = obj.Appendp(ctxt, p)
   916  		q = p
   917  		p.As = ABEQ
   918  		p.To.Type = obj.TYPE_BRANCH
   919  
   920  		p = obj.Appendp(ctxt, p)
   921  		p.As = AADD
   922  		p.From.Type = obj.TYPE_CONST
   923  		p.From.Offset = obj.StackGuard
   924  		p.Reg = REGSP
   925  		p.To.Type = obj.TYPE_REG
   926  		p.To.Reg = REG_R4
   927  
   928  		p = obj.Appendp(ctxt, p)
   929  		p.As = ASUB
   930  		p.From.Type = obj.TYPE_REG
   931  		p.From.Reg = REG_R3
   932  		p.To.Type = obj.TYPE_REG
   933  		p.To.Reg = REG_R4
   934  
   935  		p = obj.Appendp(ctxt, p)
   936  		p.As = AMOVD
   937  		p.From.Type = obj.TYPE_CONST
   938  		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
   939  		p.To.Type = obj.TYPE_REG
   940  		p.To.Reg = REGTMP
   941  
   942  		p = obj.Appendp(ctxt, p)
   943  		p.As = ACMPU
   944  		p.From.Type = obj.TYPE_REG
   945  		p.From.Reg = REGTMP
   946  		p.To.Type = obj.TYPE_REG
   947  		p.To.Reg = REG_R4
   948  	}
   949  
   950  	// q1: BLT	done
   951  	p = obj.Appendp(ctxt, p)
   952  	q1 := p
   953  
   954  	p.As = ABLT
   955  	p.To.Type = obj.TYPE_BRANCH
   956  
   957  	// MOVD	LR, R5
   958  	p = obj.Appendp(ctxt, p)
   959  
   960  	p.As = AMOVD
   961  	p.From.Type = obj.TYPE_REG
   962  	p.From.Reg = REG_LR
   963  	p.To.Type = obj.TYPE_REG
   964  	p.To.Reg = REG_R5
   965  	if q != nil {
   966  		q.Pcond = p
   967  	}
   968  
   969  	var morestacksym *obj.LSym
   970  	if ctxt.Cursym.CFunc() {
   971  		morestacksym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
   972  	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
   973  		morestacksym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
   974  	} else {
   975  		morestacksym = obj.Linklookup(ctxt, "runtime.morestack", 0)
   976  	}
   977  
   978  	if ctxt.Flag_shared {
   979  		// In PPC64 PIC code, R2 is used as TOC pointer derived from R12
   980  		// which is the address of function entry point when entering
   981  		// the function. We need to preserve R2 across call to morestack.
   982  		// Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
   983  		// the caller's frame, but not used (0(SP) is caller's saved LR,
   984  		// 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
   985  
   986  		// MOVD R12, 8(SP)
   987  		p = obj.Appendp(ctxt, p)
   988  		p.As = AMOVD
   989  		p.From.Type = obj.TYPE_REG
   990  		p.From.Reg = REG_R2
   991  		p.To.Type = obj.TYPE_MEM
   992  		p.To.Reg = REGSP
   993  		p.To.Offset = 8
   994  	}
   995  
   996  	if ctxt.Flag_dynlink {
   997  		// Avoid calling morestack via a PLT when dynamically linking. The
   998  		// PLT stubs generated by the system linker on ppc64le when "std r2,
   999  		// 24(r1)" to save the TOC pointer in their callers stack
  1000  		// frame. Unfortunately (and necessarily) morestack is called before
  1001  		// the function that calls it sets up its frame and so the PLT ends
  1002  		// up smashing the saved TOC pointer for its caller's caller.
  1003  		//
  1004  		// According to the ABI documentation there is a mechanism to avoid
  1005  		// the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
  1006  		// relocation on the nop after the call to morestack) but at the time
  1007  		// of writing it is not supported at all by gold and my attempt to
  1008  		// use it with ld.bfd caused an internal linker error. So this hack
  1009  		// seems preferable.
  1010  
  1011  		// MOVD $runtime.morestack(SB), R12
  1012  		p = obj.Appendp(ctxt, p)
  1013  		p.As = AMOVD
  1014  		p.From.Type = obj.TYPE_MEM
  1015  		p.From.Sym = morestacksym
  1016  		p.From.Name = obj.NAME_GOTREF
  1017  		p.To.Type = obj.TYPE_REG
  1018  		p.To.Reg = REG_R12
  1019  
  1020  		// MOVD R12, CTR
  1021  		p = obj.Appendp(ctxt, p)
  1022  		p.As = AMOVD
  1023  		p.From.Type = obj.TYPE_REG
  1024  		p.From.Reg = REG_R12
  1025  		p.To.Type = obj.TYPE_REG
  1026  		p.To.Reg = REG_CTR
  1027  
  1028  		// BL CTR
  1029  		p = obj.Appendp(ctxt, p)
  1030  		p.As = obj.ACALL
  1031  		p.From.Type = obj.TYPE_REG
  1032  		p.From.Reg = REG_R12
  1033  		p.To.Type = obj.TYPE_REG
  1034  		p.To.Reg = REG_CTR
  1035  	} else {
  1036  		// BL	runtime.morestack(SB)
  1037  		p = obj.Appendp(ctxt, p)
  1038  
  1039  		p.As = ABL
  1040  		p.To.Type = obj.TYPE_BRANCH
  1041  		p.To.Sym = morestacksym
  1042  	}
  1043  
  1044  	if ctxt.Flag_shared {
  1045  		// MOVD 8(SP), R2
  1046  		p = obj.Appendp(ctxt, p)
  1047  		p.As = AMOVD
  1048  		p.From.Type = obj.TYPE_MEM
  1049  		p.From.Reg = REGSP
  1050  		p.From.Offset = 8
  1051  		p.To.Type = obj.TYPE_REG
  1052  		p.To.Reg = REG_R2
  1053  	}
  1054  
  1055  	// BR	start
  1056  	p = obj.Appendp(ctxt, p)
  1057  	p.As = ABR
  1058  	p.To.Type = obj.TYPE_BRANCH
  1059  	p.Pcond = p0.Link
  1060  
  1061  	// placeholder for q1's jump target
  1062  	p = obj.Appendp(ctxt, p)
  1063  
  1064  	p.As = obj.ANOP // zero-width place holder
  1065  	q1.Pcond = p
  1066  
  1067  	return p
  1068  }
  1069  
  1070  func follow(ctxt *obj.Link, s *obj.LSym) {
  1071  	ctxt.Cursym = s
  1072  
  1073  	firstp := ctxt.NewProg()
  1074  	lastp := firstp
  1075  	xfol(ctxt, s.Text, &lastp)
  1076  	lastp.Link = nil
  1077  	s.Text = firstp.Link
  1078  }
  1079  
  1080  func relinv(a obj.As) obj.As {
  1081  	switch a {
  1082  	case ABEQ:
  1083  		return ABNE
  1084  	case ABNE:
  1085  		return ABEQ
  1086  
  1087  	case ABGE:
  1088  		return ABLT
  1089  	case ABLT:
  1090  		return ABGE
  1091  
  1092  	case ABGT:
  1093  		return ABLE
  1094  	case ABLE:
  1095  		return ABGT
  1096  
  1097  	case ABVC:
  1098  		return ABVS
  1099  	case ABVS:
  1100  		return ABVC
  1101  	}
  1102  
  1103  	return 0
  1104  }
  1105  
  1106  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
  1107  	var q *obj.Prog
  1108  	var r *obj.Prog
  1109  	var b obj.As
  1110  	var i int
  1111  
  1112  loop:
  1113  	if p == nil {
  1114  		return
  1115  	}
  1116  	a := p.As
  1117  	if a == ABR {
  1118  		q = p.Pcond
  1119  		if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) {
  1120  			p.Mark |= FOLL
  1121  			(*last).Link = p
  1122  			*last = p
  1123  			p = p.Link
  1124  			xfol(ctxt, p, last)
  1125  			p = q
  1126  			if p != nil && p.Mark&FOLL == 0 {
  1127  				goto loop
  1128  			}
  1129  			return
  1130  		}
  1131  
  1132  		if q != nil {
  1133  			p.Mark |= FOLL
  1134  			p = q
  1135  			if p.Mark&FOLL == 0 {
  1136  				goto loop
  1137  			}
  1138  		}
  1139  	}
  1140  
  1141  	if p.Mark&FOLL != 0 {
  1142  		i = 0
  1143  		q = p
  1144  		for ; i < 4; i, q = i+1, q.Link {
  1145  			if q == *last || (q.Mark&NOSCHED != 0) {
  1146  				break
  1147  			}
  1148  			b = 0 /* set */
  1149  			a = q.As
  1150  			if a == obj.ANOP {
  1151  				i--
  1152  				continue
  1153  			}
  1154  
  1155  			if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
  1156  				goto copy
  1157  			}
  1158  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
  1159  				continue
  1160  			}
  1161  			b = relinv(a)
  1162  			if b == 0 {
  1163  				continue
  1164  			}
  1165  
  1166  		copy:
  1167  			for {
  1168  				r = ctxt.NewProg()
  1169  				*r = *p
  1170  				if r.Mark&FOLL == 0 {
  1171  					fmt.Printf("can't happen 1\n")
  1172  				}
  1173  				r.Mark |= FOLL
  1174  				if p != q {
  1175  					p = p.Link
  1176  					(*last).Link = r
  1177  					*last = r
  1178  					continue
  1179  				}
  1180  
  1181  				(*last).Link = r
  1182  				*last = r
  1183  				if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
  1184  					return
  1185  				}
  1186  				r.As = b
  1187  				r.Pcond = p.Link
  1188  				r.Link = p.Pcond
  1189  				if r.Link.Mark&FOLL == 0 {
  1190  					xfol(ctxt, r.Link, last)
  1191  				}
  1192  				if r.Pcond.Mark&FOLL == 0 {
  1193  					fmt.Printf("can't happen 2\n")
  1194  				}
  1195  				return
  1196  			}
  1197  		}
  1198  
  1199  		a = ABR
  1200  		q = ctxt.NewProg()
  1201  		q.As = a
  1202  		q.Lineno = p.Lineno
  1203  		q.To.Type = obj.TYPE_BRANCH
  1204  		q.To.Offset = p.Pc
  1205  		q.Pcond = p
  1206  		p = q
  1207  	}
  1208  
  1209  	p.Mark |= FOLL
  1210  	(*last).Link = p
  1211  	*last = p
  1212  	if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID {
  1213  		if p.Mark&NOSCHED != 0 {
  1214  			p = p.Link
  1215  			goto loop
  1216  		}
  1217  
  1218  		return
  1219  	}
  1220  
  1221  	if p.Pcond != nil {
  1222  		if a != ABL && p.Link != nil {
  1223  			xfol(ctxt, p.Link, last)
  1224  			p = p.Pcond
  1225  			if p == nil || (p.Mark&FOLL != 0) {
  1226  				return
  1227  			}
  1228  			goto loop
  1229  		}
  1230  	}
  1231  
  1232  	p = p.Link
  1233  	goto loop
  1234  }
  1235  
  1236  var Linkppc64 = obj.LinkArch{
  1237  	Arch:       sys.ArchPPC64,
  1238  	Preprocess: preprocess,
  1239  	Assemble:   span9,
  1240  	Follow:     follow,
  1241  	Progedit:   progedit,
  1242  }
  1243  
  1244  var Linkppc64le = obj.LinkArch{
  1245  	Arch:       sys.ArchPPC64LE,
  1246  	Preprocess: preprocess,
  1247  	Assemble:   span9,
  1248  	Follow:     follow,
  1249  	Progedit:   progedit,
  1250  }