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
31package arm
32
33import (
34	"cmd/internal/obj"
35	"cmd/internal/objabi"
36	"cmd/internal/sys"
37)
38
39var progedit_tlsfallback *obj.LSym
40
41func 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.
130func (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.From3 != nil && p.From3.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
243const (
244	FOLL  = 1 << 0
245	LABEL = 1 << 1
246	LEAF  = 1 << 2
247)
248
249func 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	c.softfloat()
259
260	p := c.cursym.Func.Text
261	autoffset := int32(p.To.Offset)
262	if autoffset < 0 {
263		autoffset = 0
264	}
265	cursym.Func.Locals = autoffset
266	cursym.Func.Args = p.To.Val.(int32)
267
268	/*
269	 * find leaf subroutines
270	 * strip NOPs
271	 * expand RET
272	 * expand BECOME pseudo
273	 */
274	var q1 *obj.Prog
275	var q *obj.Prog
276	for p := cursym.Func.Text; p != nil; p = p.Link {
277		switch p.As {
278		case obj.ATEXT:
279			p.Mark |= LEAF
280
281		case obj.ARET:
282			break
283
284		case ADIV, ADIVU, AMOD, AMODU:
285			q = p
286			cursym.Func.Text.Mark &^= LEAF
287			continue
288
289		case obj.ANOP:
290			q1 = p.Link
291			q.Link = q1 /* q is non-nop */
292			if q1 != nil {
293				q1.Mark |= p.Mark
294			}
295			continue
296
297		case ABL,
298			ABX,
299			obj.ADUFFZERO,
300			obj.ADUFFCOPY:
301			cursym.Func.Text.Mark &^= LEAF
302			fallthrough
303
304		case AB,
305			ABEQ,
306			ABNE,
307			ABCS,
308			ABHS,
309			ABCC,
310			ABLO,
311			ABMI,
312			ABPL,
313			ABVS,
314			ABVC,
315			ABHI,
316			ABLS,
317			ABGE,
318			ABLT,
319			ABGT,
320			ABLE:
321			q1 = p.Pcond
322			if q1 != nil {
323				for q1.As == obj.ANOP {
324					q1 = q1.Link
325					p.Pcond = q1
326				}
327			}
328		}
329
330		q = p
331	}
332
333	var q2 *obj.Prog
334	for p := cursym.Func.Text; p != nil; p = p.Link {
335		o := p.As
336		switch o {
337		case obj.ATEXT:
338			autosize = int32(p.To.Offset + 4)
339			if autosize <= 4 {
340				if cursym.Func.Text.Mark&LEAF != 0 {
341					p.To.Offset = -4
342					autosize = 0
343				}
344			}
345
346			if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 {
347				if ctxt.Debugvlog {
348					ctxt.Logf("save suppressed in: %s\n", cursym.Name)
349				}
350
351				cursym.Func.Text.Mark |= LEAF
352			}
353
354			if cursym.Func.Text.Mark&LEAF != 0 {
355				cursym.Set(obj.AttrLeaf, true)
356				if autosize == 0 {
357					break
358				}
359			}
360
361			if !p.From.Sym.NoSplit() {
362				p = c.stacksplit(p, autosize) // emit split check
363			}
364
365			// MOVW.W		R14,$-autosize(SP)
366			p = obj.Appendp(p, c.newprog)
367
368			p.As = AMOVW
369			p.Scond |= C_WBIT
370			p.From.Type = obj.TYPE_REG
371			p.From.Reg = REGLINK
372			p.To.Type = obj.TYPE_MEM
373			p.To.Offset = int64(-autosize)
374			p.To.Reg = REGSP
375			p.Spadj = autosize
376
377			if cursym.Func.Text.From.Sym.Wrapper() {
378				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
379				//
380				//	MOVW g_panic(g), R1
381				//	CMP  $0, R1
382				//	B.NE checkargp
383				// end:
384				//	NOP
385				// ... function ...
386				// checkargp:
387				//	MOVW panic_argp(R1), R2
388				//	ADD  $(autosize+4), R13, R3
389				//	CMP  R2, R3
390				//	B.NE end
391				//	ADD  $4, R13, R4
392				//	MOVW R4, panic_argp(R1)
393				//	B    end
394				//
395				// The NOP is needed to give the jumps somewhere to land.
396				// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
397
398				p = obj.Appendp(p, newprog)
399				p.As = AMOVW
400				p.From.Type = obj.TYPE_MEM
401				p.From.Reg = REGG
402				p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
403				p.To.Type = obj.TYPE_REG
404				p.To.Reg = REG_R1
405
406				p = obj.Appendp(p, newprog)
407				p.As = ACMP
408				p.From.Type = obj.TYPE_CONST
409				p.From.Offset = 0
410				p.Reg = REG_R1
411
412				// B.NE checkargp
413				bne := obj.Appendp(p, newprog)
414				bne.As = ABNE
415				bne.To.Type = obj.TYPE_BRANCH
416
417				// end: NOP
418				end := obj.Appendp(bne, newprog)
419				end.As = obj.ANOP
420
421				// find end of function
422				var last *obj.Prog
423				for last = end; last.Link != nil; last = last.Link {
424				}
425
426				// MOVW panic_argp(R1), R2
427				mov := obj.Appendp(last, newprog)
428				mov.As = AMOVW
429				mov.From.Type = obj.TYPE_MEM
430				mov.From.Reg = REG_R1
431				mov.From.Offset = 0 // Panic.argp
432				mov.To.Type = obj.TYPE_REG
433				mov.To.Reg = REG_R2
434
435				// B.NE branch target is MOVW above
436				bne.Pcond = mov
437
438				// ADD $(autosize+4), R13, R3
439				p = obj.Appendp(mov, newprog)
440				p.As = AADD
441				p.From.Type = obj.TYPE_CONST
442				p.From.Offset = int64(autosize) + 4
443				p.Reg = REG_R13
444				p.To.Type = obj.TYPE_REG
445				p.To.Reg = REG_R3
446
447				// CMP R2, R3
448				p = obj.Appendp(p, newprog)
449				p.As = ACMP
450				p.From.Type = obj.TYPE_REG
451				p.From.Reg = REG_R2
452				p.Reg = REG_R3
453
454				// B.NE end
455				p = obj.Appendp(p, newprog)
456				p.As = ABNE
457				p.To.Type = obj.TYPE_BRANCH
458				p.Pcond = end
459
460				// ADD $4, R13, R4
461				p = obj.Appendp(p, newprog)
462				p.As = AADD
463				p.From.Type = obj.TYPE_CONST
464				p.From.Offset = 4
465				p.Reg = REG_R13
466				p.To.Type = obj.TYPE_REG
467				p.To.Reg = REG_R4
468
469				// MOVW R4, panic_argp(R1)
470				p = obj.Appendp(p, newprog)
471				p.As = AMOVW
472				p.From.Type = obj.TYPE_REG
473				p.From.Reg = REG_R4
474				p.To.Type = obj.TYPE_MEM
475				p.To.Reg = REG_R1
476				p.To.Offset = 0 // Panic.argp
477
478				// B end
479				p = obj.Appendp(p, newprog)
480				p.As = AB
481				p.To.Type = obj.TYPE_BRANCH
482				p.Pcond = end
483
484				// reset for subsequent passes
485				p = end
486			}
487
488		case obj.ARET:
489			nocache(p)
490			if cursym.Func.Text.Mark&LEAF != 0 {
491				if autosize == 0 {
492					p.As = AB
493					p.From = obj.Addr{}
494					if p.To.Sym != nil { // retjmp
495						p.To.Type = obj.TYPE_BRANCH
496					} else {
497						p.To.Type = obj.TYPE_MEM
498						p.To.Offset = 0
499						p.To.Reg = REGLINK
500					}
501
502					break
503				}
504			}
505
506			p.As = AMOVW
507			p.Scond |= C_PBIT
508			p.From.Type = obj.TYPE_MEM
509			p.From.Offset = int64(autosize)
510			p.From.Reg = REGSP
511			p.To.Type = obj.TYPE_REG
512			p.To.Reg = REGPC
513
514			// If there are instructions following
515			// this ARET, they come from a branch
516			// with the same stackframe, so no spadj.
517			if p.To.Sym != nil { // retjmp
518				p.To.Reg = REGLINK
519				q2 = obj.Appendp(p, newprog)
520				q2.As = AB
521				q2.To.Type = obj.TYPE_BRANCH
522				q2.To.Sym = p.To.Sym
523				p.To.Sym = nil
524				p = q2
525			}
526
527		case AADD:
528			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
529				p.Spadj = int32(-p.From.Offset)
530			}
531
532		case ASUB:
533			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
534				p.Spadj = int32(p.From.Offset)
535			}
536
537		case ADIV, ADIVU, AMOD, AMODU:
538			if cursym.Func.Text.From.Sym.NoSplit() {
539				ctxt.Diag("cannot divide in NOSPLIT function")
540			}
541			const debugdivmod = false
542			if debugdivmod {
543				break
544			}
545			if p.From.Type != obj.TYPE_REG {
546				break
547			}
548			if p.To.Type != obj.TYPE_REG {
549				break
550			}
551
552			// Make copy because we overwrite p below.
553			q1 := *p
554			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
555				ctxt.Diag("div already using REGTMP: %v", p)
556			}
557
558			/* MOV m(g),REGTMP */
559			p.As = AMOVW
560			p.Pos = q1.Pos
561			p.From.Type = obj.TYPE_MEM
562			p.From.Reg = REGG
563			p.From.Offset = 6 * 4 // offset of g.m
564			p.Reg = 0
565			p.To.Type = obj.TYPE_REG
566			p.To.Reg = REGTMP
567
568			/* MOV a,m_divmod(REGTMP) */
569			p = obj.Appendp(p, newprog)
570			p.As = AMOVW
571			p.Pos = q1.Pos
572			p.From.Type = obj.TYPE_REG
573			p.From.Reg = q1.From.Reg
574			p.To.Type = obj.TYPE_MEM
575			p.To.Reg = REGTMP
576			p.To.Offset = 8 * 4 // offset of m.divmod
577
578			/* MOV b, R8 */
579			p = obj.Appendp(p, newprog)
580			p.As = AMOVW
581			p.Pos = q1.Pos
582			p.From.Type = obj.TYPE_REG
583			p.From.Reg = q1.Reg
584			if q1.Reg == 0 {
585				p.From.Reg = q1.To.Reg
586			}
587			p.To.Type = obj.TYPE_REG
588			p.To.Reg = REG_R8
589			p.To.Offset = 0
590
591			/* CALL appropriate */
592			p = obj.Appendp(p, newprog)
593			p.As = ABL
594			p.Pos = q1.Pos
595			p.To.Type = obj.TYPE_BRANCH
596			switch o {
597			case ADIV:
598				p.To.Sym = symdiv
599			case ADIVU:
600				p.To.Sym = symdivu
601			case AMOD:
602				p.To.Sym = symmod
603			case AMODU:
604				p.To.Sym = symmodu
605			}
606
607			/* MOV REGTMP, b */
608			p = obj.Appendp(p, newprog)
609			p.As = AMOVW
610			p.Pos = q1.Pos
611			p.From.Type = obj.TYPE_REG
612			p.From.Reg = REGTMP
613			p.From.Offset = 0
614			p.To.Type = obj.TYPE_REG
615			p.To.Reg = q1.To.Reg
616
617		case AMOVW:
618			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
619				p.Spadj = int32(-p.To.Offset)
620			}
621			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
622				p.Spadj = int32(-p.From.Offset)
623			}
624			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
625				p.Spadj = int32(-p.From.Offset)
626			}
627		}
628	}
629}
630
631func isfloatreg(a *obj.Addr) bool {
632	return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15
633}
634
635func (c *ctxt5) softfloat() {
636	if objabi.GOARM > 5 {
637		return
638	}
639
640	symsfloat := c.ctxt.Lookup("runtime._sfloat")
641
642	wasfloat := 0
643	for p := c.cursym.Func.Text; p != nil; p = p.Link {
644		if p.Pcond != nil {
645			p.Pcond.Mark |= LABEL
646		}
647	}
648	var next *obj.Prog
649	for p := c.cursym.Func.Text; p != nil; p = p.Link {
650		switch p.As {
651		case AMOVW:
652			if isfloatreg(&p.To) || isfloatreg(&p.From) {
653				goto soft
654			}
655			goto notsoft
656
657		case AMOVWD,
658			AMOVWF,
659			AMOVDW,
660			AMOVFW,
661			AMOVFD,
662			AMOVDF,
663			AMOVF,
664			AMOVD,
665			ACMPF,
666			ACMPD,
667			AADDF,
668			AADDD,
669			ASUBF,
670			ASUBD,
671			AMULF,
672			AMULD,
673			ADIVF,
674			ADIVD,
675			ASQRTF,
676			ASQRTD,
677			AABSF,
678			AABSD,
679			ANEGF,
680			ANEGD:
681			goto soft
682
683		default:
684			goto notsoft
685		}
686
687	soft:
688		if wasfloat == 0 || (p.Mark&LABEL != 0) {
689			next = c.newprog()
690			*next = *p
691
692			// BL runtime·_sfloat(SB)
693			*p = obj.Prog{}
694			p.Ctxt = c.ctxt
695			p.Link = next
696			p.As = ABL
697			p.To.Type = obj.TYPE_BRANCH
698			p.To.Sym = symsfloat
699			p.Pos = next.Pos
700
701			p = next
702			wasfloat = 1
703		}
704
705		continue
706
707	notsoft:
708		wasfloat = 0
709	}
710}
711
712func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
713	// MOVW g_stackguard(g), R1
714	p = obj.Appendp(p, c.newprog)
715
716	p.As = AMOVW
717	p.From.Type = obj.TYPE_MEM
718	p.From.Reg = REGG
719	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
720	if c.cursym.CFunc() {
721		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
722	}
723	p.To.Type = obj.TYPE_REG
724	p.To.Reg = REG_R1
725
726	if framesize <= objabi.StackSmall {
727		// small stack: SP < stackguard
728		//	CMP	stackguard, SP
729		p = obj.Appendp(p, c.newprog)
730
731		p.As = ACMP
732		p.From.Type = obj.TYPE_REG
733		p.From.Reg = REG_R1
734		p.Reg = REGSP
735	} else if framesize <= objabi.StackBig {
736		// large stack: SP-framesize < stackguard-StackSmall
737		//	MOVW $-(framesize-StackSmall)(SP), R2
738		//	CMP stackguard, R2
739		p = obj.Appendp(p, c.newprog)
740
741		p.As = AMOVW
742		p.From.Type = obj.TYPE_ADDR
743		p.From.Reg = REGSP
744		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
745		p.To.Type = obj.TYPE_REG
746		p.To.Reg = REG_R2
747
748		p = obj.Appendp(p, c.newprog)
749		p.As = ACMP
750		p.From.Type = obj.TYPE_REG
751		p.From.Reg = REG_R1
752		p.Reg = REG_R2
753	} else {
754		// Such a large stack we need to protect against wraparound
755		// if SP is close to zero.
756		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
757		// The +StackGuard on both sides is required to keep the left side positive:
758		// SP is allowed to be slightly below stackguard. See stack.h.
759		//	CMP     $StackPreempt, R1
760		//	MOVW.NE $StackGuard(SP), R2
761		//	SUB.NE  R1, R2
762		//	MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
763		//	CMP.NE  R3, R2
764		p = obj.Appendp(p, c.newprog)
765
766		p.As = ACMP
767		p.From.Type = obj.TYPE_CONST
768		p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))
769		p.Reg = REG_R1
770
771		p = obj.Appendp(p, c.newprog)
772		p.As = AMOVW
773		p.From.Type = obj.TYPE_ADDR
774		p.From.Reg = REGSP
775		p.From.Offset = objabi.StackGuard
776		p.To.Type = obj.TYPE_REG
777		p.To.Reg = REG_R2
778		p.Scond = C_SCOND_NE
779
780		p = obj.Appendp(p, c.newprog)
781		p.As = ASUB
782		p.From.Type = obj.TYPE_REG
783		p.From.Reg = REG_R1
784		p.To.Type = obj.TYPE_REG
785		p.To.Reg = REG_R2
786		p.Scond = C_SCOND_NE
787
788		p = obj.Appendp(p, c.newprog)
789		p.As = AMOVW
790		p.From.Type = obj.TYPE_ADDR
791		p.From.Offset = int64(framesize) + (objabi.StackGuard - objabi.StackSmall)
792		p.To.Type = obj.TYPE_REG
793		p.To.Reg = REG_R3
794		p.Scond = C_SCOND_NE
795
796		p = obj.Appendp(p, c.newprog)
797		p.As = ACMP
798		p.From.Type = obj.TYPE_REG
799		p.From.Reg = REG_R3
800		p.Reg = REG_R2
801		p.Scond = C_SCOND_NE
802	}
803
804	// BLS call-to-morestack
805	bls := obj.Appendp(p, c.newprog)
806	bls.As = ABLS
807	bls.To.Type = obj.TYPE_BRANCH
808
809	var last *obj.Prog
810	for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
811	}
812
813	// Now we are at the end of the function, but logically
814	// we are still in function prologue. We need to fix the
815	// SP data and PCDATA.
816	spfix := obj.Appendp(last, c.newprog)
817	spfix.As = obj.ANOP
818	spfix.Spadj = -framesize
819
820	pcdata := obj.Appendp(spfix, c.newprog)
821	pcdata.Pos = c.cursym.Func.Text.Pos
822	pcdata.As = obj.APCDATA
823	pcdata.From.Type = obj.TYPE_CONST
824	pcdata.From.Offset = objabi.PCDATA_StackMapIndex
825	pcdata.To.Type = obj.TYPE_CONST
826	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
827
828	// MOVW	LR, R3
829	movw := obj.Appendp(pcdata, c.newprog)
830	movw.As = AMOVW
831	movw.From.Type = obj.TYPE_REG
832	movw.From.Reg = REGLINK
833	movw.To.Type = obj.TYPE_REG
834	movw.To.Reg = REG_R3
835
836	bls.Pcond = movw
837
838	// BL runtime.morestack
839	call := obj.Appendp(movw, c.newprog)
840	call.As = obj.ACALL
841	call.To.Type = obj.TYPE_BRANCH
842	morestack := "runtime.morestack"
843	switch {
844	case c.cursym.CFunc():
845		morestack = "runtime.morestackc"
846	case !c.cursym.Func.Text.From.Sym.NeedCtxt():
847		morestack = "runtime.morestack_noctxt"
848	}
849	call.To.Sym = c.ctxt.Lookup(morestack)
850
851	// B start
852	b := obj.Appendp(call, c.newprog)
853	b.As = obj.AJMP
854	b.To.Type = obj.TYPE_BRANCH
855	b.Pcond = c.cursym.Func.Text.Link
856	b.Spadj = +framesize
857
858	return bls
859}
860
861var unaryDst = map[obj.As]bool{
862	ASWI:  true,
863	AWORD: true,
864}
865
866var Linkarm = obj.LinkArch{
867	Arch:       sys.ArchARM,
868	Init:       buildop,
869	Preprocess: preprocess,
870	Assemble:   span5,
871	Progedit:   progedit,
872	UnaryDst:   unaryDst,
873}
874