Source file src/cmd/internal/obj/ppc64/obj9.go

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

View as plain text