github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/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  	"log"
    34  	"math/bits"
    35  
    36  	"github.com/go-asm/go/abi"
    37  	"github.com/go-asm/go/cmd/obj"
    38  	"github.com/go-asm/go/cmd/objabi"
    39  	"github.com/go-asm/go/cmd/src"
    40  	"github.com/go-asm/go/cmd/sys"
    41  )
    42  
    43  // Test if this value can encoded as a mask for
    44  // li -1, rx; rlic rx,rx,sh,mb.
    45  // Masks can also extend from the msb and wrap to
    46  // the lsb too. That is, the valid masks are 32 bit strings
    47  // of the form: 0..01..10..0 or 1..10..01..1 or 1...1
    48  func isPPC64DoublewordRotateMask(v64 int64) bool {
    49  	// Isolate rightmost 1 (if none 0) and add.
    50  	v := uint64(v64)
    51  	vp := (v & -v) + v
    52  	// Likewise, for the wrapping case.
    53  	vn := ^v
    54  	vpn := (vn & -vn) + vn
    55  	return (v&vp == 0 || vn&vpn == 0) && v != 0
    56  }
    57  
    58  // Encode a doubleword rotate mask into mb (mask begin) and
    59  // me (mask end, inclusive). Note, POWER ISA labels bits in
    60  // big endian order.
    61  func encodePPC64RLDCMask(mask int64) (mb, me int) {
    62  	// Determine boundaries and then decode them
    63  	mb = bits.LeadingZeros64(uint64(mask))
    64  	me = 64 - bits.TrailingZeros64(uint64(mask))
    65  	mbn := bits.LeadingZeros64(^uint64(mask))
    66  	men := 64 - bits.TrailingZeros64(^uint64(mask))
    67  	// Check for a wrapping mask (e.g bits at 0 and 63)
    68  	if mb == 0 && me == 64 {
    69  		// swap the inverted values
    70  		mb, me = men, mbn
    71  	}
    72  	// Note, me is inclusive.
    73  	return mb, me - 1
    74  }
    75  
    76  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    77  	p.From.Class = 0
    78  	p.To.Class = 0
    79  
    80  	c := ctxt9{ctxt: ctxt, newprog: newprog}
    81  
    82  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
    83  	switch p.As {
    84  	case ABR,
    85  		ABL,
    86  		obj.ARET,
    87  		obj.ADUFFZERO,
    88  		obj.ADUFFCOPY:
    89  		if p.To.Sym != nil {
    90  			p.To.Type = obj.TYPE_BRANCH
    91  		}
    92  	}
    93  
    94  	// Rewrite float constants to values stored in memory.
    95  	switch p.As {
    96  	case AFMOVS:
    97  		if p.From.Type == obj.TYPE_FCONST {
    98  			f32 := float32(p.From.Val.(float64))
    99  			p.From.Type = obj.TYPE_MEM
   100  			p.From.Sym = ctxt.Float32Sym(f32)
   101  			p.From.Name = obj.NAME_EXTERN
   102  			p.From.Offset = 0
   103  		}
   104  
   105  	case AFMOVD:
   106  		if p.From.Type == obj.TYPE_FCONST {
   107  			f64 := p.From.Val.(float64)
   108  			// Constant not needed in memory for float +/- 0
   109  			if f64 != 0 {
   110  				p.From.Type = obj.TYPE_MEM
   111  				p.From.Sym = ctxt.Float64Sym(f64)
   112  				p.From.Name = obj.NAME_EXTERN
   113  				p.From.Offset = 0
   114  			}
   115  		}
   116  
   117  	case AMOVW, AMOVWZ:
   118  		// Note, for backwards compatibility, MOVW $const, Rx and MOVWZ $const, Rx are identical.
   119  		if p.From.Type == obj.TYPE_CONST && p.From.Offset != 0 && p.From.Offset&0xFFFF == 0 {
   120  			// This is a constant shifted 16 bits to the left, convert it to ADDIS/ORIS $const,...
   121  			p.As = AADDIS
   122  			// Use ORIS for large constants which should not be sign extended.
   123  			if p.From.Offset >= 0x80000000 {
   124  				p.As = AORIS
   125  			}
   126  			p.Reg = REG_R0
   127  			p.From.Offset >>= 16
   128  		}
   129  
   130  	case AMOVD:
   131  		// Skip this opcode if it is not a constant load.
   132  		if p.From.Type != obj.TYPE_CONST || p.From.Name != obj.NAME_NONE || p.From.Reg != 0 {
   133  			break
   134  		}
   135  
   136  		// 32b constants (signed and unsigned) can be generated via 1 or 2 instructions. They can be assembled directly.
   137  		isS32 := int64(int32(p.From.Offset)) == p.From.Offset
   138  		isU32 := uint64(uint32(p.From.Offset)) == uint64(p.From.Offset)
   139  		// If prefixed instructions are supported, a 34b signed constant can be generated by one pli instruction.
   140  		isS34 := pfxEnabled && (p.From.Offset<<30)>>30 == p.From.Offset
   141  
   142  		// Try converting MOVD $const,Rx into ADDIS/ORIS $s32>>16,R0,Rx
   143  		switch {
   144  		case isS32 && p.From.Offset&0xFFFF == 0 && p.From.Offset != 0:
   145  			p.As = AADDIS
   146  			p.From.Offset >>= 16
   147  			p.Reg = REG_R0
   148  
   149  		case isU32 && p.From.Offset&0xFFFF == 0 && p.From.Offset != 0:
   150  			p.As = AORIS
   151  			p.From.Offset >>= 16
   152  			p.Reg = REG_R0
   153  
   154  		case isS32 || isU32 || isS34:
   155  			// The assembler can generate this opcode in 1 (on Power10) or 2 opcodes.
   156  
   157  		// Otherwise, see if the large constant can be generated with 2 instructions. If not, load it from memory.
   158  		default:
   159  			// Is this a shifted 16b constant? If so, rewrite it to avoid a creating and loading a constant.
   160  			val := p.From.Offset
   161  			shift := bits.TrailingZeros64(uint64(val))
   162  			mask := 0xFFFF << shift
   163  			if val&int64(mask) == val || (val>>(shift+16) == -1 && (val>>shift)<<shift == val) {
   164  				// Rewrite this value into MOVD $const>>shift, Rto; SLD $shift, Rto
   165  				q := obj.Appendp(p, c.newprog)
   166  				q.As = ASLD
   167  				q.From.SetConst(int64(shift))
   168  				q.To = p.To
   169  				p.From.Offset >>= shift
   170  				p = q
   171  			} else if isPPC64DoublewordRotateMask(val) {
   172  				// This constant is a mask value, generate MOVD $-1, Rto; RLDIC Rto, ^me, mb, Rto
   173  				mb, me := encodePPC64RLDCMask(val)
   174  				q := obj.Appendp(p, c.newprog)
   175  				q.As = ARLDC
   176  				q.AddRestSourceConst((^int64(me)) & 0x3F)
   177  				q.AddRestSourceConst(int64(mb))
   178  				q.From = p.To
   179  				q.To = p.To
   180  				p.From.Offset = -1
   181  				p = q
   182  			} else {
   183  				// Load the constant from memory.
   184  				p.From.Type = obj.TYPE_MEM
   185  				p.From.Sym = ctxt.Int64Sym(p.From.Offset)
   186  				p.From.Name = obj.NAME_EXTERN
   187  				p.From.Offset = 0
   188  			}
   189  		}
   190  	}
   191  
   192  	switch p.As {
   193  	// Rewrite SUB constants into ADD.
   194  	case ASUBC:
   195  		if p.From.Type == obj.TYPE_CONST {
   196  			p.From.Offset = -p.From.Offset
   197  			p.As = AADDC
   198  		}
   199  
   200  	case ASUBCCC:
   201  		if p.From.Type == obj.TYPE_CONST {
   202  			p.From.Offset = -p.From.Offset
   203  			p.As = AADDCCC
   204  		}
   205  
   206  	case ASUB:
   207  		if p.From.Type == obj.TYPE_CONST {
   208  			p.From.Offset = -p.From.Offset
   209  			p.As = AADD
   210  		}
   211  
   212  	// Rewrite ADD/OR/XOR/ANDCC $const,... forms into ADDIS/ORIS/XORIS/ANDISCC
   213  	case AADD:
   214  		// AADD can encode signed 34b values, ensure it is a valid signed 32b integer too.
   215  		if p.From.Type == obj.TYPE_CONST && p.From.Offset&0xFFFF == 0 && int64(int32(p.From.Offset)) == p.From.Offset && p.From.Offset != 0 {
   216  			p.As = AADDIS
   217  			p.From.Offset >>= 16
   218  		}
   219  	case AOR:
   220  		if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
   221  			p.As = AORIS
   222  			p.From.Offset >>= 16
   223  		}
   224  	case AXOR:
   225  		if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
   226  			p.As = AXORIS
   227  			p.From.Offset >>= 16
   228  		}
   229  	case AANDCC:
   230  		if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
   231  			p.As = AANDISCC
   232  			p.From.Offset >>= 16
   233  		}
   234  
   235  	// To maintain backwards compatibility, we accept some 4 argument usage of
   236  	// several opcodes which was likely not intended, but did work. These are not
   237  	// added to optab to avoid the chance this behavior might be used with newer
   238  	// instructions.
   239  	//
   240  	// Rewrite argument ordering like "ADDEX R3, $3, R4, R5" into
   241  	//                                "ADDEX R3, R4, $3, R5"
   242  	case AVSHASIGMAW, AVSHASIGMAD, AADDEX, AXXSLDWI, AXXPERMDI:
   243  		if len(p.RestArgs) == 2 && p.Reg == 0 && p.RestArgs[0].Addr.Type == obj.TYPE_CONST && p.RestArgs[1].Addr.Type == obj.TYPE_REG {
   244  			p.Reg = p.RestArgs[1].Addr.Reg
   245  			p.RestArgs = p.RestArgs[:1]
   246  		}
   247  	}
   248  
   249  	if c.ctxt.Headtype == objabi.Haix {
   250  		c.rewriteToUseTOC(p)
   251  	} else if c.ctxt.Flag_dynlink {
   252  		c.rewriteToUseGot(p)
   253  	}
   254  }
   255  
   256  // Rewrite p, if necessary, to access a symbol using its TOC anchor.
   257  // This code is for AIX only.
   258  func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) {
   259  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   260  		return
   261  	}
   262  
   263  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   264  		// ADUFFZERO/ADUFFCOPY is considered as an ABL except in dynamic
   265  		// link where it should be an indirect call.
   266  		if !c.ctxt.Flag_dynlink {
   267  			return
   268  		}
   269  		//     ADUFFxxx $offset
   270  		// becomes
   271  		//     MOVD runtime.duffxxx@TOC, R12
   272  		//     ADD $offset, R12
   273  		//     MOVD R12, LR
   274  		//     BL (LR)
   275  		var sym *obj.LSym
   276  		if p.As == obj.ADUFFZERO {
   277  			sym = c.ctxt.Lookup("runtime.duffzero")
   278  		} else {
   279  			sym = c.ctxt.Lookup("runtime.duffcopy")
   280  		}
   281  		// Retrieve or create the TOC anchor.
   282  		symtoc := c.ctxt.LookupInit("TOC."+sym.Name, func(s *obj.LSym) {
   283  			s.Type = objabi.SDATA
   284  			s.Set(obj.AttrDuplicateOK, true)
   285  			s.Set(obj.AttrStatic, true)
   286  			c.ctxt.Data = append(c.ctxt.Data, s)
   287  			s.WriteAddr(c.ctxt, 0, 8, sym, 0)
   288  		})
   289  
   290  		offset := p.To.Offset
   291  		p.As = AMOVD
   292  		p.From.Type = obj.TYPE_MEM
   293  		p.From.Name = obj.NAME_TOCREF
   294  		p.From.Sym = symtoc
   295  		p.To.Type = obj.TYPE_REG
   296  		p.To.Reg = REG_R12
   297  		p.To.Name = obj.NAME_NONE
   298  		p.To.Offset = 0
   299  		p.To.Sym = nil
   300  		p1 := obj.Appendp(p, c.newprog)
   301  		p1.As = AADD
   302  		p1.From.Type = obj.TYPE_CONST
   303  		p1.From.Offset = offset
   304  		p1.To.Type = obj.TYPE_REG
   305  		p1.To.Reg = REG_R12
   306  		p2 := obj.Appendp(p1, c.newprog)
   307  		p2.As = AMOVD
   308  		p2.From.Type = obj.TYPE_REG
   309  		p2.From.Reg = REG_R12
   310  		p2.To.Type = obj.TYPE_REG
   311  		p2.To.Reg = REG_LR
   312  		p3 := obj.Appendp(p2, c.newprog)
   313  		p3.As = obj.ACALL
   314  		p3.To.Type = obj.TYPE_REG
   315  		p3.To.Reg = REG_LR
   316  	}
   317  
   318  	var source *obj.Addr
   319  	if p.From.Name == obj.NAME_EXTERN || p.From.Name == obj.NAME_STATIC {
   320  		if p.From.Type == obj.TYPE_ADDR {
   321  			if p.As == ADWORD {
   322  				// ADWORD $sym doesn't need TOC anchor
   323  				return
   324  			}
   325  			if p.As != AMOVD {
   326  				c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v", p)
   327  				return
   328  			}
   329  			if p.To.Type != obj.TYPE_REG {
   330  				c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v", p)
   331  				return
   332  			}
   333  		} else if p.From.Type != obj.TYPE_MEM {
   334  			c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
   335  			return
   336  		}
   337  		source = &p.From
   338  
   339  	} else if p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC {
   340  		if p.To.Type != obj.TYPE_MEM {
   341  			c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p)
   342  			return
   343  		}
   344  		if source != nil {
   345  			c.ctxt.Diag("cannot handle symbols on both sides in %v", p)
   346  			return
   347  		}
   348  		source = &p.To
   349  	} else {
   350  		return
   351  
   352  	}
   353  
   354  	if source.Sym == nil {
   355  		c.ctxt.Diag("do not know how to handle nil symbol in %v", p)
   356  		return
   357  	}
   358  
   359  	if source.Sym.Type == objabi.STLSBSS {
   360  		return
   361  	}
   362  
   363  	// Retrieve or create the TOC anchor.
   364  	symtoc := c.ctxt.LookupInit("TOC."+source.Sym.Name, func(s *obj.LSym) {
   365  		s.Type = objabi.SDATA
   366  		s.Set(obj.AttrDuplicateOK, true)
   367  		s.Set(obj.AttrStatic, true)
   368  		c.ctxt.Data = append(c.ctxt.Data, s)
   369  		s.WriteAddr(c.ctxt, 0, 8, source.Sym, 0)
   370  	})
   371  
   372  	if source.Type == obj.TYPE_ADDR {
   373  		// MOVD $sym, Rx becomes MOVD symtoc, Rx
   374  		// MOVD $sym+<off>, Rx becomes MOVD symtoc, Rx; ADD <off>, Rx
   375  		p.From.Type = obj.TYPE_MEM
   376  		p.From.Sym = symtoc
   377  		p.From.Name = obj.NAME_TOCREF
   378  
   379  		if p.From.Offset != 0 {
   380  			q := obj.Appendp(p, c.newprog)
   381  			q.As = AADD
   382  			q.From.Type = obj.TYPE_CONST
   383  			q.From.Offset = p.From.Offset
   384  			p.From.Offset = 0
   385  			q.To = p.To
   386  		}
   387  		return
   388  
   389  	}
   390  
   391  	// MOVx sym, Ry becomes MOVD symtoc, REGTMP; MOVx (REGTMP), Ry
   392  	// MOVx Ry, sym becomes MOVD symtoc, REGTMP; MOVx Ry, (REGTMP)
   393  	// An addition may be inserted between the two MOVs if there is an offset.
   394  
   395  	q := obj.Appendp(p, c.newprog)
   396  	q.As = AMOVD
   397  	q.From.Type = obj.TYPE_MEM
   398  	q.From.Sym = symtoc
   399  	q.From.Name = obj.NAME_TOCREF
   400  	q.To.Type = obj.TYPE_REG
   401  	q.To.Reg = REGTMP
   402  
   403  	q = obj.Appendp(q, c.newprog)
   404  	q.As = p.As
   405  	q.From = p.From
   406  	q.To = p.To
   407  	if p.From.Name != obj.NAME_NONE {
   408  		q.From.Type = obj.TYPE_MEM
   409  		q.From.Reg = REGTMP
   410  		q.From.Name = obj.NAME_NONE
   411  		q.From.Sym = nil
   412  	} else if p.To.Name != obj.NAME_NONE {
   413  		q.To.Type = obj.TYPE_MEM
   414  		q.To.Reg = REGTMP
   415  		q.To.Name = obj.NAME_NONE
   416  		q.To.Sym = nil
   417  	} else {
   418  		c.ctxt.Diag("unreachable case in rewriteToUseTOC with %v", p)
   419  	}
   420  
   421  	obj.Nopout(p)
   422  }
   423  
   424  // Rewrite p, if necessary, to access global data via the global offset table.
   425  func (c *ctxt9) rewriteToUseGot(p *obj.Prog) {
   426  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   427  		//     ADUFFxxx $offset
   428  		// becomes
   429  		//     MOVD runtime.duffxxx@GOT, R12
   430  		//     ADD $offset, R12
   431  		//     MOVD R12, LR
   432  		//     BL (LR)
   433  		var sym *obj.LSym
   434  		if p.As == obj.ADUFFZERO {
   435  			sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
   436  		} else {
   437  			sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
   438  		}
   439  		offset := p.To.Offset
   440  		p.As = AMOVD
   441  		p.From.Type = obj.TYPE_MEM
   442  		p.From.Name = obj.NAME_GOTREF
   443  		p.From.Sym = sym
   444  		p.To.Type = obj.TYPE_REG
   445  		p.To.Reg = REG_R12
   446  		p.To.Name = obj.NAME_NONE
   447  		p.To.Offset = 0
   448  		p.To.Sym = nil
   449  		p1 := obj.Appendp(p, c.newprog)
   450  		p1.As = AADD
   451  		p1.From.Type = obj.TYPE_CONST
   452  		p1.From.Offset = offset
   453  		p1.To.Type = obj.TYPE_REG
   454  		p1.To.Reg = REG_R12
   455  		p2 := obj.Appendp(p1, c.newprog)
   456  		p2.As = AMOVD
   457  		p2.From.Type = obj.TYPE_REG
   458  		p2.From.Reg = REG_R12
   459  		p2.To.Type = obj.TYPE_REG
   460  		p2.To.Reg = REG_LR
   461  		p3 := obj.Appendp(p2, c.newprog)
   462  		p3.As = obj.ACALL
   463  		p3.To.Type = obj.TYPE_REG
   464  		p3.To.Reg = REG_LR
   465  	}
   466  
   467  	// We only care about global data: NAME_EXTERN means a global
   468  	// symbol in the Go sense, and p.Sym.Local is true for a few
   469  	// internally defined symbols.
   470  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   471  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   472  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   473  		if p.As != AMOVD {
   474  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   475  		}
   476  		if p.To.Type != obj.TYPE_REG {
   477  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   478  		}
   479  		p.From.Type = obj.TYPE_MEM
   480  		p.From.Name = obj.NAME_GOTREF
   481  		if p.From.Offset != 0 {
   482  			q := obj.Appendp(p, c.newprog)
   483  			q.As = AADD
   484  			q.From.Type = obj.TYPE_CONST
   485  			q.From.Offset = p.From.Offset
   486  			q.To = p.To
   487  			p.From.Offset = 0
   488  		}
   489  	}
   490  	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   491  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   492  	}
   493  	var source *obj.Addr
   494  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   495  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP)
   496  	// An addition may be inserted between the two MOVs if there is an offset.
   497  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   498  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   499  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   500  		}
   501  		source = &p.From
   502  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   503  		source = &p.To
   504  	} else {
   505  		return
   506  	}
   507  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   508  		return
   509  	}
   510  	if source.Sym.Type == objabi.STLSBSS {
   511  		return
   512  	}
   513  	if source.Type != obj.TYPE_MEM {
   514  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   515  	}
   516  	p1 := obj.Appendp(p, c.newprog)
   517  	p2 := obj.Appendp(p1, c.newprog)
   518  
   519  	p1.As = AMOVD
   520  	p1.From.Type = obj.TYPE_MEM
   521  	p1.From.Sym = source.Sym
   522  	p1.From.Name = obj.NAME_GOTREF
   523  	p1.To.Type = obj.TYPE_REG
   524  	p1.To.Reg = REGTMP
   525  
   526  	p2.As = p.As
   527  	p2.From = p.From
   528  	p2.To = p.To
   529  	if p.From.Name == obj.NAME_EXTERN {
   530  		p2.From.Reg = REGTMP
   531  		p2.From.Name = obj.NAME_NONE
   532  		p2.From.Sym = nil
   533  	} else if p.To.Name == obj.NAME_EXTERN {
   534  		p2.To.Reg = REGTMP
   535  		p2.To.Name = obj.NAME_NONE
   536  		p2.To.Sym = nil
   537  	} else {
   538  		return
   539  	}
   540  	obj.Nopout(p)
   541  }
   542  
   543  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   544  	// TODO(minux): add morestack short-cuts with small fixed frame-size.
   545  	if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
   546  		return
   547  	}
   548  
   549  	c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog}
   550  
   551  	p := c.cursym.Func().Text
   552  	textstksiz := p.To.Offset
   553  	if textstksiz == -8 {
   554  		// Compatibility hack.
   555  		p.From.Sym.Set(obj.AttrNoFrame, true)
   556  		textstksiz = 0
   557  	}
   558  	if textstksiz%8 != 0 {
   559  		c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
   560  	}
   561  	if p.From.Sym.NoFrame() {
   562  		if textstksiz != 0 {
   563  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   564  		}
   565  	}
   566  
   567  	c.cursym.Func().Args = p.To.Val.(int32)
   568  	c.cursym.Func().Locals = int32(textstksiz)
   569  
   570  	/*
   571  	 * find leaf subroutines
   572  	 * expand RET
   573  	 * expand BECOME pseudo
   574  	 */
   575  
   576  	var q *obj.Prog
   577  	var q1 *obj.Prog
   578  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   579  		switch p.As {
   580  		/* too hard, just leave alone */
   581  		case obj.ATEXT:
   582  			q = p
   583  
   584  			p.Mark |= LABEL | LEAF | SYNC
   585  			if p.Link != nil {
   586  				p.Link.Mark |= LABEL
   587  			}
   588  
   589  		case ANOR:
   590  			q = p
   591  			if p.To.Type == obj.TYPE_REG {
   592  				if p.To.Reg == REGZERO {
   593  					p.Mark |= LABEL | SYNC
   594  				}
   595  			}
   596  
   597  		case ALWAR,
   598  			ALBAR,
   599  			ASTBCCC,
   600  			ASTWCCC,
   601  			AEIEIO,
   602  			AICBI,
   603  			AISYNC,
   604  			ATLBIE,
   605  			ATLBIEL,
   606  			ASLBIA,
   607  			ASLBIE,
   608  			ASLBMFEE,
   609  			ASLBMFEV,
   610  			ASLBMTE,
   611  			ADCBF,
   612  			ADCBI,
   613  			ADCBST,
   614  			ADCBT,
   615  			ADCBTST,
   616  			ADCBZ,
   617  			ASYNC,
   618  			ATLBSYNC,
   619  			APTESYNC,
   620  			ALWSYNC,
   621  			ATW,
   622  			AWORD,
   623  			ARFI,
   624  			ARFCI,
   625  			ARFID,
   626  			AHRFID:
   627  			q = p
   628  			p.Mark |= LABEL | SYNC
   629  			continue
   630  
   631  		case AMOVW, AMOVWZ, AMOVD:
   632  			q = p
   633  			if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL {
   634  				p.Mark |= LABEL | SYNC
   635  			}
   636  			continue
   637  
   638  		case AFABS,
   639  			AFABSCC,
   640  			AFADD,
   641  			AFADDCC,
   642  			AFCTIW,
   643  			AFCTIWCC,
   644  			AFCTIWZ,
   645  			AFCTIWZCC,
   646  			AFDIV,
   647  			AFDIVCC,
   648  			AFMADD,
   649  			AFMADDCC,
   650  			AFMOVD,
   651  			AFMOVDU,
   652  			/* case AFMOVDS: */
   653  			AFMOVS,
   654  			AFMOVSU,
   655  
   656  			/* case AFMOVSD: */
   657  			AFMSUB,
   658  			AFMSUBCC,
   659  			AFMUL,
   660  			AFMULCC,
   661  			AFNABS,
   662  			AFNABSCC,
   663  			AFNEG,
   664  			AFNEGCC,
   665  			AFNMADD,
   666  			AFNMADDCC,
   667  			AFNMSUB,
   668  			AFNMSUBCC,
   669  			AFRSP,
   670  			AFRSPCC,
   671  			AFSUB,
   672  			AFSUBCC:
   673  			q = p
   674  
   675  			p.Mark |= FLOAT
   676  			continue
   677  
   678  		case ABL,
   679  			ABCL,
   680  			obj.ADUFFZERO,
   681  			obj.ADUFFCOPY:
   682  			c.cursym.Func().Text.Mark &^= LEAF
   683  			fallthrough
   684  
   685  		case ABC,
   686  			ABEQ,
   687  			ABGE,
   688  			ABGT,
   689  			ABLE,
   690  			ABLT,
   691  			ABNE,
   692  			ABR,
   693  			ABVC,
   694  			ABVS:
   695  			p.Mark |= BRANCH
   696  			q = p
   697  			q1 = p.To.Target()
   698  			if q1 != nil {
   699  				// NOPs are not removed due to #40689.
   700  
   701  				if q1.Mark&LEAF == 0 {
   702  					q1.Mark |= LABEL
   703  				}
   704  			} else {
   705  				p.Mark |= LABEL
   706  			}
   707  			q1 = p.Link
   708  			if q1 != nil {
   709  				q1.Mark |= LABEL
   710  			}
   711  			continue
   712  
   713  		case AFCMPO, AFCMPU:
   714  			q = p
   715  			p.Mark |= FCMP | FLOAT
   716  			continue
   717  
   718  		case obj.ARET:
   719  			q = p
   720  			if p.Link != nil {
   721  				p.Link.Mark |= LABEL
   722  			}
   723  			continue
   724  
   725  		case obj.ANOP:
   726  			// NOPs are not removed due to
   727  			// #40689
   728  			continue
   729  
   730  		default:
   731  			q = p
   732  			continue
   733  		}
   734  	}
   735  
   736  	autosize := int32(0)
   737  	var p1 *obj.Prog
   738  	var p2 *obj.Prog
   739  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   740  		o := p.As
   741  		switch o {
   742  		case obj.ATEXT:
   743  			autosize = int32(textstksiz)
   744  
   745  			if p.Mark&LEAF != 0 && autosize == 0 {
   746  				// A leaf function with no locals has no frame.
   747  				p.From.Sym.Set(obj.AttrNoFrame, true)
   748  			}
   749  
   750  			if !p.From.Sym.NoFrame() {
   751  				// If there is a stack frame at all, it includes
   752  				// space to save the LR.
   753  				autosize += int32(c.ctxt.Arch.FixedFrameSize)
   754  			}
   755  
   756  			if p.Mark&LEAF != 0 && autosize < abi.StackSmall {
   757  				// A leaf function with a small stack can be marked
   758  				// NOSPLIT, avoiding a stack check.
   759  				p.From.Sym.Set(obj.AttrNoSplit, true)
   760  			}
   761  
   762  			p.To.Offset = int64(autosize)
   763  
   764  			q = p
   765  
   766  			if NeedTOCpointer(c.ctxt) && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" {
   767  				// When compiling Go into PIC, without PCrel support, all functions must start
   768  				// with instructions to load the TOC pointer into r2:
   769  				//
   770  				//	addis r2, r12, .TOC.-func@ha
   771  				//	addi r2, r2, .TOC.-func@l+4
   772  				//
   773  				// We could probably skip this prologue in some situations
   774  				// but it's a bit subtle. However, it is both safe and
   775  				// necessary to leave the prologue off duffzero and
   776  				// duffcopy as we rely on being able to jump to a specific
   777  				// instruction offset for them.
   778  				//
   779  				// These are AWORDS because there is no (afaict) way to
   780  				// generate the addis instruction except as part of the
   781  				// load of a large constant, and in that case there is no
   782  				// way to use r12 as the source.
   783  				//
   784  				// Note that the same condition is tested in
   785  				// putelfsym in github.com/go-asm/go/cmd/link/ld/symtab.go
   786  				// where we set the st_other field to indicate
   787  				// the presence of these instructions.
   788  				q = obj.Appendp(q, c.newprog)
   789  				q.As = AWORD
   790  				q.Pos = p.Pos
   791  				q.From.Type = obj.TYPE_CONST
   792  				q.From.Offset = 0x3c4c0000
   793  				q = obj.Appendp(q, c.newprog)
   794  				q.As = AWORD
   795  				q.Pos = p.Pos
   796  				q.From.Type = obj.TYPE_CONST
   797  				q.From.Offset = 0x38420000
   798  				rel := obj.Addrel(c.cursym)
   799  				rel.Off = 0
   800  				rel.Siz = 8
   801  				rel.Sym = c.ctxt.Lookup(".TOC.")
   802  				rel.Type = objabi.R_ADDRPOWER_PCREL
   803  			}
   804  
   805  			if !c.cursym.Func().Text.From.Sym.NoSplit() {
   806  				q = c.stacksplit(q, autosize) // emit split check
   807  			}
   808  
   809  			if autosize != 0 {
   810  				var prologueEnd *obj.Prog
   811  				// Save the link register and update the SP.  MOVDU is used unless
   812  				// the frame size is too large.  The link register must be saved
   813  				// even for non-empty leaf functions so that traceback works.
   814  				if autosize >= -BIG && autosize <= BIG {
   815  					// Use MOVDU to adjust R1 when saving R31, if autosize is small.
   816  					q = obj.Appendp(q, c.newprog)
   817  					q.As = AMOVD
   818  					q.Pos = p.Pos
   819  					q.From.Type = obj.TYPE_REG
   820  					q.From.Reg = REG_LR
   821  					q.To.Type = obj.TYPE_REG
   822  					q.To.Reg = REGTMP
   823  					prologueEnd = q
   824  
   825  					q = obj.Appendp(q, c.newprog)
   826  					q.As = AMOVDU
   827  					q.Pos = p.Pos
   828  					q.From.Type = obj.TYPE_REG
   829  					q.From.Reg = REGTMP
   830  					q.To.Type = obj.TYPE_MEM
   831  					q.To.Offset = int64(-autosize)
   832  					q.To.Reg = REGSP
   833  					q.Spadj = autosize
   834  				} else {
   835  					// Frame size is too large for a MOVDU instruction.
   836  					// Store link register before decrementing SP, so if a signal comes
   837  					// during the execution of the function prologue, the traceback
   838  					// code will not see a half-updated stack frame.
   839  					// This sequence is not async preemptible, as if we open a frame
   840  					// at the current SP, it will clobber the saved LR.
   841  					q = obj.Appendp(q, c.newprog)
   842  					q.As = AMOVD
   843  					q.Pos = p.Pos
   844  					q.From.Type = obj.TYPE_REG
   845  					q.From.Reg = REG_LR
   846  					q.To.Type = obj.TYPE_REG
   847  					q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
   848  
   849  					q = c.ctxt.StartUnsafePoint(q, c.newprog)
   850  
   851  					q = obj.Appendp(q, c.newprog)
   852  					q.As = AMOVD
   853  					q.Pos = p.Pos
   854  					q.From.Type = obj.TYPE_REG
   855  					q.From.Reg = REG_R29
   856  					q.To.Type = obj.TYPE_MEM
   857  					q.To.Offset = int64(-autosize)
   858  					q.To.Reg = REGSP
   859  
   860  					prologueEnd = q
   861  
   862  					q = obj.Appendp(q, c.newprog)
   863  					q.As = AADD
   864  					q.Pos = p.Pos
   865  					q.From.Type = obj.TYPE_CONST
   866  					q.From.Offset = int64(-autosize)
   867  					q.To.Type = obj.TYPE_REG
   868  					q.To.Reg = REGSP
   869  					q.Spadj = +autosize
   870  
   871  					q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
   872  				}
   873  				prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
   874  			} else if c.cursym.Func().Text.Mark&LEAF == 0 {
   875  				// A very few functions that do not return to their caller
   876  				// (e.g. gogo) are not identified as leaves but still have
   877  				// no frame.
   878  				c.cursym.Func().Text.Mark |= LEAF
   879  			}
   880  
   881  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   882  				c.cursym.Set(obj.AttrLeaf, true)
   883  				break
   884  			}
   885  
   886  			if NeedTOCpointer(c.ctxt) {
   887  				q = obj.Appendp(q, c.newprog)
   888  				q.As = AMOVD
   889  				q.Pos = p.Pos
   890  				q.From.Type = obj.TYPE_REG
   891  				q.From.Reg = REG_R2
   892  				q.To.Type = obj.TYPE_MEM
   893  				q.To.Reg = REGSP
   894  				q.To.Offset = 24
   895  			}
   896  
   897  			if c.cursym.Func().Text.From.Sym.Wrapper() {
   898  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   899  				//
   900  				//	MOVD g_panic(g), R3
   901  				//	CMP R0, R3
   902  				//	BEQ end
   903  				//	MOVD panic_argp(R3), R4
   904  				//	ADD $(autosize+8), R1, R5
   905  				//	CMP R4, R5
   906  				//	BNE end
   907  				//	ADD $8, R1, R6
   908  				//	MOVD R6, panic_argp(R3)
   909  				// end:
   910  				//	NOP
   911  				//
   912  				// The NOP is needed to give the jumps somewhere to land.
   913  				// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
   914  
   915  				q = obj.Appendp(q, c.newprog)
   916  
   917  				q.As = AMOVD
   918  				q.From.Type = obj.TYPE_MEM
   919  				q.From.Reg = REGG
   920  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   921  				q.To.Type = obj.TYPE_REG
   922  				q.To.Reg = REG_R22
   923  
   924  				q = obj.Appendp(q, c.newprog)
   925  				q.As = ACMP
   926  				q.From.Type = obj.TYPE_REG
   927  				q.From.Reg = REG_R0
   928  				q.To.Type = obj.TYPE_REG
   929  				q.To.Reg = REG_R22
   930  
   931  				q = obj.Appendp(q, c.newprog)
   932  				q.As = ABEQ
   933  				q.To.Type = obj.TYPE_BRANCH
   934  				p1 = q
   935  
   936  				q = obj.Appendp(q, c.newprog)
   937  				q.As = AMOVD
   938  				q.From.Type = obj.TYPE_MEM
   939  				q.From.Reg = REG_R22
   940  				q.From.Offset = 0 // Panic.argp
   941  				q.To.Type = obj.TYPE_REG
   942  				q.To.Reg = REG_R23
   943  
   944  				q = obj.Appendp(q, c.newprog)
   945  				q.As = AADD
   946  				q.From.Type = obj.TYPE_CONST
   947  				q.From.Offset = int64(autosize) + c.ctxt.Arch.FixedFrameSize
   948  				q.Reg = REGSP
   949  				q.To.Type = obj.TYPE_REG
   950  				q.To.Reg = REG_R24
   951  
   952  				q = obj.Appendp(q, c.newprog)
   953  				q.As = ACMP
   954  				q.From.Type = obj.TYPE_REG
   955  				q.From.Reg = REG_R23
   956  				q.To.Type = obj.TYPE_REG
   957  				q.To.Reg = REG_R24
   958  
   959  				q = obj.Appendp(q, c.newprog)
   960  				q.As = ABNE
   961  				q.To.Type = obj.TYPE_BRANCH
   962  				p2 = q
   963  
   964  				q = obj.Appendp(q, c.newprog)
   965  				q.As = AADD
   966  				q.From.Type = obj.TYPE_CONST
   967  				q.From.Offset = c.ctxt.Arch.FixedFrameSize
   968  				q.Reg = REGSP
   969  				q.To.Type = obj.TYPE_REG
   970  				q.To.Reg = REG_R25
   971  
   972  				q = obj.Appendp(q, c.newprog)
   973  				q.As = AMOVD
   974  				q.From.Type = obj.TYPE_REG
   975  				q.From.Reg = REG_R25
   976  				q.To.Type = obj.TYPE_MEM
   977  				q.To.Reg = REG_R22
   978  				q.To.Offset = 0 // Panic.argp
   979  
   980  				q = obj.Appendp(q, c.newprog)
   981  
   982  				q.As = obj.ANOP
   983  				p1.To.SetTarget(q)
   984  				p2.To.SetTarget(q)
   985  			}
   986  
   987  		case obj.ARET:
   988  			if p.From.Type == obj.TYPE_CONST {
   989  				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   990  				break
   991  			}
   992  
   993  			retTarget := p.To.Sym
   994  
   995  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   996  				if autosize == 0 {
   997  					p.As = ABR
   998  					p.From = obj.Addr{}
   999  					if retTarget == nil {
  1000  						p.To.Type = obj.TYPE_REG
  1001  						p.To.Reg = REG_LR
  1002  					} else {
  1003  						p.To.Type = obj.TYPE_BRANCH
  1004  						p.To.Sym = retTarget
  1005  					}
  1006  					p.Mark |= BRANCH
  1007  					break
  1008  				}
  1009  
  1010  				p.As = AADD
  1011  				p.From.Type = obj.TYPE_CONST
  1012  				p.From.Offset = int64(autosize)
  1013  				p.To.Type = obj.TYPE_REG
  1014  				p.To.Reg = REGSP
  1015  				p.Spadj = -autosize
  1016  
  1017  				q = c.newprog()
  1018  				q.As = ABR
  1019  				q.Pos = p.Pos
  1020  				if retTarget == nil {
  1021  					q.To.Type = obj.TYPE_REG
  1022  					q.To.Reg = REG_LR
  1023  				} else {
  1024  					q.To.Type = obj.TYPE_BRANCH
  1025  					q.To.Sym = retTarget
  1026  				}
  1027  				q.Mark |= BRANCH
  1028  				q.Spadj = +autosize
  1029  
  1030  				q.Link = p.Link
  1031  				p.Link = q
  1032  				break
  1033  			}
  1034  
  1035  			p.As = AMOVD
  1036  			p.From.Type = obj.TYPE_MEM
  1037  			p.From.Offset = 0
  1038  			p.From.Reg = REGSP
  1039  			p.To.Type = obj.TYPE_REG
  1040  			p.To.Reg = REGTMP
  1041  
  1042  			q = c.newprog()
  1043  			q.As = AMOVD
  1044  			q.Pos = p.Pos
  1045  			q.From.Type = obj.TYPE_REG
  1046  			q.From.Reg = REGTMP
  1047  			q.To.Type = obj.TYPE_REG
  1048  			q.To.Reg = REG_LR
  1049  
  1050  			q.Link = p.Link
  1051  			p.Link = q
  1052  			p = q
  1053  
  1054  			if false {
  1055  				// Debug bad returns
  1056  				q = c.newprog()
  1057  
  1058  				q.As = AMOVD
  1059  				q.Pos = p.Pos
  1060  				q.From.Type = obj.TYPE_MEM
  1061  				q.From.Offset = 0
  1062  				q.From.Reg = REGTMP
  1063  				q.To.Type = obj.TYPE_REG
  1064  				q.To.Reg = REGTMP
  1065  
  1066  				q.Link = p.Link
  1067  				p.Link = q
  1068  				p = q
  1069  			}
  1070  			prev := p
  1071  			if autosize != 0 {
  1072  				q = c.newprog()
  1073  				q.As = AADD
  1074  				q.Pos = p.Pos
  1075  				q.From.Type = obj.TYPE_CONST
  1076  				q.From.Offset = int64(autosize)
  1077  				q.To.Type = obj.TYPE_REG
  1078  				q.To.Reg = REGSP
  1079  				q.Spadj = -autosize
  1080  
  1081  				q.Link = p.Link
  1082  				prev.Link = q
  1083  				prev = q
  1084  			}
  1085  
  1086  			q1 = c.newprog()
  1087  			q1.As = ABR
  1088  			q1.Pos = p.Pos
  1089  			if retTarget == nil {
  1090  				q1.To.Type = obj.TYPE_REG
  1091  				q1.To.Reg = REG_LR
  1092  			} else {
  1093  				q1.To.Type = obj.TYPE_BRANCH
  1094  				q1.To.Sym = retTarget
  1095  			}
  1096  			q1.Mark |= BRANCH
  1097  			q1.Spadj = +autosize
  1098  
  1099  			q1.Link = q.Link
  1100  			prev.Link = q1
  1101  		case AADD:
  1102  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
  1103  				p.Spadj = int32(-p.From.Offset)
  1104  			}
  1105  		case AMOVDU:
  1106  			if p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
  1107  				p.Spadj = int32(-p.To.Offset)
  1108  			}
  1109  			if p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP {
  1110  				p.Spadj = int32(-p.From.Offset)
  1111  			}
  1112  		case obj.AGETCALLERPC:
  1113  			if cursym.Leaf() {
  1114  				/* MOVD LR, Rd */
  1115  				p.As = AMOVD
  1116  				p.From.Type = obj.TYPE_REG
  1117  				p.From.Reg = REG_LR
  1118  			} else {
  1119  				/* MOVD (RSP), Rd */
  1120  				p.As = AMOVD
  1121  				p.From.Type = obj.TYPE_MEM
  1122  				p.From.Reg = REGSP
  1123  			}
  1124  		}
  1125  
  1126  		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 && p.As != ACMPU {
  1127  			f := c.cursym.Func()
  1128  			if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
  1129  				c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
  1130  				if ctxt.Debugvlog || !ctxt.IsAsm {
  1131  					ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
  1132  					if !ctxt.IsAsm {
  1133  						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
  1134  						ctxt.DiagFlush()
  1135  						log.Fatalf("bad SPWRITE")
  1136  					}
  1137  				}
  1138  			}
  1139  		}
  1140  	}
  1141  }
  1142  
  1143  /*
  1144  // instruction scheduling
  1145  
  1146  	if(debug['Q'] == 0)
  1147  		return;
  1148  
  1149  	curtext = nil;
  1150  	q = nil;	// p - 1
  1151  	q1 = firstp;	// top of block
  1152  	o = 0;		// count of instructions
  1153  	for(p = firstp; p != nil; p = p1) {
  1154  		p1 = p->link;
  1155  		o++;
  1156  		if(p->mark & NOSCHED){
  1157  			if(q1 != p){
  1158  				sched(q1, q);
  1159  			}
  1160  			for(; p != nil; p = p->link){
  1161  				if(!(p->mark & NOSCHED))
  1162  					break;
  1163  				q = p;
  1164  			}
  1165  			p1 = p;
  1166  			q1 = p;
  1167  			o = 0;
  1168  			continue;
  1169  		}
  1170  		if(p->mark & (LABEL|SYNC)) {
  1171  			if(q1 != p)
  1172  				sched(q1, q);
  1173  			q1 = p;
  1174  			o = 1;
  1175  		}
  1176  		if(p->mark & (BRANCH|SYNC)) {
  1177  			sched(q1, p);
  1178  			q1 = p1;
  1179  			o = 0;
  1180  		}
  1181  		if(o >= NSCHED) {
  1182  			sched(q1, p);
  1183  			q1 = p1;
  1184  			o = 0;
  1185  		}
  1186  		q = p;
  1187  	}
  1188  */
  1189  func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
  1190  	if c.ctxt.Flag_maymorestack != "" {
  1191  		if c.ctxt.Flag_shared || c.ctxt.Flag_dynlink {
  1192  			// See the call to morestack for why these are
  1193  			// complicated to support.
  1194  			c.ctxt.Diag("maymorestack with -shared or -dynlink is not supported")
  1195  		}
  1196  
  1197  		// Spill arguments. This has to happen before we open
  1198  		// any more frame space.
  1199  		p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
  1200  
  1201  		// Save LR and REGCTXT
  1202  		frameSize := 8 + c.ctxt.Arch.FixedFrameSize
  1203  
  1204  		// MOVD LR, REGTMP
  1205  		p = obj.Appendp(p, c.newprog)
  1206  		p.As = AMOVD
  1207  		p.From.Type = obj.TYPE_REG
  1208  		p.From.Reg = REG_LR
  1209  		p.To.Type = obj.TYPE_REG
  1210  		p.To.Reg = REGTMP
  1211  		// MOVDU REGTMP, -16(SP)
  1212  		p = obj.Appendp(p, c.newprog)
  1213  		p.As = AMOVDU
  1214  		p.From.Type = obj.TYPE_REG
  1215  		p.From.Reg = REGTMP
  1216  		p.To.Type = obj.TYPE_MEM
  1217  		p.To.Offset = -frameSize
  1218  		p.To.Reg = REGSP
  1219  		p.Spadj = int32(frameSize)
  1220  
  1221  		// MOVD REGCTXT, 8(SP)
  1222  		p = obj.Appendp(p, c.newprog)
  1223  		p.As = AMOVD
  1224  		p.From.Type = obj.TYPE_REG
  1225  		p.From.Reg = REGCTXT
  1226  		p.To.Type = obj.TYPE_MEM
  1227  		p.To.Offset = 8
  1228  		p.To.Reg = REGSP
  1229  
  1230  		// BL maymorestack
  1231  		p = obj.Appendp(p, c.newprog)
  1232  		p.As = ABL
  1233  		p.To.Type = obj.TYPE_BRANCH
  1234  		// See ../x86/obj6.go
  1235  		p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
  1236  
  1237  		// Restore LR and REGCTXT
  1238  
  1239  		// MOVD 8(SP), REGCTXT
  1240  		p = obj.Appendp(p, c.newprog)
  1241  		p.As = AMOVD
  1242  		p.From.Type = obj.TYPE_MEM
  1243  		p.From.Offset = 8
  1244  		p.From.Reg = REGSP
  1245  		p.To.Type = obj.TYPE_REG
  1246  		p.To.Reg = REGCTXT
  1247  
  1248  		// MOVD 0(SP), REGTMP
  1249  		p = obj.Appendp(p, c.newprog)
  1250  		p.As = AMOVD
  1251  		p.From.Type = obj.TYPE_MEM
  1252  		p.From.Offset = 0
  1253  		p.From.Reg = REGSP
  1254  		p.To.Type = obj.TYPE_REG
  1255  		p.To.Reg = REGTMP
  1256  
  1257  		// MOVD REGTMP, LR
  1258  		p = obj.Appendp(p, c.newprog)
  1259  		p.As = AMOVD
  1260  		p.From.Type = obj.TYPE_REG
  1261  		p.From.Reg = REGTMP
  1262  		p.To.Type = obj.TYPE_REG
  1263  		p.To.Reg = REG_LR
  1264  
  1265  		// ADD $16, SP
  1266  		p = obj.Appendp(p, c.newprog)
  1267  		p.As = AADD
  1268  		p.From.Type = obj.TYPE_CONST
  1269  		p.From.Offset = frameSize
  1270  		p.To.Type = obj.TYPE_REG
  1271  		p.To.Reg = REGSP
  1272  		p.Spadj = -int32(frameSize)
  1273  
  1274  		// Unspill arguments.
  1275  		p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
  1276  	}
  1277  
  1278  	// save entry point, but skipping the two instructions setting R2 in shared mode and maymorestack
  1279  	startPred := p
  1280  
  1281  	// MOVD	g_stackguard(g), R22
  1282  	p = obj.Appendp(p, c.newprog)
  1283  
  1284  	p.As = AMOVD
  1285  	p.From.Type = obj.TYPE_MEM
  1286  	p.From.Reg = REGG
  1287  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
  1288  	if c.cursym.CFunc() {
  1289  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
  1290  	}
  1291  	p.To.Type = obj.TYPE_REG
  1292  	p.To.Reg = REG_R22
  1293  
  1294  	// Mark the stack bound check and morestack call async nonpreemptible.
  1295  	// If we get preempted here, when resumed the preemption request is
  1296  	// cleared, but we'll still call morestack, which will double the stack
  1297  	// unnecessarily. See issue #35470.
  1298  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
  1299  
  1300  	var q *obj.Prog
  1301  	if framesize <= abi.StackSmall {
  1302  		// small stack: SP < stackguard
  1303  		//	CMP	stackguard, SP
  1304  		p = obj.Appendp(p, c.newprog)
  1305  
  1306  		p.As = ACMPU
  1307  		p.From.Type = obj.TYPE_REG
  1308  		p.From.Reg = REG_R22
  1309  		p.To.Type = obj.TYPE_REG
  1310  		p.To.Reg = REGSP
  1311  	} else {
  1312  		// large stack: SP-framesize < stackguard-StackSmall
  1313  		offset := int64(framesize) - abi.StackSmall
  1314  		if framesize > abi.StackBig {
  1315  			// Such a large stack we need to protect against underflow.
  1316  			// The runtime guarantees SP > objabi.StackBig, but
  1317  			// framesize is large enough that SP-framesize may
  1318  			// underflow, causing a direct comparison with the
  1319  			// stack guard to incorrectly succeed. We explicitly
  1320  			// guard against underflow.
  1321  			//
  1322  			//	CMPU	SP, $(framesize-StackSmall)
  1323  			//	BLT	label-of-call-to-morestack
  1324  			if offset <= 0xffff {
  1325  				p = obj.Appendp(p, c.newprog)
  1326  				p.As = ACMPU
  1327  				p.From.Type = obj.TYPE_REG
  1328  				p.From.Reg = REGSP
  1329  				p.To.Type = obj.TYPE_CONST
  1330  				p.To.Offset = offset
  1331  			} else {
  1332  				// Constant is too big for CMPU.
  1333  				p = obj.Appendp(p, c.newprog)
  1334  				p.As = AMOVD
  1335  				p.From.Type = obj.TYPE_CONST
  1336  				p.From.Offset = offset
  1337  				p.To.Type = obj.TYPE_REG
  1338  				p.To.Reg = REG_R23
  1339  
  1340  				p = obj.Appendp(p, c.newprog)
  1341  				p.As = ACMPU
  1342  				p.From.Type = obj.TYPE_REG
  1343  				p.From.Reg = REGSP
  1344  				p.To.Type = obj.TYPE_REG
  1345  				p.To.Reg = REG_R23
  1346  			}
  1347  
  1348  			p = obj.Appendp(p, c.newprog)
  1349  			q = p
  1350  			p.As = ABLT
  1351  			p.To.Type = obj.TYPE_BRANCH
  1352  		}
  1353  
  1354  		// Check against the stack guard. We've ensured this won't underflow.
  1355  		//	ADD  $-(framesize-StackSmall), SP, R4
  1356  		//	CMPU stackguard, R4
  1357  		p = obj.Appendp(p, c.newprog)
  1358  
  1359  		p.As = AADD
  1360  		p.From.Type = obj.TYPE_CONST
  1361  		p.From.Offset = -offset
  1362  		p.Reg = REGSP
  1363  		p.To.Type = obj.TYPE_REG
  1364  		p.To.Reg = REG_R23
  1365  
  1366  		p = obj.Appendp(p, c.newprog)
  1367  		p.As = ACMPU
  1368  		p.From.Type = obj.TYPE_REG
  1369  		p.From.Reg = REG_R22
  1370  		p.To.Type = obj.TYPE_REG
  1371  		p.To.Reg = REG_R23
  1372  	}
  1373  
  1374  	// q1: BLT	done
  1375  	p = obj.Appendp(p, c.newprog)
  1376  	q1 := p
  1377  
  1378  	p.As = ABLT
  1379  	p.To.Type = obj.TYPE_BRANCH
  1380  
  1381  	p = obj.Appendp(p, c.newprog)
  1382  	p.As = obj.ANOP // zero-width place holder
  1383  
  1384  	if q != nil {
  1385  		q.To.SetTarget(p)
  1386  	}
  1387  
  1388  	// Spill the register args that could be clobbered by the
  1389  	// morestack code.
  1390  
  1391  	spill := c.cursym.Func().SpillRegisterArgs(p, c.newprog)
  1392  
  1393  	// MOVD LR, R5
  1394  	p = obj.Appendp(spill, c.newprog)
  1395  	p.As = AMOVD
  1396  	p.From.Type = obj.TYPE_REG
  1397  	p.From.Reg = REG_LR
  1398  	p.To.Type = obj.TYPE_REG
  1399  	p.To.Reg = REG_R5
  1400  
  1401  	p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
  1402  
  1403  	var morestacksym *obj.LSym
  1404  	if c.cursym.CFunc() {
  1405  		morestacksym = c.ctxt.Lookup("runtime.morestackc")
  1406  	} else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
  1407  		morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt")
  1408  	} else {
  1409  		morestacksym = c.ctxt.Lookup("runtime.morestack")
  1410  	}
  1411  
  1412  	if NeedTOCpointer(c.ctxt) {
  1413  		// In PPC64 PIC code, R2 is used as TOC pointer derived from R12
  1414  		// which is the address of function entry point when entering
  1415  		// the function. We need to preserve R2 across call to morestack.
  1416  		// Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
  1417  		// the caller's frame, but not used (0(SP) is caller's saved LR,
  1418  		// 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
  1419  		// MOVD R2, 8(SP)
  1420  		p = obj.Appendp(p, c.newprog)
  1421  		p.As = AMOVD
  1422  		p.From.Type = obj.TYPE_REG
  1423  		p.From.Reg = REG_R2
  1424  		p.To.Type = obj.TYPE_MEM
  1425  		p.To.Reg = REGSP
  1426  		p.To.Offset = 8
  1427  	}
  1428  
  1429  	if c.ctxt.Flag_dynlink {
  1430  		// Avoid calling morestack via a PLT when dynamically linking. The
  1431  		// PLT stubs generated by the system linker on ppc64le when "std r2,
  1432  		// 24(r1)" to save the TOC pointer in their callers stack
  1433  		// frame. Unfortunately (and necessarily) morestack is called before
  1434  		// the function that calls it sets up its frame and so the PLT ends
  1435  		// up smashing the saved TOC pointer for its caller's caller.
  1436  		//
  1437  		// According to the ABI documentation there is a mechanism to avoid
  1438  		// the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE
  1439  		// relocation on the nop after the call to morestack) but at the time
  1440  		// of writing it is not supported at all by gold and my attempt to
  1441  		// use it with ld.bfd caused an internal linker error. So this hack
  1442  		// seems preferable.
  1443  
  1444  		// MOVD $runtime.morestack(SB), R12
  1445  		p = obj.Appendp(p, c.newprog)
  1446  		p.As = AMOVD
  1447  		p.From.Type = obj.TYPE_MEM
  1448  		p.From.Sym = morestacksym
  1449  		p.From.Name = obj.NAME_GOTREF
  1450  		p.To.Type = obj.TYPE_REG
  1451  		p.To.Reg = REG_R12
  1452  
  1453  		// MOVD R12, LR
  1454  		p = obj.Appendp(p, c.newprog)
  1455  		p.As = AMOVD
  1456  		p.From.Type = obj.TYPE_REG
  1457  		p.From.Reg = REG_R12
  1458  		p.To.Type = obj.TYPE_REG
  1459  		p.To.Reg = REG_LR
  1460  
  1461  		// BL LR
  1462  		p = obj.Appendp(p, c.newprog)
  1463  		p.As = obj.ACALL
  1464  		p.To.Type = obj.TYPE_REG
  1465  		p.To.Reg = REG_LR
  1466  	} else {
  1467  		// BL	runtime.morestack(SB)
  1468  		p = obj.Appendp(p, c.newprog)
  1469  
  1470  		p.As = ABL
  1471  		p.To.Type = obj.TYPE_BRANCH
  1472  		p.To.Sym = morestacksym
  1473  	}
  1474  
  1475  	if NeedTOCpointer(c.ctxt) {
  1476  		// MOVD 8(SP), R2
  1477  		p = obj.Appendp(p, c.newprog)
  1478  		p.As = AMOVD
  1479  		p.From.Type = obj.TYPE_MEM
  1480  		p.From.Reg = REGSP
  1481  		p.From.Offset = 8
  1482  		p.To.Type = obj.TYPE_REG
  1483  		p.To.Reg = REG_R2
  1484  	}
  1485  
  1486  	// The instructions which unspill regs should be preemptible.
  1487  	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
  1488  	unspill := c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
  1489  
  1490  	// BR	start
  1491  	p = obj.Appendp(unspill, c.newprog)
  1492  	p.As = ABR
  1493  	p.To.Type = obj.TYPE_BRANCH
  1494  	p.To.SetTarget(startPred.Link)
  1495  
  1496  	// placeholder for q1's jump target
  1497  	p = obj.Appendp(p, c.newprog)
  1498  
  1499  	p.As = obj.ANOP // zero-width place holder
  1500  	q1.To.SetTarget(p)
  1501  
  1502  	return p
  1503  }
  1504  
  1505  // MMA accumulator to/from instructions are slightly ambiguous since
  1506  // the argument represents both source and destination, specified as
  1507  // an accumulator. It is treated as a unary destination to simplify
  1508  // the code generation in ppc64map.
  1509  var unaryDst = map[obj.As]bool{
  1510  	AXXSETACCZ: true,
  1511  	AXXMTACC:   true,
  1512  	AXXMFACC:   true,
  1513  }
  1514  
  1515  var Linkppc64 = obj.LinkArch{
  1516  	Arch:           sys.ArchPPC64,
  1517  	Init:           buildop,
  1518  	Preprocess:     preprocess,
  1519  	Assemble:       span9,
  1520  	Progedit:       progedit,
  1521  	UnaryDst:       unaryDst,
  1522  	DWARFRegisters: PPC64DWARFRegisters,
  1523  }
  1524  
  1525  var Linkppc64le = obj.LinkArch{
  1526  	Arch:           sys.ArchPPC64LE,
  1527  	Init:           buildop,
  1528  	Preprocess:     preprocess,
  1529  	Assemble:       span9,
  1530  	Progedit:       progedit,
  1531  	UnaryDst:       unaryDst,
  1532  	DWARFRegisters: PPC64DWARFRegisters,
  1533  }