1// cmd/7l/noop.c, cmd/7l/obj.c, cmd/ld/pass.c from Vita Nuova.
2// https://bitbucket.org/plan9-from-bell-labs/9-cc/src/master/
3//
4// 	Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5// 	Portions Copyright © 1995-1997 C H Forsyth ([email protected])
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 ([email protected])
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 arm64
32
33import (
34	"cmd/internal/obj"
35	"cmd/internal/objabi"
36	"cmd/internal/src"
37	"cmd/internal/sys"
38	"internal/abi"
39	"internal/buildcfg"
40	"log"
41	"math"
42)
43
44// zrReplace is the set of instructions for which $0 in the From operand
45// should be replaced with REGZERO.
46var zrReplace = map[obj.As]bool{
47	AMOVD:  true,
48	AMOVW:  true,
49	AMOVWU: true,
50	AMOVH:  true,
51	AMOVHU: true,
52	AMOVB:  true,
53	AMOVBU: true,
54	ASBC:   true,
55	ASBCW:  true,
56	ASBCS:  true,
57	ASBCSW: true,
58	AADC:   true,
59	AADCW:  true,
60	AADCS:  true,
61	AADCSW: true,
62	AFMOVD: true,
63	AFMOVS: true,
64	AMSR:   true,
65}
66
67func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
68	if c.ctxt.Flag_maymorestack != "" {
69		p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
70
71		// Save LR and make room for FP, REGCTXT. Leave room
72		// for caller's saved FP.
73		const frameSize = 32
74		p = obj.Appendp(p, c.newprog)
75		p.As = AMOVD
76		p.From.Type = obj.TYPE_REG
77		p.From.Reg = REGLINK
78		p.To.Type = obj.TYPE_MEM
79		p.Scond = C_XPRE
80		p.To.Offset = -frameSize
81		p.To.Reg = REGSP
82		p.Spadj = frameSize
83
84		// Save FP.
85		p = obj.Appendp(p, c.newprog)
86		p.As = AMOVD
87		p.From.Type = obj.TYPE_REG
88		p.From.Reg = REGFP
89		p.To.Type = obj.TYPE_MEM
90		p.To.Reg = REGSP
91		p.To.Offset = -8
92
93		p = obj.Appendp(p, c.newprog)
94		p.As = ASUB
95		p.From.Type = obj.TYPE_CONST
96		p.From.Offset = 8
97		p.Reg = REGSP
98		p.To.Type = obj.TYPE_REG
99		p.To.Reg = REGFP
100
101		// Save REGCTXT (for simplicity we do this whether or
102		// not we need it.)
103		p = obj.Appendp(p, c.newprog)
104		p.As = AMOVD
105		p.From.Type = obj.TYPE_REG
106		p.From.Reg = REGCTXT
107		p.To.Type = obj.TYPE_MEM
108		p.To.Reg = REGSP
109		p.To.Offset = 8
110
111		// BL maymorestack
112		p = obj.Appendp(p, c.newprog)
113		p.As = ABL
114		p.To.Type = obj.TYPE_BRANCH
115		// See ../x86/obj6.go
116		p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
117
118		// Restore REGCTXT.
119		p = obj.Appendp(p, c.newprog)
120		p.As = AMOVD
121		p.From.Type = obj.TYPE_MEM
122		p.From.Reg = REGSP
123		p.From.Offset = 8
124		p.To.Type = obj.TYPE_REG
125		p.To.Reg = REGCTXT
126
127		// Restore FP.
128		p = obj.Appendp(p, c.newprog)
129		p.As = AMOVD
130		p.From.Type = obj.TYPE_MEM
131		p.From.Reg = REGSP
132		p.From.Offset = -8
133		p.To.Type = obj.TYPE_REG
134		p.To.Reg = REGFP
135
136		// Restore LR and SP.
137		p = obj.Appendp(p, c.newprog)
138		p.As = AMOVD
139		p.From.Type = obj.TYPE_MEM
140		p.Scond = C_XPOST
141		p.From.Offset = frameSize
142		p.From.Reg = REGSP
143		p.To.Type = obj.TYPE_REG
144		p.To.Reg = REGLINK
145		p.Spadj = -frameSize
146
147		p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
148	}
149
150	// Jump back to here after morestack returns.
151	startPred := p
152
153	// MOV	g_stackguard(g), RT1
154	p = obj.Appendp(p, c.newprog)
155
156	p.As = AMOVD
157	p.From.Type = obj.TYPE_MEM
158	p.From.Reg = REGG
159	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
160	if c.cursym.CFunc() {
161		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
162	}
163	p.To.Type = obj.TYPE_REG
164	p.To.Reg = REGRT1
165
166	// Mark the stack bound check and morestack call async nonpreemptible.
167	// If we get preempted here, when resumed the preemption request is
168	// cleared, but we'll still call morestack, which will double the stack
169	// unnecessarily. See issue #35470.
170	p = c.ctxt.StartUnsafePoint(p, c.newprog)
171
172	q := (*obj.Prog)(nil)
173	if framesize <= abi.StackSmall {
174		// small stack: SP < stackguard
175		//	CMP	stackguard, SP
176
177		p = obj.Appendp(p, c.newprog)
178		p.As = ACMP
179		p.From.Type = obj.TYPE_REG
180		p.From.Reg = REGRT1
181		p.Reg = REGSP
182	} else if framesize <= abi.StackBig {
183		// large stack: SP-framesize < stackguard-StackSmall
184		//	SUB	$(framesize-StackSmall), SP, RT2
185		//	CMP	stackguard, RT2
186		p = obj.Appendp(p, c.newprog)
187
188		p.As = ASUB
189		p.From.Type = obj.TYPE_CONST
190		p.From.Offset = int64(framesize) - abi.StackSmall
191		p.Reg = REGSP
192		p.To.Type = obj.TYPE_REG
193		p.To.Reg = REGRT2
194
195		p = obj.Appendp(p, c.newprog)
196		p.As = ACMP
197		p.From.Type = obj.TYPE_REG
198		p.From.Reg = REGRT1
199		p.Reg = REGRT2
200	} else {
201		// Such a large stack we need to protect against underflow.
202		// The runtime guarantees SP > objabi.StackBig, but
203		// framesize is large enough that SP-framesize may
204		// underflow, causing a direct comparison with the
205		// stack guard to incorrectly succeed. We explicitly
206		// guard against underflow.
207		//
208		//	SUBS	$(framesize-StackSmall), SP, RT2
209		//	// On underflow, jump to morestack
210		//	BLO	label_of_call_to_morestack
211		//	CMP	stackguard, RT2
212
213		p = obj.Appendp(p, c.newprog)
214		p.As = ASUBS
215		p.From.Type = obj.TYPE_CONST
216		p.From.Offset = int64(framesize) - abi.StackSmall
217		p.Reg = REGSP
218		p.To.Type = obj.TYPE_REG
219		p.To.Reg = REGRT2
220
221		p = obj.Appendp(p, c.newprog)
222		q = p
223		p.As = ABLO
224		p.To.Type = obj.TYPE_BRANCH
225
226		p = obj.Appendp(p, c.newprog)
227		p.As = ACMP
228		p.From.Type = obj.TYPE_REG
229		p.From.Reg = REGRT1
230		p.Reg = REGRT2
231	}
232
233	// BLS	do-morestack
234	bls := obj.Appendp(p, c.newprog)
235	bls.As = ABLS
236	bls.To.Type = obj.TYPE_BRANCH
237
238	end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
239
240	var last *obj.Prog
241	for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
242	}
243
244	// Now we are at the end of the function, but logically
245	// we are still in function prologue. We need to fix the
246	// SP data and PCDATA.
247	spfix := obj.Appendp(last, c.newprog)
248	spfix.As = obj.ANOP
249	spfix.Spadj = -framesize
250
251	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
252	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
253
254	if q != nil {
255		q.To.SetTarget(pcdata)
256	}
257	bls.To.SetTarget(pcdata)
258
259	spill := c.cursym.Func().SpillRegisterArgs(pcdata, c.newprog)
260
261	// MOV	LR, R3
262	movlr := obj.Appendp(spill, c.newprog)
263	movlr.As = AMOVD
264	movlr.From.Type = obj.TYPE_REG
265	movlr.From.Reg = REGLINK
266	movlr.To.Type = obj.TYPE_REG
267	movlr.To.Reg = REG_R3
268
269	debug := movlr
270	if false {
271		debug = obj.Appendp(debug, c.newprog)
272		debug.As = AMOVD
273		debug.From.Type = obj.TYPE_CONST
274		debug.From.Offset = int64(framesize)
275		debug.To.Type = obj.TYPE_REG
276		debug.To.Reg = REGTMP
277	}
278
279	// BL	runtime.morestack(SB)
280	call := obj.Appendp(debug, c.newprog)
281	call.As = ABL
282	call.To.Type = obj.TYPE_BRANCH
283	morestack := "runtime.morestack"
284	switch {
285	case c.cursym.CFunc():
286		morestack = "runtime.morestackc"
287	case !c.cursym.Func().Text.From.Sym.NeedCtxt():
288		morestack = "runtime.morestack_noctxt"
289	}
290	call.To.Sym = c.ctxt.Lookup(morestack)
291
292	// The instructions which unspill regs should be preemptible.
293	pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
294	unspill := c.cursym.Func().UnspillRegisterArgs(pcdata, c.newprog)
295
296	// B	start
297	jmp := obj.Appendp(unspill, c.newprog)
298	jmp.As = AB
299	jmp.To.Type = obj.TYPE_BRANCH
300	jmp.To.SetTarget(startPred.Link)
301	jmp.Spadj = +framesize
302
303	return end
304}
305
306func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
307	c := ctxt7{ctxt: ctxt, newprog: newprog}
308
309	p.From.Class = 0
310	p.To.Class = 0
311
312	// Previously we rewrote $0 to ZR, but we have now removed this change.
313	// In order to be compatible with some previous legal instruction formats,
314	// reserve the previous conversion for some specific instructions.
315	if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 && zrReplace[p.As] {
316		p.From.Type = obj.TYPE_REG
317		p.From.Reg = REGZERO
318	}
319
320	// Rewrite BR/BL to symbol as TYPE_BRANCH.
321	switch p.As {
322	case AB,
323		ABL,
324		obj.ARET,
325		obj.ADUFFZERO,
326		obj.ADUFFCOPY:
327		if p.To.Sym != nil {
328			p.To.Type = obj.TYPE_BRANCH
329		}
330		break
331	}
332
333	// Rewrite float and vector constants to values stored in memory.
334	switch p.As {
335	case AVMOVS:
336		if p.From.Type == obj.TYPE_CONST {
337			p.From.Type = obj.TYPE_MEM
338			p.From.Sym = c.ctxt.Int32Sym(p.From.Offset)
339			p.From.Name = obj.NAME_EXTERN
340			p.From.Offset = 0
341		}
342
343	case AVMOVD:
344		if p.From.Type == obj.TYPE_CONST {
345			p.From.Type = obj.TYPE_MEM
346			p.From.Sym = c.ctxt.Int64Sym(p.From.Offset)
347			p.From.Name = obj.NAME_EXTERN
348			p.From.Offset = 0
349		}
350
351	case AVMOVQ:
352		if p.From.Type == obj.TYPE_CONST {
353			p.From.Type = obj.TYPE_MEM
354			p.From.Sym = c.ctxt.Int128Sym(p.GetFrom3().Offset, p.From.Offset)
355			p.From.Name = obj.NAME_EXTERN
356			p.From.Offset = 0
357			p.RestArgs = nil
358		}
359
360	case AFMOVS:
361		if p.From.Type == obj.TYPE_FCONST {
362			f64 := p.From.Val.(float64)
363			f32 := float32(f64)
364			if c.chipfloat7(f64) > 0 {
365				break
366			}
367			if math.Float32bits(f32) == 0 {
368				p.From.Type = obj.TYPE_REG
369				p.From.Reg = REGZERO
370				break
371			}
372			p.From.Type = obj.TYPE_MEM
373			p.From.Sym = c.ctxt.Float32Sym(f32)
374			p.From.Name = obj.NAME_EXTERN
375			p.From.Offset = 0
376		}
377
378	case AFMOVD:
379		if p.From.Type == obj.TYPE_FCONST {
380			f64 := p.From.Val.(float64)
381			if c.chipfloat7(f64) > 0 {
382				break
383			}
384			if math.Float64bits(f64) == 0 {
385				p.From.Type = obj.TYPE_REG
386				p.From.Reg = REGZERO
387				break
388			}
389			p.From.Type = obj.TYPE_MEM
390			p.From.Sym = c.ctxt.Float64Sym(f64)
391			p.From.Name = obj.NAME_EXTERN
392			p.From.Offset = 0
393		}
394	}
395
396	if c.ctxt.Flag_dynlink {
397		c.rewriteToUseGot(p)
398	}
399}
400
401// Rewrite p, if necessary, to access global data via the global offset table.
402func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
403	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
404		//     ADUFFxxx $offset
405		// becomes
406		//     MOVD runtime.duffxxx@GOT, REGTMP
407		//     ADD $offset, REGTMP
408		//     CALL REGTMP
409		var sym *obj.LSym
410		if p.As == obj.ADUFFZERO {
411			sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
412		} else {
413			sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
414		}
415		offset := p.To.Offset
416		p.As = AMOVD
417		p.From.Type = obj.TYPE_MEM
418		p.From.Name = obj.NAME_GOTREF
419		p.From.Sym = sym
420		p.To.Type = obj.TYPE_REG
421		p.To.Reg = REGTMP
422		p.To.Name = obj.NAME_NONE
423		p.To.Offset = 0
424		p.To.Sym = nil
425		p1 := obj.Appendp(p, c.newprog)
426		p1.As = AADD
427		p1.From.Type = obj.TYPE_CONST
428		p1.From.Offset = offset
429		p1.To.Type = obj.TYPE_REG
430		p1.To.Reg = REGTMP
431		p2 := obj.Appendp(p1, c.newprog)
432		p2.As = obj.ACALL
433		p2.To.Type = obj.TYPE_REG
434		p2.To.Reg = REGTMP
435	}
436
437	// We only care about global data: NAME_EXTERN means a global
438	// symbol in the Go sense, and p.Sym.Local is true for a few
439	// internally defined symbols.
440	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
441		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
442		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
443		if p.As != AMOVD {
444			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
445		}
446		if p.To.Type != obj.TYPE_REG {
447			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
448		}
449		p.From.Type = obj.TYPE_MEM
450		p.From.Name = obj.NAME_GOTREF
451		if p.From.Offset != 0 {
452			q := obj.Appendp(p, c.newprog)
453			q.As = AADD
454			q.From.Type = obj.TYPE_CONST
455			q.From.Offset = p.From.Offset
456			q.To = p.To
457			p.From.Offset = 0
458		}
459	}
460	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
461		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
462	}
463	var source *obj.Addr
464	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
465	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
466	// An addition may be inserted between the two MOVs if there is an offset.
467	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
468		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
469			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
470		}
471		source = &p.From
472	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
473		source = &p.To
474	} else {
475		return
476	}
477	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
478		return
479	}
480	if source.Sym.Type == objabi.STLSBSS {
481		return
482	}
483	if source.Type != obj.TYPE_MEM {
484		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
485	}
486	p1 := obj.Appendp(p, c.newprog)
487	p2 := obj.Appendp(p1, c.newprog)
488	p1.As = AMOVD
489	p1.From.Type = obj.TYPE_MEM
490	p1.From.Sym = source.Sym
491	p1.From.Name = obj.NAME_GOTREF
492	p1.To.Type = obj.TYPE_REG
493	p1.To.Reg = REGTMP
494
495	p2.As = p.As
496	p2.From = p.From
497	p2.To = p.To
498	if p.From.Name == obj.NAME_EXTERN {
499		p2.From.Reg = REGTMP
500		p2.From.Name = obj.NAME_NONE
501		p2.From.Sym = nil
502	} else if p.To.Name == obj.NAME_EXTERN {
503		p2.To.Reg = REGTMP
504		p2.To.Name = obj.NAME_NONE
505		p2.To.Sym = nil
506	} else {
507		return
508	}
509	obj.Nopout(p)
510}
511
512func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
513	if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
514		return
515	}
516
517	c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
518
519	p := c.cursym.Func().Text
520	textstksiz := p.To.Offset
521	if textstksiz == -8 {
522		// Historical way to mark NOFRAME.
523		p.From.Sym.Set(obj.AttrNoFrame, true)
524		textstksiz = 0
525	}
526	if textstksiz < 0 {
527		c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
528	}
529	if p.From.Sym.NoFrame() {
530		if textstksiz != 0 {
531			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
532		}
533	}
534
535	c.cursym.Func().Args = p.To.Val.(int32)
536	c.cursym.Func().Locals = int32(textstksiz)
537
538	/*
539	 * find leaf subroutines
540	 */
541	for p := c.cursym.Func().Text; p != nil; p = p.Link {
542		switch p.As {
543		case obj.ATEXT:
544			p.Mark |= LEAF
545
546		case ABL,
547			obj.ADUFFZERO,
548			obj.ADUFFCOPY:
549			c.cursym.Func().Text.Mark &^= LEAF
550		}
551	}
552
553	var q *obj.Prog
554	var q1 *obj.Prog
555	for p := c.cursym.Func().Text; p != nil; p = p.Link {
556		o := p.As
557		switch o {
558		case obj.ATEXT:
559			c.cursym.Func().Text = p
560			c.autosize = int32(textstksiz)
561
562			if p.Mark&LEAF != 0 && c.autosize == 0 {
563				// A leaf function with no locals has no frame.
564				p.From.Sym.Set(obj.AttrNoFrame, true)
565			}
566
567			if !p.From.Sym.NoFrame() {
568				// If there is a stack frame at all, it includes
569				// space to save the LR.
570				c.autosize += 8
571			}
572
573			if c.autosize != 0 {
574				extrasize := int32(0)
575				if c.autosize%16 == 8 {
576					// Allocate extra 8 bytes on the frame top to save FP
577					extrasize = 8
578				} else if c.autosize&(16-1) == 0 {
579					// Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
580					extrasize = 16
581				} else {
582					c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
583				}
584				c.autosize += extrasize
585				c.cursym.Func().Locals += extrasize
586
587				// low 32 bits for autosize
588				// high 32 bits for extrasize
589				p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
590			} else {
591				// NOFRAME
592				p.To.Offset = 0
593			}
594
595			if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
596				if c.ctxt.Debugvlog {
597					c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
598				}
599				c.cursym.Func().Text.Mark |= LEAF
600			}
601
602			if cursym.Func().Text.Mark&LEAF != 0 {
603				cursym.Set(obj.AttrLeaf, true)
604				if p.From.Sym.NoFrame() {
605					break
606				}
607			}
608
609			if p.Mark&LEAF != 0 && c.autosize < abi.StackSmall {
610				// A leaf function with a small stack can be marked
611				// NOSPLIT, avoiding a stack check.
612				p.From.Sym.Set(obj.AttrNoSplit, true)
613			}
614
615			if !p.From.Sym.NoSplit() {
616				p = c.stacksplit(p, c.autosize) // emit split check
617			}
618
619			var prologueEnd *obj.Prog
620
621			aoffset := c.autosize
622			if aoffset > 0xf0 {
623				// MOVD.W offset variant range is -0x100 to 0xf8, SP should be 16-byte aligned.
624				// so the maximum aoffset value is 0xf0.
625				aoffset = 0xf0
626			}
627
628			// Frame is non-empty. Make sure to save link register, even if
629			// it is a leaf function, so that traceback works.
630			q = p
631			if c.autosize > aoffset {
632				// Frame size is too large for a MOVD.W instruction. Store the frame pointer
633				// register and link register before decrementing SP, so if a signal comes
634				// during the execution of the function prologue, the traceback code will
635				// not see a half-updated stack frame.
636
637				// SUB $autosize, RSP, R20
638				q1 = obj.Appendp(q, c.newprog)
639				q1.Pos = p.Pos
640				q1.As = ASUB
641				q1.From.Type = obj.TYPE_CONST
642				q1.From.Offset = int64(c.autosize)
643				q1.Reg = REGSP
644				q1.To.Type = obj.TYPE_REG
645				q1.To.Reg = REG_R20
646
647				prologueEnd = q1
648
649				// STP (R29, R30), -8(R20)
650				q1 = obj.Appendp(q1, c.newprog)
651				q1.Pos = p.Pos
652				q1.As = ASTP
653				q1.From.Type = obj.TYPE_REGREG
654				q1.From.Reg = REGFP
655				q1.From.Offset = REGLINK
656				q1.To.Type = obj.TYPE_MEM
657				q1.To.Reg = REG_R20
658				q1.To.Offset = -8
659
660				// This is not async preemptible, as if we open a frame
661				// at the current SP, it will clobber the saved LR.
662				q1 = c.ctxt.StartUnsafePoint(q1, c.newprog)
663
664				// MOVD R20, RSP
665				q1 = obj.Appendp(q1, c.newprog)
666				q1.Pos = p.Pos
667				q1.As = AMOVD
668				q1.From.Type = obj.TYPE_REG
669				q1.From.Reg = REG_R20
670				q1.To.Type = obj.TYPE_REG
671				q1.To.Reg = REGSP
672				q1.Spadj = c.autosize
673
674				q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
675
676				if buildcfg.GOOS == "ios" {
677					// iOS does not support SA_ONSTACK. We will run the signal handler
678					// on the G stack. If we write below SP, it may be clobbered by
679					// the signal handler. So we save FP and LR after decrementing SP.
680					// STP (R29, R30), -8(RSP)
681					q1 = obj.Appendp(q1, c.newprog)
682					q1.Pos = p.Pos
683					q1.As = ASTP
684					q1.From.Type = obj.TYPE_REGREG
685					q1.From.Reg = REGFP
686					q1.From.Offset = REGLINK
687					q1.To.Type = obj.TYPE_MEM
688					q1.To.Reg = REGSP
689					q1.To.Offset = -8
690				}
691			} else {
692				// small frame, update SP and save LR in a single MOVD.W instruction.
693				// So if a signal comes during the execution of the function prologue,
694				// the traceback code will not see a half-updated stack frame.
695				// Also, on Linux, in a cgo binary we may get a SIGSETXID signal
696				// early on before the signal stack is set, as glibc doesn't allow
697				// us to block SIGSETXID. So it is important that we don't write below
698				// the SP until the signal stack is set.
699				// Luckily, all the functions from thread entry to setting the signal
700				// stack have small frames.
701				q1 = obj.Appendp(q, c.newprog)
702				q1.As = AMOVD
703				q1.Pos = p.Pos
704				q1.From.Type = obj.TYPE_REG
705				q1.From.Reg = REGLINK
706				q1.To.Type = obj.TYPE_MEM
707				q1.Scond = C_XPRE
708				q1.To.Offset = int64(-aoffset)
709				q1.To.Reg = REGSP
710				q1.Spadj = aoffset
711
712				prologueEnd = q1
713
714				// Frame pointer.
715				q1 = obj.Appendp(q1, c.newprog)
716				q1.Pos = p.Pos
717				q1.As = AMOVD
718				q1.From.Type = obj.TYPE_REG
719				q1.From.Reg = REGFP
720				q1.To.Type = obj.TYPE_MEM
721				q1.To.Reg = REGSP
722				q1.To.Offset = -8
723			}
724
725			prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
726
727			q1 = obj.Appendp(q1, c.newprog)
728			q1.Pos = p.Pos
729			q1.As = ASUB
730			q1.From.Type = obj.TYPE_CONST
731			q1.From.Offset = 8
732			q1.Reg = REGSP
733			q1.To.Type = obj.TYPE_REG
734			q1.To.Reg = REGFP
735
736			if c.cursym.Func().Text.From.Sym.Wrapper() {
737				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
738				//
739				//	MOV  g_panic(g), RT1
740				//	CBNZ checkargp
741				// end:
742				//	NOP
743				// ... function body ...
744				// checkargp:
745				//	MOV  panic_argp(RT1), RT2
746				//	ADD  $(autosize+8), RSP, R20
747				//	CMP  RT2, R20
748				//	BNE  end
749				//	ADD  $8, RSP, R20
750				//	MOVD R20, panic_argp(RT1)
751				//	B    end
752				//
753				// The NOP is needed to give the jumps somewhere to land.
754				// It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
755				q = q1
756
757				// MOV g_panic(g), RT1
758				q = obj.Appendp(q, c.newprog)
759				q.As = AMOVD
760				q.From.Type = obj.TYPE_MEM
761				q.From.Reg = REGG
762				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
763				q.To.Type = obj.TYPE_REG
764				q.To.Reg = REGRT1
765
766				// CBNZ RT1, checkargp
767				cbnz := obj.Appendp(q, c.newprog)
768				cbnz.As = ACBNZ
769				cbnz.From.Type = obj.TYPE_REG
770				cbnz.From.Reg = REGRT1
771				cbnz.To.Type = obj.TYPE_BRANCH
772
773				// Empty branch target at the top of the function body
774				end := obj.Appendp(cbnz, c.newprog)
775				end.As = obj.ANOP
776
777				// find the end of the function
778				var last *obj.Prog
779				for last = end; last.Link != nil; last = last.Link {
780				}
781
782				// MOV panic_argp(RT1), RT2
783				mov := obj.Appendp(last, c.newprog)
784				mov.As = AMOVD
785				mov.From.Type = obj.TYPE_MEM
786				mov.From.Reg = REGRT1
787				mov.From.Offset = 0 // Panic.argp
788				mov.To.Type = obj.TYPE_REG
789				mov.To.Reg = REGRT2
790
791				// CBNZ branches to the MOV above
792				cbnz.To.SetTarget(mov)
793
794				// ADD $(autosize+8), SP, R20
795				q = obj.Appendp(mov, c.newprog)
796				q.As = AADD
797				q.From.Type = obj.TYPE_CONST
798				q.From.Offset = int64(c.autosize) + 8
799				q.Reg = REGSP
800				q.To.Type = obj.TYPE_REG
801				q.To.Reg = REG_R20
802
803				// CMP RT2, R20
804				q = obj.Appendp(q, c.newprog)
805				q.As = ACMP
806				q.From.Type = obj.TYPE_REG
807				q.From.Reg = REGRT2
808				q.Reg = REG_R20
809
810				// BNE end
811				q = obj.Appendp(q, c.newprog)
812				q.As = ABNE
813				q.To.Type = obj.TYPE_BRANCH
814				q.To.SetTarget(end)
815
816				// ADD $8, SP, R20
817				q = obj.Appendp(q, c.newprog)
818				q.As = AADD
819				q.From.Type = obj.TYPE_CONST
820				q.From.Offset = 8
821				q.Reg = REGSP
822				q.To.Type = obj.TYPE_REG
823				q.To.Reg = REG_R20
824
825				// MOV R20, panic_argp(RT1)
826				q = obj.Appendp(q, c.newprog)
827				q.As = AMOVD
828				q.From.Type = obj.TYPE_REG
829				q.From.Reg = REG_R20
830				q.To.Type = obj.TYPE_MEM
831				q.To.Reg = REGRT1
832				q.To.Offset = 0 // Panic.argp
833
834				// B end
835				q = obj.Appendp(q, c.newprog)
836				q.As = AB
837				q.To.Type = obj.TYPE_BRANCH
838				q.To.SetTarget(end)
839			}
840
841		case obj.ARET:
842			nocache(p)
843			if p.From.Type == obj.TYPE_CONST {
844				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
845				break
846			}
847
848			retJMP, retReg := p.To.Sym, p.To.Reg
849			if retReg == 0 {
850				retReg = REGLINK
851			}
852			p.To = obj.Addr{}
853			if c.cursym.Func().Text.Mark&LEAF != 0 {
854				if c.autosize != 0 {
855					// Restore frame pointer.
856					// ADD $framesize-8, RSP, R29
857					p.As = AADD
858					p.From.Type = obj.TYPE_CONST
859					p.From.Offset = int64(c.autosize) - 8
860					p.Reg = REGSP
861					p.To.Type = obj.TYPE_REG
862					p.To.Reg = REGFP
863
864					// Pop stack frame.
865					// ADD $framesize, RSP, RSP
866					p = obj.Appendp(p, c.newprog)
867					p.As = AADD
868					p.From.Type = obj.TYPE_CONST
869					p.From.Offset = int64(c.autosize)
870					p.To.Type = obj.TYPE_REG
871					p.To.Reg = REGSP
872					p.Spadj = -c.autosize
873				}
874			} else {
875				aoffset := c.autosize
876				// LDP -8(RSP), (R29, R30)
877				p.As = ALDP
878				p.From.Type = obj.TYPE_MEM
879				p.From.Offset = -8
880				p.From.Reg = REGSP
881				p.To.Type = obj.TYPE_REGREG
882				p.To.Reg = REGFP
883				p.To.Offset = REGLINK
884
885				// ADD $aoffset, RSP, RSP
886				q = newprog()
887				q.As = AADD
888				q.From.Type = obj.TYPE_CONST
889				q.From.Offset = int64(aoffset)
890				q.To.Type = obj.TYPE_REG
891				q.To.Reg = REGSP
892				q.Spadj = -aoffset
893				q.Pos = p.Pos
894				q.Link = p.Link
895				p.Link = q
896				p = q
897			}
898
899			// If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
900			// so that if you are debugging a low-level crash where PC and LR are zero,
901			// you can look at R27 to see what jumped to the zero.
902			// This is useful when bringing up Go on a new system.
903			// (There is similar code in ../ppc64/obj9.go:/if.false.)
904			const debugRETZERO = false
905			if debugRETZERO {
906				if p.As != obj.ARET {
907					q = newprog()
908					q.Pos = p.Pos
909					q.Link = p.Link
910					p.Link = q
911					p = q
912				}
913				p.As = AADR
914				p.From.Type = obj.TYPE_BRANCH
915				p.From.Offset = 0
916				p.To.Type = obj.TYPE_REG
917				p.To.Reg = REGTMP
918
919			}
920
921			if p.As != obj.ARET {
922				q = newprog()
923				q.Pos = p.Pos
924				q.Link = p.Link
925				p.Link = q
926				p = q
927			}
928
929			if retJMP != nil {
930				p.As = AB
931				p.To.Type = obj.TYPE_BRANCH
932				p.To.Sym = retJMP
933				p.Spadj = +c.autosize
934				break
935			}
936
937			p.As = obj.ARET
938			p.To.Type = obj.TYPE_MEM
939			p.To.Offset = 0
940			p.To.Reg = retReg
941			p.Spadj = +c.autosize
942
943		case AADD, ASUB:
944			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
945				if p.As == AADD {
946					p.Spadj = int32(-p.From.Offset)
947				} else {
948					p.Spadj = int32(+p.From.Offset)
949				}
950			}
951
952		case obj.AGETCALLERPC:
953			if cursym.Leaf() {
954				/* MOVD LR, Rd */
955				p.As = AMOVD
956				p.From.Type = obj.TYPE_REG
957				p.From.Reg = REGLINK
958			} else {
959				/* MOVD (RSP), Rd */
960				p.As = AMOVD
961				p.From.Type = obj.TYPE_MEM
962				p.From.Reg = REGSP
963			}
964
965		case obj.ADUFFCOPY:
966			//  ADR	ret_addr, R27
967			//  STP	(FP, R27), -24(SP)
968			//  SUB	24, SP, FP
969			//  DUFFCOPY
970			// ret_addr:
971			//  SUB	8, SP, FP
972
973			q1 := p
974			// copy DUFFCOPY from q1 to q4
975			q4 := obj.Appendp(p, c.newprog)
976			q4.Pos = p.Pos
977			q4.As = obj.ADUFFCOPY
978			q4.To = p.To
979
980			q1.As = AADR
981			q1.From.Type = obj.TYPE_BRANCH
982			q1.To.Type = obj.TYPE_REG
983			q1.To.Reg = REG_R27
984
985			q2 := obj.Appendp(q1, c.newprog)
986			q2.Pos = p.Pos
987			q2.As = ASTP
988			q2.From.Type = obj.TYPE_REGREG
989			q2.From.Reg = REGFP
990			q2.From.Offset = int64(REG_R27)
991			q2.To.Type = obj.TYPE_MEM
992			q2.To.Reg = REGSP
993			q2.To.Offset = -24
994
995			// maintain FP for DUFFCOPY
996			q3 := obj.Appendp(q2, c.newprog)
997			q3.Pos = p.Pos
998			q3.As = ASUB
999			q3.From.Type = obj.TYPE_CONST
1000			q3.From.Offset = 24
1001			q3.Reg = REGSP
1002			q3.To.Type = obj.TYPE_REG
1003			q3.To.Reg = REGFP
1004
1005			q5 := obj.Appendp(q4, c.newprog)
1006			q5.Pos = p.Pos
1007			q5.As = ASUB
1008			q5.From.Type = obj.TYPE_CONST
1009			q5.From.Offset = 8
1010			q5.Reg = REGSP
1011			q5.To.Type = obj.TYPE_REG
1012			q5.To.Reg = REGFP
1013			q1.From.SetTarget(q5)
1014			p = q5
1015
1016		case obj.ADUFFZERO:
1017			//  ADR	ret_addr, R27
1018			//  STP	(FP, R27), -24(SP)
1019			//  SUB	24, SP, FP
1020			//  DUFFZERO
1021			// ret_addr:
1022			//  SUB	8, SP, FP
1023
1024			q1 := p
1025			// copy DUFFZERO from q1 to q4
1026			q4 := obj.Appendp(p, c.newprog)
1027			q4.Pos = p.Pos
1028			q4.As = obj.ADUFFZERO
1029			q4.To = p.To
1030
1031			q1.As = AADR
1032			q1.From.Type = obj.TYPE_BRANCH
1033			q1.To.Type = obj.TYPE_REG
1034			q1.To.Reg = REG_R27
1035
1036			q2 := obj.Appendp(q1, c.newprog)
1037			q2.Pos = p.Pos
1038			q2.As = ASTP
1039			q2.From.Type = obj.TYPE_REGREG
1040			q2.From.Reg = REGFP
1041			q2.From.Offset = int64(REG_R27)
1042			q2.To.Type = obj.TYPE_MEM
1043			q2.To.Reg = REGSP
1044			q2.To.Offset = -24
1045
1046			// maintain FP for DUFFZERO
1047			q3 := obj.Appendp(q2, c.newprog)
1048			q3.Pos = p.Pos
1049			q3.As = ASUB
1050			q3.From.Type = obj.TYPE_CONST
1051			q3.From.Offset = 24
1052			q3.Reg = REGSP
1053			q3.To.Type = obj.TYPE_REG
1054			q3.To.Reg = REGFP
1055
1056			q5 := obj.Appendp(q4, c.newprog)
1057			q5.Pos = p.Pos
1058			q5.As = ASUB
1059			q5.From.Type = obj.TYPE_CONST
1060			q5.From.Offset = 8
1061			q5.Reg = REGSP
1062			q5.To.Type = obj.TYPE_REG
1063			q5.To.Reg = REGFP
1064			q1.From.SetTarget(q5)
1065			p = q5
1066		}
1067
1068		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
1069			f := c.cursym.Func()
1070			if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
1071				c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
1072				if ctxt.Debugvlog || !ctxt.IsAsm {
1073					ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
1074					if !ctxt.IsAsm {
1075						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
1076						ctxt.DiagFlush()
1077						log.Fatalf("bad SPWRITE")
1078					}
1079				}
1080			}
1081		}
1082		if p.From.Type == obj.TYPE_SHIFT && (p.To.Reg == REG_RSP || p.Reg == REG_RSP) {
1083			offset := p.From.Offset
1084			op := offset & (3 << 22)
1085			if op != SHIFT_LL {
1086				ctxt.Diag("illegal combination: %v", p)
1087			}
1088			r := (offset >> 16) & 31
1089			shift := (offset >> 10) & 63
1090			if shift > 4 {
1091				// the shift amount is out of range, in order to avoid repeated error
1092				// reportings, don't call ctxt.Diag, because asmout case 27 has the
1093				// same check.
1094				shift = 7
1095			}
1096			p.From.Type = obj.TYPE_REG
1097			p.From.Reg = int16(REG_LSL + r + (shift&7)<<5)
1098			p.From.Offset = 0
1099		}
1100	}
1101}
1102
1103func nocache(p *obj.Prog) {
1104	p.Optab = 0
1105	p.From.Class = 0
1106	p.To.Class = 0
1107}
1108
1109var unaryDst = map[obj.As]bool{
1110	AWORD:  true,
1111	ADWORD: true,
1112	ABL:    true,
1113	AB:     true,
1114	ACLREX: true,
1115}
1116
1117var Linkarm64 = obj.LinkArch{
1118	Arch:           sys.ArchARM64,
1119	Init:           buildop,
1120	Preprocess:     preprocess,
1121	Assemble:       span7,
1122	Progedit:       progedit,
1123	UnaryDst:       unaryDst,
1124	DWARFRegisters: ARM64DWARFRegisters,
1125}
1126