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