mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 12:42:25 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			1457 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1457 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
 | |
| //
 | |
| //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 | |
| //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
 | |
| //	Portions Copyright © 1997-1999 Vita Nuova Limited
 | |
| //	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
 | |
| //	Portions Copyright © 2004,2006 Bruce Ellis
 | |
| //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
 | |
| //	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
 | |
| //	Portions Copyright © 2009 The Go Authors. All rights reserved.
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| // of this software and associated documentation files (the "Software"), to deal
 | |
| // in the Software without restriction, including without limitation the rights
 | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| // copies of the Software, and to permit persons to whom the Software is
 | |
| // furnished to do so, subject to the following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included in
 | |
| // all copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
| // THE SOFTWARE.
 | |
| 
 | |
| package mips
 | |
| 
 | |
| import (
 | |
| 	"github.com/twitchyliquid64/golang-asm/obj"
 | |
| 	"github.com/twitchyliquid64/golang-asm/objabi"
 | |
| 	"github.com/twitchyliquid64/golang-asm/sys"
 | |
| 	"encoding/binary"
 | |
| 	"fmt"
 | |
| 	"math"
 | |
| )
 | |
| 
 | |
| func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
 | |
| 	c := ctxt0{ctxt: ctxt, newprog: newprog}
 | |
| 
 | |
| 	p.From.Class = 0
 | |
| 	p.To.Class = 0
 | |
| 
 | |
| 	// Rewrite JMP/JAL to symbol as TYPE_BRANCH.
 | |
