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