github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/internal/obj/arm64/obj7.go (about)

     1  // cmd/7l/noop.c, cmd/7l/obj.c, cmd/ld/pass.c from Vita Nuova.
     2  // https://code.google.com/p/ken-cc/source/browse/
     3  //
     4  // 	Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
     5  // 	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     6  // 	Portions Copyright © 1997-1999 Vita Nuova Limited
     7  // 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     8  // 	Portions Copyright © 2004,2006 Bruce Ellis
     9  // 	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    10  // 	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    11  // 	Portions Copyright © 2009 The Go Authors. All rights reserved.
    12  //
    13  // Permission is hereby granted, free of charge, to any person obtaining a copy
    14  // of this software and associated documentation files (the "Software"), to deal
    15  // in the Software without restriction, including without limitation the rights
    16  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    17  // copies of the Software, and to permit persons to whom the Software is
    18  // furnished to do so, subject to the following conditions:
    19  //
    20  // The above copyright notice and this permission notice shall be included in
    21  // all copies or substantial portions of the Software.
    22  //
    23  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    26  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    29  // THE SOFTWARE.
    30  
    31  package arm64
    32  
    33  import (
    34  	"cmd/internal/obj"
    35  	"cmd/internal/sys"
    36  	"fmt"
    37  	"log"
    38  	"math"
    39  )
    40  
    41  var complements = []obj.As{
    42  	AADD:  ASUB,
    43  	AADDW: ASUBW,
    44  	ASUB:  AADD,
    45  	ASUBW: AADDW,
    46  	ACMP:  ACMN,
    47  	ACMPW: ACMNW,
    48  	ACMN:  ACMP,
    49  	ACMNW: ACMPW,
    50  }
    51  
    52  func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
    53  	// MOV	g_stackguard(g), R1
    54  	p = obj.Appendp(ctxt, p)
    55  
    56  	p.As = AMOVD
    57  	p.From.Type = obj.TYPE_MEM
    58  	p.From.Reg = REGG
    59  	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
    60  	if ctxt.Cursym.Cfunc {
    61  		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
    62  	}
    63  	p.To.Type = obj.TYPE_REG
    64  	p.To.Reg = REG_R1
    65  
    66  	q := (*obj.Prog)(nil)
    67  	if framesize <= obj.StackSmall {
    68  		// small stack: SP < stackguard
    69  		//	MOV	SP, R2
    70  		//	CMP	stackguard, R2
    71  		p = obj.Appendp(ctxt, p)
    72  
    73  		p.As = AMOVD
    74  		p.From.Type = obj.TYPE_REG
    75  		p.From.Reg = REGSP
    76  		p.To.Type = obj.TYPE_REG
    77  		p.To.Reg = REG_R2
    78  
    79  		p = obj.Appendp(ctxt, p)
    80  		p.As = ACMP
    81  		p.From.Type = obj.TYPE_REG
    82  		p.From.Reg = REG_R1
    83  		p.Reg = REG_R2
    84  	} else if framesize <= obj.StackBig {
    85  		// large stack: SP-framesize < stackguard-StackSmall
    86  		//	SUB	$framesize, SP, R2
    87  		//	CMP	stackguard, R2
    88  		p = obj.Appendp(ctxt, p)
    89  
    90  		p.As = ASUB
    91  		p.From.Type = obj.TYPE_CONST
    92  		p.From.Offset = int64(framesize)
    93  		p.Reg = REGSP
    94  		p.To.Type = obj.TYPE_REG
    95  		p.To.Reg = REG_R2
    96  
    97  		p = obj.Appendp(ctxt, p)
    98  		p.As = ACMP
    99  		p.From.Type = obj.TYPE_REG
   100  		p.From.Reg = REG_R1
   101  		p.Reg = REG_R2
   102  	} else {
   103  		// Such a large stack we need to protect against wraparound
   104  		// if SP is close to zero.
   105  		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
   106  		// The +StackGuard on both sides is required to keep the left side positive:
   107  		// SP is allowed to be slightly below stackguard. See stack.h.
   108  		//	CMP	$StackPreempt, R1
   109  		//	BEQ	label_of_call_to_morestack
   110  		//	ADD	$StackGuard, SP, R2
   111  		//	SUB	R1, R2
   112  		//	MOV	$(framesize+(StackGuard-StackSmall)), R3
   113  		//	CMP	R3, R2
   114  		p = obj.Appendp(ctxt, p)
   115  
   116  		p.As = ACMP
   117  		p.From.Type = obj.TYPE_CONST
   118  		p.From.Offset = obj.StackPreempt
   119  		p.Reg = REG_R1
   120  
   121  		p = obj.Appendp(ctxt, p)
   122  		q = p
   123  		p.As = ABEQ
   124  		p.To.Type = obj.TYPE_BRANCH
   125  
   126  		p = obj.Appendp(ctxt, p)
   127  		p.As = AADD
   128  		p.From.Type = obj.TYPE_CONST
   129  		p.From.Offset = obj.StackGuard
   130  		p.Reg = REGSP
   131  		p.To.Type = obj.TYPE_REG
   132  		p.To.Reg = REG_R2
   133  
   134  		p = obj.Appendp(ctxt, p)
   135  		p.As = ASUB
   136  		p.From.Type = obj.TYPE_REG
   137  		p.From.Reg = REG_R1
   138  		p.To.Type = obj.TYPE_REG
   139  		p.To.Reg = REG_R2
   140  
   141  		p = obj.Appendp(ctxt, p)
   142  		p.As = AMOVD
   143  		p.From.Type = obj.TYPE_CONST
   144  		p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
   145  		p.To.Type = obj.TYPE_REG
   146  		p.To.Reg = REG_R3
   147  
   148  		p = obj.Appendp(ctxt, p)
   149  		p.As = ACMP
   150  		p.From.Type = obj.TYPE_REG
   151  		p.From.Reg = REG_R3
   152  		p.Reg = REG_R2
   153  	}
   154  
   155  	// BLS	do-morestack
   156  	bls := obj.Appendp(ctxt, p)
   157  	bls.As = ABLS
   158  	bls.To.Type = obj.TYPE_BRANCH
   159  
   160  	var last *obj.Prog
   161  	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
   162  	}
   163  
   164  	spfix := obj.Appendp(ctxt, last)
   165  	spfix.As = obj.ANOP
   166  	spfix.Spadj = -framesize
   167  
   168  	// MOV	LR, R3
   169  	movlr := obj.Appendp(ctxt, spfix)
   170  	movlr.As = AMOVD
   171  	movlr.From.Type = obj.TYPE_REG
   172  	movlr.From.Reg = REGLINK
   173  	movlr.To.Type = obj.TYPE_REG
   174  	movlr.To.Reg = REG_R3
   175  	if q != nil {
   176  		q.Pcond = movlr
   177  	}
   178  	bls.Pcond = movlr
   179  
   180  	debug := movlr
   181  	if false {
   182  		debug = obj.Appendp(ctxt, debug)
   183  		debug.As = AMOVD
   184  		debug.From.Type = obj.TYPE_CONST
   185  		debug.From.Offset = int64(framesize)
   186  		debug.To.Type = obj.TYPE_REG
   187  		debug.To.Reg = REGTMP
   188  	}
   189  
   190  	// BL	runtime.morestack(SB)
   191  	call := obj.Appendp(ctxt, debug)
   192  	call.As = ABL
   193  	call.To.Type = obj.TYPE_BRANCH
   194  	morestack := "runtime.morestack"
   195  	switch {
   196  	case ctxt.Cursym.Cfunc:
   197  		morestack = "runtime.morestackc"
   198  	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
   199  		morestack = "runtime.morestack_noctxt"
   200  	}
   201  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
   202  
   203  	// B	start
   204  	jmp := obj.Appendp(ctxt, call)
   205  	jmp.As = AB
   206  	jmp.To.Type = obj.TYPE_BRANCH
   207  	jmp.Pcond = ctxt.Cursym.Text.Link
   208  	jmp.Spadj = +framesize
   209  
   210  	// placeholder for bls's jump target
   211  	// p = obj.Appendp(ctxt, p)
   212  	// p.As = obj.ANOP
   213  
   214  	return bls
   215  }
   216  
   217  func progedit(ctxt *obj.Link, p *obj.Prog) {
   218  	p.From.Class = 0
   219  	p.To.Class = 0
   220  
   221  	// $0 results in C_ZCON, which matches both C_REG and various
   222  	// C_xCON, however the C_REG cases in asmout don't expect a
   223  	// constant, so they will use the register fields and assemble
   224  	// a R0. To prevent that, rewrite $0 as ZR.
   225  	if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
   226  		p.From.Type = obj.TYPE_REG
   227  		p.From.Reg = REGZERO
   228  	}
   229  	if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
   230  		p.To.Type = obj.TYPE_REG
   231  		p.To.Reg = REGZERO
   232  	}
   233  
   234  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
   235  	switch p.As {
   236  	case AB,
   237  		ABL,
   238  		obj.ARET,
   239  		obj.ADUFFZERO,
   240  		obj.ADUFFCOPY:
   241  		if p.To.Sym != nil {
   242  			p.To.Type = obj.TYPE_BRANCH
   243  		}
   244  		break
   245  	}
   246  
   247  	// Rewrite float constants to values stored in memory.
   248  	switch p.As {
   249  	case AFMOVS:
   250  		if p.From.Type == obj.TYPE_FCONST {
   251  			f32 := float32(p.From.Val.(float64))
   252  			i32 := math.Float32bits(f32)
   253  			literal := fmt.Sprintf("$f32.%08x", i32)
   254  			s := obj.Linklookup(ctxt, literal, 0)
   255  			s.Size = 4
   256  			p.From.Type = obj.TYPE_MEM
   257  			p.From.Sym = s
   258  			p.From.Sym.Local = true
   259  			p.From.Name = obj.NAME_EXTERN
   260  			p.From.Offset = 0
   261  		}
   262  
   263  	case AFMOVD:
   264  		if p.From.Type == obj.TYPE_FCONST {
   265  			i64 := math.Float64bits(p.From.Val.(float64))
   266  			literal := fmt.Sprintf("$f64.%016x", i64)
   267  			s := obj.Linklookup(ctxt, literal, 0)
   268  			s.Size = 8
   269  			p.From.Type = obj.TYPE_MEM
   270  			p.From.Sym = s
   271  			p.From.Sym.Local = true
   272  			p.From.Name = obj.NAME_EXTERN
   273  			p.From.Offset = 0
   274  		}
   275  
   276  		break
   277  	}
   278  
   279  	// Rewrite negative immediates as positive immediates with
   280  	// complementary instruction.
   281  	switch p.As {
   282  	case AADD, ASUB, ACMP, ACMN:
   283  		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
   284  			p.From.Offset = -p.From.Offset
   285  			p.As = complements[p.As]
   286  		}
   287  	case AADDW, ASUBW, ACMPW, ACMNW:
   288  		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
   289  			p.From.Offset = -p.From.Offset
   290  			p.As = complements[p.As]
   291  		}
   292  	}
   293  
   294  	// For 32-bit logical instruction with constant,
   295  	// rewrite the high 32-bit to be a repetition of
   296  	// the low 32-bit, so that the BITCON test can be
   297  	// shared for both 32-bit and 64-bit. 32-bit ops
   298  	// will zero the high 32-bit of the destination
   299  	// register anyway.
   300  	switch p.As {
   301  	case AANDW, AORRW, AEORW, AANDSW:
   302  		if p.From.Type == obj.TYPE_CONST {
   303  			v := p.From.Offset & 0xffffffff
   304  			p.From.Offset = v | v<<32
   305  		}
   306  	}
   307  
   308  	if ctxt.Flag_dynlink {
   309  		rewriteToUseGot(ctxt, p)
   310  	}
   311  }
   312  
   313  // Rewrite p, if necessary, to access global data via the global offset table.
   314  func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
   315  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   316  		//     ADUFFxxx $offset
   317  		// becomes
   318  		//     MOVD runtime.duffxxx@GOT, REGTMP
   319  		//     ADD $offset, REGTMP
   320  		//     CALL REGTMP
   321  		var sym *obj.LSym
   322  		if p.As == obj.ADUFFZERO {
   323  			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
   324  		} else {
   325  			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
   326  		}
   327  		offset := p.To.Offset
   328  		p.As = AMOVD
   329  		p.From.Type = obj.TYPE_MEM
   330  		p.From.Name = obj.NAME_GOTREF
   331  		p.From.Sym = sym
   332  		p.To.Type = obj.TYPE_REG
   333  		p.To.Reg = REGTMP
   334  		p.To.Name = obj.NAME_NONE
   335  		p.To.Offset = 0
   336  		p.To.Sym = nil
   337  		p1 := obj.Appendp(ctxt, p)
   338  		p1.As = AADD
   339  		p1.From.Type = obj.TYPE_CONST
   340  		p1.From.Offset = offset
   341  		p1.To.Type = obj.TYPE_REG
   342  		p1.To.Reg = REGTMP
   343  		p2 := obj.Appendp(ctxt, p1)
   344  		p2.As = obj.ACALL
   345  		p2.To.Type = obj.TYPE_REG
   346  		p2.To.Reg = REGTMP
   347  	}
   348  
   349  	// We only care about global data: NAME_EXTERN means a global
   350  	// symbol in the Go sense, and p.Sym.Local is true for a few
   351  	// internally defined symbols.
   352  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   353  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   354  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   355  		if p.As != AMOVD {
   356  			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   357  		}
   358  		if p.To.Type != obj.TYPE_REG {
   359  			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   360  		}
   361  		p.From.Type = obj.TYPE_MEM
   362  		p.From.Name = obj.NAME_GOTREF
   363  		if p.From.Offset != 0 {
   364  			q := obj.Appendp(ctxt, p)
   365  			q.As = AADD
   366  			q.From.Type = obj.TYPE_CONST
   367  			q.From.Offset = p.From.Offset
   368  			q.To = p.To
   369  			p.From.Offset = 0
   370  		}
   371  	}
   372  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   373  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   374  	}
   375  	var source *obj.Addr
   376  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   377  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   378  	// An addition may be inserted between the two MOVs if there is an offset.
   379  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   380  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   381  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   382  		}
   383  		source = &p.From
   384  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   385  		source = &p.To
   386  	} else {
   387  		return
   388  	}
   389  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   390  		return
   391  	}
   392  	if source.Sym.Type == obj.STLSBSS {
   393  		return
   394  	}
   395  	if source.Type != obj.TYPE_MEM {
   396  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   397  	}
   398  	p1 := obj.Appendp(ctxt, p)
   399  	p2 := obj.Appendp(ctxt, p1)
   400  	p1.As = AMOVD
   401  	p1.From.Type = obj.TYPE_MEM
   402  	p1.From.Sym = source.Sym
   403  	p1.From.Name = obj.NAME_GOTREF
   404  	p1.To.Type = obj.TYPE_REG
   405  	p1.To.Reg = REGTMP
   406  
   407  	p2.As = p.As
   408  	p2.From = p.From
   409  	p2.To = p.To
   410  	if p.From.Name == obj.NAME_EXTERN {
   411  		p2.From.Reg = REGTMP
   412  		p2.From.Name = obj.NAME_NONE
   413  		p2.From.Sym = nil
   414  	} else if p.To.Name == obj.NAME_EXTERN {
   415  		p2.To.Reg = REGTMP
   416  		p2.To.Name = obj.NAME_NONE
   417  		p2.To.Sym = nil
   418  	} else {
   419  		return
   420  	}
   421  	obj.Nopout(p)
   422  }
   423  
   424  func follow(ctxt *obj.Link, s *obj.LSym) {
   425  	ctxt.Cursym = s
   426  
   427  	firstp := ctxt.NewProg()
   428  	lastp := firstp
   429  	xfol(ctxt, s.Text, &lastp)
   430  	lastp.Link = nil
   431  	s.Text = firstp.Link
   432  }
   433  
   434  func relinv(a obj.As) obj.As {
   435  	switch a {
   436  	case ABEQ:
   437  		return ABNE
   438  	case ABNE:
   439  		return ABEQ
   440  	case ABCS:
   441  		return ABCC
   442  	case ABHS:
   443  		return ABLO
   444  	case ABCC:
   445  		return ABCS
   446  	case ABLO:
   447  		return ABHS
   448  	case ABMI:
   449  		return ABPL
   450  	case ABPL:
   451  		return ABMI
   452  	case ABVS:
   453  		return ABVC
   454  	case ABVC:
   455  		return ABVS
   456  	case ABHI:
   457  		return ABLS
   458  	case ABLS:
   459  		return ABHI
   460  	case ABGE:
   461  		return ABLT
   462  	case ABLT:
   463  		return ABGE
   464  	case ABGT:
   465  		return ABLE
   466  	case ABLE:
   467  		return ABGT
   468  	}
   469  
   470  	log.Fatalf("unknown relation: %s", Anames[a])
   471  	return 0
   472  }
   473  
   474  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   475  	var q *obj.Prog
   476  	var r *obj.Prog
   477  	var i int
   478  
   479  loop:
   480  	if p == nil {
   481  		return
   482  	}
   483  	a := p.As
   484  	if a == AB {
   485  		q = p.Pcond
   486  		if q != nil {
   487  			p.Mark |= FOLL
   488  			p = q
   489  			if !(p.Mark&FOLL != 0) {
   490  				goto loop
   491  			}
   492  		}
   493  	}
   494  
   495  	if p.Mark&FOLL != 0 {
   496  		i = 0
   497  		q = p
   498  		for ; i < 4; i, q = i+1, q.Link {
   499  			if q == *last || q == nil {
   500  				break
   501  			}
   502  			a = q.As
   503  			if a == obj.ANOP {
   504  				i--
   505  				continue
   506  			}
   507  
   508  			if a == AB || a == obj.ARET || a == AERET {
   509  				goto copy
   510  			}
   511  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   512  				continue
   513  			}
   514  			if a != ABEQ && a != ABNE {
   515  				continue
   516  			}
   517  
   518  		copy:
   519  			for {
   520  				r = ctxt.NewProg()
   521  				*r = *p
   522  				if !(r.Mark&FOLL != 0) {
   523  					fmt.Printf("can't happen 1\n")
   524  				}
   525  				r.Mark |= FOLL
   526  				if p != q {
   527  					p = p.Link
   528  					(*last).Link = r
   529  					*last = r
   530  					continue
   531  				}
   532  
   533  				(*last).Link = r
   534  				*last = r
   535  				if a == AB || a == obj.ARET || a == AERET {
   536  					return
   537  				}
   538  				if a == ABNE {
   539  					r.As = ABEQ
   540  				} else {
   541  					r.As = ABNE
   542  				}
   543  				r.Pcond = p.Link
   544  				r.Link = p.Pcond
   545  				if !(r.Link.Mark&FOLL != 0) {
   546  					xfol(ctxt, r.Link, last)
   547  				}
   548  				if !(r.Pcond.Mark&FOLL != 0) {
   549  					fmt.Printf("can't happen 2\n")
   550  				}
   551  				return
   552  			}
   553  		}
   554  
   555  		a = AB
   556  		q = ctxt.NewProg()
   557  		q.As = a
   558  		q.Lineno = p.Lineno
   559  		q.To.Type = obj.TYPE_BRANCH
   560  		q.To.Offset = p.Pc
   561  		q.Pcond = p
   562  		p = q
   563  	}
   564  
   565  	p.Mark |= FOLL
   566  	(*last).Link = p
   567  	*last = p
   568  	if a == AB || a == obj.ARET || a == AERET {
   569  		return
   570  	}
   571  	if p.Pcond != nil {
   572  		if a != ABL && p.Link != nil {
   573  			q = obj.Brchain(ctxt, p.Link)
   574  			if a != obj.ATEXT {
   575  				if q != nil && (q.Mark&FOLL != 0) {
   576  					p.As = relinv(a)
   577  					p.Link = p.Pcond
   578  					p.Pcond = q
   579  				}
   580  			}
   581  
   582  			xfol(ctxt, p.Link, last)
   583  			q = obj.Brchain(ctxt, p.Pcond)
   584  			if q == nil {
   585  				q = p.Pcond
   586  			}
   587  			if q.Mark&FOLL != 0 {
   588  				p.Pcond = q
   589  				return
   590  			}
   591  
   592  			p = q
   593  			goto loop
   594  		}
   595  	}
   596  
   597  	p = p.Link
   598  	goto loop
   599  }
   600  
   601  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   602  	ctxt.Cursym = cursym
   603  
   604  	if cursym.Text == nil || cursym.Text.Link == nil {
   605  		return
   606  	}
   607  
   608  	p := cursym.Text
   609  	textstksiz := p.To.Offset
   610  	aoffset := int32(textstksiz)
   611  
   612  	cursym.Args = p.To.Val.(int32)
   613  	cursym.Locals = int32(textstksiz)
   614  
   615  	/*
   616  	 * find leaf subroutines
   617  	 * strip NOPs
   618  	 * expand RET
   619  	 */
   620  	q := (*obj.Prog)(nil)
   621  	var q1 *obj.Prog
   622  	for p := cursym.Text; p != nil; p = p.Link {
   623  		switch p.As {
   624  		case obj.ATEXT:
   625  			p.Mark |= LEAF
   626  
   627  		case obj.ARET:
   628  			break
   629  
   630  		case obj.ANOP:
   631  			q1 = p.Link
   632  			q.Link = q1 /* q is non-nop */
   633  			q1.Mark |= p.Mark
   634  			continue
   635  
   636  		case ABL,
   637  			obj.ADUFFZERO,
   638  			obj.ADUFFCOPY:
   639  			cursym.Text.Mark &^= LEAF
   640  			fallthrough
   641  
   642  		case ACBNZ,
   643  			ACBZ,
   644  			ACBNZW,
   645  			ACBZW,
   646  			ATBZ,
   647  			ATBNZ,
   648  			AB,
   649  			ABEQ,
   650  			ABNE,
   651  			ABCS,
   652  			ABHS,
   653  			ABCC,
   654  			ABLO,
   655  			ABMI,
   656  			ABPL,
   657  			ABVS,
   658  			ABVC,
   659  			ABHI,
   660  			ABLS,
   661  			ABGE,
   662  			ABLT,
   663  			ABGT,
   664  			ABLE,
   665  			AADR, /* strange */
   666  			AADRP:
   667  			q1 = p.Pcond
   668  
   669  			if q1 != nil {
   670  				for q1.As == obj.ANOP {
   671  					q1 = q1.Link
   672  					p.Pcond = q1
   673  				}
   674  			}
   675  
   676  			break
   677  		}
   678  
   679  		q = p
   680  	}
   681  
   682  	var q2 *obj.Prog
   683  	var retjmp *obj.LSym
   684  	for p := cursym.Text; p != nil; p = p.Link {
   685  		o := p.As
   686  		switch o {
   687  		case obj.ATEXT:
   688  			cursym.Text = p
   689  			if textstksiz < 0 {
   690  				ctxt.Autosize = 0
   691  			} else {
   692  				ctxt.Autosize = int32(textstksiz + 8)
   693  			}
   694  			if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 {
   695  				ctxt.Autosize = 0
   696  			} else if ctxt.Autosize&(16-1) != 0 {
   697  				// The frame includes an LR.
   698  				// If the frame size is 8, it's only an LR,
   699  				// so there's no potential for breaking references to
   700  				// local variables by growing the frame size,
   701  				// because there are no local variables.
   702  				// But otherwise, if there is a non-empty locals section,
   703  				// the author of the code is responsible for making sure
   704  				// that the frame size is 8 mod 16.
   705  				if ctxt.Autosize == 8 {
   706  					ctxt.Autosize += 8
   707  					cursym.Locals += 8
   708  				} else {
   709  					ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8)
   710  				}
   711  			}
   712  			p.To.Offset = int64(ctxt.Autosize) - 8
   713  			if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) {
   714  				if ctxt.Debugvlog != 0 {
   715  					ctxt.Logf("save suppressed in: %s\n", cursym.Text.From.Sym.Name)
   716  				}
   717  				cursym.Text.Mark |= LEAF
   718  			}
   719  
   720  			if !(p.From3.Offset&obj.NOSPLIT != 0) {
   721  				p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check
   722  			}
   723  
   724  			aoffset = ctxt.Autosize
   725  			if aoffset > 0xF0 {
   726  				aoffset = 0xF0
   727  			}
   728  			if cursym.Text.Mark&LEAF != 0 {
   729  				cursym.Leaf = true
   730  				if ctxt.Autosize == 0 {
   731  					break
   732  				}
   733  				aoffset = 0
   734  			}
   735  
   736  			q = p
   737  			if ctxt.Autosize > aoffset {
   738  				q = ctxt.NewProg()
   739  				q.As = ASUB
   740  				q.Lineno = p.Lineno
   741  				q.From.Type = obj.TYPE_CONST
   742  				q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
   743  				q.To.Type = obj.TYPE_REG
   744  				q.To.Reg = REGSP
   745  				q.Spadj = int32(q.From.Offset)
   746  				q.Link = p.Link
   747  				p.Link = q
   748  				if cursym.Text.Mark&LEAF != 0 {
   749  					break
   750  				}
   751  			}
   752  
   753  			q1 = ctxt.NewProg()
   754  			q1.As = AMOVD
   755  			q1.Lineno = p.Lineno
   756  			q1.From.Type = obj.TYPE_REG
   757  			q1.From.Reg = REGLINK
   758  			q1.To.Type = obj.TYPE_MEM
   759  			q1.Scond = C_XPRE
   760  			q1.To.Offset = int64(-aoffset)
   761  			q1.To.Reg = REGSP
   762  			q1.Link = q.Link
   763  			q1.Spadj = aoffset
   764  			q.Link = q1
   765  
   766  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   767  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   768  				//
   769  				//	MOV g_panic(g), R1
   770  				//	CMP ZR, R1
   771  				//	BEQ end
   772  				//	MOV panic_argp(R1), R2
   773  				//	ADD $(autosize+8), RSP, R3
   774  				//	CMP R2, R3
   775  				//	BNE end
   776  				//	ADD $8, RSP, R4
   777  				//	MOVD R4, panic_argp(R1)
   778  				// end:
   779  				//	NOP
   780  				//
   781  				// The NOP is needed to give the jumps somewhere to land.
   782  				// It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes.
   783  				q = q1
   784  
   785  				q = obj.Appendp(ctxt, q)
   786  				q.As = AMOVD
   787  				q.From.Type = obj.TYPE_MEM
   788  				q.From.Reg = REGG
   789  				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   790  				q.To.Type = obj.TYPE_REG
   791  				q.To.Reg = REG_R1
   792  
   793  				q = obj.Appendp(ctxt, q)
   794  				q.As = ACMP
   795  				q.From.Type = obj.TYPE_REG
   796  				q.From.Reg = REGZERO
   797  				q.Reg = REG_R1
   798  
   799  				q = obj.Appendp(ctxt, q)
   800  				q.As = ABEQ
   801  				q.To.Type = obj.TYPE_BRANCH
   802  				q1 = q
   803  
   804  				q = obj.Appendp(ctxt, q)
   805  				q.As = AMOVD
   806  				q.From.Type = obj.TYPE_MEM
   807  				q.From.Reg = REG_R1
   808  				q.From.Offset = 0 // Panic.argp
   809  				q.To.Type = obj.TYPE_REG
   810  				q.To.Reg = REG_R2
   811  
   812  				q = obj.Appendp(ctxt, q)
   813  				q.As = AADD
   814  				q.From.Type = obj.TYPE_CONST
   815  				q.From.Offset = int64(ctxt.Autosize) + 8
   816  				q.Reg = REGSP
   817  				q.To.Type = obj.TYPE_REG
   818  				q.To.Reg = REG_R3
   819  
   820  				q = obj.Appendp(ctxt, q)
   821  				q.As = ACMP
   822  				q.From.Type = obj.TYPE_REG
   823  				q.From.Reg = REG_R2
   824  				q.Reg = REG_R3
   825  
   826  				q = obj.Appendp(ctxt, q)
   827  				q.As = ABNE
   828  				q.To.Type = obj.TYPE_BRANCH
   829  				q2 = q
   830  
   831  				q = obj.Appendp(ctxt, q)
   832  				q.As = AADD
   833  				q.From.Type = obj.TYPE_CONST
   834  				q.From.Offset = 8
   835  				q.Reg = REGSP
   836  				q.To.Type = obj.TYPE_REG
   837  				q.To.Reg = REG_R4
   838  
   839  				q = obj.Appendp(ctxt, q)
   840  				q.As = AMOVD
   841  				q.From.Type = obj.TYPE_REG
   842  				q.From.Reg = REG_R4
   843  				q.To.Type = obj.TYPE_MEM
   844  				q.To.Reg = REG_R1
   845  				q.To.Offset = 0 // Panic.argp
   846  
   847  				q = obj.Appendp(ctxt, q)
   848  
   849  				q.As = obj.ANOP
   850  				q1.Pcond = q
   851  				q2.Pcond = q
   852  			}
   853  
   854  		case obj.ARET:
   855  			nocache(p)
   856  			if p.From.Type == obj.TYPE_CONST {
   857  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   858  				break
   859  			}
   860  
   861  			retjmp = p.To.Sym
   862  			p.To = obj.Addr{}
   863  			if cursym.Text.Mark&LEAF != 0 {
   864  				if ctxt.Autosize != 0 {
   865  					p.As = AADD
   866  					p.From.Type = obj.TYPE_CONST
   867  					p.From.Offset = int64(ctxt.Autosize)
   868  					p.To.Type = obj.TYPE_REG
   869  					p.To.Reg = REGSP
   870  					p.Spadj = -ctxt.Autosize
   871  				}
   872  			} else {
   873  				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
   874  				aoffset = ctxt.Autosize
   875  
   876  				if aoffset > 0xF0 {
   877  					aoffset = 0xF0
   878  				}
   879  				p.As = AMOVD
   880  				p.From.Type = obj.TYPE_MEM
   881  				p.Scond = C_XPOST
   882  				p.From.Offset = int64(aoffset)
   883  				p.From.Reg = REGSP
   884  				p.To.Type = obj.TYPE_REG
   885  				p.To.Reg = REGLINK
   886  				p.Spadj = -aoffset
   887  				if ctxt.Autosize > aoffset {
   888  					q = ctxt.NewProg()
   889  					q.As = AADD
   890  					q.From.Type = obj.TYPE_CONST
   891  					q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
   892  					q.To.Type = obj.TYPE_REG
   893  					q.To.Reg = REGSP
   894  					q.Link = p.Link
   895  					q.Spadj = int32(-q.From.Offset)
   896  					q.Lineno = p.Lineno
   897  					p.Link = q
   898  					p = q
   899  				}
   900  			}
   901  
   902  			if p.As != obj.ARET {
   903  				q = ctxt.NewProg()
   904  				q.Lineno = p.Lineno
   905  				q.Link = p.Link
   906  				p.Link = q
   907  				p = q
   908  			}
   909  
   910  			if retjmp != nil { // retjmp
   911  				p.As = AB
   912  				p.To.Type = obj.TYPE_BRANCH
   913  				p.To.Sym = retjmp
   914  				p.Spadj = +ctxt.Autosize
   915  				break
   916  			}
   917  
   918  			p.As = obj.ARET
   919  			p.To.Type = obj.TYPE_MEM
   920  			p.To.Offset = 0
   921  			p.To.Reg = REGLINK
   922  			p.Spadj = +ctxt.Autosize
   923  
   924  		case AADD, ASUB:
   925  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   926  				if p.As == AADD {
   927  					p.Spadj = int32(-p.From.Offset)
   928  				} else {
   929  					p.Spadj = int32(+p.From.Offset)
   930  				}
   931  			}
   932  			break
   933  		}
   934  	}
   935  }
   936  
   937  func nocache(p *obj.Prog) {
   938  	p.Optab = 0
   939  	p.From.Class = 0
   940  	p.To.Class = 0
   941  }
   942  
   943  var unaryDst = map[obj.As]bool{
   944  	AWORD:  true,
   945  	ADWORD: true,
   946  	ABL:    true,
   947  	AB:     true,
   948  	ASVC:   true,
   949  }
   950  
   951  var Linkarm64 = obj.LinkArch{
   952  	Arch:       sys.ArchARM64,
   953  	Preprocess: preprocess,
   954  	Assemble:   span7,
   955  	Follow:     follow,
   956  	Progedit:   progedit,
   957  	UnaryDst:   unaryDst,
   958  }