1// Copyright 2022 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package loong64
6
7import (
8	"cmd/internal/obj"
9	"cmd/internal/objabi"
10	"cmd/internal/sys"
11	"internal/abi"
12	"log"
13	"math"
14)
15
16func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
17	// Rewrite JMP/JAL to symbol as TYPE_BRANCH.
18	switch p.As {
19	case AJMP,
20		AJAL,
21		ARET,
22		obj.ADUFFZERO,
23		obj.ADUFFCOPY:
24		if p.To.Sym != nil {
25			p.To.Type = obj.TYPE_BRANCH
26		}
27	}
28
29	// Rewrite float constants to values stored in memory.
30	switch p.As {
31	case AMOVF:
32		if p.From.Type == obj.TYPE_FCONST {
33			f32 := float32(p.From.Val.(float64))
34			if math.Float32bits(f32) == 0 {
35				p.As = AMOVW
36				p.From.Type = obj.TYPE_REG
37				p.From.Reg = REGZERO
38				break
39			}
40			p.From.Type = obj.TYPE_MEM
41			p.From.Sym = ctxt.Float32Sym(f32)
42			p.From.Name = obj.NAME_EXTERN
43			p.From.Offset = 0
44		}
45
46	case AMOVD:
47		if p.From.Type == obj.TYPE_FCONST {
48			f64 := p.From.Val.(float64)
49			if math.Float64bits(f64) == 0 {
50				p.As = AMOVV
51				p.From.Type = obj.TYPE_REG
52				p.From.Reg = REGZERO
53				break
54			}
55			p.From.Type = obj.TYPE_MEM
56			p.From.Sym = ctxt.Float64Sym(f64)
57			p.From.Name = obj.NAME_EXTERN
58			p.From.Offset = 0
59		}
60	}
61
62	// Rewrite SUB constants into ADD.
63	switch p.As {
64	case ASUB:
65		if p.From.Type == obj.TYPE_CONST {
66			p.From.Offset = -p.From.Offset
67			p.As = AADD
68		}
69
70	case ASUBU:
71		if p.From.Type == obj.TYPE_CONST {
72			p.From.Offset = -p.From.Offset
73			p.As = AADDU
74		}
75
76	case ASUBV:
77		if p.From.Type == obj.TYPE_CONST {
78			p.From.Offset = -p.From.Offset
79			p.As = AADDV
80		}
81
82	case ASUBVU:
83		if p.From.Type == obj.TYPE_CONST {
84			p.From.Offset = -p.From.Offset
85			p.As = AADDVU
86		}
87	}
88
89	if ctxt.Flag_dynlink {
90		rewriteToUseGot(ctxt, p, newprog)
91	}
92}
93
94func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
95	//     ADUFFxxx $offset
96	// becomes
97	//     MOVV runtime.duffxxx@GOT, REGTMP
98	//     ADD $offset, REGTMP
99	//     JAL REGTMP
100	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
101		var sym *obj.LSym
102		if p.As == obj.ADUFFZERO {
103			sym = ctxt.Lookup("runtime.duffzero")
104		} else {
105			sym = ctxt.Lookup("runtime.duffcopy")
106		}
107		offset := p.To.Offset
108		p.As = AMOVV
109		p.From.Type = obj.TYPE_MEM
110		p.From.Sym = sym
111		p.From.Name = obj.NAME_GOTREF
112		p.To.Type = obj.TYPE_REG
113		p.To.Reg = REGTMP
114		p.To.Name = obj.NAME_NONE
115		p.To.Offset = 0
116		p.To.Sym = nil
117		p1 := obj.Appendp(p, newprog)
118		p1.As = AADDV
119		p1.From.Type = obj.TYPE_CONST
120		p1.From.Offset = offset
121		p1.To.Type = obj.TYPE_REG
122		p1.To.Reg = REGTMP
123		p2 := obj.Appendp(p1, newprog)
124		p2.As = AJAL
125		p2.To.Type = obj.TYPE_MEM
126		p2.To.Reg = REGTMP
127	}
128
129	// We only care about global data: NAME_EXTERN means a global
130	// symbol in the Go sense, and p.Sym.Local is true for a few
131	// internally defined symbols.
132	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
133		// MOVV $sym, Rx becomes MOVV sym@GOT, Rx
134		// MOVV $sym+<off>, Rx becomes MOVV sym@GOT, Rx; ADD <off>, Rx
135		if p.As != AMOVV {
136			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -shared", p)
137		}
138		if p.To.Type != obj.TYPE_REG {
139			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -shared", p)
140		}
141		p.From.Type = obj.TYPE_MEM
142		p.From.Name = obj.NAME_GOTREF
143		if p.From.Offset != 0 {
144			q := obj.Appendp(p, newprog)
145			q.As = AADDV
146			q.From.Type = obj.TYPE_CONST
147			q.From.Offset = p.From.Offset
148			q.To = p.To
149			p.From.Offset = 0
150		}
151	}
152	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
153		ctxt.Diag("don't know how to handle %v with -shared", p)
154	}
155
156	var source *obj.Addr
157	// MOVx sym, Ry becomes MOVV sym@GOT, REGTMP; MOVx (REGTMP), Ry
158	// MOVx Ry, sym becomes MOVV sym@GOT, REGTMP; MOVx Ry, (REGTMP)
159	// An addition may be inserted between the two MOVs if there is an offset.
160	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
161		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
162			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -shared", p)
163		}
164		source = &p.From
165	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
166		source = &p.To
167	} else {
168		return
169	}
170	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
171		return
172	}
173	if source.Sym.Type == objabi.STLSBSS {
174		return
175	}
176	if source.Type != obj.TYPE_MEM {
177		ctxt.Diag("don't know how to handle %v with -shared", p)
178	}
179	p1 := obj.Appendp(p, newprog)
180	p2 := obj.Appendp(p1, newprog)
181	p1.As = AMOVV
182	p1.From.Type = obj.TYPE_MEM
183	p1.From.Sym = source.Sym
184	p1.From.Name = obj.NAME_GOTREF
185	p1.To.Type = obj.TYPE_REG
186	p1.To.Reg = REGTMP
187
188	p2.As = p.As
189	p2.From = p.From
190	p2.To = p.To
191	if p.From.Name == obj.NAME_EXTERN {
192		p2.From.Reg = REGTMP
193		p2.From.Name = obj.NAME_NONE
194		p2.From.Sym = nil
195	} else if p.To.Name == obj.NAME_EXTERN {
196		p2.To.Reg = REGTMP
197		p2.To.Name = obj.NAME_NONE
198		p2.To.Sym = nil
199	} else {
200		return
201	}
202
203	obj.Nopout(p)
204}
205
206func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
207	c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym}
208
209	p := c.cursym.Func().Text
210	textstksiz := p.To.Offset
211
212	if textstksiz < 0 {
213		c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
214	}
215	if p.From.Sym.NoFrame() {
216		if textstksiz != 0 {
217			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
218		}
219	}
220
221	c.cursym.Func().Args = p.To.Val.(int32)
222	c.cursym.Func().Locals = int32(textstksiz)
223
224	/*
225	 * find leaf subroutines
226	 * expand RET
227	 */
228
229	for p := c.cursym.Func().Text; p != nil; p = p.Link {
230		switch p.As {
231		case obj.ATEXT:
232			p.Mark |= LABEL | LEAF | SYNC
233			if p.Link != nil {
234				p.Link.Mark |= LABEL
235			}
236
237		case AMOVW,
238			AMOVV:
239			if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
240				p.Mark |= LABEL | SYNC
241				break
242			}
243			if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
244				p.Mark |= LABEL | SYNC
245			}
246
247		case ASYSCALL,
248			AWORD:
249			p.Mark |= LABEL | SYNC
250
251		case ANOR:
252			if p.To.Type == obj.TYPE_REG {
253				if p.To.Reg == REGZERO {
254					p.Mark |= LABEL | SYNC
255				}
256			}
257
258		case AJAL,
259			obj.ADUFFZERO,
260			obj.ADUFFCOPY:
261			c.cursym.Func().Text.Mark &^= LEAF
262			fallthrough
263
264		case AJMP,
265			ABEQ,
266			ABGEU,
267			ABLTU,
268			ABLTZ,
269			ABNE,
270			ABFPT, ABFPF:
271			p.Mark |= BRANCH
272			q1 := p.To.Target()
273			if q1 != nil {
274				for q1.As == obj.ANOP {
275					q1 = q1.Link
276					p.To.SetTarget(q1)
277				}
278
279				if q1.Mark&LEAF == 0 {
280					q1.Mark |= LABEL
281				}
282			}
283			q1 = p.Link
284			if q1 != nil {
285				q1.Mark |= LABEL
286			}
287
288		case ARET:
289			if p.Link != nil {
290				p.Link.Mark |= LABEL
291			}
292		}
293	}
294
295	var mov, add obj.As
296
297	add = AADDV
298	mov = AMOVV
299
300	var q *obj.Prog
301	var q1 *obj.Prog
302	autosize := int32(0)
303	var p1 *obj.Prog
304	var p2 *obj.Prog
305	for p := c.cursym.Func().Text; p != nil; p = p.Link {
306		o := p.As
307		switch o {
308		case obj.ATEXT:
309			autosize = int32(textstksiz)
310
311			if p.Mark&LEAF != 0 && autosize == 0 {
312				// A leaf function with no locals has no frame.
313				p.From.Sym.Set(obj.AttrNoFrame, true)
314			}
315
316			if !p.From.Sym.NoFrame() {
317				// If there is a stack frame at all, it includes
318				// space to save the LR.
319				autosize += int32(c.ctxt.Arch.FixedFrameSize)
320			}
321
322			if autosize&4 != 0 {
323				autosize += 4
324			}
325
326			if autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
327				if c.cursym.Func().Text.From.Sym.NoSplit() {
328					if ctxt.Debugvlog {
329						ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
330					}
331
332					c.cursym.Func().Text.Mark |= LEAF
333				}
334			}
335
336			p.To.Offset = int64(autosize) - ctxt.Arch.FixedFrameSize
337
338			if c.cursym.Func().Text.Mark&LEAF != 0 {
339				c.cursym.Set(obj.AttrLeaf, true)
340				if p.From.Sym.NoFrame() {
341					break
342				}
343			}
344
345			if !p.From.Sym.NoSplit() {
346				p = c.stacksplit(p, autosize) // emit split check
347			}
348
349			q = p
350
351			if autosize != 0 {
352				// Make sure to save link register for non-empty frame, even if
353				// it is a leaf function, so that traceback works.
354				// Store link register before decrement SP, so if a signal comes
355				// during the execution of the function prologue, the traceback
356				// code will not see a half-updated stack frame.
357				// This sequence is not async preemptible, as if we open a frame
358				// at the current SP, it will clobber the saved LR.
359				q = c.ctxt.StartUnsafePoint(q, c.newprog)
360
361				q = obj.Appendp(q, newprog)
362				q.As = mov
363				q.Pos = p.Pos
364				q.From.Type = obj.TYPE_REG
365				q.From.Reg = REGLINK
366				q.To.Type = obj.TYPE_MEM
367				q.To.Offset = int64(-autosize)
368				q.To.Reg = REGSP
369
370				q = obj.Appendp(q, newprog)
371				q.As = add
372				q.Pos = p.Pos
373				q.From.Type = obj.TYPE_CONST
374				q.From.Offset = int64(-autosize)
375				q.To.Type = obj.TYPE_REG
376				q.To.Reg = REGSP
377				q.Spadj = +autosize
378
379				q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
380
381				// On Linux, in a cgo binary we may get a SIGSETXID signal early on
382				// before the signal stack is set, as glibc doesn't allow us to block
383				// SIGSETXID. So a signal may land on the current stack and clobber
384				// the content below the SP. We store the LR again after the SP is
385				// decremented.
386				q = obj.Appendp(q, newprog)
387				q.As = mov
388				q.Pos = p.Pos
389				q.From.Type = obj.TYPE_REG
390				q.From.Reg = REGLINK
391				q.To.Type = obj.TYPE_MEM
392				q.To.Offset = 0
393				q.To.Reg = REGSP
394			}
395
396			if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 {
397				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
398				//
399				//	MOV	g_panic(g), R20
400				//	BEQ	R20, end
401				//	MOV	panic_argp(R20), R24
402				//	ADD	$(autosize+FIXED_FRAME), R3, R30
403				//	BNE	R24, R30, end
404				//	ADD	$FIXED_FRAME, R3, R24
405				//	MOV	R24, panic_argp(R20)
406				// end:
407				//	NOP
408				//
409				// The NOP is needed to give the jumps somewhere to land.
410				// It is a liblink NOP, not a hardware NOP: it encodes to 0 instruction bytes.
411				//
412				// We don't generate this for leafs because that means the wrapped
413				// function was inlined into the wrapper.
414
415				q = obj.Appendp(q, newprog)
416
417				q.As = mov
418				q.From.Type = obj.TYPE_MEM
419				q.From.Reg = REGG
420				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
421				q.To.Type = obj.TYPE_REG
422				q.To.Reg = REG_R20
423
424				q = obj.Appendp(q, newprog)
425				q.As = ABEQ
426				q.From.Type = obj.TYPE_REG
427				q.From.Reg = REG_R20
428				q.To.Type = obj.TYPE_BRANCH
429				q.Mark |= BRANCH
430				p1 = q
431
432				q = obj.Appendp(q, newprog)
433				q.As = mov
434				q.From.Type = obj.TYPE_MEM
435				q.From.Reg = REG_R20
436				q.From.Offset = 0 // Panic.argp
437				q.To.Type = obj.TYPE_REG
438				q.To.Reg = REG_R24
439
440				q = obj.Appendp(q, newprog)
441				q.As = add
442				q.From.Type = obj.TYPE_CONST
443				q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize
444				q.Reg = REGSP
445				q.To.Type = obj.TYPE_REG
446				q.To.Reg = REG_R30
447
448				q = obj.Appendp(q, newprog)
449				q.As = ABNE
450				q.From.Type = obj.TYPE_REG
451				q.From.Reg = REG_R24
452				q.Reg = REG_R30
453				q.To.Type = obj.TYPE_BRANCH
454				q.Mark |= BRANCH
455				p2 = q
456
457				q = obj.Appendp(q, newprog)
458				q.As = add
459				q.From.Type = obj.TYPE_CONST
460				q.From.Offset = ctxt.Arch.FixedFrameSize
461				q.Reg = REGSP
462				q.To.Type = obj.TYPE_REG
463				q.To.Reg = REG_R24
464
465				q = obj.Appendp(q, newprog)
466				q.As = mov
467				q.From.Type = obj.TYPE_REG
468				q.From.Reg = REG_R24
469				q.To.Type = obj.TYPE_MEM
470				q.To.Reg = REG_R20
471				q.To.Offset = 0 // Panic.argp
472
473				q = obj.Appendp(q, newprog)
474
475				q.As = obj.ANOP
476				p1.To.SetTarget(q)
477				p2.To.SetTarget(q)
478			}
479
480		case ARET:
481			if p.From.Type == obj.TYPE_CONST {
482				ctxt.Diag("using BECOME (%v) is not supported!", p)
483				break
484			}
485
486			retSym := p.To.Sym
487			p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
488			p.To.Sym = nil
489
490			if c.cursym.Func().Text.Mark&LEAF != 0 {
491				if autosize == 0 {
492					p.As = AJMP
493					p.From = obj.Addr{}
494					if retSym != nil { // retjmp
495						p.To.Type = obj.TYPE_BRANCH
496						p.To.Name = obj.NAME_EXTERN
497						p.To.Sym = retSym
498					} else {
499						p.To.Type = obj.TYPE_MEM
500						p.To.Reg = REGLINK
501						p.To.Offset = 0
502					}
503					p.Mark |= BRANCH
504					break
505				}
506
507				p.As = add
508				p.From.Type = obj.TYPE_CONST
509				p.From.Offset = int64(autosize)
510				p.To.Type = obj.TYPE_REG
511				p.To.Reg = REGSP
512				p.Spadj = -autosize
513
514				q = c.newprog()
515				q.As = AJMP
516				q.Pos = p.Pos
517				if retSym != nil { // retjmp
518					q.To.Type = obj.TYPE_BRANCH
519					q.To.Name = obj.NAME_EXTERN
520					q.To.Sym = retSym
521				} else {
522					q.To.Type = obj.TYPE_MEM
523					q.To.Offset = 0
524					q.To.Reg = REGLINK
525				}
526				q.Mark |= BRANCH
527				q.Spadj = +autosize
528
529				q.Link = p.Link
530				p.Link = q
531				break
532			}
533
534			p.As = mov
535			p.From.Type = obj.TYPE_MEM
536			p.From.Offset = 0
537			p.From.Reg = REGSP
538			p.To.Type = obj.TYPE_REG
539			p.To.Reg = REGLINK
540
541			if autosize != 0 {
542				q = c.newprog()
543				q.As = add
544				q.Pos = p.Pos
545				q.From.Type = obj.TYPE_CONST
546				q.From.Offset = int64(autosize)
547				q.To.Type = obj.TYPE_REG
548				q.To.Reg = REGSP
549				q.Spadj = -autosize
550
551				q.Link = p.Link
552				p.Link = q
553			}
554
555			q1 = c.newprog()
556			q1.As = AJMP
557			q1.Pos = p.Pos
558			if retSym != nil { // retjmp
559				q1.To.Type = obj.TYPE_BRANCH
560				q1.To.Name = obj.NAME_EXTERN
561				q1.To.Sym = retSym
562			} else {
563				q1.To.Type = obj.TYPE_MEM
564				q1.To.Offset = 0
565				q1.To.Reg = REGLINK
566			}
567			q1.Mark |= BRANCH
568			q1.Spadj = +autosize
569
570			q1.Link = q.Link
571			q.Link = q1
572
573		case AADD,
574			AADDU,
575			AADDV,
576			AADDVU:
577			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
578				p.Spadj = int32(-p.From.Offset)
579			}
580
581		case obj.AGETCALLERPC:
582			if cursym.Leaf() {
583				// MOV LR, Rd
584				p.As = mov
585				p.From.Type = obj.TYPE_REG
586				p.From.Reg = REGLINK
587			} else {
588				// MOV (RSP), Rd
589				p.As = mov
590				p.From.Type = obj.TYPE_MEM
591				p.From.Reg = REGSP
592			}
593		}
594
595		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
596			f := c.cursym.Func()
597			if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
598				c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
599				if ctxt.Debugvlog || !ctxt.IsAsm {
600					ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
601					if !ctxt.IsAsm {
602						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
603						ctxt.DiagFlush()
604						log.Fatalf("bad SPWRITE")
605					}
606				}
607			}
608		}
609	}
610}
611
612func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
613	var mov, add obj.As
614
615	add = AADDV
616	mov = AMOVV
617	if c.ctxt.Flag_maymorestack != "" {
618		// Save LR and REGCTXT.
619		frameSize := 2 * c.ctxt.Arch.PtrSize
620
621		p = c.ctxt.StartUnsafePoint(p, c.newprog)
622
623		// Spill Arguments. This has to happen before we open
624		// any more frame space.
625		p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
626
627		// MOV	REGLINK, -8/-16(SP)
628		p = obj.Appendp(p, c.newprog)
629		p.As = mov
630		p.From.Type = obj.TYPE_REG
631		p.From.Reg = REGLINK
632		p.To.Type = obj.TYPE_MEM
633		p.To.Offset = int64(-frameSize)
634		p.To.Reg = REGSP
635
636		// MOV	REGCTXT, -4/-8(SP)
637		p = obj.Appendp(p, c.newprog)
638		p.As = mov
639		p.From.Type = obj.TYPE_REG
640		p.From.Reg = REGCTXT
641		p.To.Type = obj.TYPE_MEM
642		p.To.Offset = -int64(c.ctxt.Arch.PtrSize)
643		p.To.Reg = REGSP
644
645		// ADD	$-8/$-16, SP
646		p = obj.Appendp(p, c.newprog)
647		p.As = add
648		p.From.Type = obj.TYPE_CONST
649		p.From.Offset = int64(-frameSize)
650		p.To.Type = obj.TYPE_REG
651		p.To.Reg = REGSP
652		p.Spadj = int32(frameSize)
653
654		// JAL	maymorestack
655		p = obj.Appendp(p, c.newprog)
656		p.As = AJAL
657		p.To.Type = obj.TYPE_BRANCH
658		// See ../x86/obj6.go
659		p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
660		p.Mark |= BRANCH
661
662		// Restore LR and REGCTXT.
663
664		// MOV	0(SP), REGLINK
665		p = obj.Appendp(p, c.newprog)
666		p.As = mov
667		p.From.Type = obj.TYPE_MEM
668		p.From.Offset = 0
669		p.From.Reg = REGSP
670		p.To.Type = obj.TYPE_REG
671		p.To.Reg = REGLINK
672
673		// MOV	4/8(SP), REGCTXT
674		p = obj.Appendp(p, c.newprog)
675		p.As = mov
676		p.From.Type = obj.TYPE_MEM
677		p.From.Offset = int64(c.ctxt.Arch.PtrSize)
678		p.From.Reg = REGSP
679		p.To.Type = obj.TYPE_REG
680		p.To.Reg = REGCTXT
681
682		// ADD	$8/$16, SP
683		p = obj.Appendp(p, c.newprog)
684		p.As = add
685		p.From.Type = obj.TYPE_CONST
686		p.From.Offset = int64(frameSize)
687		p.To.Type = obj.TYPE_REG
688		p.To.Reg = REGSP
689		p.Spadj = int32(-frameSize)
690
691		// Unspill arguments
692		p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
693		p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
694	}
695
696	// Jump back to here after morestack returns.
697	startPred := p
698
699	// MOV	g_stackguard(g), R20
700	p = obj.Appendp(p, c.newprog)
701
702	p.As = mov
703	p.From.Type = obj.TYPE_MEM
704	p.From.Reg = REGG
705	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
706	if c.cursym.CFunc() {
707		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
708	}
709	p.To.Type = obj.TYPE_REG
710	p.To.Reg = REG_R20
711
712	// Mark the stack bound check and morestack call async nonpreemptible.
713	// If we get preempted here, when resumed the preemption request is
714	// cleared, but we'll still call morestack, which will double the stack
715	// unnecessarily. See issue #35470.
716	p = c.ctxt.StartUnsafePoint(p, c.newprog)
717
718	var q *obj.Prog
719	if framesize <= abi.StackSmall {
720		// small stack: SP < stackguard
721		//	AGTU	SP, stackguard, R20
722		p = obj.Appendp(p, c.newprog)
723
724		p.As = ASGTU
725		p.From.Type = obj.TYPE_REG
726		p.From.Reg = REGSP
727		p.Reg = REG_R20
728		p.To.Type = obj.TYPE_REG
729		p.To.Reg = REG_R20
730	} else {
731		// large stack: SP-framesize < stackguard-StackSmall
732		offset := int64(framesize) - abi.StackSmall
733		if framesize > abi.StackBig {
734			// Such a large stack we need to protect against underflow.
735			// The runtime guarantees SP > objabi.StackBig, but
736			// framesize is large enough that SP-framesize may
737			// underflow, causing a direct comparison with the
738			// stack guard to incorrectly succeed. We explicitly
739			// guard against underflow.
740			//
741			//      SGTU    $(framesize-StackSmall), SP, R24
742			//      BNE     R24, label-of-call-to-morestack
743
744			p = obj.Appendp(p, c.newprog)
745			p.As = ASGTU
746			p.From.Type = obj.TYPE_CONST
747			p.From.Offset = offset
748			p.Reg = REGSP
749			p.To.Type = obj.TYPE_REG
750			p.To.Reg = REG_R24
751
752			p = obj.Appendp(p, c.newprog)
753			q = p
754			p.As = ABNE
755			p.From.Type = obj.TYPE_REG
756			p.From.Reg = REG_R24
757			p.To.Type = obj.TYPE_BRANCH
758			p.Mark |= BRANCH
759		}
760
761		p = obj.Appendp(p, c.newprog)
762
763		p.As = add
764		p.From.Type = obj.TYPE_CONST
765		p.From.Offset = -offset
766		p.Reg = REGSP
767		p.To.Type = obj.TYPE_REG
768		p.To.Reg = REG_R24
769
770		p = obj.Appendp(p, c.newprog)
771		p.As = ASGTU
772		p.From.Type = obj.TYPE_REG
773		p.From.Reg = REG_R24
774		p.Reg = REG_R20
775		p.To.Type = obj.TYPE_REG
776		p.To.Reg = REG_R20
777	}
778
779	// q1: BNE	R20, done
780	p = obj.Appendp(p, c.newprog)
781	q1 := p
782
783	p.As = ABNE
784	p.From.Type = obj.TYPE_REG
785	p.From.Reg = REG_R20
786	p.To.Type = obj.TYPE_BRANCH
787	p.Mark |= BRANCH
788
789	// MOV	LINK, R31
790	p = obj.Appendp(p, c.newprog)
791
792	p.As = mov
793	p.From.Type = obj.TYPE_REG
794	p.From.Reg = REGLINK
795	p.To.Type = obj.TYPE_REG
796	p.To.Reg = REG_R31
797	if q != nil {
798		q.To.SetTarget(p)
799		p.Mark |= LABEL
800	}
801
802	p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
803
804	// Spill the register args that could be clobbered by the
805	// morestack code
806	p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
807
808	// JAL	runtime.morestack(SB)
809	p = obj.Appendp(p, c.newprog)
810
811	p.As = AJAL
812	p.To.Type = obj.TYPE_BRANCH
813	if c.cursym.CFunc() {
814		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
815	} else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
816		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
817	} else {
818		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
819	}
820	p.Mark |= BRANCH
821
822	p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
823	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
824
825	// JMP	start
826	p = obj.Appendp(p, c.newprog)
827
828	p.As = AJMP
829	p.To.Type = obj.TYPE_BRANCH
830	p.To.SetTarget(startPred.Link)
831	startPred.Link.Mark |= LABEL
832	p.Mark |= BRANCH
833
834	// placeholder for q1's jump target
835	p = obj.Appendp(p, c.newprog)
836
837	p.As = obj.ANOP // zero-width place holder
838	q1.To.SetTarget(p)
839
840	return p
841}
842
843func (c *ctxt0) addnop(p *obj.Prog) {
844	q := c.newprog()
845	q.As = ANOOP
846	q.Pos = p.Pos
847	q.Link = p.Link
848	p.Link = q
849}
850
851var Linkloong64 = obj.LinkArch{
852	Arch:           sys.ArchLoong64,
853	Init:           buildop,
854	Preprocess:     preprocess,
855	Assemble:       span0,
856	Progedit:       progedit,
857	DWARFRegisters: LOONG64DWARFRegisters,
858}
859