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