github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/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,
   283  		AADDW,
   284  		ASUB,
   285  		ASUBW,
   286  		ACMP,
   287  		ACMPW,
   288  		ACMN,
   289  		ACMNW:
   290  		if p.From.Type == obj.NAME_EXTERN && p.From.Offset < 0 {
   291  			p.From.Offset = -p.From.Offset
   292  			p.As = complements[p.As]
   293  		}
   294  
   295  		break
   296  	}
   297  
   298  	if ctxt.Flag_dynlink {
   299  		rewriteToUseGot(ctxt, p)
   300  	}
   301  }
   302  
   303  // Rewrite p, if necessary, to access global data via the global offset table.
   304  func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
   305  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   306  		//     ADUFFxxx $offset
   307  		// becomes
   308  		//     MOVD runtime.duffxxx@GOT, REGTMP
   309  		//     ADD $offset, REGTMP
   310  		//     CALL REGTMP
   311  		var sym *obj.LSym
   312  		if p.As == obj.ADUFFZERO {
   313  			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
   314  		} else {
   315  			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
   316  		}
   317  		offset := p.To.Offset
   318  		p.As = AMOVD
   319  		p.From.Type = obj.TYPE_MEM
   320  		p.From.Name = obj.NAME_GOTREF
   321  		p.From.Sym = sym
   322  		p.To.Type = obj.TYPE_REG
   323  		p.To.Reg = REGTMP
   324  		p.To.Name = obj.NAME_NONE
   325  		p.To.Offset = 0
   326  		p.To.Sym = nil
   327  		p1 := obj.Appendp(ctxt, p)
   328  		p1.As = AADD
   329  		p1.From.Type = obj.TYPE_CONST
   330  		p1.From.Offset = offset
   331  		p1.To.Type = obj.TYPE_REG
   332  		p1.To.Reg = REGTMP
   333  		p2 := obj.Appendp(ctxt, p1)
   334  		p2.As = obj.ACALL
   335  		p2.To.Type = obj.TYPE_REG
   336  		p2.To.Reg = REGTMP
   337  	}
   338  
   339  	// We only care about global data: NAME_EXTERN means a global
   340  	// symbol in the Go sense, and p.Sym.Local is true for a few
   341  	// internally defined symbols.
   342  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   343  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   344  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   345  		if p.As != AMOVD {
   346  			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   347  		}
   348  		if p.To.Type != obj.TYPE_REG {
   349  			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   350  		}
   351  		p.From.Type = obj.TYPE_MEM
   352  		p.From.Name = obj.NAME_GOTREF
   353  		if p.From.Offset != 0 {
   354  			q := obj.Appendp(ctxt, p)
   355  			q.As = AADD
   356  			q.From.Type = obj.TYPE_CONST
   357  			q.From.Offset = p.From.Offset
   358  			q.To = p.To
   359  			p.From.Offset = 0
   360  		}
   361  	}
   362  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   363  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   364  	}
   365  	var source *obj.Addr
   366  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   367  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   368  	// An addition may be inserted between the two MOVs if there is an offset.
   369  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   370  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   371  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   372  		}
   373  		source = &p.From
   374  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   375  		source = &p.To
   376  	} else {
   377  		return
   378  	}
   379  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   380  		return
   381  	}
   382  	if source.Sym.Type == obj.STLSBSS {
   383  		return
   384  	}
   385  	if source.Type != obj.TYPE_MEM {
   386  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   387  	}
   388  	p1 := obj.Appendp(ctxt, p)
   389  	p2 := obj.Appendp(ctxt, p1)
   390  	p1.As = AMOVD
   391  	p1.From.Type = obj.TYPE_MEM
   392  	p1.From.Sym = source.Sym
   393  	p1.From.Name = obj.NAME_GOTREF
   394  	p1.To.Type = obj.TYPE_REG
   395  	p1.To.Reg = REGTMP
   396  
   397  	p2.As = p.As
   398  	p2.From = p.From
   399  	p2.To = p.To
   400  	if p.From.Name == obj.NAME_EXTERN {
   401  		p2.From.Reg = REGTMP
   402  		p2.From.Name = obj.NAME_NONE
   403  		p2.From.Sym = nil
   404  	} else if p.To.Name == obj.NAME_EXTERN {
   405  		p2.To.Reg = REGTMP
   406  		p2.To.Name = obj.NAME_NONE
   407  		p2.To.Sym = nil
   408  	} else {
   409  		return
   410  	}
   411  	obj.Nopout(p)
   412  }
   413  
   414  func follow(ctxt *obj.Link, s *obj.LSym) {
   415  	ctxt.Cursym = s
   416  
   417  	firstp := ctxt.NewProg()
   418  	lastp := firstp
   419  	xfol(ctxt, s.Text, &lastp)
   420  	lastp.Link = nil
   421  	s.Text = firstp.Link
   422  }
   423  
   424  func relinv(a obj.As) obj.As {
   425  	switch a {
   426  	case ABEQ:
   427  		return ABNE
   428  	case ABNE:
   429  		return ABEQ
   430  	case ABCS:
   431  		return ABCC
   432  	case ABHS:
   433  		return ABLO
   434  	case ABCC:
   435  		return ABCS
   436  	case ABLO:
   437  		return ABHS
   438  	case ABMI:
   439  		return ABPL
   440  	case ABPL:
   441  		return ABMI
   442  	case ABVS:
   443  		return ABVC
   444  	case ABVC:
   445  		return ABVS
   446  	case ABHI:
   447  		return ABLS
   448  	case ABLS:
   449  		return ABHI
   450  	case ABGE:
   451  		return ABLT
   452  	case ABLT:
   453  		return ABGE
   454  	case ABGT:
   455  		return ABLE
   456  	case ABLE:
   457  		return ABGT
   458  	}
   459  
   460  	log.Fatalf("unknown relation: %s", Anames[a])
   461  	return 0
   462  }
   463  
   464  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   465  	var q *obj.Prog
   466  	var r *obj.Prog
   467  	var i int
   468  
   469  loop:
   470  	if p == nil {
   471  		return
   472  	}
   473  	a := p.As
   474  	if a == AB {
   475  		q = p.Pcond
   476  		if q != nil {
   477  			p.Mark |= FOLL
   478  			p = q
   479  			if !(p.Mark&FOLL != 0) {
   480  				goto loop
   481  			}
   482  		}
   483  	}
   484  
   485  	if p.Mark&FOLL != 0 {
   486  		i = 0
   487  		q = p
   488  		for ; i < 4; i, q = i+1, q.Link {
   489  			if q == *last || q == nil {
   490  				break
   491  			}
   492  			a = q.As
   493  			if a == obj.ANOP {
   494  				i--
   495  				continue
   496  			}
   497  
   498  			if a == AB || a == obj.ARET || a == AERET {
   499  				goto copy
   500  			}
   501  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   502  				continue
   503  			}
   504  			if a != ABEQ && a != ABNE {
   505  				continue
   506  			}
   507  
   508  		copy:
   509  			for {
   510  				r = ctxt.NewProg()
   511  				*r = *p
   512  				if !(r.Mark&FOLL != 0) {
   513  					fmt.Printf("can't happen 1\n")
   514  				}
   515  				r.Mark |= FOLL
   516  				if p != q {
   517  					p = p.Link
   518  					(*last).Link = r
   519  					*last = r
   520  					continue
   521  				}
   522  
   523  				(*last).Link = r
   524  				*last = r
   525  				if a == AB || a == obj.ARET || a == AERET {
   526  					return
   527  				}
   528  				if a == ABNE {
   529  					r.As = ABEQ
   530  				} else {
   531  					r.As = ABNE
   532  				}
   533  				r.Pcond = p.Link
   534  				r.Link = p.Pcond
   535  				if !(r.Link.Mark&FOLL != 0) {
   536  					xfol(ctxt, r.Link, last)
   537  				}
   538  				if !(r.Pcond.Mark&FOLL != 0) {
   539  					fmt.Printf("can't happen 2\n")
   540  				}
   541  				return
   542  			}
   543  		}
   544  
   545  		a = AB
   546  		q = ctxt.NewProg()
   547  		q.As = a
   548  		q.Lineno = p.Lineno
   549  		q.To.Type = obj.TYPE_BRANCH
   550  		q.To.Offset = p.Pc
   551  		q.Pcond = p
   552  		p = q
   553  	}
   554  
   555  	p.Mark |= FOLL
   556  	(*last).Link = p
   557  	*last = p
   558  	if a == AB || a == obj.ARET || a == AERET {
   559  		return
   560  	}
   561  	if p.Pcond != nil {
   562  		if a != ABL && p.Link != nil {
   563  			q = obj.Brchain(ctxt, p.Link)
   564  			if a != obj.ATEXT {
   565  				if q != nil && (q.Mark&FOLL != 0) {
   566  					p.As = relinv(a)
   567  					p.Link = p.Pcond
   568  					p.Pcond = q
   569  				}
   570  			}
   571  
   572  			xfol(ctxt, p.Link, last)
   573  			q = obj.Brchain(ctxt, p.Pcond)
   574  			if q == nil {
   575  				q = p.Pcond
   576  			}
   577  			if q.Mark&FOLL != 0 {
   578  				p.Pcond = q
   579  				return
   580  			}
   581  
   582  			p = q
   583  			goto loop
   584  		}
   585  	}
   586  
   587  	p = p.Link
   588  	goto loop
   589  }
   590  
   591  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   592  	ctxt.Cursym = cursym
   593  
   594  	if cursym.Text == nil || cursym.Text.Link == nil {
   595  		return
   596  	}
   597  
   598  	p := cursym.Text
   599  	textstksiz := p.To.Offset
   600  	aoffset := int32(textstksiz)
   601  
   602  	cursym.Args = p.To.Val.(int32)
   603  	cursym.Locals = int32(textstksiz)
   604  
   605  	/*
   606  	 * find leaf subroutines
   607  	 * strip NOPs
   608  	 * expand RET
   609  	 */
   610  	ctxt.Bso.Flush()
   611  	q := (*obj.Prog)(nil)
   612  	var q1 *obj.Prog
   613  	for p := cursym.Text; p != nil; p = p.Link {
   614  		switch p.As {
   615  		case obj.ATEXT:
   616  			p.Mark |= LEAF
   617  
   618  		case obj.ARET:
   619  			break
   620  
   621  		case obj.ANOP:
   622  			q1 = p.Link
   623  			q.Link = q1 /* q is non-nop */
   624  			q1.Mark |= p.Mark
   625  			continue
   626  
   627  		case ABL,
   628  			obj.ADUFFZERO,
   629  			obj.ADUFFCOPY:
   630  			cursym.Text.Mark &^= LEAF
   631  			fallthrough
   632  
   633  		case ACBNZ,
   634  			ACBZ,
   635  			ACBNZW,
   636  			ACBZW,
   637  			ATBZ,
   638  			ATBNZ,
   639  			AB,
   640  			ABEQ,
   641  			ABNE,
   642  			ABCS,
   643  			ABHS,
   644  			ABCC,
   645  			ABLO,
   646  			ABMI,
   647  			ABPL,
   648  			ABVS,
   649  			ABVC,
   650  			ABHI,
   651  			ABLS,
   652  			ABGE,
   653  			ABLT,
   654  			ABGT,
   655  			ABLE,
   656  			AADR, /* strange */
   657  			AADRP:
   658  			q1 = p.Pcond
   659  
   660  			if q1 != nil {
   661  				for q1.As == obj.ANOP {
   662  					q1 = q1.Link
   663  					p.Pcond = q1
   664  				}
   665  			}
   666  
   667  			break
   668  		}
   669  
   670  		q = p
   671  	}
   672  
   673  	var q2 *obj.Prog
   674  	var retjmp *obj.LSym
   675  	for p := cursym.Text; p != nil; p = p.Link {
   676  		o := p.As
   677  		switch o {
   678  		case obj.ATEXT:
   679  			cursym.Text = p
   680  			if textstksiz < 0 {
   681  				ctxt.Autosize = 0
   682  			} else {
   683  				ctxt.Autosize = int32(textstksiz + 8)
   684  			}
   685  			if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 {
   686  				ctxt.Autosize = 0
   687  			} else if ctxt.Autosize&(16-1) != 0 {
   688  				// The frame includes an LR.
   689  				// If the frame size is 8, it's only an LR,
   690  				// so there's no potential for breaking references to
   691  				// local variables by growing the frame size,
   692  				// because there are no local variables.
   693  				// But otherwise, if there is a non-empty locals section,
   694  				// the author of the code is responsible for making sure
   695  				// that the frame size is 8 mod 16.
   696  				if ctxt.Autosize == 8 {
   697  					ctxt.Autosize += 8
   698  					cursym.Locals += 8
   699  				} else {
   700  					ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8)
   701  				}
   702  			}
   703  			p.To.Offset = int64(ctxt.Autosize) - 8
   704  			if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) {
   705  				if ctxt.Debugvlog != 0 {
   706  					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Text.From.Sym.Name)
   707  				}
   708  				ctxt.Bso.Flush()
   709  				cursym.Text.Mark |= LEAF
   710  			}
   711  
   712  			if !(p.From3.Offset&obj.NOSPLIT != 0) {
   713  				p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check
   714  			}
   715  
   716  			aoffset = ctxt.Autosize
   717  			if aoffset > 0xF0 {
   718  				aoffset = 0xF0
   719  			}
   720  			if cursym.Text.Mark&LEAF != 0 {
   721  				cursym.Leaf = true
   722  				if ctxt.Autosize == 0 {
   723  					break
   724  				}
   725  				aoffset = 0
   726  			}
   727  
   728  			q = p
   729  			if ctxt.Autosize > aoffset {
   730  				q = ctxt.NewProg()
   731  				q.As = ASUB
   732  				q.Lineno = p.Lineno
   733  				q.From.Type = obj.TYPE_CONST
   734  				q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
   735  				q.To.Type = obj.TYPE_REG
   736  				q.To.Reg = REGSP
   737  				q.Spadj = int32(q.From.Offset)
   738  				q.Link = p.Link
   739  				p.Link = q
   740  				if cursym.Text.Mark&LEAF != 0 {
   741  					break
   742  				}
   743  			}
   744  
   745  			q1 = ctxt.NewProg()
   746  			q1.As = AMOVD
   747  			q1.Lineno = p.Lineno
   748  			q1.From.Type = obj.TYPE_REG
   749  			q1.From.Reg = REGLINK
   750  			q1.To.Type = obj.TYPE_MEM
   751  			q1.Scond = C_XPRE
   752  			q1.To.Offset = int64(-aoffset)
   753  			q1.To.Reg = REGSP
   754  			q1.Link = q.Link
   755  			q1.Spadj = aoffset
   756  			q.Link = q1
   757  
   758  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   759  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   760  				//
   761  				//	MOV g_panic(g), R1
   762  				//	CMP ZR, R1
   763  				//	BEQ end
   764  				//	MOV panic_argp(R1), R2
   765  				//	ADD $(autosize+8), RSP, R3
   766  				//	CMP R2, R3
   767  				//	BNE end
   768  				//	ADD $8, RSP, R4
   769  				//	MOVD R4, panic_argp(R1)
   770  				// end:
   771  				//	NOP
   772  				//
   773  				// The NOP is needed to give the jumps somewhere to land.
   774  				// It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes.
   775  				q = q1
   776  
   777  				q = obj.Appendp(ctxt, q)
   778  				q.As = AMOVD
   779  				q.From.Type = obj.TYPE_MEM
   780  				q.From.Reg = REGG
   781  				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   782  				q.To.Type = obj.TYPE_REG
   783  				q.To.Reg = REG_R1
   784  
   785  				q = obj.Appendp(ctxt, q)
   786  				q.As = ACMP
   787  				q.From.Type = obj.TYPE_REG
   788  				q.From.Reg = REGZERO
   789  				q.Reg = REG_R1
   790  
   791  				q = obj.Appendp(ctxt, q)
   792  				q.As = ABEQ
   793  				q.To.Type = obj.TYPE_BRANCH
   794  				q1 = q
   795  
   796  				q = obj.Appendp(ctxt, q)
   797  				q.As = AMOVD
   798  				q.From.Type = obj.TYPE_MEM
   799  				q.From.Reg = REG_R1
   800  				q.From.Offset = 0 // Panic.argp
   801  				q.To.Type = obj.TYPE_REG
   802  				q.To.Reg = REG_R2
   803  
   804  				q = obj.Appendp(ctxt, q)
   805  				q.As = AADD
   806  				q.From.Type = obj.TYPE_CONST
   807  				q.From.Offset = int64(ctxt.Autosize) + 8
   808  				q.Reg = REGSP
   809  				q.To.Type = obj.TYPE_REG
   810  				q.To.Reg = REG_R3
   811  
   812  				q = obj.Appendp(ctxt, q)
   813  				q.As = ACMP
   814  				q.From.Type = obj.TYPE_REG
   815  				q.From.Reg = REG_R2
   816  				q.Reg = REG_R3
   817  
   818  				q = obj.Appendp(ctxt, q)
   819  				q.As = ABNE
   820  				q.To.Type = obj.TYPE_BRANCH
   821  				q2 = q
   822  
   823  				q = obj.Appendp(ctxt, q)
   824  				q.As = AADD
   825  				q.From.Type = obj.TYPE_CONST
   826  				q.From.Offset = 8
   827  				q.Reg = REGSP
   828  				q.To.Type = obj.TYPE_REG
   829  				q.To.Reg = REG_R4
   830  
   831  				q = obj.Appendp(ctxt, q)
   832  				q.As = AMOVD
   833  				q.From.Type = obj.TYPE_REG
   834  				q.From.Reg = REG_R4
   835  				q.To.Type = obj.TYPE_MEM
   836  				q.To.Reg = REG_R1
   837  				q.To.Offset = 0 // Panic.argp
   838  
   839  				q = obj.Appendp(ctxt, q)
   840  
   841  				q.As = obj.ANOP
   842  				q1.Pcond = q
   843  				q2.Pcond = q
   844  			}
   845  
   846  		case obj.ARET:
   847  			nocache(p)
   848  			if p.From.Type == obj.TYPE_CONST {
   849  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   850  				break
   851  			}
   852  
   853  			retjmp = p.To.Sym
   854  			p.To = obj.Addr{}
   855  			if cursym.Text.Mark&LEAF != 0 {
   856  				if ctxt.Autosize != 0 {
   857  					p.As = AADD
   858  					p.From.Type = obj.TYPE_CONST
   859  					p.From.Offset = int64(ctxt.Autosize)
   860  					p.To.Type = obj.TYPE_REG
   861  					p.To.Reg = REGSP
   862  					p.Spadj = -ctxt.Autosize
   863  				}
   864  			} else {
   865  				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
   866  				aoffset = ctxt.Autosize
   867  
   868  				if aoffset > 0xF0 {
   869  					aoffset = 0xF0
   870  				}
   871  				p.As = AMOVD
   872  				p.From.Type = obj.TYPE_MEM
   873  				p.Scond = C_XPOST
   874  				p.From.Offset = int64(aoffset)
   875  				p.From.Reg = REGSP
   876  				p.To.Type = obj.TYPE_REG
   877  				p.To.Reg = REGLINK
   878  				p.Spadj = -aoffset
   879  				if ctxt.Autosize > aoffset {
   880  					q = ctxt.NewProg()
   881  					q.As = AADD
   882  					q.From.Type = obj.TYPE_CONST
   883  					q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
   884  					q.To.Type = obj.TYPE_REG
   885  					q.To.Reg = REGSP
   886  					q.Link = p.Link
   887  					q.Spadj = int32(-q.From.Offset)
   888  					q.Lineno = p.Lineno
   889  					p.Link = q
   890  					p = q
   891  				}
   892  			}
   893  
   894  			if p.As != obj.ARET {
   895  				q = ctxt.NewProg()
   896  				q.Lineno = p.Lineno
   897  				q.Link = p.Link
   898  				p.Link = q
   899  				p = q
   900  			}
   901  
   902  			if retjmp != nil { // retjmp
   903  				p.As = AB
   904  				p.To.Type = obj.TYPE_BRANCH
   905  				p.To.Sym = retjmp
   906  				p.Spadj = +ctxt.Autosize
   907  				break
   908  			}
   909  
   910  			p.As = obj.ARET
   911  			p.To.Type = obj.TYPE_MEM
   912  			p.To.Offset = 0
   913  			p.To.Reg = REGLINK
   914  			p.Spadj = +ctxt.Autosize
   915  
   916  		case AADD, ASUB:
   917  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   918  				if p.As == AADD {
   919  					p.Spadj = int32(-p.From.Offset)
   920  				} else {
   921  					p.Spadj = int32(+p.From.Offset)
   922  				}
   923  			}
   924  			break
   925  		}
   926  	}
   927  }
   928  
   929  func nocache(p *obj.Prog) {
   930  	p.Optab = 0
   931  	p.From.Class = 0
   932  	p.To.Class = 0
   933  }
   934  
   935  var unaryDst = map[obj.As]bool{
   936  	AWORD:  true,
   937  	ADWORD: true,
   938  	ABL:    true,
   939  	AB:     true,
   940  	ASVC:   true,
   941  }
   942  
   943  var Linkarm64 = obj.LinkArch{
   944  	Arch:       sys.ArchARM64,
   945  	Preprocess: preprocess,
   946  	Assemble:   span7,
   947  	Follow:     follow,
   948  	Progedit:   progedit,
   949  	UnaryDst:   unaryDst,
   950  }