1// cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
2//
3//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
4//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
5//	Portions Copyright © 1997-1999 Vita Nuova Limited
6//	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
7//	Portions Copyright © 2004,2006 Bruce Ellis
8//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
9//	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
10//	Portions Copyright © 2009 The Go Authors. All rights reserved.
11//
12// Permission is hereby granted, free of charge, to any person obtaining a copy
13// of this software and associated documentation files (the "Software"), to deal
14// in the Software without restriction, including without limitation the rights
15// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16// copies of the Software, and to permit persons to whom the Software is
17// furnished to do so, subject to the following conditions:
18//
19// The above copyright notice and this permission notice shall be included in
20// all copies or substantial portions of the Software.
21//
22// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
25// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28// THE SOFTWARE.
29
30package mips
31
32import (
33	"cmd/internal/obj"
34	"cmd/internal/objabi"
35	"cmd/internal/sys"
36	"encoding/binary"
37	"fmt"
38	"math"
39)
40
41func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
42	c := ctxt0{ctxt: ctxt, newprog: newprog}
43
44	p.From.Class = 0
45	p.To.Class = 0
46
47	// Rewrite JMP/JAL to symbol as TYPE_BRANCH.
48	switch p.As {
49	case AJMP,
50		AJAL,
51		ARET,
52		obj.ADUFFZERO,
53		obj.ADUFFCOPY:
54		if p.To.Sym != nil {
55			p.To.Type = obj.TYPE_BRANCH
56		}
57	}
58
59	// Rewrite float constants to values stored in memory.
60	switch p.As {
61	case AMOVF:
62		if p.From.Type == obj.TYPE_FCONST {
63			f32 := float32(p.From.Val.(float64))
64			if math.Float32bits(f32) == 0 {
65				p.As = AMOVW
66				p.From.Type = obj.TYPE_REG
67				p.From.Reg = REGZERO
68				break
69			}
70			p.From.Type = obj.TYPE_MEM
71			p.From.Sym = ctxt.Float32Sym(f32)
72			p.From.Name = obj.NAME_EXTERN
73			p.From.Offset = 0
74		}
75
76	case AMOVD:
77		if p.From.Type == obj.TYPE_FCONST {
78			f64 := p.From.Val.(float64)
79			if math.Float64bits(f64) == 0 && c.ctxt.Arch.Family == sys.MIPS64 {
80				p.As = AMOVV
81				p.From.Type = obj.TYPE_REG
82				p.From.Reg = REGZERO
83				break
84			}
85			p.From.Type = obj.TYPE_MEM
86			p.From.Sym = ctxt.Float64Sym(f64)
87			p.From.Name = obj.NAME_EXTERN
88			p.From.Offset = 0
89		}
90
91		// Put >32-bit constants in memory and load them
92	case AMOVV:
93		if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset {
94			p.From.Type = obj.TYPE_MEM
95			p.From.Sym = ctxt.Int64Sym(p.From.Offset)
96			p.From.Name = obj.NAME_EXTERN
97			p.From.Offset = 0
98		}
99	}
100
101	// Rewrite SUB constants into ADD.
102	switch p.As {
103	case ASUB:
104		if p.From.Type == obj.TYPE_CONST {
105			p.From.Offset = -p.From.Offset
106			p.As = AADD
107		}
108
109	case ASUBU:
110		if p.From.Type == obj.TYPE_CONST {
111			p.From.Offset = -p.From.Offset
112			p.As = AADDU
113		}
114
115	case ASUBV:
116		if p.From.Type == obj.TYPE_CONST {
117			p.From.Offset = -p.From.Offset
118			p.As = AADDV
119		}
120
121	case ASUBVU:
122		if p.From.Type == obj.TYPE_CONST {
123			p.From.Offset = -p.From.Offset
124			p.As = AADDVU
125		}
126	}
127}
128
129func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
130	// TODO(minux): add morestack short-cuts with small fixed frame-size.
131	c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym}
132
133	// a switch for enabling/disabling instruction scheduling
134	nosched := true
135
136	if c.cursym.Func.Text == nil || c.cursym.Func.Text.Link == nil {
137		return
138	}
139
140	p := c.cursym.Func.Text
141	textstksiz := p.To.Offset
142
143	c.cursym.Func.Args = p.To.Val.(int32)
144	c.cursym.Func.Locals = int32(textstksiz)
145
146	/*
147	 * find leaf subroutines
148	 * strip NOPs
149	 * expand RET
150	 * expand BECOME pseudo
151	 */
152
153	var q *obj.Prog
154	var q1 *obj.Prog
155	for p := c.cursym.Func.Text; p != nil; p = p.Link {
156		switch p.As {
157		/* too hard, just leave alone */
158		case obj.ATEXT:
159			q = p
160
161			p.Mark |= LABEL | LEAF | SYNC
162			if p.Link != nil {
163				p.Link.Mark |= LABEL
164			}
165
166		/* too hard, just leave alone */
167		case AMOVW,
168			AMOVV:
169			q = p
170			if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
171				p.Mark |= LABEL | SYNC
172				break
173			}
174			if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
175				p.Mark |= LABEL | SYNC
176			}
177
178		/* too hard, just leave alone */
179		case ASYSCALL,
180			AWORD,
181			ATLBWR,
182			ATLBWI,
183			ATLBP,
184			ATLBR:
185			q = p
186			p.Mark |= LABEL | SYNC
187
188		case ANOR:
189			q = p
190			if p.To.Type == obj.TYPE_REG {
191				if p.To.Reg == REGZERO {
192					p.Mark |= LABEL | SYNC
193				}
194			}
195
196		case ABGEZAL,
197			ABLTZAL,
198			AJAL,
199			obj.ADUFFZERO,
200			obj.ADUFFCOPY:
201			c.cursym.Func.Text.Mark &^= LEAF
202			fallthrough
203
204		case AJMP,
205			ABEQ,
206			ABGEZ,
207			ABGTZ,
208			ABLEZ,
209			ABLTZ,
210			ABNE,
211			ABFPT, ABFPF:
212			if p.As == ABFPT || p.As == ABFPF {
213				// We don't treat ABFPT and ABFPF as branches here,
214				// so that we will always fill nop (0x0) in their
215				// delay slot during assembly.
216				// This is to workaround a kernel FPU emulator bug
217				// where it uses the user stack to simulate the
218				// instruction in the delay slot if it's not 0x0,
219				// and somehow that leads to SIGSEGV when the kernel
220				// jump to the stack.
221				p.Mark |= SYNC
222			} else {
223				p.Mark |= BRANCH
224			}
225			q = p
226			q1 = p.Pcond
227			if q1 != nil {
228				for q1.As == obj.ANOP {
229					q1 = q1.Link
230					p.Pcond = q1
231				}
232
233				if q1.Mark&LEAF == 0 {
234					q1.Mark |= LABEL
235				}
236			}
237			//else {
238			//	p.Mark |= LABEL
239			//}
240			q1 = p.Link
241			if q1 != nil {
242				q1.Mark |= LABEL
243			}
244			continue
245
246		case ARET:
247			q = p
248			if p.Link != nil {
249				p.Link.Mark |= LABEL
250			}
251			continue
252
253		case obj.ANOP:
254			q1 = p.Link
255			q.Link = q1 /* q is non-nop */
256			q1.Mark |= p.Mark
257			continue
258
259		default:
260			q = p
261			continue
262		}
263	}
264
265	var mov, add obj.As
266	if c.ctxt.Arch.Family == sys.MIPS64 {
267		add = AADDV
268		mov = AMOVV
269	} else {
270		add = AADDU
271		mov = AMOVW
272	}
273
274	autosize := int32(0)
275	var p1 *obj.Prog
276	var p2 *obj.Prog
277	for p := c.cursym.Func.Text; p != nil; p = p.Link {
278		o := p.As
279		switch o {
280		case obj.ATEXT:
281			autosize = int32(textstksiz + ctxt.FixedFrameSize())
282			if (p.Mark&LEAF != 0) && autosize <= int32(ctxt.FixedFrameSize()) {
283				autosize = 0
284			} else if autosize&4 != 0 && c.ctxt.Arch.Family == sys.MIPS64 {
285				autosize += 4
286			}
287
288			p.To.Offset = int64(autosize) - ctxt.FixedFrameSize()
289
290			if !p.From.Sym.NoSplit() {
291				p = c.stacksplit(p, autosize) // emit split check
292			}
293
294			q = p
295
296			if autosize != 0 {
297				// Make sure to save link register for non-empty frame, even if
298				// it is a leaf function, so that traceback works.
299				// Store link register before decrement SP, so if a signal comes
300				// during the execution of the function prologue, the traceback
301				// code will not see a half-updated stack frame.
302				q = obj.Appendp(q, newprog)
303				q.As = mov
304				q.Pos = p.Pos
305				q.From.Type = obj.TYPE_REG
306				q.From.Reg = REGLINK
307				q.To.Type = obj.TYPE_MEM
308				q.To.Offset = int64(-autosize)
309				q.To.Reg = REGSP
310
311				q = obj.Appendp(q, newprog)
312				q.As = add
313				q.Pos = p.Pos
314				q.From.Type = obj.TYPE_CONST
315				q.From.Offset = int64(-autosize)
316				q.To.Type = obj.TYPE_REG
317				q.To.Reg = REGSP
318				q.Spadj = +autosize
319			} else if c.cursym.Func.Text.Mark&LEAF == 0 {
320				if c.cursym.Func.Text.From.Sym.NoSplit() {
321					if ctxt.Debugvlog {
322						ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
323					}
324
325					c.cursym.Func.Text.Mark |= LEAF
326				}
327			}
328
329			if c.cursym.Func.Text.Mark&LEAF != 0 {
330				c.cursym.Set(obj.AttrLeaf, true)
331				break
332			}
333
334			if c.cursym.Func.Text.From.Sym.Wrapper() {
335				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
336				//
337				//	MOV	g_panic(g), R1
338				//	BEQ	R1, end
339				//	MOV	panic_argp(R1), R2
340				//	ADD	$(autosize+FIXED_FRAME), R29, R3
341				//	BNE	R2, R3, end
342				//	ADD	$FIXED_FRAME, R29, R2
343				//	MOV	R2, panic_argp(R1)
344				// end:
345				//	NOP
346				//
347				// The NOP is needed to give the jumps somewhere to land.
348				// It is a liblink NOP, not an mips NOP: it encodes to 0 instruction bytes.
349
350				q = obj.Appendp(q, newprog)
351
352				q.As = mov
353				q.From.Type = obj.TYPE_MEM
354				q.From.Reg = REGG
355				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
356				q.To.Type = obj.TYPE_REG
357				q.To.Reg = REG_R1
358
359				q = obj.Appendp(q, newprog)
360				q.As = ABEQ
361				q.From.Type = obj.TYPE_REG
362				q.From.Reg = REG_R1
363				q.To.Type = obj.TYPE_BRANCH
364				q.Mark |= BRANCH
365				p1 = q
366
367				q = obj.Appendp(q, newprog)
368				q.As = mov
369				q.From.Type = obj.TYPE_MEM
370				q.From.Reg = REG_R1
371				q.From.Offset = 0 // Panic.argp
372				q.To.Type = obj.TYPE_REG
373				q.To.Reg = REG_R2
374
375				q = obj.Appendp(q, newprog)
376				q.As = add
377				q.From.Type = obj.TYPE_CONST
378				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
379				q.Reg = REGSP
380				q.To.Type = obj.TYPE_REG
381				q.To.Reg = REG_R3
382
383				q = obj.Appendp(q, newprog)
384				q.As = ABNE
385				q.From.Type = obj.TYPE_REG
386				q.From.Reg = REG_R2
387				q.Reg = REG_R3
388				q.To.Type = obj.TYPE_BRANCH
389				q.Mark |= BRANCH
390				p2 = q
391
392				q = obj.Appendp(q, newprog)
393				q.As = add
394				q.From.Type = obj.TYPE_CONST
395				q.From.Offset = ctxt.FixedFrameSize()
396				q.Reg = REGSP
397				q.To.Type = obj.TYPE_REG
398				q.To.Reg = REG_R2
399
400				q = obj.Appendp(q, newprog)
401				q.As = mov
402				q.From.Type = obj.TYPE_REG
403				q.From.Reg = REG_R2
404				q.To.Type = obj.TYPE_MEM
405				q.To.Reg = REG_R1
406				q.To.Offset = 0 // Panic.argp
407
408				q = obj.Appendp(q, newprog)
409
410				q.As = obj.ANOP
411				p1.Pcond = q
412				p2.Pcond = q
413			}
414
415		case ARET:
416			if p.From.Type == obj.TYPE_CONST {
417				ctxt.Diag("using BECOME (%v) is not supported!", p)
418				break
419			}
420
421			retSym := p.To.Sym
422			p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
423			p.To.Sym = nil
424
425			if c.cursym.Func.Text.Mark&LEAF != 0 {
426				if autosize == 0 {
427					p.As = AJMP
428					p.From = obj.Addr{}
429					if retSym != nil { // retjmp
430						p.To.Type = obj.TYPE_BRANCH
431						p.To.Name = obj.NAME_EXTERN
432						p.To.Sym = retSym
433					} else {
434						p.To.Type = obj.TYPE_MEM
435						p.To.Reg = REGLINK
436						p.To.Offset = 0
437					}
438					p.Mark |= BRANCH
439					break
440				}
441
442				p.As = add
443				p.From.Type = obj.TYPE_CONST
444				p.From.Offset = int64(autosize)
445				p.To.Type = obj.TYPE_REG
446				p.To.Reg = REGSP
447				p.Spadj = -autosize
448
449				q = c.newprog()
450				q.As = AJMP
451				q.Pos = p.Pos
452				q.To.Type = obj.TYPE_MEM
453				q.To.Offset = 0
454				q.To.Reg = REGLINK
455				q.Mark |= BRANCH
456				q.Spadj = +autosize
457
458				q.Link = p.Link
459				p.Link = q
460				break
461			}
462
463			p.As = mov
464			p.From.Type = obj.TYPE_MEM
465			p.From.Offset = 0
466			p.From.Reg = REGSP
467			p.To.Type = obj.TYPE_REG
468			p.To.Reg = REG_R4
469			if retSym != nil { // retjmp from non-leaf, need to restore LINK register
470				p.To.Reg = REGLINK
471			}
472
473			if autosize != 0 {
474				q = c.newprog()
475				q.As = add
476				q.Pos = p.Pos
477				q.From.Type = obj.TYPE_CONST
478				q.From.Offset = int64(autosize)
479				q.To.Type = obj.TYPE_REG
480				q.To.Reg = REGSP
481				q.Spadj = -autosize
482
483				q.Link = p.Link
484				p.Link = q
485			}
486
487			q1 = c.newprog()
488			q1.As = AJMP
489			q1.Pos = p.Pos
490			if retSym != nil { // retjmp
491				q1.To.Type = obj.TYPE_BRANCH
492				q1.To.Name = obj.NAME_EXTERN
493				q1.To.Sym = retSym
494			} else {
495				q1.To.Type = obj.TYPE_MEM
496				q1.To.Offset = 0
497				q1.To.Reg = REG_R4
498			}
499			q1.Mark |= BRANCH
500			q1.Spadj = +autosize
501
502			q1.Link = q.Link
503			q.Link = q1
504
505		case AADD,
506			AADDU,
507			AADDV,
508			AADDVU:
509			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
510				p.Spadj = int32(-p.From.Offset)
511			}
512		}
513	}
514
515	if c.ctxt.Arch.Family == sys.MIPS {
516		// rewrite MOVD into two MOVF in 32-bit mode to avoid unaligned memory access
517		for p = c.cursym.Func.Text; p != nil; p = p1 {
518			p1 = p.Link
519
520			if p.As != AMOVD {
521				continue
522			}
523			if p.From.Type != obj.TYPE_MEM && p.To.Type != obj.TYPE_MEM {
524				continue
525			}
526
527			p.As = AMOVF
528			q = c.newprog()
529			*q = *p
530			q.Link = p.Link
531			p.Link = q
532			p1 = q.Link
533
534			var regOff int16
535			if c.ctxt.Arch.ByteOrder == binary.BigEndian {
536				regOff = 1 // load odd register first
537			}
538			if p.From.Type == obj.TYPE_MEM {
539				reg := REG_F0 + (p.To.Reg-REG_F0)&^1
540				p.To.Reg = reg + regOff
541				q.To.Reg = reg + 1 - regOff
542				q.From.Offset += 4
543			} else if p.To.Type == obj.TYPE_MEM {
544				reg := REG_F0 + (p.From.Reg-REG_F0)&^1
545				p.From.Reg = reg + regOff
546				q.From.Reg = reg + 1 - regOff
547				q.To.Offset += 4
548			}
549		}
550	}
551
552	if nosched {
553		// if we don't do instruction scheduling, simply add
554		// NOP after each branch instruction.
555		for p = c.cursym.Func.Text; p != nil; p = p.Link {
556			if p.Mark&BRANCH != 0 {
557				c.addnop(p)
558			}
559		}
560		return
561	}
562
563	// instruction scheduling
564	q = nil                 // p - 1
565	q1 = c.cursym.Func.Text // top of block
566	o := 0                  // count of instructions
567	for p = c.cursym.Func.Text; p != nil; p = p1 {
568		p1 = p.Link
569		o++
570		if p.Mark&NOSCHED != 0 {
571			if q1 != p {
572				c.sched(q1, q)
573			}
574			for ; p != nil; p = p.Link {
575				if p.Mark&NOSCHED == 0 {
576					break
577				}
578				q = p
579			}
580			p1 = p
581			q1 = p
582			o = 0
583			continue
584		}
585		if p.Mark&(LABEL|SYNC) != 0 {
586			if q1 != p {
587				c.sched(q1, q)
588			}
589			q1 = p
590			o = 1
591		}
592		if p.Mark&(BRANCH|SYNC) != 0 {
593			c.sched(q1, p)
594			q1 = p1
595			o = 0
596		}
597		if o >= NSCHED {
598			c.sched(q1, p)
599			q1 = p1
600			o = 0
601		}
602		q = p
603	}
604}
605
606func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
607	// Leaf function with no frame is effectively NOSPLIT.
608	if framesize == 0 {
609		return p
610	}
611
612	var mov, add, sub obj.As
613
614	if c.ctxt.Arch.Family == sys.MIPS64 {
615		add = AADDV
616		mov = AMOVV
617		sub = ASUBVU
618	} else {
619		add = AADDU
620		mov = AMOVW
621		sub = ASUBU
622	}
623
624	// MOV	g_stackguard(g), R1
625	p = obj.Appendp(p, c.newprog)
626
627	p.As = mov
628	p.From.Type = obj.TYPE_MEM
629	p.From.Reg = REGG
630	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
631	if c.cursym.CFunc() {
632		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
633	}
634	p.To.Type = obj.TYPE_REG
635	p.To.Reg = REG_R1
636
637	var q *obj.Prog
638	if framesize <= objabi.StackSmall {
639		// small stack: SP < stackguard
640		//	AGTU	SP, stackguard, R1
641		p = obj.Appendp(p, c.newprog)
642
643		p.As = ASGTU
644		p.From.Type = obj.TYPE_REG
645		p.From.Reg = REGSP
646		p.Reg = REG_R1
647		p.To.Type = obj.TYPE_REG
648		p.To.Reg = REG_R1
649	} else if framesize <= objabi.StackBig {
650		// large stack: SP-framesize < stackguard-StackSmall
651		//	ADD	$-(framesize-StackSmall), SP, R2
652		//	SGTU	R2, stackguard, R1
653		p = obj.Appendp(p, c.newprog)
654
655		p.As = add
656		p.From.Type = obj.TYPE_CONST
657		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
658		p.Reg = REGSP
659		p.To.Type = obj.TYPE_REG
660		p.To.Reg = REG_R2
661
662		p = obj.Appendp(p, c.newprog)
663		p.As = ASGTU
664		p.From.Type = obj.TYPE_REG
665		p.From.Reg = REG_R2
666		p.Reg = REG_R1
667		p.To.Type = obj.TYPE_REG
668		p.To.Reg = REG_R1
669	} else {
670		// Such a large stack we need to protect against wraparound.
671		// If SP is close to zero:
672		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
673		// The +StackGuard on both sides is required to keep the left side positive:
674		// SP is allowed to be slightly below stackguard. See stack.h.
675		//
676		// Preemption sets stackguard to StackPreempt, a very large value.
677		// That breaks the math above, so we have to check for that explicitly.
678		//	// stackguard is R1
679		//	MOV	$StackPreempt, R2
680		//	BEQ	R1, R2, label-of-call-to-morestack
681		//	ADD	$StackGuard, SP, R2
682		//	SUB	R1, R2
683		//	MOV	$(framesize+(StackGuard-StackSmall)), R1
684		//	SGTU	R2, R1, R1
685		p = obj.Appendp(p, c.newprog)
686
687		p.As = mov
688		p.From.Type = obj.TYPE_CONST
689		p.From.Offset = objabi.StackPreempt
690		p.To.Type = obj.TYPE_REG
691		p.To.Reg = REG_R2
692
693		p = obj.Appendp(p, c.newprog)
694		q = p
695		p.As = ABEQ
696		p.From.Type = obj.TYPE_REG
697		p.From.Reg = REG_R1
698		p.Reg = REG_R2
699		p.To.Type = obj.TYPE_BRANCH
700		p.Mark |= BRANCH
701
702		p = obj.Appendp(p, c.newprog)
703		p.As = add
704		p.From.Type = obj.TYPE_CONST
705		p.From.Offset = objabi.StackGuard
706		p.Reg = REGSP
707		p.To.Type = obj.TYPE_REG
708		p.To.Reg = REG_R2
709
710		p = obj.Appendp(p, c.newprog)
711		p.As = sub
712		p.From.Type = obj.TYPE_REG
713		p.From.Reg = REG_R1
714		p.To.Type = obj.TYPE_REG
715		p.To.Reg = REG_R2
716
717		p = obj.Appendp(p, c.newprog)
718		p.As = mov
719		p.From.Type = obj.TYPE_CONST
720		p.From.Offset = int64(framesize) + objabi.StackGuard - objabi.StackSmall
721		p.To.Type = obj.TYPE_REG
722		p.To.Reg = REG_R1
723
724		p = obj.Appendp(p, c.newprog)
725		p.As = ASGTU
726		p.From.Type = obj.TYPE_REG
727		p.From.Reg = REG_R2
728		p.Reg = REG_R1
729		p.To.Type = obj.TYPE_REG
730		p.To.Reg = REG_R1
731	}
732
733	// q1: BNE	R1, done
734	p = obj.Appendp(p, c.newprog)
735	q1 := p
736
737	p.As = ABNE
738	p.From.Type = obj.TYPE_REG
739	p.From.Reg = REG_R1
740	p.To.Type = obj.TYPE_BRANCH
741	p.Mark |= BRANCH
742
743	// MOV	LINK, R3
744	p = obj.Appendp(p, c.newprog)
745
746	p.As = mov
747	p.From.Type = obj.TYPE_REG
748	p.From.Reg = REGLINK
749	p.To.Type = obj.TYPE_REG
750	p.To.Reg = REG_R3
751	if q != nil {
752		q.Pcond = p
753		p.Mark |= LABEL
754	}
755
756	// JAL	runtime.morestack(SB)
757	p = obj.Appendp(p, c.newprog)
758
759	p.As = AJAL
760	p.To.Type = obj.TYPE_BRANCH
761	if c.cursym.CFunc() {
762		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
763	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
764		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
765	} else {
766		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
767	}
768	p.Mark |= BRANCH
769
770	// JMP	start
771	p = obj.Appendp(p, c.newprog)
772
773	p.As = AJMP
774	p.To.Type = obj.TYPE_BRANCH
775	p.Pcond = c.cursym.Func.Text.Link
776	p.Mark |= BRANCH
777
778	// placeholder for q1's jump target
779	p = obj.Appendp(p, c.newprog)
780
781	p.As = obj.ANOP // zero-width place holder
782	q1.Pcond = p
783
784	return p
785}
786
787func (c *ctxt0) addnop(p *obj.Prog) {
788	q := c.newprog()
789	q.As = ANOOP
790	q.Pos = p.Pos
791	q.Link = p.Link
792	p.Link = q
793}
794
795const (
796	E_HILO  = 1 << 0
797	E_FCR   = 1 << 1
798	E_MCR   = 1 << 2
799	E_MEM   = 1 << 3
800	E_MEMSP = 1 << 4 /* uses offset and size */
801	E_MEMSB = 1 << 5 /* uses offset and size */
802	ANYMEM  = E_MEM | E_MEMSP | E_MEMSB
803	//DELAY = LOAD|BRANCH|FCMP
804	DELAY = BRANCH /* only schedule branch */
805)
806
807type Dep struct {
808	ireg uint32
809	freg uint32
810	cc   uint32
811}
812
813type Sch struct {
814	p       obj.Prog
815	set     Dep
816	used    Dep
817	soffset int32
818	size    uint8
819	nop     uint8
820	comp    bool
821}
822
823func (c *ctxt0) sched(p0, pe *obj.Prog) {
824	var sch [NSCHED]Sch
825
826	/*
827	 * build side structure
828	 */
829	s := sch[:]
830	for p := p0; ; p = p.Link {
831		s[0].p = *p
832		c.markregused(&s[0])
833		if p == pe {
834			break
835		}
836		s = s[1:]
837	}
838	se := s
839
840	for i := cap(sch) - cap(se); i >= 0; i-- {
841		s = sch[i:]
842		if s[0].p.Mark&DELAY == 0 {
843			continue
844		}
845		if -cap(s) < -cap(se) {
846			if !conflict(&s[0], &s[1]) {
847				continue
848			}
849		}
850
851		var t []Sch
852		var j int
853		for j = cap(sch) - cap(s) - 1; j >= 0; j-- {
854			t = sch[j:]
855			if t[0].comp {
856				if s[0].p.Mark&BRANCH != 0 {
857					goto no2
858				}
859			}
860			if t[0].p.Mark&DELAY != 0 {
861				if -cap(s) >= -cap(se) || conflict(&t[0], &s[1]) {
862					goto no2
863				}
864			}
865			for u := t[1:]; -cap(u) <= -cap(s); u = u[1:] {
866				if c.depend(&u[0], &t[0]) {
867					goto no2
868				}
869			}
870			goto out2
871		no2:
872		}
873
874		if s[0].p.Mark&BRANCH != 0 {
875			s[0].nop = 1
876		}
877		continue
878
879	out2:
880		// t[0] is the instruction being moved to fill the delay
881		stmp := t[0]
882		copy(t[:i-j], t[1:i-j+1])
883		s[0] = stmp
884
885		if t[i-j-1].p.Mark&BRANCH != 0 {
886			// t[i-j] is being put into a branch delay slot
887			// combine its Spadj with the branch instruction
888			t[i-j-1].p.Spadj += t[i-j].p.Spadj
889			t[i-j].p.Spadj = 0
890		}
891
892		i--
893	}
894
895	/*
896	 * put it all back
897	 */
898	var p *obj.Prog
899	var q *obj.Prog
900	for s, p = sch[:], p0; -cap(s) <= -cap(se); s, p = s[1:], q {
901		q = p.Link
902		if q != s[0].p.Link {
903			*p = s[0].p
904			p.Link = q
905		}
906		for s[0].nop != 0 {
907			s[0].nop--
908			c.addnop(p)
909		}
910	}
911}
912
913func (c *ctxt0) markregused(s *Sch) {
914	p := &s.p
915	s.comp = c.compound(p)
916	s.nop = 0
917	if s.comp {
918		s.set.ireg |= 1 << (REGTMP - REG_R0)
919		s.used.ireg |= 1 << (REGTMP - REG_R0)
920	}
921
922	ar := 0  /* dest is really reference */
923	ad := 0  /* source/dest is really address */
924	ld := 0  /* opcode is load instruction */
925	sz := 20 /* size of load/store for overlap computation */
926
927	/*
928	 * flags based on opcode
929	 */
930	switch p.As {
931	case obj.ATEXT:
932		c.autosize = int32(p.To.Offset + 8)
933		ad = 1
934
935	case AJAL:
936		r := p.Reg
937		if r == 0 {
938			r = REGLINK
939		}
940		s.set.ireg |= 1 << uint(r-REG_R0)
941		ar = 1
942		ad = 1
943
944	case ABGEZAL,
945		ABLTZAL:
946		s.set.ireg |= 1 << (REGLINK - REG_R0)
947		fallthrough
948	case ABEQ,
949		ABGEZ,
950		ABGTZ,
951		ABLEZ,
952		ABLTZ,
953		ABNE:
954		ar = 1
955		ad = 1
956
957	case ABFPT,
958		ABFPF:
959		ad = 1
960		s.used.cc |= E_FCR
961
962	case ACMPEQD,
963		ACMPEQF,
964		ACMPGED,
965		ACMPGEF,
966		ACMPGTD,
967		ACMPGTF:
968		ar = 1
969		s.set.cc |= E_FCR
970		p.Mark |= FCMP
971
972	case AJMP:
973		ar = 1
974		ad = 1
975
976	case AMOVB,
977		AMOVBU:
978		sz = 1
979		ld = 1
980
981	case AMOVH,
982		AMOVHU:
983		sz = 2
984		ld = 1
985
986	case AMOVF,
987		AMOVW,
988		AMOVWL,
989		AMOVWR:
990		sz = 4
991		ld = 1
992
993	case AMOVD,
994		AMOVV,
995		AMOVVL,
996		AMOVVR:
997		sz = 8
998		ld = 1
999
1000	case ADIV,
1001		ADIVU,
1002		AMUL,
1003		AMULU,
1004		AREM,
1005		AREMU,
1006		ADIVV,
1007		ADIVVU,
1008		AMULV,
1009		AMULVU,
1010		AREMV,
1011		AREMVU:
1012		s.set.cc = E_HILO
1013		fallthrough
1014	case AADD,
1015		AADDU,
1016		AADDV,
1017		AADDVU,
1018		AAND,
1019		ANOR,
1020		AOR,
1021		ASGT,
1022		ASGTU,
1023		ASLL,
1024		ASRA,
1025		ASRL,
1026		ASLLV,
1027		ASRAV,
1028		ASRLV,
1029		ASUB,
1030		ASUBU,
1031		ASUBV,
1032		ASUBVU,
1033		AXOR,
1034
1035		AADDD,
1036		AADDF,
1037		AADDW,
1038		ASUBD,
1039		ASUBF,
1040		ASUBW,
1041		AMULF,
1042		AMULD,
1043		AMULW,
1044		ADIVF,
1045		ADIVD,
1046		ADIVW:
1047		if p.Reg == 0 {
1048			if p.To.Type == obj.TYPE_REG {
1049				p.Reg = p.To.Reg
1050			}
1051			//if(p->reg == NREG)
1052			//	print("botch %P\n", p);
1053		}
1054	}
1055
1056	/*
1057	 * flags based on 'to' field
1058	 */
1059	cls := int(p.To.Class)
1060	if cls == 0 {
1061		cls = c.aclass(&p.To) + 1
1062		p.To.Class = int8(cls)
1063	}
1064	cls--
1065	switch cls {
1066	default:
1067		fmt.Printf("unknown class %d %v\n", cls, p)
1068
1069	case C_ZCON,
1070		C_SCON,
1071		C_ADD0CON,
1072		C_AND0CON,
1073		C_ADDCON,
1074		C_ANDCON,
1075		C_UCON,
1076		C_LCON,
1077		C_NONE,
1078		C_SBRA,
1079		C_LBRA,
1080		C_ADDR,
1081		C_TEXTSIZE:
1082		break
1083
1084	case C_HI,
1085		C_LO:
1086		s.set.cc |= E_HILO
1087
1088	case C_FCREG:
1089		s.set.cc |= E_FCR
1090
1091	case C_MREG:
1092		s.set.cc |= E_MCR
1093
1094	case C_ZOREG,
1095		C_SOREG,
1096		C_LOREG:
1097		cls = int(p.To.Reg)
1098		s.used.ireg |= 1 << uint(cls-REG_R0)
1099		if ad != 0 {
1100			break
1101		}
1102		s.size = uint8(sz)
1103		s.soffset = c.regoff(&p.To)
1104
1105		m := uint32(ANYMEM)
1106		if cls == REGSB {
1107			m = E_MEMSB
1108		}
1109		if cls == REGSP {
1110			m = E_MEMSP
1111		}
1112
1113		if ar != 0 {
1114			s.used.cc |= m
1115		} else {
1116			s.set.cc |= m
1117		}
1118
1119	case C_SACON,
1120		C_LACON:
1121		s.used.ireg |= 1 << (REGSP - REG_R0)
1122
1123	case C_SECON,
1124		C_LECON:
1125		s.used.ireg |= 1 << (REGSB - REG_R0)
1126
1127	case C_REG:
1128		if ar != 0 {
1129			s.used.ireg |= 1 << uint(p.To.Reg-REG_R0)
1130		} else {
1131			s.set.ireg |= 1 << uint(p.To.Reg-REG_R0)
1132		}
1133
1134	case C_FREG:
1135		if ar != 0 {
1136			s.used.freg |= 1 << uint(p.To.Reg-REG_F0)
1137		} else {
1138			s.set.freg |= 1 << uint(p.To.Reg-REG_F0)
1139		}
1140		if ld != 0 && p.From.Type == obj.TYPE_REG {
1141			p.Mark |= LOAD
1142		}
1143
1144	case C_SAUTO,
1145		C_LAUTO:
1146		s.used.ireg |= 1 << (REGSP - REG_R0)
1147		if ad != 0 {
1148			break
1149		}
1150		s.size = uint8(sz)
1151		s.soffset = c.regoff(&p.To)
1152
1153		if ar != 0 {
1154			s.used.cc |= E_MEMSP
1155		} else {
1156			s.set.cc |= E_MEMSP
1157		}
1158
1159	case C_SEXT,
1160		C_LEXT:
1161		s.used.ireg |= 1 << (REGSB - REG_R0)
1162		if ad != 0 {
1163			break
1164		}
1165		s.size = uint8(sz)
1166		s.soffset = c.regoff(&p.To)
1167
1168		if ar != 0 {
1169			s.used.cc |= E_MEMSB
1170		} else {
1171			s.set.cc |= E_MEMSB
1172		}
1173	}
1174
1175	/*
1176	 * flags based on 'from' field
1177	 */
1178	cls = int(p.From.Class)
1179	if cls == 0 {
1180		cls = c.aclass(&p.From) + 1
1181		p.From.Class = int8(cls)
1182	}
1183	cls--
1184	switch cls {
1185	default:
1186		fmt.Printf("unknown class %d %v\n", cls, p)
1187
1188	case C_ZCON,
1189		C_SCON,
1190		C_ADD0CON,
1191		C_AND0CON,
1192		C_ADDCON,
1193		C_ANDCON,
1194		C_UCON,
1195		C_LCON,
1196		C_NONE,
1197		C_SBRA,
1198		C_LBRA,
1199		C_ADDR,
1200		C_TEXTSIZE:
1201		break
1202
1203	case C_HI,
1204		C_LO:
1205		s.used.cc |= E_HILO
1206
1207	case C_FCREG:
1208		s.used.cc |= E_FCR
1209
1210	case C_MREG:
1211		s.used.cc |= E_MCR
1212
1213	case C_ZOREG,
1214		C_SOREG,
1215		C_LOREG:
1216		cls = int(p.From.Reg)
1217		s.used.ireg |= 1 << uint(cls-REG_R0)
1218		if ld != 0 {
1219			p.Mark |= LOAD
1220		}
1221		s.size = uint8(sz)
1222		s.soffset = c.regoff(&p.From)
1223
1224		m := uint32(ANYMEM)
1225		if cls == REGSB {
1226			m = E_MEMSB
1227		}
1228		if cls == REGSP {
1229			m = E_MEMSP
1230		}
1231
1232		s.used.cc |= m
1233
1234	case C_SACON,
1235		C_LACON:
1236		cls = int(p.From.Reg)
1237		if cls == 0 {
1238			cls = REGSP
1239		}
1240		s.used.ireg |= 1 << uint(cls-REG_R0)
1241
1242	case C_SECON,
1243		C_LECON:
1244		s.used.ireg |= 1 << (REGSB - REG_R0)
1245
1246	case C_REG:
1247		s.used.ireg |= 1 << uint(p.From.Reg-REG_R0)
1248
1249	case C_FREG:
1250		s.used.freg |= 1 << uint(p.From.Reg-REG_F0)
1251		if ld != 0 && p.To.Type == obj.TYPE_REG {
1252			p.Mark |= LOAD
1253		}
1254
1255	case C_SAUTO,
1256		C_LAUTO:
1257		s.used.ireg |= 1 << (REGSP - REG_R0)
1258		if ld != 0 {
1259			p.Mark |= LOAD
1260		}
1261		if ad != 0 {
1262			break
1263		}
1264		s.size = uint8(sz)
1265		s.soffset = c.regoff(&p.From)
1266
1267		s.used.cc |= E_MEMSP
1268
1269	case C_SEXT:
1270	case C_LEXT:
1271		s.used.ireg |= 1 << (REGSB - REG_R0)
1272		if ld != 0 {
1273			p.Mark |= LOAD
1274		}
1275		if ad != 0 {
1276			break
1277		}
1278		s.size = uint8(sz)
1279		s.soffset = c.regoff(&p.From)
1280
1281		s.used.cc |= E_MEMSB
1282	}
1283
1284	cls = int(p.Reg)
1285	if cls != 0 {
1286		if REG_F0 <= cls && cls <= REG_F31 {
1287			s.used.freg |= 1 << uint(cls-REG_F0)
1288		} else {
1289			s.used.ireg |= 1 << uint(cls-REG_R0)
1290		}
1291	}
1292	s.set.ireg &^= (1 << (REGZERO - REG_R0)) /* R0 can't be set */
1293}
1294
1295/*
1296 * test to see if two instructions can be
1297 * interchanged without changing semantics
1298 */
1299func (c *ctxt0) depend(sa, sb *Sch) bool {
1300	if sa.set.ireg&(sb.set.ireg|sb.used.ireg) != 0 {
1301		return true
1302	}
1303	if sb.set.ireg&sa.used.ireg != 0 {
1304		return true
1305	}
1306
1307	if sa.set.freg&(sb.set.freg|sb.used.freg) != 0 {
1308		return true
1309	}
1310	if sb.set.freg&sa.used.freg != 0 {
1311		return true
1312	}
1313
1314	/*
1315	 * special case.
1316	 * loads from same address cannot pass.
1317	 * this is for hardware fifo's and the like
1318	 */
1319	if sa.used.cc&sb.used.cc&E_MEM != 0 {
1320		if sa.p.Reg == sb.p.Reg {
1321			if c.regoff(&sa.p.From) == c.regoff(&sb.p.From) {
1322				return true
1323			}
1324		}
1325	}
1326
1327	x := (sa.set.cc & (sb.set.cc | sb.used.cc)) | (sb.set.cc & sa.used.cc)
1328	if x != 0 {
1329		/*
1330		 * allow SB and SP to pass each other.
1331		 * allow SB to pass SB iff doffsets are ok
1332		 * anything else conflicts
1333		 */
1334		if x != E_MEMSP && x != E_MEMSB {
1335			return true
1336		}
1337		x = sa.set.cc | sb.set.cc | sa.used.cc | sb.used.cc
1338		if x&E_MEM != 0 {
1339			return true
1340		}
1341		if offoverlap(sa, sb) {
1342			return true
1343		}
1344	}
1345
1346	return false
1347}
1348
1349func offoverlap(sa, sb *Sch) bool {
1350	if sa.soffset < sb.soffset {
1351		if sa.soffset+int32(sa.size) > sb.soffset {
1352			return true
1353		}
1354		return false
1355	}
1356	if sb.soffset+int32(sb.size) > sa.soffset {
1357		return true
1358	}
1359	return false
1360}
1361
1362/*
1363 * test 2 adjacent instructions
1364 * and find out if inserted instructions
1365 * are desired to prevent stalls.
1366 */
1367func conflict(sa, sb *Sch) bool {
1368	if sa.set.ireg&sb.used.ireg != 0 {
1369		return true
1370	}
1371	if sa.set.freg&sb.used.freg != 0 {
1372		return true
1373	}
1374	if sa.set.cc&sb.used.cc != 0 {
1375		return true
1376	}
1377	return false
1378}
1379
1380func (c *ctxt0) compound(p *obj.Prog) bool {
1381	o := c.oplook(p)
1382	if o.size != 4 {
1383		return true
1384	}
1385	if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSB {
1386		return true
1387	}
1388	return false
1389}
1390
1391var Linkmips64 = obj.LinkArch{
1392	Arch:       sys.ArchMIPS64,
1393	Init:       buildop,
1394	Preprocess: preprocess,
1395	Assemble:   span0,
1396	Progedit:   progedit,
1397}
1398
1399var Linkmips64le = obj.LinkArch{
1400	Arch:       sys.ArchMIPS64LE,
1401	Init:       buildop,
1402	Preprocess: preprocess,
1403	Assemble:   span0,
1404	Progedit:   progedit,
1405}
1406
1407var Linkmips = obj.LinkArch{
1408	Arch:       sys.ArchMIPS,
1409	Init:       buildop,
1410	Preprocess: preprocess,
1411	Assemble:   span0,
1412	Progedit:   progedit,
1413}
1414
1415var Linkmipsle = obj.LinkArch{
1416	Arch:       sys.ArchMIPSLE,
1417	Init:       buildop,
1418	Preprocess: preprocess,
1419	Assemble:   span0,
1420	Progedit:   progedit,
1421}
1422