github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/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  	// Now we are at the end of the function, but logically
   165  	// we are still in function prologue. We need to fix the
   166  	// SP data and PCDATA.
   167  	spfix := obj.Appendp(ctxt, last)
   168  	spfix.As = obj.ANOP
   169  	spfix.Spadj = -framesize
   170  
   171  	pcdata := obj.Appendp(ctxt, spfix)
   172  	pcdata.Lineno = ctxt.Cursym.Text.Lineno
   173  	pcdata.Mode = ctxt.Cursym.Text.Mode
   174  	pcdata.As = obj.APCDATA
   175  	pcdata.From.Type = obj.TYPE_CONST
   176  	pcdata.From.Offset = obj.PCDATA_StackMapIndex
   177  	pcdata.To.Type = obj.TYPE_CONST
   178  	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
   179  
   180  	// MOV	LR, R3
   181  	movlr := obj.Appendp(ctxt, pcdata)
   182  	movlr.As = AMOVD
   183  	movlr.From.Type = obj.TYPE_REG
   184  	movlr.From.Reg = REGLINK
   185  	movlr.To.Type = obj.TYPE_REG
   186  	movlr.To.Reg = REG_R3
   187  	if q != nil {
   188  		q.Pcond = movlr
   189  	}
   190  	bls.Pcond = movlr
   191  
   192  	debug := movlr
   193  	if false {
   194  		debug = obj.Appendp(ctxt, debug)
   195  		debug.As = AMOVD
   196  		debug.From.Type = obj.TYPE_CONST
   197  		debug.From.Offset = int64(framesize)
   198  		debug.To.Type = obj.TYPE_REG
   199  		debug.To.Reg = REGTMP
   200  	}
   201  
   202  	// BL	runtime.morestack(SB)
   203  	call := obj.Appendp(ctxt, debug)
   204  	call.As = ABL
   205  	call.To.Type = obj.TYPE_BRANCH
   206  	morestack := "runtime.morestack"
   207  	switch {
   208  	case ctxt.Cursym.CFunc():
   209  		morestack = "runtime.morestackc"
   210  	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
   211  		morestack = "runtime.morestack_noctxt"
   212  	}
   213  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
   214  
   215  	// B	start
   216  	jmp := obj.Appendp(ctxt, call)
   217  	jmp.As = AB
   218  	jmp.To.Type = obj.TYPE_BRANCH
   219  	jmp.Pcond = ctxt.Cursym.Text.Link
   220  	jmp.Spadj = +framesize
   221  
   222  	// placeholder for bls's jump target
   223  	// p = obj.Appendp(ctxt, p)
   224  	// p.As = obj.ANOP
   225  
   226  	return bls
   227  }
   228  
   229  func progedit(ctxt *obj.Link, p *obj.Prog) {
   230  	p.From.Class = 0
   231  	p.To.Class = 0
   232  
   233  	// $0 results in C_ZCON, which matches both C_REG and various
   234  	// C_xCON, however the C_REG cases in asmout don't expect a
   235  	// constant, so they will use the register fields and assemble
   236  	// a R0. To prevent that, rewrite $0 as ZR.
   237  	if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
   238  		p.From.Type = obj.TYPE_REG
   239  		p.From.Reg = REGZERO
   240  	}
   241  	if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
   242  		p.To.Type = obj.TYPE_REG
   243  		p.To.Reg = REGZERO
   244  	}
   245  
   246  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
   247  	switch p.As {
   248  	case AB,
   249  		ABL,
   250  		obj.ARET,
   251  		obj.ADUFFZERO,
   252  		obj.ADUFFCOPY:
   253  		if p.To.Sym != nil {
   254  			p.To.Type = obj.TYPE_BRANCH
   255  		}
   256  		break
   257  	}
   258  
   259  	// Rewrite float constants to values stored in memory.
   260  	switch p.As {
   261  	case AFMOVS:
   262  		if p.From.Type == obj.TYPE_FCONST {
   263  			f32 := float32(p.From.Val.(float64))
   264  			i32 := math.Float32bits(f32)
   265  			if i32 == 0 {
   266  				p.From.Type = obj.TYPE_REG
   267  				p.From.Reg = REGZERO
   268  				break
   269  			}
   270  			literal := fmt.Sprintf("$f32.%08x", i32)
   271  			s := obj.Linklookup(ctxt, literal, 0)
   272  			s.Size = 4
   273  			p.From.Type = obj.TYPE_MEM
   274  			p.From.Sym = s
   275  			p.From.Sym.Set(obj.AttrLocal, true)
   276  			p.From.Name = obj.NAME_EXTERN
   277  			p.From.Offset = 0
   278  		}
   279  
   280  	case AFMOVD:
   281  		if p.From.Type == obj.TYPE_FCONST {
   282  			i64 := math.Float64bits(p.From.Val.(float64))
   283  			if i64 == 0 {
   284  				p.From.Type = obj.TYPE_REG
   285  				p.From.Reg = REGZERO
   286  				break
   287  			}
   288  			literal := fmt.Sprintf("$f64.%016x", i64)
   289  			s := obj.Linklookup(ctxt, literal, 0)
   290  			s.Size = 8
   291  			p.From.Type = obj.TYPE_MEM
   292  			p.From.Sym = s
   293  			p.From.Sym.Set(obj.AttrLocal, true)
   294  			p.From.Name = obj.NAME_EXTERN
   295  			p.From.Offset = 0
   296  		}
   297  
   298  		break
   299  	}
   300  
   301  	// Rewrite negative immediates as positive immediates with
   302  	// complementary instruction.
   303  	switch p.As {
   304  	case AADD, ASUB, ACMP, ACMN:
   305  		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
   306  			p.From.Offset = -p.From.Offset
   307  			p.As = complements[p.As]
   308  		}
   309  	case AADDW, ASUBW, ACMPW, ACMNW:
   310  		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
   311  			p.From.Offset = -p.From.Offset
   312  			p.As = complements[p.As]
   313  		}
   314  	}
   315  
   316  	// For 32-bit logical instruction with constant,
   317  	// rewrite the high 32-bit to be a repetition of
   318  	// the low 32-bit, so that the BITCON test can be
   319  	// shared for both 32-bit and 64-bit. 32-bit ops
   320  	// will zero the high 32-bit of the destination
   321  	// register anyway.
   322  	switch p.As {
   323  	case AANDW, AORRW, AEORW, AANDSW:
   324  		if p.From.Type == obj.TYPE_CONST {
   325  			v := p.From.Offset & 0xffffffff
   326  			p.From.Offset = v | v<<32
   327  		}
   328  	}
   329  
   330  	if ctxt.Flag_dynlink {
   331  		rewriteToUseGot(ctxt, p)
   332  	}
   333  }
   334  
   335  // Rewrite p, if necessary, to access global data via the global offset table.
   336  func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
   337  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   338  		//     ADUFFxxx $offset
   339  		// becomes
   340  		//     MOVD runtime.duffxxx@GOT, REGTMP
   341  		//     ADD $offset, REGTMP
   342  		//     CALL REGTMP
   343  		var sym *obj.LSym
   344  		if p.As == obj.ADUFFZERO {
   345  			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
   346  		} else {
   347  			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
   348  		}
   349  		offset := p.To.Offset
   350  		p.As = AMOVD
   351  		p.From.Type = obj.TYPE_MEM
   352  		p.From.Name = obj.NAME_GOTREF
   353  		p.From.Sym = sym
   354  		p.To.Type = obj.TYPE_REG
   355  		p.To.Reg = REGTMP
   356  		p.To.Name = obj.NAME_NONE
   357  		p.To.Offset = 0
   358  		p.To.Sym = nil
   359  		p1 := obj.Appendp(ctxt, p)
   360  		p1.As = AADD
   361  		p1.From.Type = obj.TYPE_CONST
   362  		p1.From.Offset = offset
   363  		p1.To.Type = obj.TYPE_REG
   364  		p1.To.Reg = REGTMP
   365  		p2 := obj.Appendp(ctxt, p1)
   366  		p2.As = obj.ACALL
   367  		p2.To.Type = obj.TYPE_REG
   368  		p2.To.Reg = REGTMP
   369  	}
   370  
   371  	// We only care about global data: NAME_EXTERN means a global
   372  	// symbol in the Go sense, and p.Sym.Local is true for a few
   373  	// internally defined symbols.
   374  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   375  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   376  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   377  		if p.As != AMOVD {
   378  			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   379  		}
   380  		if p.To.Type != obj.TYPE_REG {
   381  			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   382  		}
   383  		p.From.Type = obj.TYPE_MEM
   384  		p.From.Name = obj.NAME_GOTREF
   385  		if p.From.Offset != 0 {
   386  			q := obj.Appendp(ctxt, p)
   387  			q.As = AADD
   388  			q.From.Type = obj.TYPE_CONST
   389  			q.From.Offset = p.From.Offset
   390  			q.To = p.To
   391  			p.From.Offset = 0
   392  		}
   393  	}
   394  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   395  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   396  	}
   397  	var source *obj.Addr
   398  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   399  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   400  	// An addition may be inserted between the two MOVs if there is an offset.
   401  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   402  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   403  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   404  		}
   405  		source = &p.From
   406  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   407  		source = &p.To
   408  	} else {
   409  		return
   410  	}
   411  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   412  		return
   413  	}
   414  	if source.Sym.Type == obj.STLSBSS {
   415  		return
   416  	}
   417  	if source.Type != obj.TYPE_MEM {
   418  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   419  	}
   420  	p1 := obj.Appendp(ctxt, p)
   421  	p2 := obj.Appendp(ctxt, p1)
   422  	p1.As = AMOVD
   423  	p1.From.Type = obj.TYPE_MEM
   424  	p1.From.Sym = source.Sym
   425  	p1.From.Name = obj.NAME_GOTREF
   426  	p1.To.Type = obj.TYPE_REG
   427  	p1.To.Reg = REGTMP
   428  
   429  	p2.As = p.As
   430  	p2.From = p.From
   431  	p2.To = p.To
   432  	if p.From.Name == obj.NAME_EXTERN {
   433  		p2.From.Reg = REGTMP
   434  		p2.From.Name = obj.NAME_NONE
   435  		p2.From.Sym = nil
   436  	} else if p.To.Name == obj.NAME_EXTERN {
   437  		p2.To.Reg = REGTMP
   438  		p2.To.Name = obj.NAME_NONE
   439  		p2.To.Sym = nil
   440  	} else {
   441  		return
   442  	}
   443  	obj.Nopout(p)
   444  }
   445  
   446  func follow(ctxt *obj.Link, s *obj.LSym) {
   447  	ctxt.Cursym = s
   448  
   449  	firstp := ctxt.NewProg()
   450  	lastp := firstp
   451  	xfol(ctxt, s.Text, &lastp)
   452  	lastp.Link = nil
   453  	s.Text = firstp.Link
   454  }
   455  
   456  func relinv(a obj.As) obj.As {
   457  	switch a {
   458  	case ABEQ:
   459  		return ABNE
   460  	case ABNE:
   461  		return ABEQ
   462  	case ABCS:
   463  		return ABCC
   464  	case ABHS:
   465  		return ABLO
   466  	case ABCC:
   467  		return ABCS
   468  	case ABLO:
   469  		return ABHS
   470  	case ABMI:
   471  		return ABPL
   472  	case ABPL:
   473  		return ABMI
   474  	case ABVS:
   475  		return ABVC
   476  	case ABVC:
   477  		return ABVS
   478  	case ABHI:
   479  		return ABLS
   480  	case ABLS:
   481  		return ABHI
   482  	case ABGE:
   483  		return ABLT
   484  	case ABLT:
   485  		return ABGE
   486  	case ABGT:
   487  		return ABLE
   488  	case ABLE:
   489  		return ABGT
   490  	case ACBZ:
   491  		return ACBNZ
   492  	case ACBNZ:
   493  		return ACBZ
   494  	case ACBZW:
   495  		return ACBNZW
   496  	case ACBNZW:
   497  		return ACBZW
   498  	}
   499  
   500  	log.Fatalf("unknown relation: %s", Anames[a-obj.ABaseARM64])
   501  	return 0
   502  }
   503  
   504  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   505  	var q *obj.Prog
   506  	var r *obj.Prog
   507  	var i int
   508  
   509  loop:
   510  	if p == nil {
   511  		return
   512  	}
   513  	a := p.As
   514  	if a == AB {
   515  		q = p.Pcond
   516  		if q != nil {
   517  			p.Mark |= FOLL
   518  			p = q
   519  			if !(p.Mark&FOLL != 0) {
   520  				goto loop
   521  			}
   522  		}
   523  	}
   524  
   525  	if p.Mark&FOLL != 0 {
   526  		i = 0
   527  		q = p
   528  		for ; i < 4; i, q = i+1, q.Link {
   529  			if q == *last || q == nil {
   530  				break
   531  			}
   532  			a = q.As
   533  			if a == obj.ANOP {
   534  				i--
   535  				continue
   536  			}
   537  
   538  			if a == AB || a == obj.ARET || a == AERET {
   539  				goto copy
   540  			}
   541  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   542  				continue
   543  			}
   544  			if a != ABEQ && a != ABNE {
   545  				continue
   546  			}
   547  
   548  		copy:
   549  			for {
   550  				r = ctxt.NewProg()
   551  				*r = *p
   552  				if !(r.Mark&FOLL != 0) {
   553  					fmt.Printf("can't happen 1\n")
   554  				}
   555  				r.Mark |= FOLL
   556  				if p != q {
   557  					p = p.Link
   558  					(*last).Link = r
   559  					*last = r
   560  					continue
   561  				}
   562  
   563  				(*last).Link = r
   564  				*last = r
   565  				if a == AB || a == obj.ARET || a == AERET {
   566  					return
   567  				}
   568  				if a == ABNE {
   569  					r.As = ABEQ
   570  				} else {
   571  					r.As = ABNE
   572  				}
   573  				r.Pcond = p.Link
   574  				r.Link = p.Pcond
   575  				if !(r.Link.Mark&FOLL != 0) {
   576  					xfol(ctxt, r.Link, last)
   577  				}
   578  				if !(r.Pcond.Mark&FOLL != 0) {
   579  					fmt.Printf("can't happen 2\n")
   580  				}
   581  				return
   582  			}
   583  		}
   584  
   585  		a = AB
   586  		q = ctxt.NewProg()
   587  		q.As = a
   588  		q.Lineno = p.Lineno
   589  		q.To.Type = obj.TYPE_BRANCH
   590  		q.To.Offset = p.Pc
   591  		q.Pcond = p
   592  		p = q
   593  	}
   594  
   595  	p.Mark |= FOLL
   596  	(*last).Link = p
   597  	*last = p
   598  	if a == AB || a == obj.ARET || a == AERET {
   599  		return
   600  	}
   601  	if p.Pcond != nil {
   602  		if a != ABL && p.Link != nil {
   603  			q = obj.Brchain(ctxt, p.Link)
   604  			if a != obj.ATEXT {
   605  				if q != nil && (q.Mark&FOLL != 0) {
   606  					p.As = relinv(a)
   607  					p.Link = p.Pcond
   608  					p.Pcond = q
   609  				}
   610  			}
   611  
   612  			xfol(ctxt, p.Link, last)
   613  			q = obj.Brchain(ctxt, p.Pcond)
   614  			if q == nil {
   615  				q = p.Pcond
   616  			}
   617  			if q.Mark&FOLL != 0 {
   618  				p.Pcond = q
   619  				return
   620  			}
   621  
   622  			p = q
   623  			goto loop
   624  		}
   625  	}
   626  
   627  	p = p.Link
   628  	goto loop
   629  }
   630  
   631  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   632  	ctxt.Cursym = cursym
   633  
   634  	if cursym.Text == nil || cursym.Text.Link == nil {
   635  		return
   636  	}
   637  
   638  	p := cursym.Text
   639  	textstksiz := p.To.Offset
   640  	aoffset := int32(textstksiz)
   641  
   642  	cursym.Args = p.To.Val.(int32)
   643  	cursym.Locals = int32(textstksiz)
   644  
   645  	/*
   646  	 * find leaf subroutines
   647  	 * strip NOPs
   648  	 * expand RET
   649  	 */
   650  	q := (*obj.Prog)(nil)
   651  	var q1 *obj.Prog
   652  	for p := cursym.Text; p != nil; p = p.Link {
   653  		switch p.As {
   654  		case obj.ATEXT:
   655  			p.Mark |= LEAF
   656  
   657  		case obj.ARET:
   658  			break
   659  
   660  		case obj.ANOP:
   661  			q1 = p.Link
   662  			q.Link = q1 /* q is non-nop */
   663  			q1.Mark |= p.Mark
   664  			continue
   665  
   666  		case ABL,
   667  			obj.ADUFFZERO,
   668  			obj.ADUFFCOPY:
   669  			cursym.Text.Mark &^= LEAF
   670  			fallthrough
   671  
   672  		case ACBNZ,
   673  			ACBZ,
   674  			ACBNZW,
   675  			ACBZW,
   676  			ATBZ,
   677  			ATBNZ,
   678  			AB,
   679  			ABEQ,
   680  			ABNE,
   681  			ABCS,
   682  			ABHS,
   683  			ABCC,
   684  			ABLO,
   685  			ABMI,
   686  			ABPL,
   687  			ABVS,
   688  			ABVC,
   689  			ABHI,
   690  			ABLS,
   691  			ABGE,
   692  			ABLT,
   693  			ABGT,
   694  			ABLE,
   695  			AADR, /* strange */
   696  			AADRP:
   697  			q1 = p.Pcond
   698  
   699  			if q1 != nil {
   700  				for q1.As == obj.ANOP {
   701  					q1 = q1.Link
   702  					p.Pcond = q1
   703  				}
   704  			}
   705  
   706  			break
   707  		}
   708  
   709  		q = p
   710  	}
   711  
   712  	var q2 *obj.Prog
   713  	var retjmp *obj.LSym
   714  	for p := cursym.Text; p != nil; p = p.Link {
   715  		o := p.As
   716  		switch o {
   717  		case obj.ATEXT:
   718  			cursym.Text = p
   719  			if textstksiz < 0 {
   720  				ctxt.Autosize = 0
   721  			} else {
   722  				ctxt.Autosize = int32(textstksiz + 8)
   723  			}
   724  			if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 {
   725  				ctxt.Autosize = 0
   726  			} else if ctxt.Autosize&(16-1) != 0 {
   727  				// The frame includes an LR.
   728  				// If the frame size is 8, it's only an LR,
   729  				// so there's no potential for breaking references to
   730  				// local variables by growing the frame size,
   731  				// because there are no local variables.
   732  				// But otherwise, if there is a non-empty locals section,
   733  				// the author of the code is responsible for making sure
   734  				// that the frame size is 8 mod 16.
   735  				if ctxt.Autosize == 8 {
   736  					ctxt.Autosize += 8
   737  					cursym.Locals += 8
   738  				} else {
   739  					ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8)
   740  				}
   741  			}
   742  			p.To.Offset = int64(ctxt.Autosize) - 8
   743  			if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) {
   744  				if ctxt.Debugvlog != 0 {
   745  					ctxt.Logf("save suppressed in: %s\n", cursym.Text.From.Sym.Name)
   746  				}
   747  				cursym.Text.Mark |= LEAF
   748  			}
   749  
   750  			if !(p.From3.Offset&obj.NOSPLIT != 0) {
   751  				p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check
   752  			}
   753  
   754  			aoffset = ctxt.Autosize
   755  			if aoffset > 0xF0 {
   756  				aoffset = 0xF0
   757  			}
   758  			if cursym.Text.Mark&LEAF != 0 {
   759  				cursym.Set(obj.AttrLeaf, true)
   760  				if ctxt.Autosize == 0 {
   761  					break
   762  				}
   763  			}
   764  
   765  			// Frame is non-empty. Make sure to save link register, even if
   766  			// it is a leaf function, so that traceback works.
   767  			q = p
   768  			if ctxt.Autosize > aoffset {
   769  				// Frame size is too large for a MOVD.W instruction.
   770  				// Store link register before decrementing SP, so if a signal comes
   771  				// during the execution of the function prologue, the traceback
   772  				// code will not see a half-updated stack frame.
   773  				q = obj.Appendp(ctxt, q)
   774  				q.Lineno = p.Lineno
   775  				q.As = ASUB
   776  				q.From.Type = obj.TYPE_CONST
   777  				q.From.Offset = int64(ctxt.Autosize)
   778  				q.Reg = REGSP
   779  				q.To.Type = obj.TYPE_REG
   780  				q.To.Reg = REGTMP
   781  
   782  				q = obj.Appendp(ctxt, q)
   783  				q.Lineno = p.Lineno
   784  				q.As = AMOVD
   785  				q.From.Type = obj.TYPE_REG
   786  				q.From.Reg = REGLINK
   787  				q.To.Type = obj.TYPE_MEM
   788  				q.To.Reg = REGTMP
   789  
   790  				q1 = obj.Appendp(ctxt, q)
   791  				q1.Lineno = p.Lineno
   792  				q1.As = AMOVD
   793  				q1.From.Type = obj.TYPE_REG
   794  				q1.From.Reg = REGTMP
   795  				q1.To.Type = obj.TYPE_REG
   796  				q1.To.Reg = REGSP
   797  				q1.Spadj = ctxt.Autosize
   798  			} else {
   799  				// small frame, update SP and save LR in a single MOVD.W instruction
   800  				q1 = obj.Appendp(ctxt, q)
   801  				q1.As = AMOVD
   802  				q1.Lineno = p.Lineno
   803  				q1.From.Type = obj.TYPE_REG
   804  				q1.From.Reg = REGLINK
   805  				q1.To.Type = obj.TYPE_MEM
   806  				q1.Scond = C_XPRE
   807  				q1.To.Offset = int64(-aoffset)
   808  				q1.To.Reg = REGSP
   809  				q1.Spadj = aoffset
   810  			}
   811  
   812  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   813  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   814  				//
   815  				//	MOV g_panic(g), R1
   816  				//	CMP ZR, R1
   817  				//	BEQ end
   818  				//	MOV panic_argp(R1), R2
   819  				//	ADD $(autosize+8), RSP, R3
   820  				//	CMP R2, R3
   821  				//	BNE end
   822  				//	ADD $8, RSP, R4
   823  				//	MOVD R4, panic_argp(R1)
   824  				// end:
   825  				//	NOP
   826  				//
   827  				// The NOP is needed to give the jumps somewhere to land.
   828  				// It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes.
   829  				q = q1
   830  
   831  				q = obj.Appendp(ctxt, q)
   832  				q.As = AMOVD
   833  				q.From.Type = obj.TYPE_MEM
   834  				q.From.Reg = REGG
   835  				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   836  				q.To.Type = obj.TYPE_REG
   837  				q.To.Reg = REG_R1
   838  
   839  				q = obj.Appendp(ctxt, q)
   840  				q.As = ACMP
   841  				q.From.Type = obj.TYPE_REG
   842  				q.From.Reg = REGZERO
   843  				q.Reg = REG_R1
   844  
   845  				q = obj.Appendp(ctxt, q)
   846  				q.As = ABEQ
   847  				q.To.Type = obj.TYPE_BRANCH
   848  				q1 = q
   849  
   850  				q = obj.Appendp(ctxt, q)
   851  				q.As = AMOVD
   852  				q.From.Type = obj.TYPE_MEM
   853  				q.From.Reg = REG_R1
   854  				q.From.Offset = 0 // Panic.argp
   855  				q.To.Type = obj.TYPE_REG
   856  				q.To.Reg = REG_R2
   857  
   858  				q = obj.Appendp(ctxt, q)
   859  				q.As = AADD
   860  				q.From.Type = obj.TYPE_CONST
   861  				q.From.Offset = int64(ctxt.Autosize) + 8
   862  				q.Reg = REGSP
   863  				q.To.Type = obj.TYPE_REG
   864  				q.To.Reg = REG_R3
   865  
   866  				q = obj.Appendp(ctxt, q)
   867  				q.As = ACMP
   868  				q.From.Type = obj.TYPE_REG
   869  				q.From.Reg = REG_R2
   870  				q.Reg = REG_R3
   871  
   872  				q = obj.Appendp(ctxt, q)
   873  				q.As = ABNE
   874  				q.To.Type = obj.TYPE_BRANCH
   875  				q2 = q
   876  
   877  				q = obj.Appendp(ctxt, q)
   878  				q.As = AADD
   879  				q.From.Type = obj.TYPE_CONST
   880  				q.From.Offset = 8
   881  				q.Reg = REGSP
   882  				q.To.Type = obj.TYPE_REG
   883  				q.To.Reg = REG_R4
   884  
   885  				q = obj.Appendp(ctxt, q)
   886  				q.As = AMOVD
   887  				q.From.Type = obj.TYPE_REG
   888  				q.From.Reg = REG_R4
   889  				q.To.Type = obj.TYPE_MEM
   890  				q.To.Reg = REG_R1
   891  				q.To.Offset = 0 // Panic.argp
   892  
   893  				q = obj.Appendp(ctxt, q)
   894  
   895  				q.As = obj.ANOP
   896  				q1.Pcond = q
   897  				q2.Pcond = q
   898  			}
   899  
   900  		case obj.ARET:
   901  			nocache(p)
   902  			if p.From.Type == obj.TYPE_CONST {
   903  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   904  				break
   905  			}
   906  
   907  			retjmp = p.To.Sym
   908  			p.To = obj.Addr{}
   909  			if cursym.Text.Mark&LEAF != 0 {
   910  				if ctxt.Autosize != 0 {
   911  					p.As = AADD
   912  					p.From.Type = obj.TYPE_CONST
   913  					p.From.Offset = int64(ctxt.Autosize)
   914  					p.To.Type = obj.TYPE_REG
   915  					p.To.Reg = REGSP
   916  					p.Spadj = -ctxt.Autosize
   917  				}
   918  			} else {
   919  				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
   920  				aoffset = ctxt.Autosize
   921  
   922  				if aoffset > 0xF0 {
   923  					aoffset = 0xF0
   924  				}
   925  				p.As = AMOVD
   926  				p.From.Type = obj.TYPE_MEM
   927  				p.Scond = C_XPOST
   928  				p.From.Offset = int64(aoffset)
   929  				p.From.Reg = REGSP
   930  				p.To.Type = obj.TYPE_REG
   931  				p.To.Reg = REGLINK
   932  				p.Spadj = -aoffset
   933  				if ctxt.Autosize > aoffset {
   934  					q = ctxt.NewProg()
   935  					q.As = AADD
   936  					q.From.Type = obj.TYPE_CONST
   937  					q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
   938  					q.To.Type = obj.TYPE_REG
   939  					q.To.Reg = REGSP
   940  					q.Link = p.Link
   941  					q.Spadj = int32(-q.From.Offset)
   942  					q.Lineno = p.Lineno
   943  					p.Link = q
   944  					p = q
   945  				}
   946  			}
   947  
   948  			if p.As != obj.ARET {
   949  				q = ctxt.NewProg()
   950  				q.Lineno = p.Lineno
   951  				q.Link = p.Link
   952  				p.Link = q
   953  				p = q
   954  			}
   955  
   956  			if retjmp != nil { // retjmp
   957  				p.As = AB
   958  				p.To.Type = obj.TYPE_BRANCH
   959  				p.To.Sym = retjmp
   960  				p.Spadj = +ctxt.Autosize
   961  				break
   962  			}
   963  
   964  			p.As = obj.ARET
   965  			p.To.Type = obj.TYPE_MEM
   966  			p.To.Offset = 0
   967  			p.To.Reg = REGLINK
   968  			p.Spadj = +ctxt.Autosize
   969  
   970  		case AADD, ASUB:
   971  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   972  				if p.As == AADD {
   973  					p.Spadj = int32(-p.From.Offset)
   974  				} else {
   975  					p.Spadj = int32(+p.From.Offset)
   976  				}
   977  			}
   978  			break
   979  		}
   980  	}
   981  }
   982  
   983  func nocache(p *obj.Prog) {
   984  	p.Optab = 0
   985  	p.From.Class = 0
   986  	p.To.Class = 0
   987  }
   988  
   989  var unaryDst = map[obj.As]bool{
   990  	AWORD:  true,
   991  	ADWORD: true,
   992  	ABL:    true,
   993  	AB:     true,
   994  	ASVC:   true,
   995  }
   996  
   997  var Linkarm64 = obj.LinkArch{
   998  	Arch:       sys.ArchARM64,
   999  	Preprocess: preprocess,
  1000  	Assemble:   span7,
  1001  	Follow:     follow,
  1002  	Progedit:   progedit,
  1003  	UnaryDst:   unaryDst,
  1004  }