...

Source file src/pkg/cmd/internal/obj/arm/obj5.go

     1	// Derived from Inferno utils/5c/swt.c
     2	// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5c/swt.c
     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 arm
    32	
    33	import (
    34		"cmd/internal/obj"
    35		"cmd/internal/objabi"
    36		"cmd/internal/sys"
    37	)
    38	
    39	var progedit_tlsfallback *obj.LSym
    40	
    41	func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    42		p.From.Class = 0
    43		p.To.Class = 0
    44	
    45		c := ctxt5{ctxt: ctxt, newprog: newprog}
    46	
    47		// Rewrite B/BL to symbol as TYPE_BRANCH.
    48		switch p.As {
    49		case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY:
    50			if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
    51				p.To.Type = obj.TYPE_BRANCH
    52			}
    53		}
    54	
    55		// Replace TLS register fetches on older ARM processors.
    56		switch p.As {
    57		// Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
    58		case AMRC:
    59			if p.To.Offset&0xffff0fff == 0xee1d0f70 {
    60				// Because the instruction might be rewritten to a BL which returns in R0
    61				// the register must be zero.
    62				if p.To.Offset&0xf000 != 0 {
    63					ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
    64				}
    65	
    66				if objabi.GOARM < 7 {
    67					// Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.
    68					if progedit_tlsfallback == nil {
    69						progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback")
    70					}
    71	
    72					// MOVW	LR, R11
    73					p.As = AMOVW
    74	
    75					p.From.Type = obj.TYPE_REG
    76					p.From.Reg = REGLINK
    77					p.To.Type = obj.TYPE_REG
    78					p.To.Reg = REGTMP
    79	
    80					// BL	runtime.read_tls_fallback(SB)
    81					p = obj.Appendp(p, newprog)
    82	
    83					p.As = ABL
    84					p.To.Type = obj.TYPE_BRANCH
    85					p.To.Sym = progedit_tlsfallback
    86					p.To.Offset = 0
    87	
    88					// MOVW	R11, LR
    89					p = obj.Appendp(p, newprog)
    90	
    91					p.As = AMOVW
    92					p.From.Type = obj.TYPE_REG
    93					p.From.Reg = REGTMP
    94					p.To.Type = obj.TYPE_REG
    95					p.To.Reg = REGLINK
    96					break
    97				}
    98			}
    99	
   100			// Otherwise, MRC/MCR instructions need no further treatment.
   101			p.As = AWORD
   102		}
   103	
   104		// Rewrite float constants to values stored in memory.
   105		switch p.As {
   106		case AMOVF:
   107			if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
   108				f32 := float32(p.From.Val.(float64))
   109				p.From.Type = obj.TYPE_MEM
   110				p.From.Sym = ctxt.Float32Sym(f32)
   111				p.From.Name = obj.NAME_EXTERN
   112				p.From.Offset = 0
   113			}
   114	
   115		case AMOVD:
   116			if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
   117				p.From.Type = obj.TYPE_MEM
   118				p.From.Sym = ctxt.Float64Sym(p.From.Val.(float64))
   119				p.From.Name = obj.NAME_EXTERN
   120				p.From.Offset = 0
   121			}
   122		}
   123	
   124		if ctxt.Flag_dynlink {
   125			c.rewriteToUseGot(p)
   126		}
   127	}
   128	
   129	// Rewrite p, if necessary, to access global data via the global offset table.
   130	func (c *ctxt5) rewriteToUseGot(p *obj.Prog) {
   131		if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   132			//     ADUFFxxx $offset
   133			// becomes
   134			//     MOVW runtime.duffxxx@GOT, R9
   135			//     ADD $offset, R9
   136			//     CALL (R9)
   137			var sym *obj.LSym
   138			if p.As == obj.ADUFFZERO {
   139				sym = c.ctxt.Lookup("runtime.duffzero")
   140			} else {
   141				sym = c.ctxt.Lookup("runtime.duffcopy")
   142			}
   143			offset := p.To.Offset
   144			p.As = AMOVW
   145			p.From.Type = obj.TYPE_MEM
   146			p.From.Name = obj.NAME_GOTREF
   147			p.From.Sym = sym
   148			p.To.Type = obj.TYPE_REG
   149			p.To.Reg = REG_R9
   150			p.To.Name = obj.NAME_NONE
   151			p.To.Offset = 0
   152			p.To.Sym = nil
   153			p1 := obj.Appendp(p, c.newprog)
   154			p1.As = AADD
   155			p1.From.Type = obj.TYPE_CONST
   156			p1.From.Offset = offset
   157			p1.To.Type = obj.TYPE_REG
   158			p1.To.Reg = REG_R9
   159			p2 := obj.Appendp(p1, c.newprog)
   160			p2.As = obj.ACALL
   161			p2.To.Type = obj.TYPE_MEM
   162			p2.To.Reg = REG_R9
   163			return
   164		}
   165	
   166		// We only care about global data: NAME_EXTERN means a global
   167		// symbol in the Go sense, and p.Sym.Local is true for a few
   168		// internally defined symbols.
   169		if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   170			// MOVW $sym, Rx becomes MOVW sym@GOT, Rx
   171			// MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx
   172			if p.As != AMOVW {
   173				c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   174			}
   175			if p.To.Type != obj.TYPE_REG {
   176				c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   177			}
   178			p.From.Type = obj.TYPE_MEM
   179			p.From.Name = obj.NAME_GOTREF
   180			if p.From.Offset != 0 {
   181				q := obj.Appendp(p, c.newprog)
   182				q.As = AADD
   183				q.From.Type = obj.TYPE_CONST
   184				q.From.Offset = p.From.Offset
   185				q.To = p.To
   186				p.From.Offset = 0
   187			}
   188		}
   189		if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   190			c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   191		}
   192		var source *obj.Addr
   193		// MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry
   194		// MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9)
   195		// An addition may be inserted between the two MOVs if there is an offset.
   196		if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   197			if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   198				c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   199			}
   200			source = &p.From
   201		} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   202			source = &p.To
   203		} else {
   204			return
   205		}
   206		if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   207			return
   208		}
   209		if source.Sym.Type == objabi.STLSBSS {
   210			return
   211		}
   212		if source.Type != obj.TYPE_MEM {
   213			c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   214		}
   215		p1 := obj.Appendp(p, c.newprog)
   216		p2 := obj.Appendp(p1, c.newprog)
   217	
   218		p1.As = AMOVW
   219		p1.From.Type = obj.TYPE_MEM
   220		p1.From.Sym = source.Sym
   221		p1.From.Name = obj.NAME_GOTREF
   222		p1.To.Type = obj.TYPE_REG
   223		p1.To.Reg = REG_R9
   224	
   225		p2.As = p.As
   226		p2.From = p.From
   227		p2.To = p.To
   228		if p.From.Name == obj.NAME_EXTERN {
   229			p2.From.Reg = REG_R9
   230			p2.From.Name = obj.NAME_NONE
   231			p2.From.Sym = nil
   232		} else if p.To.Name == obj.NAME_EXTERN {
   233			p2.To.Reg = REG_R9
   234			p2.To.Name = obj.NAME_NONE
   235			p2.To.Sym = nil
   236		} else {
   237			return
   238		}
   239		obj.Nopout(p)
   240	}
   241	
   242	// Prog.mark
   243	const (
   244		FOLL  = 1 << 0
   245		LABEL = 1 << 1
   246		LEAF  = 1 << 2
   247	)
   248	
   249	func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   250		autosize := int32(0)
   251	
   252		if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
   253			return
   254		}
   255	
   256		c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog}
   257	
   258		p := c.cursym.Func.Text
   259		autoffset := int32(p.To.Offset)
   260		if autoffset == -4 {
   261			// Historical way to mark NOFRAME.
   262			p.From.Sym.Set(obj.AttrNoFrame, true)
   263			autoffset = 0
   264		}
   265		if autoffset < 0 || autoffset%4 != 0 {
   266			c.ctxt.Diag("frame size %d not 0 or a positive multiple of 4", autoffset)
   267		}
   268		if p.From.Sym.NoFrame() {
   269			if autoffset != 0 {
   270				c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", autoffset)
   271			}
   272		}
   273	
   274		cursym.Func.Locals = autoffset
   275		cursym.Func.Args = p.To.Val.(int32)
   276	
   277		/*
   278		 * find leaf subroutines
   279		 * strip NOPs
   280		 * expand RET
   281		 * expand BECOME pseudo
   282		 */
   283		var q1 *obj.Prog
   284		var q *obj.Prog
   285		for p := cursym.Func.Text; p != nil; p = p.Link {
   286			switch p.As {
   287			case obj.ATEXT:
   288				p.Mark |= LEAF
   289	
   290			case obj.ARET:
   291				break
   292	
   293			case ADIV, ADIVU, AMOD, AMODU:
   294				q = p
   295				cursym.Func.Text.Mark &^= LEAF
   296				continue
   297	
   298			case obj.ANOP:
   299				q1 = p.Link
   300				q.Link = q1 /* q is non-nop */
   301				if q1 != nil {
   302					q1.Mark |= p.Mark
   303				}
   304				continue
   305	
   306			case ABL,
   307				ABX,
   308				obj.ADUFFZERO,
   309				obj.ADUFFCOPY:
   310				cursym.Func.Text.Mark &^= LEAF
   311				fallthrough
   312	
   313			case AB,
   314				ABEQ,
   315				ABNE,
   316				ABCS,
   317				ABHS,
   318				ABCC,
   319				ABLO,
   320				ABMI,
   321				ABPL,
   322				ABVS,
   323				ABVC,
   324				ABHI,
   325				ABLS,
   326				ABGE,
   327				ABLT,
   328				ABGT,
   329				ABLE:
   330				q1 = p.Pcond
   331				if q1 != nil {
   332					for q1.As == obj.ANOP {
   333						q1 = q1.Link
   334						p.Pcond = q1
   335					}
   336				}
   337			}
   338	
   339			q = p
   340		}
   341	
   342		var q2 *obj.Prog
   343		for p := cursym.Func.Text; p != nil; p = p.Link {
   344			o := p.As
   345			switch o {
   346			case obj.ATEXT:
   347				autosize = autoffset
   348	
   349				if p.Mark&LEAF != 0 && autosize == 0 {
   350					// A leaf function with no locals has no frame.
   351					p.From.Sym.Set(obj.AttrNoFrame, true)
   352				}
   353	
   354				if !p.From.Sym.NoFrame() {
   355					// If there is a stack frame at all, it includes
   356					// space to save the LR.
   357					autosize += 4
   358				}
   359	
   360				if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 {
   361					// A very few functions that do not return to their caller
   362					// are not identified as leaves but still have no frame.
   363					if ctxt.Debugvlog {
   364						ctxt.Logf("save suppressed in: %s\n", cursym.Name)
   365					}
   366	
   367					cursym.Func.Text.Mark |= LEAF
   368				}
   369	
   370				// FP offsets need an updated p.To.Offset.
   371				p.To.Offset = int64(autosize) - 4
   372	
   373				if cursym.Func.Text.Mark&LEAF != 0 {
   374					cursym.Set(obj.AttrLeaf, true)
   375					if p.From.Sym.NoFrame() {
   376						break
   377					}
   378				}
   379	
   380				if !p.From.Sym.NoSplit() {
   381					p = c.stacksplit(p, autosize) // emit split check
   382				}
   383	
   384				// MOVW.W		R14,$-autosize(SP)
   385				p = obj.Appendp(p, c.newprog)
   386	
   387				p.As = AMOVW
   388				p.Scond |= C_WBIT
   389				p.From.Type = obj.TYPE_REG
   390				p.From.Reg = REGLINK
   391				p.To.Type = obj.TYPE_MEM
   392				p.To.Offset = int64(-autosize)
   393				p.To.Reg = REGSP
   394				p.Spadj = autosize
   395	
   396				if cursym.Func.Text.From.Sym.Wrapper() {
   397					// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   398					//
   399					//	MOVW g_panic(g), R1
   400					//	CMP  $0, R1
   401					//	B.NE checkargp
   402					// end:
   403					//	NOP
   404					// ... function ...
   405					// checkargp:
   406					//	MOVW panic_argp(R1), R2
   407					//	ADD  $(autosize+4), R13, R3
   408					//	CMP  R2, R3
   409					//	B.NE end
   410					//	ADD  $4, R13, R4
   411					//	MOVW R4, panic_argp(R1)
   412					//	B    end
   413					//
   414					// The NOP is needed to give the jumps somewhere to land.
   415					// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
   416	
   417					p = obj.Appendp(p, newprog)
   418					p.As = AMOVW
   419					p.From.Type = obj.TYPE_MEM
   420					p.From.Reg = REGG
   421					p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   422					p.To.Type = obj.TYPE_REG
   423					p.To.Reg = REG_R1
   424	
   425					p = obj.Appendp(p, newprog)
   426					p.As = ACMP
   427					p.From.Type = obj.TYPE_CONST
   428					p.From.Offset = 0
   429					p.Reg = REG_R1
   430	
   431					// B.NE checkargp
   432					bne := obj.Appendp(p, newprog)
   433					bne.As = ABNE
   434					bne.To.Type = obj.TYPE_BRANCH
   435	
   436					// end: NOP
   437					end := obj.Appendp(bne, newprog)
   438					end.As = obj.ANOP
   439	
   440					// find end of function
   441					var last *obj.Prog
   442					for last = end; last.Link != nil; last = last.Link {
   443					}
   444	
   445					// MOVW panic_argp(R1), R2
   446					mov := obj.Appendp(last, newprog)
   447					mov.As = AMOVW
   448					mov.From.Type = obj.TYPE_MEM
   449					mov.From.Reg = REG_R1
   450					mov.From.Offset = 0 // Panic.argp
   451					mov.To.Type = obj.TYPE_REG
   452					mov.To.Reg = REG_R2
   453	
   454					// B.NE branch target is MOVW above
   455					bne.Pcond = mov
   456	
   457					// ADD $(autosize+4), R13, R3
   458					p = obj.Appendp(mov, newprog)
   459					p.As = AADD
   460					p.From.Type = obj.TYPE_CONST
   461					p.From.Offset = int64(autosize) + 4
   462					p.Reg = REG_R13
   463					p.To.Type = obj.TYPE_REG
   464					p.To.Reg = REG_R3
   465	
   466					// CMP R2, R3
   467					p = obj.Appendp(p, newprog)
   468					p.As = ACMP
   469					p.From.Type = obj.TYPE_REG
   470					p.From.Reg = REG_R2
   471					p.Reg = REG_R3
   472	
   473					// B.NE end
   474					p = obj.Appendp(p, newprog)
   475					p.As = ABNE
   476					p.To.Type = obj.TYPE_BRANCH
   477					p.Pcond = end
   478	
   479					// ADD $4, R13, R4
   480					p = obj.Appendp(p, newprog)
   481					p.As = AADD
   482					p.From.Type = obj.TYPE_CONST
   483					p.From.Offset = 4
   484					p.Reg = REG_R13
   485					p.To.Type = obj.TYPE_REG
   486					p.To.Reg = REG_R4
   487	
   488					// MOVW R4, panic_argp(R1)
   489					p = obj.Appendp(p, newprog)
   490					p.As = AMOVW
   491					p.From.Type = obj.TYPE_REG
   492					p.From.Reg = REG_R4
   493					p.To.Type = obj.TYPE_MEM
   494					p.To.Reg = REG_R1
   495					p.To.Offset = 0 // Panic.argp
   496	
   497					// B end
   498					p = obj.Appendp(p, newprog)
   499					p.As = AB
   500					p.To.Type = obj.TYPE_BRANCH
   501					p.Pcond = end
   502	
   503					// reset for subsequent passes
   504					p = end
   505				}
   506	
   507			case obj.ARET:
   508				nocache(p)
   509				if cursym.Func.Text.Mark&LEAF != 0 {
   510					if autosize == 0 {
   511						p.As = AB
   512						p.From = obj.Addr{}
   513						if p.To.Sym != nil { // retjmp
   514							p.To.Type = obj.TYPE_BRANCH
   515						} else {
   516							p.To.Type = obj.TYPE_MEM
   517							p.To.Offset = 0
   518							p.To.Reg = REGLINK
   519						}
   520	
   521						break
   522					}
   523				}
   524	
   525				p.As = AMOVW
   526				p.Scond |= C_PBIT
   527				p.From.Type = obj.TYPE_MEM
   528				p.From.Offset = int64(autosize)
   529				p.From.Reg = REGSP
   530				p.To.Type = obj.TYPE_REG
   531				p.To.Reg = REGPC
   532	
   533				// If there are instructions following
   534				// this ARET, they come from a branch
   535				// with the same stackframe, so no spadj.
   536				if p.To.Sym != nil { // retjmp
   537					p.To.Reg = REGLINK
   538					q2 = obj.Appendp(p, newprog)
   539					q2.As = AB
   540					q2.To.Type = obj.TYPE_BRANCH
   541					q2.To.Sym = p.To.Sym
   542					p.To.Sym = nil
   543					p = q2
   544				}
   545	
   546			case AADD:
   547				if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   548					p.Spadj = int32(-p.From.Offset)
   549				}
   550	
   551			case ASUB:
   552				if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   553					p.Spadj = int32(p.From.Offset)
   554				}
   555	
   556			case ADIV, ADIVU, AMOD, AMODU:
   557				if cursym.Func.Text.From.Sym.NoSplit() {
   558					ctxt.Diag("cannot divide in NOSPLIT function")
   559				}
   560				const debugdivmod = false
   561				if debugdivmod {
   562					break
   563				}
   564				if p.From.Type != obj.TYPE_REG {
   565					break
   566				}
   567				if p.To.Type != obj.TYPE_REG {
   568					break
   569				}
   570	
   571				// Make copy because we overwrite p below.
   572				q1 := *p
   573				if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
   574					ctxt.Diag("div already using REGTMP: %v", p)
   575				}
   576	
   577				/* MOV m(g),REGTMP */
   578				p.As = AMOVW
   579				p.Pos = q1.Pos
   580				p.From.Type = obj.TYPE_MEM
   581				p.From.Reg = REGG
   582				p.From.Offset = 6 * 4 // offset of g.m
   583				p.Reg = 0
   584				p.To.Type = obj.TYPE_REG
   585				p.To.Reg = REGTMP
   586	
   587				/* MOV a,m_divmod(REGTMP) */
   588				p = obj.Appendp(p, newprog)
   589				p.As = AMOVW
   590				p.Pos = q1.Pos
   591				p.From.Type = obj.TYPE_REG
   592				p.From.Reg = q1.From.Reg
   593				p.To.Type = obj.TYPE_MEM
   594				p.To.Reg = REGTMP
   595				p.To.Offset = 8 * 4 // offset of m.divmod
   596	
   597				/* MOV b, R8 */
   598				p = obj.Appendp(p, newprog)
   599				p.As = AMOVW
   600				p.Pos = q1.Pos
   601				p.From.Type = obj.TYPE_REG
   602				p.From.Reg = q1.Reg
   603				if q1.Reg == 0 {
   604					p.From.Reg = q1.To.Reg
   605				}
   606				p.To.Type = obj.TYPE_REG
   607				p.To.Reg = REG_R8
   608				p.To.Offset = 0
   609	
   610				/* CALL appropriate */
   611				p = obj.Appendp(p, newprog)
   612				p.As = ABL
   613				p.Pos = q1.Pos
   614				p.To.Type = obj.TYPE_BRANCH
   615				switch o {
   616				case ADIV:
   617					p.To.Sym = symdiv
   618				case ADIVU:
   619					p.To.Sym = symdivu
   620				case AMOD:
   621					p.To.Sym = symmod
   622				case AMODU:
   623					p.To.Sym = symmodu
   624				}
   625	
   626				/* MOV REGTMP, b */
   627				p = obj.Appendp(p, newprog)
   628				p.As = AMOVW
   629				p.Pos = q1.Pos
   630				p.From.Type = obj.TYPE_REG
   631				p.From.Reg = REGTMP
   632				p.From.Offset = 0
   633				p.To.Type = obj.TYPE_REG
   634				p.To.Reg = q1.To.Reg
   635	
   636			case AMOVW:
   637				if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
   638					p.Spadj = int32(-p.To.Offset)
   639				}
   640				if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
   641					p.Spadj = int32(-p.From.Offset)
   642				}
   643				if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   644					p.Spadj = int32(-p.From.Offset)
   645				}
   646	
   647			case obj.AGETCALLERPC:
   648				if cursym.Leaf() {
   649					/* MOVW LR, Rd */
   650					p.As = AMOVW
   651					p.From.Type = obj.TYPE_REG
   652					p.From.Reg = REGLINK
   653				} else {
   654					/* MOVW (RSP), Rd */
   655					p.As = AMOVW
   656					p.From.Type = obj.TYPE_MEM
   657					p.From.Reg = REGSP
   658				}
   659			}
   660		}
   661	}
   662	
   663	func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   664		// MOVW g_stackguard(g), R1
   665		p = obj.Appendp(p, c.newprog)
   666	
   667		p.As = AMOVW
   668		p.From.Type = obj.TYPE_MEM
   669		p.From.Reg = REGG
   670		p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   671		if c.cursym.CFunc() {
   672			p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   673		}
   674		p.To.Type = obj.TYPE_REG
   675		p.To.Reg = REG_R1
   676	
   677		if framesize <= objabi.StackSmall {
   678			// small stack: SP < stackguard
   679			//	CMP	stackguard, SP
   680			p = obj.Appendp(p, c.newprog)
   681	
   682			p.As = ACMP
   683			p.From.Type = obj.TYPE_REG
   684			p.From.Reg = REG_R1
   685			p.Reg = REGSP
   686		} else if framesize <= objabi.StackBig {
   687			// large stack: SP-framesize < stackguard-StackSmall
   688			//	MOVW $-(framesize-StackSmall)(SP), R2
   689			//	CMP stackguard, R2
   690			p = obj.Appendp(p, c.newprog)
   691	
   692			p.As = AMOVW
   693			p.From.Type = obj.TYPE_ADDR
   694			p.From.Reg = REGSP
   695			p.From.Offset = -(int64(framesize) - objabi.StackSmall)
   696			p.To.Type = obj.TYPE_REG
   697			p.To.Reg = REG_R2
   698	
   699			p = obj.Appendp(p, c.newprog)
   700			p.As = ACMP
   701			p.From.Type = obj.TYPE_REG
   702			p.From.Reg = REG_R1
   703			p.Reg = REG_R2
   704		} else {
   705			// Such a large stack we need to protect against wraparound
   706			// if SP is close to zero.
   707			//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
   708			// The +StackGuard on both sides is required to keep the left side positive:
   709			// SP is allowed to be slightly below stackguard. See stack.h.
   710			//	CMP     $StackPreempt, R1
   711			//	MOVW.NE $StackGuard(SP), R2
   712			//	SUB.NE  R1, R2
   713			//	MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
   714			//	CMP.NE  R3, R2
   715			p = obj.Appendp(p, c.newprog)
   716	
   717			p.As = ACMP
   718			p.From.Type = obj.TYPE_CONST
   719			p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))
   720			p.Reg = REG_R1
   721	
   722			p = obj.Appendp(p, c.newprog)
   723			p.As = AMOVW
   724			p.From.Type = obj.TYPE_ADDR
   725			p.From.Reg = REGSP
   726			p.From.Offset = int64(objabi.StackGuard)
   727			p.To.Type = obj.TYPE_REG
   728			p.To.Reg = REG_R2
   729			p.Scond = C_SCOND_NE
   730	
   731			p = obj.Appendp(p, c.newprog)
   732			p.As = ASUB
   733			p.From.Type = obj.TYPE_REG
   734			p.From.Reg = REG_R1
   735			p.To.Type = obj.TYPE_REG
   736			p.To.Reg = REG_R2
   737			p.Scond = C_SCOND_NE
   738	
   739			p = obj.Appendp(p, c.newprog)
   740			p.As = AMOVW
   741			p.From.Type = obj.TYPE_ADDR
   742			p.From.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall)
   743			p.To.Type = obj.TYPE_REG
   744			p.To.Reg = REG_R3
   745			p.Scond = C_SCOND_NE
   746	
   747			p = obj.Appendp(p, c.newprog)
   748			p.As = ACMP
   749			p.From.Type = obj.TYPE_REG
   750			p.From.Reg = REG_R3
   751			p.Reg = REG_R2
   752			p.Scond = C_SCOND_NE
   753		}
   754	
   755		// BLS call-to-morestack
   756		bls := obj.Appendp(p, c.newprog)
   757		bls.As = ABLS
   758		bls.To.Type = obj.TYPE_BRANCH
   759	
   760		var last *obj.Prog
   761		for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
   762		}
   763	
   764		// Now we are at the end of the function, but logically
   765		// we are still in function prologue. We need to fix the
   766		// SP data and PCDATA.
   767		spfix := obj.Appendp(last, c.newprog)
   768		spfix.As = obj.ANOP
   769		spfix.Spadj = -framesize
   770	
   771		pcdata := c.ctxt.EmitEntryLiveness(c.cursym, spfix, c.newprog)
   772	
   773		// MOVW	LR, R3
   774		movw := obj.Appendp(pcdata, c.newprog)
   775		movw.As = AMOVW
   776		movw.From.Type = obj.TYPE_REG
   777		movw.From.Reg = REGLINK
   778		movw.To.Type = obj.TYPE_REG
   779		movw.To.Reg = REG_R3
   780	
   781		bls.Pcond = movw
   782	
   783		// BL runtime.morestack
   784		call := obj.Appendp(movw, c.newprog)
   785		call.As = obj.ACALL
   786		call.To.Type = obj.TYPE_BRANCH
   787		morestack := "runtime.morestack"
   788		switch {
   789		case c.cursym.CFunc():
   790			morestack = "runtime.morestackc"
   791		case !c.cursym.Func.Text.From.Sym.NeedCtxt():
   792			morestack = "runtime.morestack_noctxt"
   793		}
   794		call.To.Sym = c.ctxt.Lookup(morestack)
   795	
   796		// B start
   797		b := obj.Appendp(call, c.newprog)
   798		b.As = obj.AJMP
   799		b.To.Type = obj.TYPE_BRANCH
   800		b.Pcond = c.cursym.Func.Text.Link
   801		b.Spadj = +framesize
   802	
   803		return bls
   804	}
   805	
   806	var unaryDst = map[obj.As]bool{
   807		ASWI:  true,
   808		AWORD: true,
   809	}
   810	
   811	var Linkarm = obj.LinkArch{
   812		Arch:           sys.ArchARM,
   813		Init:           buildop,
   814		Preprocess:     preprocess,
   815		Assemble:       span5,
   816		Progedit:       progedit,
   817		UnaryDst:       unaryDst,
   818		DWARFRegisters: ARMDWARFRegisters,
   819	}
   820	

View as plain text