...

Source file src/pkg/cmd/internal/obj/arm64/obj7.go

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

View as plain text