| 	switch p.As {
 | |
| 	case AJMP,
 | |
| 		AJAL,
 | |
| 		ARET,
 | |
| 		obj.ADUFFZERO,
 | |
| 		obj.ADUFFCOPY:
 | |
| 		if p.To.Sym != nil {
 | |
| 			p.To.Type = obj.TYPE_BRANCH
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Rewrite float constants to values stored in memory.
 | |
| 	switch p.As {
 | |
| 	case AMOVF:
 | |
| 		if p.From.Type == obj.TYPE_FCONST {
 | |
| 			f32 := float32(p.From.Val.(float64))
 | |
| 			if math.Float32bits(f32) == 0 {
 | |
| 				p.As = AMOVW
 | |
| 				p.From.Type = obj.TYPE_REG
 | |
| 				p.From.Reg = REGZERO
 | |
| 				break
 | |
| 			}
 | |
| 			p.From.Type = obj.TYPE_MEM
 | |
| 			p.From.Sym = ctxt.Float32Sym(f32)
 | |
| 			p.From.Name = obj.NAME_EXTERN
 | |
| 			p.From.Offset = 0
 | |
| 		}
 | |
| 
 | |
| 	case AMOVD:
 | |
| 		if p.From.Type == obj.TYPE_FCONST {
 | |
| 			f64 := p.From.Val.(float64)
 | |
| 			if math.Float64bits(f64) == 0 && c.ctxt.Arch.Family == sys.MIPS64 {
 | |
| 				p.As = AMOVV
 | |
| 				p.From.Type = obj.TYPE_REG
 | |
| 				p.From.Reg = REGZERO
 | |
| 				break
 | |
| 			}
 | |
| 			p.From.Type = obj.TYPE_MEM
 | |
| 			p.From.Sym = ctxt.Float64Sym(f64)
 | |
| 			p.From.Name = obj.NAME_EXTERN
 | |
| 			p.From.Offset = 0
 | |
| 		}
 | |
| 
 | |
| 		// Put >32-bit constants in memory and load them
 | |
| 	case AMOVV:
 | |
| 		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 {
 | |
| 			p.From.Type = obj.TYPE_MEM
 | |
| 			p.From.Sym = ctxt.Int64Sym(p.From.Offset)
 | |
| 			p.From.Name = obj.NAME_EXTERN
 | |
| 			p.From.Offset = 0
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Rewrite SUB constants into ADD.
 | |
| 	switch p.As {
 | |
| 	case ASUB:
 | |
| 		if p.From.Type == obj.TYPE_CONST {
 | |
| 			p.From.Offset = -p.From.Offset
 | |
| 			p.As = AADD
 | |
| 		}
 | |
| 
 | |
| 	case ASUBU:
 | |
| 		if p.From.Type == obj.TYPE_CONST {
 | |
| 			p.From.Offset = -p.From.Offset
 | |
| 			p.As = AADDU
 | |
| 		}
 | |
| 
 | |
| 	case ASUBV:
 | |
| 		if p.From.Type == obj.TYPE_CONST {
 | |
| 			p.From.Offset = -p.From.Offset
 | |
| 			p.As = AADDV
 | |
| 		}
 | |
| 
 | |
| 	case ASUBVU:
 | |
| 		if p.From.Type == obj.TYPE_CONST {
 | |
| 			p.From.Offset = -p.From.Offset
 | |
| 			p.As = AADDVU
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
 | |
| 	// TODO(minux): add morestack short-cuts with small fixed frame-size.
 | |
| 	c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym}
 | |
| 
 | |
| 	// a switch for enabling/disabling instruction scheduling
 | |
| 	nosched := true
 | |
| 
 | |
| 	if c.cursym.Func.Text == nil || c.cursym.Func.Text.Link == nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	p := c.cursym.Func.Text
 | |
| 	textstksiz := p.To.Offset
 | |
| 	if textstksiz == -ctxt.FixedFrameSize() {
 | |
| 		// Historical way to mark NOFRAME.
 | |
| 		p.From.Sym.Set(obj.AttrNoFrame, true)
 | |
| 		textstksiz = 0
 | |
| 	}
 | |
| 	if textstksiz < 0 {
 | |
| 		c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
 | |
| 	}
 | |
| 	if p.From.Sym.NoFrame() {
 | |
| 		if textstksiz != 0 {
 | |
| 			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	c.cursym.Func.Args = p.To.Val.(int32)
 | |
| 	c.cursym.Func.Locals = int32(textstksiz)
 | |
| 
 | |
| 	/*
 | |
| 	 * find leaf subroutines
 | |
| 	 * expand RET
 | |
| 	 * expand BECOME pseudo
 | |
| 	 */
 | |
| 
 | |
| 	for p := c.cursym.Func.Text; p != nil; p = p.Link {
 | |
| 		switch p.As {
 | |
| 		/* too hard, just leave alone */
 | |
| 		case obj.ATEXT:
 | |
| 			p.Mark |= LABEL | LEAF | SYNC
 | |
| 			if p.Link != nil {
 | |
| 				p.Link.Mark |= LABEL
 | |
| 			}
 | |
| 
 | |
| 		/* too hard, just leave alone */
 | |
| 		case AMOVW,
 | |
| 			AMOVV:
 | |
| 			if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
 | |
| 				p.Mark |= LABEL | SYNC
 | |
| 				break
 | |
| 			}
 | |
| 			if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
 | |
| 				p.Mark |= LABEL | SYNC
 | |
| 			}
 | |
| 
 | |
| 		/* too hard, just leave alone */
 | |
| 		case ASYSCALL,
 | |
| 			AWORD,
 | |
| 			ATLBWR,
 | |
| 			ATLBWI,
 | |
| 			ATLBP,
 | |
| 			ATLBR:
 | |
| 			p.Mark |= LABEL | SYNC
 | |
| 
 | |
| 		case ANOR:
 | |
| 			if p.To.Type == obj.TYPE_REG {
 | |
| 				if p.To.Reg == REGZERO {
 | |
| 					p.Mark |= LABEL | SYNC
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 		case ABGEZAL,
 | |
| 			ABLTZAL,
 | |
| 			AJAL,
 | |
| 			obj.ADUFFZERO,
 | |
| 			obj.ADUFFCOPY:
 | |
| 			c.cursym.Func.Text.Mark &^= LEAF
 | |
| 			fallthrough
 | |
| 
 | |
| 		case AJMP,
 | |
| 			ABEQ,
 | |
| 			ABGEZ,
 | |
| 			ABGTZ,
 | |
| 			ABLEZ,
 | |
| 			ABLTZ,
 | |
| 			ABNE,
 | |
| 			ABFPT, ABFPF:
 | |
| 			if p.As == ABFPT || p.As == ABFPF {
 | |
| 				// We don't treat ABFPT and ABFPF as branches here,
 | |
| 				// so that we will always fill nop (0x0) in their
 | |
| 				// delay slot during assembly.
 | |
| 				// This is to workaround a kernel FPU emulator bug
 | |
| 				// where it uses the user stack to simulate the
 | |
| 				// instruction in the delay slot if it's not 0x0,
 | |
| 				// and somehow that leads to SIGSEGV when the kernel
 | |
| 				// jump to the stack.
 | |
| 				p.Mark |= SYNC
 | |
| 			} else {
 | |
| 				p.Mark |= BRANCH
 | |
| 			}
 | |
| 			q1 := p.To.Target()
 | |
| 			if q1 != nil {
 | |
| 				for q1.As == obj.ANOP {
 | |
| 					q1 = q1.Link
 | |
| 					p.To.SetTarget(q1)
 | |
| 				}
 | |
| 
 | |
| 				if q1.Mark&LEAF == 0 {
 | |
| 					q1.Mark |= LABEL
 | |
| 				}
 | |
| 			}
 | |
| 			//else {
 | |
| 			//	p.Mark |= LABEL
 | |
| 			//}
 | |
| 			q1 = p.Link
 | |
| 			if q1 != nil {
 | |
| 				q1.Mark |= LABEL
 | |
| 			}
 | |
| 
 | |
| 		case ARET:
 | |
| 			if p.Link != nil {
 | |
| 				p.Link.Mark |= LABEL
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var mov, add obj.As
 | |
| 	if c.ctxt.Arch.Family == sys.MIPS64 {
 | |
| 		add = AADDV
 | |
| 		mov = AMOVV
 | |
| 	} else {
 | |
| 		add = AADDU
 | |
| 		mov = AMOVW
 | |
| 	}
 | |
| 
 | |
| 	var q *obj.Prog
 | |
| 	var q1 *obj.Prog
 | |
| 	autosize := int32(0)
 | |
| 	var p1 *obj.Prog
 | |
| 	var p2 *obj.Prog
 | |
| 	for p := c.cursym.Func.Text; p != nil; p = p.Link {
 | |
| 		o := p.As
 | |
| 		switch o {
 | |
| 		case obj.ATEXT:
 | |
| 			autosize = int32(textstksiz)
 | |
| 
 | |
| 			if p.Mark&LEAF != 0 && autosize == 0 {
 | |
| 				// A leaf function with no locals has no frame.
 | |
| 				p.From.Sym.Set(obj.AttrNoFrame, true)
 | |
| 			}
 | |
| 
 | |
| 			if !p.From.Sym.NoFrame() {
 | |
| 				// If there is a stack frame at all, it includes
 | |
| 				// space to save the LR.
 | |
| 				autosize += int32(c.ctxt.FixedFrameSize())
 | |
| 			}
 | |
| 
 | |
| 			if autosize&4 != 0 && c.ctxt.Arch.Family == sys.MIPS64 {
 | |
| 				autosize += 4
 | |
| 			}
 | |
| 
 | |
| 			if autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
 | |
| 				if c.cursym.Func.Text.From.Sym.NoSplit() {
 | |
| 					if ctxt.Debugvlog {
 | |
| 						ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
 | |
| 					}
 | |
| 
 | |
| 					c.cursym.Func.Text.Mark |= LEAF
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			p.To.Offset = int64(autosize) - ctxt.FixedFrameSize()
 | |
| 
 | |
| 			if c.cursym.Func.Text.Mark&LEAF != 0 {
 | |
| 				c.cursym.Set(obj.AttrLeaf, true)
 | |
| 				if p.From.Sym.NoFrame() {
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if !p.From.Sym.NoSplit() {
 | |
| 				p = c.stacksplit(p, autosize) // emit split check
 | |
| 			}
 | |
| 
 | |
| 			q = p
 | |
| 
 | |
| 			if autosize != 0 {
 | |
| 				// Make sure to save link register for non-empty frame, even if
 | |
| 				// it is a leaf function, so that traceback works.
 | |
| 				// Store link register before decrement SP, so if a signal comes
 | |
| 				// during the execution of the function prologue, the traceback
 | |
| 				// code will not see a half-updated stack frame.
 | |
| 				// This sequence is not async preemptible, as if we open a frame
 | |
| 				// at the current SP, it will clobber the saved LR.
 | |
| 				q = c.ctxt.StartUnsafePoint(q, c.newprog)
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 				q.As = mov
 | |
| 				q.Pos = p.Pos
 | |
| 				q.From.Type = obj.TYPE_REG
 | |
| 				q.From.Reg = REGLINK
 | |
| 				q.To.Type = obj.TYPE_MEM
 | |
| 				q.To.Offset = int64(-autosize)
 | |
| 				q.To.Reg = REGSP
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 				q.As = add
 | |
| 				q.Pos = p.Pos
 | |
| 				q.From.Type = obj.TYPE_CONST
 | |
| 				q.From.Offset = int64(-autosize)
 | |
| 				q.To.Type = obj.TYPE_REG
 | |
| 				q.To.Reg = REGSP
 | |
| 				q.Spadj = +autosize
 | |
| 
 | |
| 				q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
 | |
| 			}
 | |
| 
 | |
| 			if c.cursym.Func.Text.From.Sym.Wrapper() && c.cursym.Func.Text.Mark&LEAF == 0 {
 | |
| 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
 | |
| 				//
 | |
| 				//	MOV	g_panic(g), R1
 | |
| 				//	BEQ	R1, end
 | |
| 				//	MOV	panic_argp(R1), R2
 | |
| 				//	ADD	$(autosize+FIXED_FRAME), R29, R3
 | |
| 				//	BNE	R2, R3, end
 | |
| 				//	ADD	$FIXED_FRAME, R29, R2
 | |
| 				//	MOV	R2, panic_argp(R1)
 | |
| 				// end:
 | |
| 				//	NOP
 | |
| 				//
 | |
| 				// The NOP is needed to give the jumps somewhere to land.
 | |
| 				// It is a liblink NOP, not an mips NOP: it encodes to 0 instruction bytes.
 | |
| 				//
 | |
| 				// We don't generate this for leafs because that means the wrapped
 | |
| 				// function was inlined into the wrapper.
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 
 | |
| 				q.As = mov
 | |
| 				q.From.Type = obj.TYPE_MEM
 | |
| 				q.From.Reg = REGG
 | |
| 				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
 | |
| 				q.To.Type = obj.TYPE_REG
 | |
| 				q.To.Reg = REG_R1
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 				q.As = ABEQ
 | |
| 				q.From.Type = obj.TYPE_REG
 | |
| 				q.From.Reg = REG_R1
 | |
| 				q.To.Type = obj.TYPE_BRANCH
 | |
| 				q.Mark |= BRANCH
 | |
| 				p1 = q
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 				q.As = mov
 | |
| 				q.From.Type = obj.TYPE_MEM
 | |
| 				q.From.Reg = REG_R1
 | |
| 				q.From.Offset = 0 // Panic.argp
 | |
| 				q.To.Type = obj.TYPE_REG
 | |
| 				q.To.Reg = REG_R2
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 				q.As = add
 | |
| 				q.From.Type = obj.TYPE_CONST
 | |
| 				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
 | |
| 				q.Reg = REGSP
 | |
| 				q.To.Type = obj.TYPE_REG
 | |
| 				q.To.Reg = REG_R3
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 				q.As = ABNE
 | |
| 				q.From.Type = obj.TYPE_REG
 | |
| 				q.From.Reg = REG_R2
 | |
| 				q.Reg = REG_R3
 | |
| 				q.To.Type = obj.TYPE_BRANCH
 | |
| 				q.Mark |= BRANCH
 | |
| 				p2 = q
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 				q.As = add
 | |
| 				q.From.Type = obj.TYPE_CONST
 | |
| 				q.From.Offset = ctxt.FixedFrameSize()
 | |
| 				q.Reg = REGSP
 | |
| 				q.To.Type = obj.TYPE_REG
 | |
| 				q.To.Reg = REG_R2
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 				q.As = mov
 | |
| 				q.From.Type = obj.TYPE_REG
 | |
| 				q.From.Reg = REG_R2
 | |
| 				q.To.Type = obj.TYPE_MEM
 | |
| 				q.To.Reg = REG_R1
 | |
| 				q.To.Offset = 0 // Panic.argp
 | |
| 
 | |
| 				q = obj.Appendp(q, newprog)
 | |
| 
 | |
| 				q.As = obj.ANOP
 | |
| 				p1.To.SetTarget(q)
 | |
| 				p2.To.SetTarget(q)
 | |
| 			}
 | |
| 
 | |
| 		case ARET:
 | |
| 			if p.From.Type == obj.TYPE_CONST {
 | |
| 				ctxt.Diag("using BECOME (%v) is not supported!", p)
 | |
| 				break
 | |
| 			}
 | |
| 
 | |
| 			retSym := p.To.Sym
 | |
| 			p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
 | |
| 			p.To.Sym = nil
 | |
| 
 | |
| 			if c.cursym.Func.Text.Mark&LEAF != 0 {
 | |
| 				if autosize == 0 {
 | |
| 					p.As = AJMP
 | |
| 					p.From = obj.Addr{}
 | |
| 					if retSym != nil { // retjmp
 | |
| 						p.To.Type = obj.TYPE_BRANCH
 | |
| 						p.To.Name = obj.NAME_EXTERN
 | |
| 						p.To.Sym = retSym
 | |
| 					} else {
 | |
| 						p.To.Type = obj.TYPE_MEM
 | |
| 						p.To.Reg = REGLINK
 | |
| 						p.To.Offset = 0
 | |
| 					}
 | |
| 					p.Mark |= BRANCH
 | |
| 					break
 | |
| 				}
 | |
| 
 | |
| 				p.As = add
 | |
| 				p.From.Type = obj.TYPE_CONST
 | |
| 				p.From.Offset = int64(autosize)
 | |
| 				p.To.Type = obj.TYPE_REG
 | |
| 				p.To.Reg = REGSP
 | |
| 				p.Spadj = -autosize
 | |
| 
 | |
| 				q = c.newprog()
 | |
| 				q.As = AJMP
 | |
| 				q.Pos = p.Pos
 | |
| 				q.To.Type = obj.TYPE_MEM
 | |
| 				q.To.Offset = 0
 | |
| 				q.To.Reg = REGLINK
 | |
| 				q.Mark |= BRANCH
 | |
| 				q.Spadj = +autosize
 | |
| 
 | |
| 				q.Link = p.Link
 | |
| 				p.Link = q
 | |
| 				break
 | |
| 			}
 | |
| 
 | |
| 			p.As = mov
 | |
| 			p.From.Type = obj.TYPE_MEM
 | |
| 			p.From.Offset = 0
 | |
| 			p.From.Reg = REGSP
 | |
| 			p.To.Type = obj.TYPE_REG
 | |
| 			p.To.Reg = REGLINK
 | |
| 
 | |
| 			if autosize != 0 {
 | |
| 				q = c.newprog()
 | |
| 				q.As = add
 | |
| 				q.Pos = p.Pos
 | |
| 				q.From.Type = obj.TYPE_CONST
 | |
| 				q.From.Offset = int64(autosize)
 | |
| 				q.To.Type = obj.TYPE_REG
 | |
| 				q.To.Reg = REGSP
 | |
| 				q.Spadj = -autosize
 | |
| 
 | |
| 				q.Link = p.Link
 | |
| 				p.Link = q
 | |
| 			}
 | |
| 
 | |
| 			q1 = c.newprog()
 | |
| 			q1.As = AJMP
 | |
| 			q1.Pos = p.Pos
 | |
| 			if retSym != nil { // retjmp
 | |
| 				q1.To.Type = obj.TYPE_BRANCH
 | |
| 				q1.To.Name = obj.NAME_EXTERN
 | |
| 				q1.To.Sym = retSym
 | |
| 			} else {
 | |
| 				q1.To.Type = obj.TYPE_MEM
 | |
| 				q1.To.Offset = 0
 | |
| 				q1.To.Reg = REGLINK
 | |
| 			}
 | |
| 			q1.Mark |= BRANCH
 | |
| 			q1.Spadj = +autosize
 | |
| 
 | |
| 			q1.Link = q.Link
 | |
| 			q.Link = q1
 | |
| 
 | |
| 		case AADD,
 | |
| 			AADDU,
 | |
| 			AADDV,
 | |
| 			AADDVU:
 | |
| 			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
 | |
| 				p.Spadj = int32(-p.From.Offset)
 | |
| 			}
 | |
| 
 | |
| 		case obj.AGETCALLERPC:
 | |
| 			if cursym.Leaf() {
 | |
| 				/* MOV LR, Rd */
 | |
| 				p.As = mov
 | |
| 				p.From.Type = obj.TYPE_REG
 | |
| 				p.From.Reg = REGLINK
 | |
| 			} else {
 | |
| 				/* MOV (RSP), Rd */
 | |
| 				p.As = mov
 | |
| 				p.From.Type = obj.TYPE_MEM
 | |
| 				p.From.Reg = REGSP
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if c.ctxt.Arch.Family == sys.MIPS {
 | |
| 		// rewrite MOVD into two MOVF in 32-bit mode to avoid unaligned memory access
 | |
| 		for p = c.cursym.Func.Text; p != nil; p = p1 {
 | |
| 			p1 = p.Link
 | |
| 
 | |
| 			if p.As != AMOVD {
 | |
| 				continue
 | |
| 			}
 | |
| 			if p.From.Type != obj.TYPE_MEM && p.To.Type != obj.TYPE_MEM {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			p.As = AMOVF
 | |
| 			q = c.newprog()
 | |
| 			*q = *p
 | |
| 			q.Link = p.Link
 | |
| 			p.Link = q
 | |
| 			p1 = q.Link
 | |
| 
 | |
| 			var addrOff int64
 | |
| 			if c.ctxt.Arch.ByteOrder == binary.BigEndian {
 | |
| 				addrOff = 4 // swap load/save order
 | |
| 			}
 | |
| 			if p.From.Type == obj.TYPE_MEM {
 | |
| 				reg := REG_F0 + (p.To.Reg-REG_F0)&^1
 | |
| 				p.To.Reg = reg
 | |
| 				q.To.Reg = reg + 1
 | |
| 				p.From.Offset += addrOff
 | |
| 				q.From.Offset += 4 - addrOff
 | |
| 			} else if p.To.Type == obj.TYPE_MEM {
 | |
| 				reg := REG_F0 + (p.From.Reg-REG_F0)&^1
 | |
| 				p.From.Reg = reg
 | |
| 				q.From.Reg = reg + 1
 | |
| 				p.To.Offset += addrOff
 | |
| 				q.To.Offset += 4 - addrOff
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if nosched {
 | |
| 		// if we don't do instruction scheduling, simply add
 | |
| 		// NOP after each branch instruction.
 | |
| 		for p = c.cursym.Func.Text; p != nil; p = p.Link {
 | |
| 			if p.Mark&BRANCH != 0 {
 | |
| 				c.addnop(p)
 | |
| 			}
 | |
| 		}
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// instruction scheduling
 | |
| 	q = nil                 // p - 1
 | |
| 	q1 = c.cursym.Func.Text // top of block
 | |
| 	o := 0                  // count of instructions
 | |
| 	for p = c.cursym.Func.Text; p != nil; p = p1 {
 | |
| 		p1 = p.Link
 | |
| 		o++
 | |
| 		if p.Mark&NOSCHED != 0 {
 | |
| 			if q1 != p {
 | |
| 				c.sched(q1, q)
 | |
| 			}
 | |
| 			for ; p != nil; p = p.Link {
 | |
| 				if p.Mark&NOSCHED == 0 {
 | |
| 					break
 | |
| 				}
 | |
| 				q = p
 | |
| 			}
 | |
| 			p1 = p
 | |
| 			q1 = p
 | |
| 			o = 0
 | |
| 			continue
 | |
| 		}
 | |
| 		if p.Mark&(LABEL|SYNC) != 0 {
 | |
| 			if q1 != p {
 | |
| 				c.sched(q1, q)
 | |
| 			}
 | |
| 			q1 = p
 | |
| 			o = 1
 | |
| 		}
 | |
| 		if p.Mark&(BRANCH|SYNC) != 0 {
 | |
| 			c.sched(q1, p)
 | |
| 			q1 = p1
 | |
| 			o = 0
 | |
| 		}
 | |
| 		if o >= NSCHED {
 | |
| 			c.sched(q1, p)
 | |
| 			q1 = p1
 | |
| 			o = 0
 | |
| 		}
 | |
| 		q = p
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
 | |
| 	var mov, add, sub obj.As
 | |
| 
 | |
| 	if c.ctxt.Arch.Family == sys.MIPS64 {
 | |
| 		add = AADDV
 | |
| 		mov = AMOVV
 | |
| 		sub = ASUBVU
 | |
| 	} else {
 | |
| 		add = AADDU
 | |
| 		mov = AMOVW
 | |
| 		sub = ASUBU
 | |
| 	}
 | |
| 
 | |
| 	// MOV	g_stackguard(g), R1
 | |
| 	p = obj.Appendp(p, c.newprog)
 | |
| 
 | |
| 	p.As = mov
 | |
| 	p.From.Type = obj.TYPE_MEM
 | |
| 	p.From.Reg = REGG
 | |
| 	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
 | |
| 	if c.cursym.CFunc() {
 | |
| 		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
 | |
| 	}
 | |
| 	p.To.Type = obj.TYPE_REG
 | |
| 	p.To.Reg = REG_R1
 | |
| 
 | |
| 	// Mark the stack bound check and morestack call async nonpreemptible.
 | |
| 	// If we get preempted here, when resumed the preemption request is
 | |
| 	// cleared, but we'll still call morestack, which will double the stack
 | |
| 	// unnecessarily. See issue #35470.
 | |
| 	p = c.ctxt.StartUnsafePoint(p, c.newprog)
 | |
| 
 | |
| 	var q *obj.Prog
 | |
| 	if framesize <= objabi.StackSmall {
 | |
| 		// small stack: SP < stackguard
 | |
| 		//	AGTU	SP, stackguard, R1
 | |
| 		p = obj.Appendp(p, c.newprog)
 | |
| 
 | |
| 		p.As = ASGTU
 | |
| 		p.From.Type = obj.TYPE_REG
 | |
| 		p.From.Reg = REGSP
 | |
| 		p.Reg = REG_R1
 | |
| 		p.To.Type = obj.TYPE_REG
 | |
| 		p.To.Reg = REG_R1
 | |
| 	} else if framesize <= objabi.StackBig {
 | |
| 		// large stack: SP-framesize < stackguard-StackSmall
 | |
| 		//	ADD	$-(framesize-StackSmall), SP, R2
 | |
| 		//	SGTU	R2, stackguard, R1
 | |
| 		p = obj.Appendp(p, c.newprog)
 | |
| 
 | |
| 		p.As = add
 | |
| 		p.From.Type = obj.TYPE_CONST
 | |
| 		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
 | |
| 		p.Reg = REGSP
 | |
| 		p.To.Type = obj.TYPE_REG
 | |
| 		p.To.Reg = REG_R2
 | |
| 
 | |
| 		p = obj.Appendp(p, c.newprog)
 | |
| 		p.As = ASGTU
 | |
| 		p.From.Type = obj.TYPE_REG
 | |
| 		p.From.Reg = REG_R2
 | |
| 		p.Reg = REG_R1
 | |
| 		p.To.Type = obj.TYPE_REG
 | |
| 		p.To.Reg = REG_R1
 | |
| 	} else {
 | |
| 		// Such a large stack we need to protect against wraparound.
 | |
| 		// If SP is close to zero:
 | |
| 		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
 | |
| 		// The +StackGuard on both sides is required to keep the left side positive:
 | |
| 		// SP is allowed to be slightly below stackguard. See stack.h.
 | |
| 		//
 | |
| 		// Preemption sets stackguard to StackPreempt, a very large value.
 | |
| 		// That breaks the math above, so we have to check for that explicitly.
 | |
| 		//	// stackguard is R1
 | |
| 		//	MOV	$StackPreempt, R2
 | |
| 		//	BEQ	R1, R2, label-of-call-to-morestack
 | |
| 		//	ADD	$StackGuard, SP, R2
 | |
| 		//	SUB	R1, R2
 | |
| 		//	MOV	$(framesize+(StackGuard-StackSmall)), R1
 | |
| 		//	SGTU	R2, R1, R1
 | |
| 		p = obj.Appendp(p, c.newprog)
 | |
| 
 | |
| 		p.As = mov
 | |
| 		p.From.Type = obj.TYPE_CONST
 | |
| 		p.From.Offset = objabi.StackPreempt
 | |
| 		p.To.Type = obj.TYPE_REG
 | |
| 		p.To.Reg = REG_R2
 | |
| 
 | |
| 		p = obj.Appendp(p, c.newprog)
 | |
| 		q = p
 | |
| 		p.As = ABEQ
 | |
| 		p.From.Type = obj.TYPE_REG
 | |
| 		p.From.Reg = REG_R1
 | |
| 		p.Reg = REG_R2
 | |
| 		p.To.Type = obj.TYPE_BRANCH
 | |
| 		p.Mark |= BRANCH
 | |
| 
 | |
| 		p = obj.Appendp(p, c.newprog)
 | |
| 		p.As = add
 | |
| 		p.From.Type = obj.TYPE_CONST
 | |
| 		p.From.Offset = int64(objabi.StackGuard)
 | |
| 		p.Reg = REGSP
 | |
| 		p.To.Type = obj.TYPE_REG
 | |
| 		p.To.Reg = REG_R2
 | |
| 
 | |
| 		p = obj.Appendp(p, c.newprog)
 | |
| 		p.As = sub
 | |
| 		p.From.Type = obj.TYPE_REG
 | |
| 		p.From.Reg = REG_R1
 | |
| 		p.To.Type = obj.TYPE_REG
 | |
| 		p.To.Reg = REG_R2
 | |
| 
 | |
| 		p = obj.Appendp(p, c.newprog)
 | |
| 		p.As = mov
 | |
| 		p.From.Type = obj.TYPE_CONST
 | |
| 		p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
 | |
| 		p.To.Type = obj.TYPE_REG
 | |
| 		p.To.Reg = REG_R1
 | |
| 
 | |
| 		p = obj.Appendp(p, c.newprog)
 | |
| 		p.As = ASGTU
 | |
| 		p.From.Type = obj.TYPE_REG
 | |
| 		p.From.Reg = REG_R2
 | |
| 		p.Reg = REG_R1
 | |
| 		p.To.Type = obj.TYPE_REG
 | |
| 		p.To.Reg = REG_R1
 | |
| 	}
 | |
| 
 | |
| 	// q1: BNE	R1, done
 | |
| 	p = obj.Appendp(p, c.newprog)
 | |
| 	q1 := p
 | |
| 
 | |
| 	p.As = ABNE
 | |
| 	p.From.Type = obj.TYPE_REG
 | |
| 	p.From.Reg = REG_R1
 | |
| 	p.To.Type = obj.TYPE_BRANCH
 | |
| 	p.Mark |= BRANCH
 | |
| 
 | |
| 	// MOV	LINK, R3
 | |
| 	p = obj.Appendp(p, c.newprog)
 | |
| 
 | |
| 	p.As = mov
 | |
| 	p.From.Type = obj.TYPE_REG
 | |
| 	p.From.Reg = REGLINK
 | |
| 	p.To.Type = obj.TYPE_REG
 | |
| 	p.To.Reg = REG_R3
 | |
| 	if q != nil {
 | |
| 		q.To.SetTarget(p)
 | |
| 		p.Mark |= LABEL
 | |
| 	}
 | |
| 
 | |
| 	p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
 | |
| 
 | |
| 	// JAL	runtime.morestack(SB)
 | |
| 	p = obj.Appendp(p, c.newprog)
 | |
| 
 | |
| 	p.As = AJAL
 | |
| 	p.To.Type = obj.TYPE_BRANCH
 | |
| 	if c.cursym.CFunc() {
 | |
| 		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
 | |
| 	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
 | |
| 		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
 | |
| 	} else {
 | |
| 		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
 | |
| 	}
 | |
| 	p.Mark |= BRANCH
 | |
| 
 | |
| 	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
 | |
| 
 | |
| 	// JMP	start
 | |
| 	p = obj.Appendp(p, c.newprog)
 | |
| 
 | |
| 	p.As = AJMP
 | |
| 	p.To.Type = obj.TYPE_BRANCH
 | |
| 	p.To.SetTarget(c.cursym.Func.Text.Link)
 | |
| 	p.Mark |= BRANCH
 | |
| 
 | |
| 	// placeholder for q1's jump target
 | |
| 	p = obj.Appendp(p, c.newprog)
 | |
| 
 | |
| 	p.As = obj.ANOP // zero-width place holder
 | |
| 	q1.To.SetTarget(p)
 | |
| 
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| func (c *ctxt0) addnop(p *obj.Prog) {
 | |
| 	q := c.newprog()
 | |
| 	q.As = ANOOP
 | |
| 	q.Pos = p.Pos
 | |
| 	q.Link = p.Link
 | |
| 	p.Link = q
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	E_HILO  = 1 << 0
 | |
| 	E_FCR   = 1 << 1
 | |
| 	E_MCR   = 1 << 2
 | |
| 	E_MEM   = 1 << 3
 | |
| 	E_MEMSP = 1 << 4 /* uses offset and size */
 | |
| 	E_MEMSB = 1 << 5 /* uses offset and size */
 | |
| 	ANYMEM  = E_MEM | E_MEMSP | E_MEMSB
 | |
| 	//DELAY = LOAD|BRANCH|FCMP
 | |
| 	DELAY = BRANCH /* only schedule branch */
 | |
| )
 | |
| 
 | |
| type Dep struct {
 | |
| 	ireg uint32
 | |
| 	freg uint32
 | |
| 	cc   uint32
 | |
| }
 | |
| 
 | |
| type Sch struct {
 | |
| 	p       obj.Prog
 | |
| 	set     Dep
 | |
| 	used    Dep
 | |
| 	soffset int32
 | |
| 	size    uint8
 | |
| 	nop     uint8
 | |
| 	comp    bool
 | |
| }
 | |
| 
 | |
| func (c *ctxt0) sched(p0, pe *obj.Prog) {
 | |
| 	var sch [NSCHED]Sch
 | |
| 
 | |
| 	/*
 | |
| 	 * build side structure
 | |
| 	 */
 | |
| 	s := sch[:]
 | |
| 	for p := p0; ; p = p.Link {
 | |
| 		s[0].p = *p
 | |
| 		c.markregused(&s[0])
 | |
| 		if p == pe {
 | |
| 			break
 | |
| 		}
 | |
| 		s = s[1:]
 | |
| 	}
 | |
| 	se := s
 | |
| 
 | |
| 	for i := cap(sch) - cap(se); i >= 0; i-- {
 | |
| 		s = sch[i:]
 | |
| 		if s[0].p.Mark&DELAY == 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 		if -cap(s) < -cap(se) {
 | |
| 			if !conflict(&s[0], &s[1]) {
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		var t []Sch
 | |
| 		var j int
 | |
| 		for j = cap(sch) - cap(s) - 1; j >= 0; j-- {
 | |
| 			t = sch[j:]
 | |
| 			if t[0].comp {
 | |
| 				if s[0].p.Mark&BRANCH != 0 {
 | |
| 					continue
 | |
| 				}
 | |
| 			}
 | |
| 			if t[0].p.Mark&DELAY != 0 {
 | |
| 				if -cap(s) >= -cap(se) || conflict(&t[0], &s[1]) {
 | |
| 					continue
 | |
| 				}
 | |
| 			}
 | |
| 			for u := t[1:]; -cap(u) <= -cap(s); u = u[1:] {
 | |
| 				if c.depend(&u[0], &t[0]) {
 | |
| 					continue
 | |
| 				}
 | |
| 			}
 | |
| 			goto out2
 | |
| 		}
 | |
| 
 | |
| 		if s[0].p.Mark&BRANCH != 0 {
 | |
| 			s[0].nop = 1
 | |
| 		}
 | |
| 		continue
 | |
| 
 | |
| 	out2:
 | |
| 		// t[0] is the instruction being moved to fill the delay
 | |
| 		stmp := t[0]
 | |
| 		copy(t[:i-j], t[1:i-j+1])
 | |
| 		s[0] = stmp
 | |
| 
 | |
| 		if t[i-j-1].p.Mark&BRANCH != 0 {
 | |
| 			// t[i-j] is being put into a branch delay slot
 | |
| 			// combine its Spadj with the branch instruction
 | |
| 			t[i-j-1].p.Spadj += t[i-j].p.Spadj
 | |
| 			t[i-j].p.Spadj = 0
 | |
| 		}
 | |
| 
 | |
| 		i--
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * put it all back
 | |
| 	 */
 | |
| 	var p *obj.Prog
 | |
| 	var q *obj.Prog
 | |
| 	for s, p = sch[:], p0; -cap(s) <= -cap(se); s, p = s[1:], q {
 | |
| 		q = p.Link
 | |
| 		if q != s[0].p.Link {
 | |
| 			*p = s[0].p
 | |
| 			p.Link = q
 | |
| 		}
 | |
| 		for s[0].nop != 0 {
 | |
| 			s[0].nop--
 | |
| 			c.addnop(p)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (c *ctxt0) markregused(s *Sch) {
 | |
| 	p := &s.p
 | |
| 	s.comp = c.compound(p)
 | |
| 	s.nop = 0
 | |
| 	if s.comp {
 | |
| 		s.set.ireg |= 1 << (REGTMP - REG_R0)
 | |
| 		s.used.ireg |= 1 << (REGTMP - REG_R0)
 | |
| 	}
 | |
| 
 | |
| 	ar := 0  /* dest is really reference */
 | |
| 	ad := 0  /* source/dest is really address */
 | |
| 	ld := 0  /* opcode is load instruction */
 | |
| 	sz := 20 /* size of load/store for overlap computation */
 | |
| 
 | |
| 	/*
 | |
| 	 * flags based on opcode
 | |
| 	 */
 | |
| 	switch p.As {
 | |
| 	case obj.ATEXT:
 | |
| 		c.autosize = int32(p.To.Offset + 8)
 | |
| 		ad = 1
 | |
| 
 | |
| 	case AJAL:
 | |
| 		r := p.Reg
 | |
| 		if r == 0 {
 | |
| 			r = REGLINK
 | |
| 		}
 | |
| 		s.set.ireg |= 1 << uint(r-REG_R0)
 | |
| 		ar = 1
 | |
| 		ad = 1
 | |
| 
 | |
| 	case ABGEZAL,
 | |
| 		ABLTZAL:
 | |
| 		s.set.ireg |= 1 << (REGLINK - REG_R0)
 | |
| 		fallthrough
 | |
| 	case ABEQ,
 | |
| 		ABGEZ,
 | |
| 		ABGTZ,
 | |
| 		ABLEZ,
 | |
| 		ABLTZ,
 | |
| 		ABNE:
 | |
| 		ar = 1
 | |
| 		ad = 1
 | |
| 
 | |
| 	case ABFPT,
 | |
| 		ABFPF:
 | |
| 		ad = 1
 | |
| 		s.used.cc |= E_FCR
 | |
| 
 | |
| 	case ACMPEQD,
 | |
| 		ACMPEQF,
 | |
| 		ACMPGED,
 | |
| 		ACMPGEF,
 | |
| 		ACMPGTD,
 | |
| 		ACMPGTF:
 | |
| 		ar = 1
 | |
| 		s.set.cc |= E_FCR
 | |
| 		p.Mark |= FCMP
 | |
| 
 | |
| 	case AJMP:
 | |
| 		ar = 1
 | |
| 		ad = 1
 | |
| 
 | |
| 	case AMOVB,
 | |
| 		AMOVBU:
 | |
| 		sz = 1
 | |
| 		ld = 1
 | |
| 
 | |
| 	case AMOVH,
 | |
| 		AMOVHU:
 | |
| 		sz = 2
 | |
| 		ld = 1
 | |
| 
 | |
| 	case AMOVF,
 | |
| 		AMOVW,
 | |
| 		AMOVWL,
 | |
| 		AMOVWR:
 | |
| 		sz = 4
 | |
| 		ld = 1
 | |
| 
 | |
| 	case AMOVD,
 | |
| 		AMOVV,
 | |
| 		AMOVVL,
 | |
| 		AMOVVR:
 | |
| 		sz = 8
 | |
| 		ld = 1
 | |
| 
 | |
| 	case ADIV,
 | |
| 		ADIVU,
 | |
| 		AMUL,
 | |
| 		AMULU,
 | |
| 		AREM,
 | |
| 		AREMU,
 | |
| 		ADIVV,
 | |
| 		ADIVVU,
 | |
| 		AMULV,
 | |
| 		AMULVU,
 | |
| 		AREMV,
 | |
| 		AREMVU:
 | |
| 		s.set.cc = E_HILO
 | |
| 		fallthrough
 | |
| 	case AADD,
 | |
| 		AADDU,
 | |
| 		AADDV,
 | |
| 		AADDVU,
 | |
| 		AAND,
 | |
| 		ANOR,
 | |
| 		AOR,
 | |
| 		ASGT,
 | |
| 		ASGTU,
 | |
| 		ASLL,
 | |
| 		ASRA,
 | |
| 		ASRL,
 | |
| 		ASLLV,
 | |
| 		ASRAV,
 | |
| 		ASRLV,
 | |
| 		ASUB,
 | |
| 		ASUBU,
 | |
| 		ASUBV,
 | |
| 		ASUBVU,
 | |
| 		AXOR,
 | |
| 
 | |
| 		AADDD,
 | |
| 		AADDF,
 | |
| 		AADDW,
 | |
| 		ASUBD,
 | |
| 		ASUBF,
 | |
| 		ASUBW,
 | |
| 		AMULF,
 | |
| 		AMULD,
 | |
| 		AMULW,
 | |
| 		ADIVF,
 | |
| 		ADIVD,
 | |
| 		ADIVW:
 | |
| 		if p.Reg == 0 {
 | |
| 			if p.To.Type == obj.TYPE_REG {
 | |
| 				p.Reg = p.To.Reg
 | |
| 			}
 | |
| 			//if(p->reg == NREG)
 | |
| 			//	print("botch %P\n", p);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * flags based on 'to' field
 | |
| 	 */
 | |
| 	cls := int(p.To.Class)
 | |
| 	if cls == 0 {
 | |
| 		cls = c.aclass(&p.To) + 1
 | |
| 		p.To.Class = int8(cls)
 | |
| 	}
 | |
| 	cls--
 | |
| 	switch cls {
 | |
| 	default:
 | |
| 		fmt.Printf("unknown class %d %v\n", cls, p)
 | |
| 
 | |
| 	case C_ZCON,
 | |
| 		C_SCON,
 | |
| 		C_ADD0CON,
 | |
| 		C_AND0CON,
 | |
| 		C_ADDCON,
 | |
| 		C_ANDCON,
 | |
| 		C_UCON,
 | |
| 		C_LCON,
 | |
| 		C_NONE,
 | |
| 		C_SBRA,
 | |
| 		C_LBRA,
 | |
| 		C_ADDR,
 | |
| 		C_TEXTSIZE:
 | |
| 		break
 | |
| 
 | |
| 	case C_HI,
 | |
| 		C_LO:
 | |
| 		s.set.cc |= E_HILO
 | |
| 
 | |
| 	case C_FCREG:
 | |
| 		s.set.cc |= E_FCR
 | |
| 
 | |
| 	case C_MREG:
 | |
| 		s.set.cc |= E_MCR
 | |
| 
 | |
| 	case C_ZOREG,
 | |
| 		C_SOREG,
 | |
| 		C_LOREG:
 | |
| 		cls = int(p.To.Reg)
 | |
| 		s.used.ireg |= 1 << uint(cls-REG_R0)
 | |
| 		if ad != 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		s.size = uint8(sz)
 | |
| 		s.soffset = c.regoff(&p.To)
 | |
| 
 | |
| 		m := uint32(ANYMEM)
 | |
| 		if cls == REGSB {
 | |
| 			m = E_MEMSB
 | |
| 		}
 | |
| 		if cls == REGSP {
 | |
| 			m = E_MEMSP
 | |
| 		}
 | |
| 
 | |
| 		if ar != 0 {
 | |
| 			s.used.cc |= m
 | |
| 		} else {
 | |
| 			s.set.cc |= m
 | |
| 		}
 | |
| 
 | |
| 	case C_SACON,
 | |
| 		C_LACON:
 | |
| 		s.used.ireg |= 1 << (REGSP - REG_R0)
 | |
| 
 | |
| 	case C_SECON,
 | |
| 		C_LECON:
 | |
| 		s.used.ireg |= 1 << (REGSB - REG_R0)
 | |
| 
 | |
| 	case C_REG:
 | |
| 		if ar != 0 {
 | |
| 			s.used.ireg |= 1 << uint(p.To.Reg-REG_R0)
 | |
| 		} else {
 | |
| 			s.set.ireg |= 1 << uint(p.To.Reg-REG_R0)
 | |
| 		}
 | |
| 
 | |
| 	case C_FREG:
 | |
| 		if ar != 0 {
 | |
| 			s.used.freg |= 1 << uint(p.To.Reg-REG_F0)
 | |
| 		} else {
 | |
| 			s.set.freg |= 1 << uint(p.To.Reg-REG_F0)
 | |
| 		}
 | |
| 		if ld != 0 && p.From.Type == obj.TYPE_REG {
 | |
| 			p.Mark |= LOAD
 | |
| 		}
 | |
| 
 | |
| 	case C_SAUTO,
 | |
| 		C_LAUTO:
 | |
| 		s.used.ireg |= 1 << (REGSP - REG_R0)
 | |
| 		if ad != 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		s.size = uint8(sz)
 | |
| 		s.soffset = c.regoff(&p.To)
 | |
| 
 | |
| 		if ar != 0 {
 | |
| 			s.used.cc |= E_MEMSP
 | |
| 		} else {
 | |
| 			s.set.cc |= E_MEMSP
 | |
| 		}
 | |
| 
 | |
| 	case C_SEXT,
 | |
| 		C_LEXT:
 | |
| 		s.used.ireg |= 1 << (REGSB - REG_R0)
 | |
| 		if ad != 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		s.size = uint8(sz)
 | |
| 		s.soffset = c.regoff(&p.To)
 | |
| 
 | |
| 		if ar != 0 {
 | |
| 			s.used.cc |= E_MEMSB
 | |
| 		} else {
 | |
| 			s.set.cc |= E_MEMSB
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * flags based on 'from' field
 | |
| 	 */
 | |
| 	cls = int(p.From.Class)
 | |
| 	if cls == 0 {
 | |
| 		cls = c.aclass(&p.From) + 1
 | |
| 		p.From.Class = int8(cls)
 | |
| 	}
 | |
| 	cls--
 | |
| 	switch cls {
 | |
| 	default:
 | |
| 		fmt.Printf("unknown class %d %v\n", cls, p)
 | |
| 
 | |
| 	case C_ZCON,
 | |
| 		C_SCON,
 | |
| 		C_ADD0CON,
 | |
| 		C_AND0CON,
 | |
| 		C_ADDCON,
 | |
| 		C_ANDCON,
 | |
| 		C_UCON,
 | |
| 		C_LCON,
 | |
| 		C_NONE,
 | |
| 		C_SBRA,
 | |
| 		C_LBRA,
 | |
| 		C_ADDR,
 | |
| 		C_TEXTSIZE:
 | |
| 		break
 | |
| 
 | |
| 	case C_HI,
 | |
| 		C_LO:
 | |
| 		s.used.cc |= E_HILO
 | |
| 
 | |
| 	case C_FCREG:
 | |
| 		s.used.cc |= E_FCR
 | |
| 
 | |
| 	case C_MREG:
 | |
| 		s.used.cc |= E_MCR
 | |
| 
 | |
| 	case C_ZOREG,
 | |
| 		C_SOREG,
 | |
| 		C_LOREG:
 | |
| 		cls = int(p.From.Reg)
 | |
| 		s.used.ireg |= 1 << uint(cls-REG_R0)
 | |
| 		if ld != 0 {
 | |
| 			p.Mark |= LOAD
 | |
| 		}
 | |
| 		s.size = uint8(sz)
 | |
| 		s.soffset = c.regoff(&p.From)
 | |
| 
 | |
| 		m := uint32(ANYMEM)
 | |
| 		if cls == REGSB {
 | |
| 			m = E_MEMSB
 | |
| 		}
 | |
| 		if cls == REGSP {
 | |
| 			m = E_MEMSP
 | |
| 		}
 | |
| 
 | |
| 		s.used.cc |= m
 | |
| 
 | |
| 	case C_SACON,
 | |
| 		C_LACON:
 | |
| 		cls = int(p.From.Reg)
 | |
| 		if cls == 0 {
 | |
| 			cls = REGSP
 | |
| 		}
 | |
| 		s.used.ireg |= 1 << uint(cls-REG_R0)
 | |
| 
 | |
| 	case C_SECON,
 | |
| 		C_LECON:
 | |
| 		s.used.ireg |= 1 << (REGSB - REG_R0)
 | |
| 
 | |
| 	case C_REG:
 | |
| 		s.used.ireg |= 1 << uint(p.From.Reg-REG_R0)
 | |
| 
 | |
| 	case C_FREG:
 | |
| 		s.used.freg |= 1 << uint(p.From.Reg-REG_F0)
 | |
| 		if ld != 0 && p.To.Type == obj.TYPE_REG {
 | |
| 			p.Mark |= LOAD
 | |
| 		}
 | |
| 
 | |
| 	case C_SAUTO,
 | |
| 		C_LAUTO:
 | |
| 		s.used.ireg |= 1 << (REGSP - REG_R0)
 | |
| 		if ld != 0 {
 | |
| 			p.Mark |= LOAD
 | |
| 		}
 | |
| 		if ad != 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		s.size = uint8(sz)
 | |
| 		s.soffset = c.regoff(&p.From)
 | |
| 
 | |
| 		s.used.cc |= E_MEMSP
 | |
| 
 | |
| 	case C_SEXT:
 | |
| 	case C_LEXT:
 | |
| 		s.used.ireg |= 1 << (REGSB - REG_R0)
 | |
| 		if ld != 0 {
 | |
| 			p.Mark |= LOAD
 | |
| 		}
 | |
| 		if ad != 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		s.size = uint8(sz)
 | |
| 		s.soffset = c.regoff(&p.From)
 | |
| 
 | |
| 		s.used.cc |= E_MEMSB
 | |
| 	}
 | |
| 
 | |
| 	cls = int(p.Reg)
 | |
| 	if cls != 0 {
 | |
| 		if REG_F0 <= cls && cls <= REG_F31 {
 | |
| 			s.used.freg |= 1 << uint(cls-REG_F0)
 | |
| 		} else {
 | |
| 			s.used.ireg |= 1 << uint(cls-REG_R0)
 | |
| 		}
 | |
| 	}
 | |
| 	s.set.ireg &^= (1 << (REGZERO - REG_R0)) /* R0 can't be set */
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * test to see if two instructions can be
 | |
|  * interchanged without changing semantics
 | |
|  */
 | |
| func (c *ctxt0) depend(sa, sb *Sch) bool {
 | |
| 	if sa.set.ireg&(sb.set.ireg|sb.used.ireg) != 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 	if sb.set.ireg&sa.used.ireg != 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	if sa.set.freg&(sb.set.freg|sb.used.freg) != 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 	if sb.set.freg&sa.used.freg != 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * special case.
 | |
| 	 * loads from same address cannot pass.
 | |
| 	 * this is for hardware fifo's and the like
 | |
| 	 */
 | |
| 	if sa.used.cc&sb.used.cc&E_MEM != 0 {
 | |
| 		if sa.p.Reg == sb.p.Reg {
 | |
| 			if c.regoff(&sa.p.From) == c.regoff(&sb.p.From) {
 | |
| 				return true
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	x := (sa.set.cc & (sb.set.cc | sb.used.cc)) | (sb.set.cc & sa.used.cc)
 | |
| 	if x != 0 {
 | |
| 		/*
 | |
| 		 * allow SB and SP to pass each other.
 | |
| 		 * allow SB to pass SB iff doffsets are ok
 | |
| 		 * anything else conflicts
 | |
| 		 */
 | |
| 		if x != E_MEMSP && x != E_MEMSB {
 | |
| 			return true
 | |
| 		}
 | |
| 		x = sa.set.cc | sb.set.cc | sa.used.cc | sb.used.cc
 | |
| 		if x&E_MEM != 0 {
 | |
| 			return true
 | |
| 		}
 | |
| 		if offoverlap(sa, sb) {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func offoverlap(sa, sb *Sch) bool {
 | |
| 	if sa.soffset < sb.soffset {
 | |
| 		if sa.soffset+int32(sa.size) > sb.soffset {
 | |
| 			return true
 | |
| 		}
 | |
| 		return false
 | |
| 	}
 | |
| 	if sb.soffset+int32(sb.size) > sa.soffset {
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * test 2 adjacent instructions
 | |
|  * and find out if inserted instructions
 | |
|  * are desired to prevent stalls.
 | |
|  */
 | |
| func conflict(sa, sb *Sch) bool {
 | |
| 	if sa.set.ireg&sb.used.ireg != 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 	if sa.set.freg&sb.used.freg != 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 	if sa.set.cc&sb.used.cc != 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (c *ctxt0) compound(p *obj.Prog) bool {
 | |
| 	o := c.oplook(p)
 | |
| 	if o.size != 4 {
 | |
| 		return true
 | |
| 	}
 | |
| 	if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSB {
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| var Linkmips64 = obj.LinkArch{
 | |
| 	Arch:           sys.ArchMIPS64,
 | |
| 	Init:           buildop,
 | |
| 	Preprocess:     preprocess,
 | |
| 	Assemble:       span0,
 | |
| 	Progedit:       progedit,
 | |
| 	DWARFRegisters: MIPSDWARFRegisters,
 | |
| }
 | |
| 
 | |
| var Linkmips64le = obj.LinkArch{
 | |
| 	Arch:           sys.ArchMIPS64LE,
 | |
| 	Init:           buildop,
 | |
| 	Preprocess:     preprocess,
 | |
| 	Assemble:       span0,
 | |
| 	Progedit:       progedit,
 | |
| 	DWARFRegisters: MIPSDWARFRegisters,
 | |
| }
 | |
| 
 | |
| var Linkmips = obj.LinkArch{
 | |
| 	Arch:           sys.ArchMIPS,
 | |
| 	Init:           buildop,
 | |
| 	Preprocess:     preprocess,
 | |
| 	Assemble:       span0,
 | |
| 	Progedit:       progedit,
 | |
| 	DWARFRegisters: MIPSDWARFRegisters,
 | |
| }
 | |
| 
 | |
| var Linkmipsle = obj.LinkArch{
 | |
| 	Arch:           sys.ArchMIPSLE,
 | |
| 	Init:           buildop,
 | |
| 	Preprocess:     preprocess,
 | |
| 	Assemble:       span0,
 | |
| 	Progedit:       progedit,
 | |
| 	DWARFRegisters: MIPSDWARFRegisters,
 | |
| }
 |